/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.common.util.concurrent;

import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import com.thesett.common.error.NotImplementedException;

/**
 * Synchronous/Asynchronous puts. Asynchronous is easiest, just wait till can write to queue and deposit data.
 * Synchronous is harder. Deposit data, but then must wait until deposited element/elements are taken before being
 * allowed to unblock and continue. Consumer needs some options here too. Can just get the data from the buffer and
 * allow any producers unblocked as a result to continue, or can get data but continue blocking while the data is
 * processed before sending a message to do the unblocking. Synch/Asynch mode to be controlled by a switch.
 * Unblocking/not unblocking during consumer processing to be controlled by the consumers calls.
 *
 *
Implementing sub-classes only need to supply an implementation of a queue to produce a valid concrete
 * implementation of this. This queue is only accessed through the methods {@link #insert}, {@link #extract},
 * {@link #getBufferRemainingCapacity()}, {@link #peekAtBufferHead()}. An implementation can override these methods to
 * implement the buffer other than by a queue, for example, by using an array.
 *
 *
Normal queue methods to work asynchronously.
 *
 *
Put, take and drain methods from the BlockingQueue interface work synchronously but unlbock producers immediately
 * when their data is taken.
 *
 *
The additional put, take and drain methods from the BatchSynchQueue interface work synchronously and provide the
 * option to keep producers blocked until the consumer decides to release them.
 *
 *
Removed take method that keeps producers blocked as it is pointless. Essentially it reduces this class to
 * synchronous processing of individual data items, which negates the point of the hand-off design. The efficiency gain
 * of the hand off design comes in being able to batch consume requests, ammortizing latency (such as caused by io)
 * accross many producers. The only advantage of the single blocking take method is that it did take advantage of the
 * queue ordering, which ma be usefull, for example to apply a priority ordering amongst producers. This is also an
 * advantage over the java.util.concurrent.SynchronousQueue which doesn't have a backing queue which can be used to
 * apply orderings. If a single item take is really needed can just use the drainTo method with a maximum of one item.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *

 *
 * @author Rupert Smith
 * @todo   To create zero garbage collecting implemention will need to adapt the queue element containers (SynchRefImpl)
 *         in such a way that one is needed per array element, they can be taken from/put back/cleared in the queue
 *         without actually being moved from the array and they implement a way of forming them into a collection (or
 *         Iterable) to pass to consumers (using a linked list scheme?).
 */
public abstract class BatchSynchQueueBase<E> extends AbstractQueue<E> implements BatchSynchQueue<E>
{
    /** Used for logging. */
    /* private static final Logger log = Logger.getLogger(BatchSynchQueueBase.class.getName()); */

    /** Holds a reference to the queue implementation that holds the buffer. */
    Queue<SynchRecordImpl<E>> buffer;

    /** Holds the number of items in the queue. */
    private int count;

    /** Main lock guarding all access. */
    private ReentrantLock lock;

    /** Condition for waiting takes. */
    private Condition notEmpty;

    /** Condition for waiting puts. */
    private Condition notFull;

    /** Creates a batch synch queue without fair thread scheduling. */
    public BatchSynchQueueBase()
    {
        this(false);
    }

    /**
     * Ensures that the underlying buffer implementation is created.
     *
     * @param fair <tt>true</tt> if fairness is to be applied to threads waiting to access the buffer.
     */
    public BatchSynchQueueBase(boolean fair)
    {
        buffer = this.createQueue();

        // Create the buffer lock with the fairness flag set accordingly.
        lock = new ReentrantLock(fair);

        // Create the non-empty and non-full condition monitors on the buffer lock.
        notEmpty = lock.newCondition();
        notFull = lock.newCondition();
    }

    /**
     * Returns an iterator over the elements contained in this collection.
     *
     * @return An iterator over the elements contained in this collection.
     */
    public Iterator<E> iterator()
    {
        throw new NotImplementedException();
    }

    /**
     * Returns the number of elements in this collection. If the collection contains more than <tt>
     * Integer.MAX_VALUE</tt> elements, returns <tt>Integer.MAX_VALUE</tt>.
     *
     * @return The number of elements in this collection.
     */
    public int size()
    {
        ReentrantLock lock = this.lock;
        lock.lock();

        try
        {
            return count;
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * Inserts the specified element into this queue, if possible. When using queues that may impose insertion
     * restrictions (for example capacity bounds), method <tt>offer</tt> is generally preferable to method
     * {@link java.util.Collection#add}, which can fail to insert an element only by throwing an exception.
     *
     * @param  e The element to insert, may not be null.
     *
     * @return <tt>true</tt> if it was possible to add the element to this queue, else <tt>false</tt>
     *
     * @throws IllegalArgumentException If the specified element is <tt>null</tt>.
     */
    public boolean offer(E e)
    {
        if (e == null)
        {
            throw new IllegalArgumentException("The 'e' parameter may not be null.");
        }

        ReentrantLock lock = this.lock;
        lock.lock();

        try
        {
            return insert(e, false);
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * Inserts the specified element into this queue, waiting if necessary up to the specified wait time for space to
     * become available.
     *
     * @param  e       The element to add, may not be null.
     * @param  timeout How long to wait before giving up, in units of <tt>unit</tt>
     * @param  unit    A <tt>TimeUnit</tt> determining how to interpret the <tt>timeout</tt> parameter.
     *
     * @return <tt>true</tt> if successful, or <tt>false</tt> if the specified waiting time elapses before space is
     *         available.
     *
     * @throws InterruptedException     If interrupted while waiting.
     * @throws IllegalArgumentException If the specified element is <tt>null</tt>.
     */
    public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException
    {
        if (e == null)
        {
            throw new IllegalArgumentException("The 'e' parameter may not be null.");
        }

        ReentrantLock lock = this.lock;
        lock.lockInterruptibly();

        long nanos = unit.toNanos(timeout);

        try
        {
            do
            {
                if (insert(e, false))
                {
                    return true;
                }

                try
                {
                    nanos = notFull.awaitNanos(nanos);
                }
                catch (InterruptedException ie)
                {
                    // Wake up another thread waiting on notFull, as the condition may be true, but this thread
                    // was interrupted so cannot make use of it.
                    notFull.signal();

                    throw ie;
                }
            }
            while (nanos > 0);

            return false;
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * Retrieves and removes the head of this queue, or <tt>null</tt> if this queue is empty.
     *
     * @return The head of this queue, or <tt>null</tt> if this queue is empty.
     */
    public E poll()
    {
        ReentrantLock lock = this.lock;

        lock.lock();

        try
        {
            if (count == 0)
            {
                return null;
            }

            return extract(true, true).getElement();
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * Retrieves and removes the head of this queue, waiting if necessary up to the specified wait time if no elements
     * are present on this queue.
     *
     * @param  timeout How long to wait before giving up, in units of <tt>unit</tt>.
     * @param  unit    A <tt>TimeUnit</tt> determining how to interpret the <tt>timeout</tt> parameter.
     *
     * @return The head of this queue, or <tt>null</tt> if the specified waiting time elapses before an element is
     *         present.
     *
     * @throws InterruptedException If interrupted while waiting.
     */
    public E poll(long timeout, TimeUnit unit) throws InterruptedException
    {
        ReentrantLock lock = this.lock;
        lock.lockInterruptibly();

        try
        {
            long nanos = unit.toNanos(timeout);

            do
            {
                if (count != 0)
                {
                    return extract(true, true).getElement();
                }

                try
                {
                    nanos = notEmpty.awaitNanos(nanos);
                }
                catch (InterruptedException ie)
                {
                    notEmpty.signal(); // propagate to non-interrupted thread
                    throw ie;
                }
            }
            while (nanos > 0);

            return null;
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * Retrieves, but does not remove, the head of this queue, returning <tt>null</tt> if this queue is empty.
     *
     * @return The head of this queue, or <tt>null</tt> if this queue is empty.
     */
    public E peek()
    {
        ReentrantLock lock = this.lock;
        lock.lock();

        try
        {
            return peekAtBufferHead();
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * Returns the number of elements that this queue can ideally (in the absence of memory or resource constraints)
     * accept without blocking, or <tt>Integer.MAX_VALUE</tt> if there is no intrinsic limit.
     *
     *
Note that you cannot always tell if an attempt to <tt>add</tt> an element will succeed by inspecting
     * <tt>remainingCapacity</tt> because it may be the case that another thread is about to <tt>put</tt> or <tt>
     * take</tt> an element.
     *
     * @return The remaining capacity.
     */
    public int remainingCapacity()
    {
        ReentrantLock lock = this.lock;
        lock.lock();

        try
        {
            return getBufferRemainingCapacity();
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * Adds the specified element to this queue, waiting if necessary for space to become available.
     *
     *
This method delegated to {@link #tryPut} which can raise {@link SynchException}s. If any are raised this
     * method silently ignores them. Use the {@link #tryPut} method directly if you want to catch these exceptions.
     *
     * @param  e The element to add.
     *
     * @throws InterruptedException If interrupted while waiting.
     */
    public void put(E e) throws InterruptedException
    {
        try
        {
            tryPut(e);
        }
        catch (SynchException ex)
        {
            // This exception is deliberately ignored. See the method comment for information about this.
            ex = null;
        }
    }

    /**
     * Tries a synchronous put into the queue. If a consumer encounters an exception condition whilst processing the
     * data that is put, then this is returned to the caller wrapped inside a {@link SynchException}.
     *
     * @param  e The data element to put into the queue, may not be null.
     *
     * @throws InterruptedException     If the thread is interrupted whilst waiting to write to the queue or whilst
     *                                  waiting on its entry in the queue being consumed.
     * @throws SynchException           If a consumer encounters an error whilst processing the data element.
     * @throws IllegalArgumentException If the specified element is <tt>null</tt>.
     */
    public void tryPut(E e) throws InterruptedException, SynchException
    {
        if (e == null)
        {
            throw new IllegalArgumentException("The 'e' parameter may not be null.");
        }

        // final Queue<E> items = this.buffer;
        ReentrantLock lock = this.lock;
        lock.lockInterruptibly();

        try
        {
            while (getBufferRemainingCapacity() == 0)
            {
                // Release the lock and wait until the queue is not full.
                notFull.await();
            }
        }
        catch (InterruptedException ie)
        {
            notFull.signal(); // propagate to non-interrupted thread
            throw ie;
        }

        // There is room in the queue so insert must succeed. Insert into the queu, release the lock and block
        // the producer until its data is taken.
        insert(e, true);
    }

    /**
     * Retrieves and removes the head of this queue, waiting if no elements are present on this queue. Any producer that
     * has its data element taken by this call will be immediately unblocked. To keep the producer blocked whilst taking
     * just a single item, use the {@link #drainTo(java.util.Collection< SynchRecord <E>>, int, boolean)} method. There
     * is no take method to do that because there is not usually any advantage in a synchronous hand off design that
     * consumes data one item at a time. It is normal to consume data in chunks to ammortize consumption latencies
     * accross many producers where possible.
     *
     * @return The head of this queue.
     *
     * @throws InterruptedException if interrupted while waiting.
     */
    public E take() throws InterruptedException
    {
        ReentrantLock lock = this.lock;
        lock.lockInterruptibly();

        try
        {
            try
            {
                while (count == 0)
                {
                    // Release the lock and wait until the queue becomes non-empty.
                    notEmpty.await();
                }
            }
            catch (InterruptedException ie)
            {
                notEmpty.signal(); // propagate to non-interrupted thread
                throw ie;
            }

            // There is data in the queue so extraction must succeed. Notify any waiting threads that the queue is
            // not full, and unblock the producer that owns the data item that is taken.
            return extract(true, true).getElement();
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * Removes all available elements from this queue and adds them into the given collection. This operation may be
     * more efficient than repeatedly polling this queue. A failure encountered while attempting to <tt>add</tt>
     * elements to collection <tt>c</tt> may result in elements being in neither, either or both collections when the
     * associated exception is thrown. Attempts to drain a queue to itself result in <tt>IllegalArgumentException</tt>.
     * Further, the behavior of this operation is undefined if the specified collection is modified while the operation
     * is in progress.
     *
     * @param  objects The collection to transfer elements into.
     *
     * @return The number of elements transferred.
     *
     * @throws NullPointerException     If objects is null.
     * @throws IllegalArgumentException If objects is this queue.
     */
    public int drainTo(Collection<? super E> objects)
    {
        return drainTo(objects, -1);
    }

    /**
     * Removes at most the given number of available elements from this queue and adds them into the given collection. A
     * failure encountered while attempting to <tt>add</tt> elements to collection <tt>c</tt> may result in elements
     * being in neither, either or both collections when the associated exception is thrown. Attempts to drain a queue
     * to itself result in <tt>IllegalArgumentException</tt>. Further, the behavior of this operation is undefined if
     * the specified collection is modified while the operation is in progress.
     *
     * @param  collection  The collection to transfer elements into.
     * @param  maxElements The maximum number of elements to transfer. If this is -1 then that is interpreted as meaning
     *                     all elements.
     *
     * @return The number of elements transferred.
     *
     * @throws IllegalArgumentException If collection is this queue or null.
     */
    public int drainTo(Collection<? super E> collection, int maxElements)
    {
        if (collection == null)
        {
            throw new IllegalArgumentException("The 'collection' parameter may not be null.");
        }

        if (collection == this)
        {
            throw new IllegalArgumentException("The 'collection' parameter may not be this object.");
        }

        // final Queue<E> items = this.buffer;
        ReentrantLock lock = this.lock;
        lock.lock();

        try
        {
            int n = 0;

            for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++)
            {
                // Take items from the queue, do unblock the producers, but don't send not full signals yet.
                collection.add(extract(true, false).getElement());
            }

            if (n > 0)
            {
                // count -= n;
                notFull.signalAll();
            }

            return n;
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * Takes all available data items from the queue or blocks until some become available. The returned items are
     * wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their producers,
     * where the producers are still blocked.
     *
     * @param  c       The collection to drain the data items into.
     * @param  unblock If set to <tt>true</tt> the producers for the taken items will be immediately unblocked.
     *
     * @return A count of the number of elements that were drained from the queue.
     */
    public SynchRef drainTo(Collection<SynchRecord<E>> c, boolean unblock)
    {
        return drainTo(c, -1, unblock);
    }

    /**
     * Takes up to maxElements available data items from the queue or blocks until some become available. The returned
     * items are wrapped in a {@link SynchRecord} which provides an interface to requeue them or send errors to their
     * producers, where the producers are still blocked.
     *
     * @param  collection  The collection to drain the data items into.
     * @param  maxElements The maximum number of elements to drain.
     * @param  unblock     If set to <tt>true</tt> the producers for the taken items will be immediately unblocked.
     *
     * @return A count of the number of elements that were drained from the queue.
     *
     * @throws IllegalArgumentException If collection is this queue or null.
     */
    public SynchRef drainTo(Collection<SynchRecord<E>> collection, int maxElements, boolean unblock)
    {
        if (collection == null)
        {
            throw new IllegalArgumentException("The 'collection' parameter may not be null.");
        }

        if (collection == this)
        {
            throw new IllegalArgumentException("The 'collection' parameter may not be this object.");
        }

        // final Queue<E> items = this.buffer;
        ReentrantLock lock = this.lock;
        lock.lock();

        try
        {
            int n = 0;

            for (int max = ((maxElements >= count) || (maxElements < 0)) ? count : maxElements; n < max; n++)
            {
                // Extract the next record from the queue, don't signal the not full condition yet and release
                // producers depending on whether the caller wants to or not.
                collection.add(extract(false, unblock));
            }

            if (n > 0)
            {
                // count -= n;
                notFull.signalAll();
            }

            return new SynchRefImpl(n, collection);
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * This abstract method should be overriden to return an empty queue. Different implementations of producer consumer
     * buffers can control the order in which data is accessed using different queue implementations. This method allows
     * the type of queue to be abstracted out of this class and to be supplied by concrete implementations.
     *
     * @param  <T> The type of queue element of the queue to create.
     *
     * @return An empty queue.
     */
    protected abstract <T> Queue<T> createQueue();

    /**
     * Insert element into the queue, then possibly signal that the queue is not empty and block the producer on the
     * element until permission to procede is given.
     *
     *
If the producer is to be blocked then the lock must be released first, otherwise no other process will be
     * able to get access to the queue. Hence, unlock and block are always set together.
     *
     *
Call only when holding the global lock.
     *
     * @param  element        The element to insert in the queue.
     * @param  unlockAndBlock <tt>true</tt>If the global queue lock should be released and the producer should be
     *                        blocked.
     *
     * @return <tt>true</tt> if the operation succeeded, <tt>false</tt> otherwise. If the result is <tt>true</tt> this
     *         method may not return straight away, but only after the producer is unblocked by having its data consumed
     *         if the unlockAndBlock flag is set. In the false case the method will return straight away, no matter what
     *         value the unlockAndBlock flag has, leaving the global lock on.
     */
    protected boolean insert(E element, boolean unlockAndBlock)
    {
        // Create a new record for the data item.
        SynchRecordImpl<E> record = new SynchRecordImpl<E>(element);

        boolean result = buffer.offer(record);

        if (result)
        {
            count++;

            // Tell any waiting consumers that the queue is not empty.
            notEmpty.signal();

            if (unlockAndBlock)
            {
                // Allow other threads to read/write the queue.
                lock.unlock();

                // Wait until a consumer takes this data item.
                record.waitForConsumer();
            }

            return true;
        }
        else
        {
            return false;
        }
    }

    /**
     * Removes an element from the buffer, and optionally unblocks the producer of the element, if it is waiting, and
     * optionally signals that the {@link #notFull} condition may now be true.
     *
     *
Warning: This method must only be called when holding the main {@link #lock}.
     *
     * @param  unblock <tt>true</tt> if the elements producer should be released.
     * @param  signal  <tt>trye</tt> if the {@link #notFull} condition should be signalled.
     *
     * @return The synch record for the extracted item.
     */
    protected SynchRecordImpl<E> extract(boolean unblock, boolean signal)
    {
        SynchRecordImpl<E> result = buffer.remove();
        count--;

        if (signal)
        {
            notFull.signal();
        }

        if (unblock)
        {
            result.releaseImmediately();
        }

        return result;
    }

    /**
     * Get the capacity of the buffer. If the buffer has no maximum capacity then Integer.MAX_VALUE is returned.
     *
     *
Call only when holding lock.
     *
     * @return The maximum capacity of the buffer.
     */
    protected int getBufferRemainingCapacity()
    {
        if (buffer instanceof Capacity)
        {
            return ((Capacity) buffer).remainingCapacity();
        }
        else
        {
            return Integer.MAX_VALUE;
        }
    }

    /**
     * Return the head element from the buffer.
     *
     *
Call only when holding lock.
     *
     * @return The head element from the buffer.
     */
    protected E peekAtBufferHead()
    {
        return buffer.peek().getElement();
    }

    /**
     * A SynchRef is an interface which is returned from the synchronous take and drain methods. It allows the consumer
     * to communicate when it wants producers that have had their data taken to be unblocked.
     *
     *

CRC Card

    *
ResponsibilitiesCollaborations
     *
     *

     * Provide the number of records in this reference.
     *
     *

     * Allow all of the producers of the elements in this reference to be unblocked.
     *  {@link SynchRecord}
     *

     */
    public class SynchRefImpl implements SynchRef
    {
        /** Holds the number of synch records associated with this reference. */
        int numRecords;

        /** Holds a reference to the collection of synch records managed by this. */
        Collection<SynchRecord<E>> records;

        /**
         * Creates a new reference on the specified collection of records.
         *
         * @param n       The number of elements in the collection.
         * @param records The collection of records to wrap as a reference.
         */
        public SynchRefImpl(int n, Collection<SynchRecord<E>> records)
        {
            this.numRecords = n;
            this.records = records;
        }

        /**
         * Provides the number of records associated with this reference.
         *
         * @return The number of records associated with this reference.
         */
        public int getNumRecords()
        {
            return numRecords;
        }

        /**
         * Any producers that have had their data elements taken from the queue but have not been unblocked are
         * unblocked when this method is called. The exception to this is producers that have had their data put back
         * onto the queue by a consumer. Producers that have had exceptions for their data items registered by consumers
         * will be unblocked but will not return from their put call normally, but with an exception instead.
         */
        public void unblockProducers()
        {
            /*log.fine("public void unblockProducers(): called");*/

            if (records != null)
            {
                for (SynchRecord<E> record : records)
                {
                    // This call takes account of items that have already been released, are to be requeued or are in
                    // error.
                    record.releaseImmediately();
                }
            }

            records = null;
        }
    }

    /**
     * A SynchRecordImpl is used by a {@link BatchSynchQueue} to pair together a producer with its data. This allows the
     * producer of data to be identified so that it can be unblocked when its data is consumed or sent errors when its
     * data cannot be consumed.
     */
    public class SynchRecordImpl<E> implements SynchRecord<E>
    {
        /** A boolean latch that determines when the producer for this data item will be allowed to continue. */
        BooleanLatch latch = new BooleanLatch();

        /** The data element associated with this item. */
        E element;

        /**
         * Create a new synch record.
         *
         * @param e The data element that the record encapsulates.
         */
        public SynchRecordImpl(E e)
        {
            // Keep the data element.
            element = e;
        }

        /** Waits until the producer is given permission to proceded by a consumer. */
        public void waitForConsumer()
        {
            latch.await();
        }

        /**
         * Gets the data element contained by this record.
         *
         * @return The data element contained by this record.
         */
        public E getElement()
        {
            return element;
        }

        /**
         * Immediately releases the producer of this data record. Consumers can bring the synchronization time of
         * producers to a minimum by using this method to release them at the earliest possible moment when batch
         * consuming records from sychronized producers.
         */
        public void releaseImmediately()
        {
            // Check that the record has not already been released, is in error or is to be requeued.
            latch.signal();

            // Propagate errors to the producer.

            // Requeue items to be requeued.
        }

        /**
         * Tells the synch queue to put this element back onto the queue instead of releasing its producer. The element
         * is not requeued immediately but upon calling the {@link SynchRef#unblockProducers()} method or the
         * {@link #releaseImmediately()} method.
         *
         *
This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this
         * element has already been unblocked.
         */
        public void reQueue()
        {
            throw new NotImplementedException();
        }

        /**
         * Tells the synch queue to raise an exception with this elements producer. The exception is not raised
         * immediately but upon calling the {@link SynchRef#unblockProducers()} method or the
         * {@link #releaseImmediately()} method. The exception will be wrapped in a {@link SynchException} before it is
         * raised on the producer.
         *
         *
This method is unusual in that it accepts an exception as an argument. This is non-standard but is used
         * because the exception is to be passed onto a different thread.
         *
         *
This method will raise a runtime exception {@link AlreadyUnblockedException} if the producer for this
         * element has already been unblocked.
         *
         * @param e The exception to raise on the producer.
         */
        public void inError(Exception e)
        {
            throw new NotImplementedException();
        }
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.common.util.distributed;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.Iterator;

/**
 * The client portion of the distributed iterator support. This class implements the {@link Iterator} interface for a
 * distributed iterator. Using distributed iterators, you can ship a collection across the network one element at a
 * time, thus transmitting only the data required by the application. Furthermore, by avoiding transmitting the entire
 * collection, you enable access to the initial elements of the collection quicker than would be possible through raw
 * serialization of a collection.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Provide client proxy to remote iterator.
 *  {@link DistributedIterator}
 *

 *
 * @author Rupert Smith
 */
public class ClientIterator implements Iterator, Serializable
{
    /**
     * The remote iterator to which this client is referencing.
     *
     * @serial
     */
    private DistributedIterator source;

    /** Required constructor for serialization. */
    public ClientIterator()
    {
        super();
    }

    /**
     * Constructs a new ClientIterator using the named DistributedIterator as its remote source.
     *
     * @param src The server-based distributed iterator.
     */
    public ClientIterator(DistributedIterator src)
    {
        super();
        source = src;
    }

    /**
     * Check if more elements are available in this iterator.
     *
     * @return True if more elements are available in the iterator.
     */
    public boolean hasNext()
    {
        try
        {
            return source.hasNext();
        }
        catch (RemoteException e)
        {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Fetches the next element from this iterator.
     *
     * @return The next element in the iterator.
     */
    public Object next()
    {
        try
        {
            Object ob = source.next();

            return ob;
        }
        catch (RemoteException e)
        {
            throw new IllegalStateException(e);
        }
    }

    /** Removes the current element from this iterator if the underlying remote iterator supports it. */
    public void remove()
    {
        try
        {
            source.remove();
        }
        catch (RemoteException e)
        {
            throw new IllegalStateException(e);
        }
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.common.properties;

import java.util.Properties;

/**
 * ParsedProperties extends the basic Properties class with methods to extract properties, not as strings but as strings
 * parsed into basic types.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Wrap a properties object as parsed properties.
 *
Set properties from basic Java types.
 *
Extract properties to basic Java types.
 *
Allow properties to be set, only if they are not already set.
 *

 *
 * @author Rupert Smith
 */
public class ParsedProperties extends Properties
{
    /** Creates an empty ParsedProperties. */
    public ParsedProperties()
    {
        super();
    }

    /**
     * Creates a ParsedProperties initialized with the specified properties.
     *
     * @param props The properties to initialize this with.
     */
    public ParsedProperties(Properties props)
    {
        super(props);
    }

    /**
     * Helper method for setting system properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public static boolean setSysPropertyIfNull(String propname, boolean value)
    {
        return Boolean.parseBoolean(setSysPropertyIfNull(propname, Boolean.toString(value)));
    }

    /**
     * Helper method for setting system properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public static short setSysPropertyIfNull(String propname, short value)
    {
        return Short.parseShort(setSysPropertyIfNull(propname, Short.toString(value)));
    }

    /**
     * Helper method for setting system properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public static int setSysPropertyIfNull(String propname, int value)
    {
        return Integer.parseInt(setSysPropertyIfNull(propname, Integer.toString(value)));
    }

    /**
     * Helper method for setting system properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public static long setSysPropertyIfNull(String propname, long value)
    {
        return Long.parseLong(setSysPropertyIfNull(propname, Long.toString(value)));
    }

    /**
     * Helper method for setting system properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public static float setSysPropertyIfNull(String propname, float value)
    {
        return Float.parseFloat(setSysPropertyIfNull(propname, Float.toString(value)));
    }

    /**
     * Helper method for setting system properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public static double setSysPropertyIfNull(String propname, double value)
    {
        return Double.parseDouble(setSysPropertyIfNull(propname, Double.toString(value)));
    }

    /**
     * Helper method for setting system properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the system property after this method call.
     */
    public static String setSysPropertyIfNull(String propname, String value)
    {
        String property = System.getProperty(propname);

        if (property == null)
        {
            System.setProperty(propname, value);

            return value;
        }
        else
        {
            return property;
        }
    }

    /**
     * Helper method for setting properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public boolean setPropertyIfNull(String propname, boolean value)
    {
        return Boolean.parseBoolean(setPropertyIfNull(propname, Boolean.toString(value)));
    }

    /**
     * Helper method for setting properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public short setPropertyIfNull(String propname, short value)
    {
        return Short.parseShort(setPropertyIfNull(propname, Short.toString(value)));
    }

    /**
     * Helper method for setting properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public int setPropertyIfNull(String propname, int value)
    {
        return Integer.parseInt(setPropertyIfNull(propname, Integer.toString(value)));
    }

    /**
     * Helper method for setting properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public long setPropertyIfNull(String propname, long value)
    {
        return Long.parseLong(setPropertyIfNull(propname, Long.toString(value)));
    }

    /**
     * Helper method for setting properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public float setPropertyIfNull(String propname, float value)
    {
        return Float.parseFloat(setPropertyIfNull(propname, Float.toString(value)));
    }

    /**
     * Helper method for setting properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public double setPropertyIfNull(String propname, double value)
    {
        return Double.parseDouble(setPropertyIfNull(propname, Double.toString(value)));
    }

    /**
     * Helper method for setting properties to defaults when they are not already set.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public String setPropertyIfNull(String propname, String value)
    {
        String property = super.getProperty(propname);

        if (property == null)
        {
            super.setProperty(propname, value);

            return value;
        }
        else
        {
            return property;
        }
    }

    /**
     * Helper method for setting properties.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public boolean setProperty(String propname, boolean value)
    {
        setProperty(propname, Boolean.toString(value));

        return value;
    }

    /**
     * Helper method for setting properties.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public short setProperty(String propname, short value)
    {
        setProperty(propname, Short.toString(value));

        return value;
    }

    /**
     * Helper method for setting properties.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public int setProperty(String propname, int value)
    {
        setProperty(propname, Integer.toString(value));

        return value;
    }

    /**
     * Helper method for setting properties.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public long setProperty(String propname, long value)
    {
        setProperty(propname, Long.toString(value));

        return value;
    }

    /**
     * Helper method for setting properties.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public float setProperty(String propname, float value)
    {
        setProperty(propname, Float.toString(value));

        return value;
    }

    /**
     * Helper method for setting properties.
     *
     * @param  propname The name of the system property to set.
     * @param  value    The value to set it to.
     *
     * @return The value of the property after this method call.
     */
    public double setProperty(String propname, double value)
    {
        setProperty(propname, Double.toString(value));

        return value;
    }

    /**
     * Parses a property as a boolean.
     *
     * @param  propName The property.
     *
     * @return The property as a boolean, or false if it does not exist.
     */
    public boolean getPropertyAsBoolean(String propName)
    {
        String prop = getProperty(propName);

        return (prop != null) && Boolean.parseBoolean(prop);
    }

    /**
     * Parses a property as an integer.
     *
     * @param  propName The property.
     *
     * @return The property as a integer, or null if it does not exist.
     */
    public Integer getPropertyAsInteger(String propName)
    {
        String prop = getProperty(propName);

        return (prop != null) ? Integer.valueOf(prop) : null;
    }

    /**
     * Parses a property as a long.
     *
     * @param  propName The property.
     *
     * @return The property as a long, or null if it does not exist.
     */
    public Long getPropertyAsLong(String propName)
    {
        String prop = getProperty(propName);

        return (prop != null) ? Long.valueOf(prop) : null;
    }
}


/**
 * Copyright 2010 Bazaarvoice, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author J. Ryan Stinnett (ryan.stinnett@bazaarvoice.com)
 */

package com.bazaarvoice.jless.ast.node;

import com.bazaarvoice.jless.ast.util.NodeTreeUtils;
import com.bazaarvoice.jless.ast.visitor.InclusiveNodeVisitor;
import com.bazaarvoice.jless.ast.visitor.NodeAdditionVisitor;
import com.bazaarvoice.jless.ast.visitor.NodeNavigationVisitor;
import com.bazaarvoice.jless.exception.IllegalMixinArgumentException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ScopeNode extends InternalNode {

    private static final String FILTER_PROPERTY = "filter";

    private Map<String, ExpressionGroupNode> _variableNameToValueMap = new HashMap<String, ExpressionGroupNode>();
    private Map<String, RuleSetNode> _selectorToRuleSetMap = new HashMap<String, RuleSetNode>();
    private List<VariableDefinitionNode> _parameterDefinitions = new ArrayList<VariableDefinitionNode>();
    private Map<String, PropertyNode> _propertyNameToNodeMap = new HashMap<String, PropertyNode>();
    private ScopeNode _parentScope;
    private boolean _bracketsDisplayed = true;

    public ScopeNode() {
        super();
        setAdditionVisitor();
    }

    public ScopeNode(Node node) {
        this();
        addChild(node);
    }

    public ScopeNode(Collection<Node> nodeCollection) {
        this();
        addChildren(nodeCollection);
    }

    public boolean isVariableDefined(String name) {
        return _variableNameToValueMap.containsKey(name);
    }

    public ExpressionGroupNode getVariable(String name) {
        return _variableNameToValueMap.get(name);
    }

    public RuleSetNode getRuleSet(String selectorGroup) {
        return _selectorToRuleSetMap.get(selectorGroup);
    }

    public ScopeNode getParentScope() {
        return _parentScope;
    }

    public void setParentScope(ScopeNode parentScope) {
        _parentScope = parentScope;
    }

    public boolean isBracketsDisplayed() {
        return _bracketsDisplayed;
    }

    public void setBracketsDisplayed(boolean bracketsDisplayed) {
        _bracketsDisplayed = bracketsDisplayed;
    }

    /**
     * Creates a clone of this scope to be attached to the tree at the site of a mixin reference. If an ArgumentsNode is passed,
     * each of its values override those defined by the mixin's parameters.
     */
    public ScopeNode callMixin(String name, ArgumentsNode arguments) {
        List<ExpressionGroupNode> argumentList = (arguments != null) ? NodeTreeUtils.getChildren(arguments, ExpressionGroupNode.class) : Collections.<ExpressionGroupNode>emptyList();
        if (argumentList.size() > _parameterDefinitions.size()) {
            throw new IllegalMixinArgumentException(name, _parameterDefinitions.size());
        }

        // Clone scope and filter out any white space
        ScopeNode mixinScope = clone();
        NodeTreeUtils.filterLineBreaks(mixinScope);

        // If arguments were passed, apply them
        for (int i = 0; i < argumentList.size(); i++) {
            ExpressionGroupNode argument = argumentList.get(i);
            // Replace the value of the definition
            VariableDefinitionNode parameter = mixinScope._parameterDefinitions.get(i);
            parameter.clearChildren();
            parameter.addChild(argument);
        }

        // Mark this scope's containing rule set as invisible since it has been used as a mixin
        getParent().setVisible(false);

        return mixinScope;
    }

    /**
     * Some nodes are captured in additional structures to aid later resolution.
     */
    private void setAdditionVisitor() {
        setAdditionVisitor(new InclusiveNodeVisitor() {
            /**
             * Add parameter set as a child for printing input, but also add each defined value to the variable map.
             */
            @Override
            public boolean add(ParametersNode node) {
                for (VariableDefinitionNode variable : NodeTreeUtils.getChildren(node, VariableDefinitionNode.class)) {
                    _parameterDefinitions.add(variable);
                    add(variable);
                }
                return super.add(node);
            }

            /**
             * Store the rule set's scope by selector group
             */
            @Override
            public boolean add(RuleSetNode node) {
                SelectorGroupNode selectorGroup = NodeTreeUtils.getFirstChild(node, SelectorGroupNode.class);
                for (SelectorNode selectorNode : NodeTreeUtils.getChildren(selectorGroup, SelectorNode.class)) {
                    StringBuilder sb = new StringBuilder();
                    for (Node selectorChild : selectorNode.getChildren()) {
                        sb.append(selectorChild.toString());
                    }
                    String selector = sb.toString();
                    // Mixins lock on first definition
                    if (!_selectorToRuleSetMap.containsKey(selector)) {
                        _selectorToRuleSetMap.put(selector, node);
                    }
                }
                return super.add(node);
            }

            /**
             * Absorb all children of the given scope. This assumes that cloning is not necessary.
             */
            @Override
            public boolean add(ScopeNode node) {
                NodeTreeUtils.moveChildren(node, ScopeNode.this);
                return false; // Don't add the original scope itself
            }

            /**
             * Store variable definitions in a map by name
             */
            @Override
            public boolean add(VariableDefinitionNode node) {
                String name = node.getName();
                // "Variables" lock on first definition
                if (!_variableNameToValueMap.containsKey(name)) {
                    _variableNameToValueMap.put(name, NodeTreeUtils.getFirstChild(node, ExpressionGroupNode.class));
                }
                return super.add(node);
            }

            /**
             * Store property nodes by name. If there are multiple properties for a given name, only retain the last one.
             */
            @Override
            public boolean add(PropertyNode node) {
                String name = node.getName();

                // If this is the IE-specific "filter" property, always add it
                if (name.equals(FILTER_PROPERTY)) {
                    return super.add(node);
                }

                // If the value of this property node is a vendor-specific keyword, always add it
                if (node.getChildren().get(0).toString().startsWith("-")) {
                    return super.add(node);
                }

                // Check if this property has been seen before
                if (_propertyNameToNodeMap.containsKey(name)) {
                    PropertyNode oldPropertyNode = _propertyNameToNodeMap.get(name);
                    int oldPropertyIndex = getChildren().indexOf(oldPropertyNode);

                    if (oldPropertyNode.isVisible()) {
                        // Hide the unneeded property
                        oldPropertyNode.setVisible(false);

                        // Attempt to hide one surrounding white space node
                        if (!hideWhiteSpaceNode(oldPropertyIndex - 1)) {
                            hideWhiteSpaceNode(oldPropertyIndex + 1);
                        }
                    }
                }

                // Store the property as the latest for this name
                _propertyNameToNodeMap.put(name, node);

                return super.add(node);
            }
        });
    }

    private boolean hideWhiteSpaceNode(int index) {
        if (index < 0 || index >= getChildren().size()) {
            return false;
        }

        Node node = getChildren().get(index);
        if (!(node instanceof WhiteSpaceCollectionNode || node instanceof SpacingNode) || !node.isVisible()) {
            return false;
        }

        node.setVisible(false);
        return true;
    }

    @Override
    protected boolean add(NodeAdditionVisitor visitor) {
        return visitor.add(this);
    }

    @Override
    protected boolean enter(NodeNavigationVisitor visitor) {
        return visitor.enter(this);
    }

    @Override
    protected boolean exit(NodeNavigationVisitor visitor) {
        return visitor.exit(this);
    }

    @Override
    protected boolean visitInvisible(NodeNavigationVisitor visitor) {
        return visitor.visitInvisible(this);
    }

    /**
     * The internal state of this node will be rebuilt as cloned children are added by the super class.
     */
    @Override
    public ScopeNode clone() {
        return (ScopeNode) super.clone();
    }

    /**
     * Recreate internal state before children are cloned.
     */
    @Override
    protected void cloneChildren(InternalNode node) {
        ScopeNode scope = (ScopeNode) node;

        // Reset internal state
        scope._variableNameToValueMap = new HashMap<String, ExpressionGroupNode>();
        scope._selectorToRuleSetMap = new HashMap<String, RuleSetNode>();
        scope._parameterDefinitions = new ArrayList<VariableDefinitionNode>();
        scope._propertyNameToNodeMap = new HashMap<String, PropertyNode>();
        scope.setAdditionVisitor();

        super.cloneChildren(node);
    }
}


/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.hobsoft.microbrowser.jsoup;

import java.io.IOException;
import java.net.URL;

import org.hobsoft.microbrowser.Link;
import org.hobsoft.microbrowser.MicrobrowserException;
import org.hobsoft.microbrowser.MicrodataDocument;
import org.jsoup.Connection;
import org.jsoup.Connection.Method;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;

import static org.hobsoft.microbrowser.spi.Urls.newUrlOrNull;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * {@code Link} adapter to a jsoup {@code Element}.
 */
class JsoupLink implements Link
{
	// ----------------------------------------------------------------------------------------------------------------
	// fields
	// ----------------------------------------------------------------------------------------------------------------

	private final JsoupMicrodataDocument document;

	private final Element element;

	// ----------------------------------------------------------------------------------------------------------------
	// constructors
	// ----------------------------------------------------------------------------------------------------------------

	JsoupLink(JsoupMicrodataDocument document, Element element)
	{
		this.document = checkNotNull(document, "document");
		this.element = checkNotNull(element, "element");
	}

	// ----------------------------------------------------------------------------------------------------------------
	// Link methods
	// ----------------------------------------------------------------------------------------------------------------

	public String getRel()
	{
		return element.attr("rel");
	}

	public URL getHref()
	{
		return newUrlOrNull(element.absUrl("href"));
	}

	public MicrodataDocument follow()
	{
		JsoupMicrodataDocument nextDocument;

		try
		{
			nextDocument = new JsoupMicrodataDocument(document.getCookies(), getConnection().execute());
		}
		catch (IOException exception)
		{
			throw new MicrobrowserException("Error fetching page: " + getHref(), exception);
		}

		return nextDocument;
	}

	// ----------------------------------------------------------------------------------------------------------------
	// Unwrappable methods
	// ----------------------------------------------------------------------------------------------------------------

	public <T> T unwrap(Class<T> type)
	{
		checkArgument(Element.class.equals(type), "Cannot unwrap to: %s", type);

		return type.cast(element);
	}

	// ----------------------------------------------------------------------------------------------------------------
	// private methods
	// ----------------------------------------------------------------------------------------------------------------

	private Connection getConnection()
	{
		URL href = getHref();
		checkArgument(href != null, "Invalid URL: " + element.attr("href"));

		return Jsoup.connect(href.toString())
			.method(Method.GET)
			.cookies(document.getCookies());
	}
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.aima.search.util.backtracking;

import java.util.Collection;
import java.util.Queue;

import com.thesett.aima.search.SearchNode;
import com.thesett.aima.search.SearchNotExhaustiveException;
import com.thesett.aima.search.impl.BaseQueueSearchAlgorithm;
import com.thesett.aima.search.spi.QueueSearchState;
import com.thesett.common.util.logic.UnaryPredicate;

/**
 * Implements a back-tracking search algorithm. This algorithm is mentioned but not explained on page 84 of "Artificial
 * Intelligence, A Modern Approach".
 *
 *
There are a number of different approaches to implementing back-tracking of which this is only one. All searches
 * are back-tracking in the sense that they explore a search space, retreating from fruitless paths and trying to direct
 * effort towards potential solution paths. This back-tracking algorithm is more explicit in that it walks over search
 * nodes in a forward direction, calling the {@link ReTraversable#applyOperator()} method on them as it advances, then
 * when retreating from exhausted search paths, back-tracks over search nodes in the reverse order that it moved
 * forwards over them, calling the {@link ReTraversable#undoOperator()} method as it goes.
 *
 *
For example, in the constraint search described on page 84, as the search moves forward it binds variables, as it
 * back-tracks it unbinds them. The same effect can be achieved without using an explicitly back-tracking search by
 * passing a fresh copy of the variable state to each child search state created. When the search changes direction to a
 * different area of the search space, the abandoned states can simply be forgotten (garbage collected) without being
 * undone. This has the disadvantage of having to replicate a potentially large amount of shared data, but the advantage
 * that the search ordering can be more free without coming up with a way of sharing state.
 *
 *
When the search order is depth first, operators on shared state can be applied cumulatively to the shared state
 * built up by their predecessors. For example, in the constraint search, each new variable binding builds on those
 * already set up. Each undone operator undoes a single variable binding to revert to the binding set of a predecessor.
 * When using a depth first ordering only the states between the root of the search and the state being currently
 * examined will be 'active' in the sense that they have had their operator applied and not undone. That is to say that,
 * under depth first orderings only one search path is active at any one time.
 *
 *
This search algorithm can be used usefully with orderings other that depth first. Consider a search where each
 * child is given a state composed of the parent state plus one more element. The parent state is shared and not
 * replicated. For example, its a linked list, with the child nodes extra element linked into the end in such a way that
 * it is only visible to that child node and not other nodes sharing earlier parts of the same list. The search is now
 * free to move in whatever order it likes. Upon backtracking over exhausted areas of the search child nodes clean up
 * their local piece of the state, so it can be garbage collected. In the constraint search example, each new state can
 * add its variable binding onto the linked list in such a way that it is only visible on search paths from that state
 * onwards. If the search order moves to a completely different search path, the bindings will not be visible there, and
 * the same variable could be bound to a different value on another path. Some degree of indirection in looking up the
 * value of a variable must be introduced to achieve this. With general search orderings multiple search paths may be
 * active at once.
 *
 *
To summarize, a backtracking search with depth first ordering should be used when shared state can be built
 * cumulatively, the space or time requirements of replicating shared state is prohibitive, where the complexity of
 * representing state with path-local visibility is not needed, or where the search ordering of depth first is not an
 * inconvenience. A backtracking search with other orderings can be used where path-local visibility of state can be
 * achieved.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Search based on the queue ordering until a goal node is found or the maximum allowed number of steps is
 *          reached.
 *
 Apply operators to establish states when searching forwards.
 *
 Undo operators on states in exact reverse order when back-tracking over states.
 *

 *
 * @author Rupert Smith
 * @todo   This can also be used with depth bounded and iterative deepening. Can those algorithms be generified to work
 *         over this and max steps interchangeably? They may even work unchanged.
 */
public class BacktrackingAlgorithm<O, T extends ReTraversable<O>> extends BaseQueueSearchAlgorithm<O, T>
{
    /**
     * Holds the most recently discovered goal state, in order that subsequent searches remember to backtrack over it.
     */
    protected SearchNode<O, T> mostRecentGoalNode;

    /** {@inheritDoc} */
    public SearchNode<O, T> search(QueueSearchState<O, T> initSearch, Collection<T> startStates, int maxSteps,
        int searchSteps) throws SearchNotExhaustiveException
    {
        // Initialize the queue with the start states set up in search nodes if this has not already been done.
        // This will only be done on the first call to this method, as enqueueStartStates sets a flag when it is
        // done. Subsequent searches continue from where the previous search left off. Have to call reset on
        // the search method to really start the search again from the start states.
        Queue<SearchNode<O, T>> queue = initSearch.enqueueStartStates(startStates);

        // Get the goal predicate configured as part of the enqueueing start states process.
        UnaryPredicate goalPredicate = initSearch.getGoalPredicate();

        // Backtrack the most recent goal state, if one has been established.
        if (mostRecentGoalNode != null)
        {
            backtrack(mostRecentGoalNode);

            // Clear the most recent goal, now that it has been backtracked if required.
            mostRecentGoalNode = null;
        }

        // Keep running until the queue becomes empty or a goal state is found.
        while (!queue.isEmpty())
        {
            // Extract or peek at the head element from the queue.
            SearchNode headNode = queue.remove();

            // Apply the current nodes operator to establish its shared state.
            Reversable reversableState = (ReTraversable) headNode.getState();
            reversableState.applyOperator();

            // Expand the successors into the queue whether the current node is a goal state or not.
            // This prepares the queue for subsequent searches, ensuring that goal states do not block
            // subsequent goal states that exist beyond them.
            headNode.unexaminedSuccessorCount = headNode.expandSuccessors(queue, reverseEnqueue);

            // As the head node is about to be goal tested, reduce the unexamined successor count of its predecessor.
            if (headNode.getParent() != null)
            {
                headNode.getParent().unexaminedSuccessorCount--;
            }

            // Check if the current node is a goal state.
            if (goalPredicate.evaluate(headNode.getState()))
            {
                // Remember this goal node so that subsequent searches remember to backtrack over it.
                mostRecentGoalNode = headNode;

                return headNode;
            }

            // Backtrack over all fully exhausted nodes from the current position, as required.
            backtrack(headNode);

            // Check if there is a maximum number of steps limit and increase the step count and check the limit if so.
            if (maxSteps > 0)
            {
                searchSteps++;

                // Update the search state with the number of steps taken so far.
                initSearch.setStepsTaken(searchSteps);

                if (searchSteps >= maxSteps)
                {
                    // The maximum number of steps has been reached, however if the queue is now empty then the search
                    // has just completed within the maximum. Check if the queue is empty and return null if so.
                    if (queue.isEmpty())
                    {
                        return null;
                    }

                    // Quit without a solution as the max number of steps has been reached but because there are still
                    // more states in the queue then raise a search failure exception.
                    else
                    {
                        throw new SearchNotExhaustiveException("Maximum number of steps reached.", null);
                    }
                }
            }
        }

        // No goal state was found so return null
        return null;
    }

    /** {@inheritDoc} */
    public void setPeekAtHead(boolean flag)
    {
        if (!flag)
        {
            throw new IllegalArgumentException("BacktrackingAlgorithm only supports false for the 'peekAtHead' flag.");
        }
    }

    /** Resests the state of this algorithm by clearing any saved goal node that is pending backtracking. */
    public void reset()
    {
        mostRecentGoalNode = null;
    }

    /**
     * Back-tracks from the specified node, moving succesively upwards through the chain of parent nodes, until a node
     * is encountered that has unexamined successors. This method implements the backtracking searches reverse
     * direction. By checking for the presence of unexamined successors, this method only backtracks where necessary.
     *
     * @param checkNode The search node to start back-tracking from.
     */
    protected void backtrack(SearchNode checkNode)
    {
        while ((checkNode != null) && (checkNode.unexaminedSuccessorCount == 0))
        {
            Reversable undoState = (ReTraversable) checkNode.getState();
            undoState.undoOperator();

            checkNode = checkNode.getParent();
        }
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.aima.logic.fol.wam.machine;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import com.thesett.aima.logic.fol.Functor;
import com.thesett.aima.logic.fol.FunctorName;
import com.thesett.aima.logic.fol.LinkageException;
import com.thesett.aima.logic.fol.Resolver;
import com.thesett.aima.logic.fol.Term;
import com.thesett.aima.logic.fol.Variable;
import com.thesett.aima.logic.fol.VariableAndFunctorInterner;
import com.thesett.aima.logic.fol.wam.compiler.WAMCallPoint;
import com.thesett.aima.logic.fol.wam.compiler.WAMCompiledPredicate;
import com.thesett.aima.logic.fol.wam.compiler.WAMCompiledQuery;
import com.thesett.aima.logic.fol.wam.compiler.WAMInstruction;
import static com.thesett.aima.logic.fol.wam.compiler.WAMInstruction.REF;
import static com.thesett.aima.logic.fol.wam.compiler.WAMInstruction.STR;
import com.thesett.common.util.doublemaps.SymbolTable;

/**
 * A {@link Resolver} implements a resolution (or proof) procedure over logical clauses. This abstract class is the root
 * of all resolvers that operate over compiled clauses in the WAM language. Implementations may interpret the compiled
 * code directly, or further reduce it into more efficient binary forms.
 *
 *
Clauses to be queried over, have their binary byte code inserted into the machine, in preparation for calling by
 * queries. Queries also have their binary byte code inserted into the machine, and a reference to the most recently
 * inserted query is retained for invocation by the search method.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Resolve a query over a set of compiled Horn clauses in the WAM language.
 *
 Decode results into an abstract source tree from the binary heap format.  {@link Term}.
 *

 *
 * @author Rupert Smith
 */
public abstract class WAMResolvingMachine extends WAMBaseMachine
    implements Resolver<WAMCompiledPredicate, WAMCompiledQuery>, WAMResolvingMachineDPI
{
    /** Used for debugging. */
    /* private static final Logger log = Logger.getLogger(WAMResolvingMachine.class.getName()); */

    /** Static counter for inventing new variable names. */
    protected static AtomicInteger varNameId = new AtomicInteger();

    /** Holds the most recently set query, to run when the resolution search is invoked. */
    protected WAMCompiledQuery currentQuery;

    /**
     * Holds the buffer of executable code in a direct byte buffer so that no copying out to the native machine needs to
     * be done.
     */
    protected ByteBuffer codeBuffer;

    /** Holds the abstract machine debugging monitor, or <tt>null</tt> if none is attached. */
    protected WAMResolvingMachineDPIMonitor monitor;

    /**
     * Creates a resolving machine with the specified symbol table.
     *
     * @param symbolTable The symbol table.
     */
    protected WAMResolvingMachine(SymbolTable<Integer, String, Object> symbolTable)
    {
        super(symbolTable);
    }

    /** {@inheritDoc} */
    public void emmitCode(WAMCompiledPredicate predicate) throws LinkageException
    {
        // Keep track of the offset into which the code was loaded.
        int entryPoint = codeBuffer.position();
        int length = (int) predicate.sizeof();

        // If the code is for a program clause, store the programs entry point in the call table.
        WAMCallPoint callPoint = setCodeAddress(predicate.getName(), entryPoint, length);

        // Emmit code for the clause into this machine.
        predicate.emmitCode(codeBuffer, this, callPoint);

        // Notify the native machine of the addition of new code.
        codeAdded(codeBuffer, entryPoint, length);

        // Notify any attached DPI monitor of the addition of new code.
        if (monitor != null)
        {
            monitor.onCodeUpdate(this, entryPoint, length);
        }
    }

    /** {@inheritDoc} */
    public void emmitCode(WAMCompiledQuery query) throws LinkageException
    {
        // Keep track of the offset into which the code was loaded.
        int entryPoint = codeBuffer.position();
        int length = (int) query.sizeof();

        // If the code is for a program clause, store the programs entry point in the call table.
        WAMCallPoint callPoint = new WAMCallPoint(entryPoint, length, -1);

        // Emmit code for the clause into this machine.
        query.emmitCode(codeBuffer, this, callPoint);

        // Notify the native machine of the addition of new code.
        codeAdded(codeBuffer, entryPoint, length);

        // Notify any attached DPI monitor of the addition of new code.
        if (monitor != null)
        {
            monitor.onCodeUpdate(this, entryPoint, length);
        }
    }

    /**
     * Extracts the raw byte code from the machine for a given call table entry.
     *
     * @param  callPoint The call table entry giving the location and length of the code.
     *
     * @return The byte code at the specified location.
     */
    public byte[] retrieveCode(WAMCallPoint callPoint)
    {
        byte[] result = new byte[callPoint.length];

        codeBuffer.get(result, callPoint.entryPoint, callPoint.length);

        return result;
    }

    /** {@inheritDoc} */
    public void emmitCode(int offset, int address)
    {
        codeBuffer.putInt(offset, address);
    }

    /** {@inheritDoc} */
    public void addToDomain(WAMCompiledPredicate term) throws LinkageException
    {
        /*log.fine("public void addToDomain(WAMCompiledClause term = " + term + "): called");*/

        // Emmit code for the term into this machine.
        emmitCode(term);
    }

    /** {@inheritDoc} */
    public void setQuery(WAMCompiledQuery query) throws LinkageException
    {
        /*log.fine("public void setQuery(WAMCompiledClause query = " + query + "): called");*/

        // Emmit code for the clause into this machine.
        emmitCode(query);

        // Keep hold of the query to run.
        currentQuery = query;
    }

    /** {@inheritDoc} */
    public Set<Variable> resolve()
    {
        // Check that a query has been set to resolve.
        if (currentQuery == null)
        {
            throw new IllegalStateException("No query set to resolve.");
        }

        // Execute the byte code, starting from the first functor of the query.
        return executeAndExtractBindings(currentQuery);
    }

    /** {@inheritDoc} */
    public void attachMonitor(WAMResolvingMachineDPIMonitor monitor)
    {
        this.monitor = monitor;
    }

    /** {@inheritDoc} */
    public ByteBuffer getCodeBuffer(int start, int length)
    {
        // Take a read only slice onto an appropriate section of the code buffer.
        ByteBuffer readOnlyBuffer = codeBuffer.asReadOnlyBuffer();
        readOnlyBuffer.position(start);
        readOnlyBuffer.limit(start + length);

        return readOnlyBuffer;
    }

    /** {@inheritDoc} */
    public VariableAndFunctorInterner getVariableAndFunctorInterner()
    {
        return this;
    }

    /**
     * Notified whenever code is added to the machine.
     *
     * @param codeBuffer The code buffer.
     * @param codeOffset The start offset of the new code.
     * @param length     The length of the new code.
     */
    protected abstract void codeAdded(ByteBuffer codeBuffer, int codeOffset, int length);

    /**
     * Dereferences an offset from the current environment frame on the stack. Storage slots in the current environment
     * may point to other environment frames, but should not contain unbound variables, so ultimately this dereferencing
     * should resolve onto a structure or variable on the heap.
     *
     * @param  a The offset into the current environment stack frame to dereference.
     *
     * @return The dereferences structure or variable.
     */
    protected abstract int derefStack(int a);

    /**
     * Executes compiled code at the specified call point returning an indication of whether or not the execution was
     * succesfull.
     *
     * @param  callPoint The call point of the compiled byte code to execute.
     *
     * @return <tt>true</tt> iff execution succeeded.
     */
    protected abstract boolean execute(WAMCallPoint callPoint);

    /**
     * Dereferences a heap pointer (or register), returning the address that it refers to after following all reference
     * chains to their conclusion. This method is also side effecting, in that the contents of the refered to heap cell
     * are also loaded into fields and made available through the {@link #getDerefTag()} and {@link #getDerefVal()}
     * methods.
     *
     * @param  a The address to dereference.
     *
     * @return The address that the reference refers to.
     */
    protected abstract int deref(int a);

    /**
     * Gets the heap cell tag for the most recent dereference operation.
     *
     * @return The heap cell tag for the most recent dereference operation.
     */
    protected abstract byte getDerefTag();

    /**
     * Gets the heap call value for the most recent dereference operation.
     *
     * @return The heap call value for the most recent dereference operation.
     */
    protected abstract int getDerefVal();

    /**
     * Gets the value of the heap cell at the specified location.
     *
     * @param  addr The address to fetch from the heap.
     *
     * @return The heap cell at the specified location.
     */
    protected abstract int getHeap(int addr);

    /**
     * Runs a query, and for every non-anonymous variable in the query, decodes its binding value from the heap and
     * returns it in a set of variable bindings.
     *
     * @param  query The query to execute.
     *
     * @return A set of variable bindings resulting from the query.
     */
    protected Set<Variable> executeAndExtractBindings(WAMCompiledQuery query)
    {
        // Execute the query and program. The starting point for the execution is the first functor in the query
        // body, this will follow on to the subsequent functors and make calls to functors in the compiled programs.
        boolean success = execute(query.getCallPoint());

        // Used to collect the results in.
        Set<Variable> results = null;

        // Collect the results only if the resolution was successfull.
        if (success)
        {
            results = new HashSet<Variable>();

            // The same variable context is used accross all of the results, for common use of variables in the
            // results.
            Map<Integer, Variable> varContext = new HashMap<Integer, Variable>();

            // For each of the free variables in the query, extract its value from the location on the heap pointed to
            // by the register that holds the variable.
            /*log.fine("query.getVarNames().size() =  " + query.getVarNames().size());*/

            for (byte reg : query.getVarNames().keySet())
            {
                int varName = query.getVarNames().get(reg);

                if (query.getNonAnonymousFreeVariables().contains(varName))
                {
                    int addr = derefStack(reg);
                    Term term = decodeHeap(addr, varContext);

                    results.add(new Variable(varName, term, false));
                }
            }
        }

        return results;
    }

    /**
     * Decodes a term from the raw byte representation on the machines heap, into an abstract syntax tree.
     *
     * @param  start           The start offset of the term on the heap.
     * @param  variableContext The variable context for the decoded variables. This may be shared amongst all variables
     *                         decoded for a particular unifcation.
     *
     * @return The term decoded from its heap representation.
     */
    protected Term decodeHeap(int start, Map<Integer, Variable> variableContext)
    {
        /*log.fine("private Term decodeHeap(int start = " + start + ", Map<Integer, Variable> variableContext = " +
            variableContext + "): called");*/

        // Used to hold the decoded argument in.
        Term result = null;

        // Dereference the initial heap pointer.
        int addr = deref(start);
        byte tag = getDerefTag();
        int val = getDerefVal();

        /*log.fine("addr = " + addr);*/
        /*log.fine("tag = " + tag);*/
        /*log.fine("val = " + val);*/

        switch (tag)
        {
        case REF:
        {
            // Check if a variable for the address has already been created in this context, and use it if so.
            Variable var = variableContext.get(val);

            if (var == null)
            {
                var = new Variable(varNameId.decrementAndGet(), null, false);

                variableContext.put(val, var);
            }

            result = var;

            break;
        }

        case STR:
        {
            // Decode f/n from the STR data.
            int fn = getHeap(val);
            int f = fn & 0x00ffffff;

            /*log.fine("fn = " + fn);*/
            /*log.fine("f = " + f);*/

            // Look up and initialize this functor name from the symbol table.
            FunctorName functorName = getDeinternedFunctorName(f);

            // Fill in this functors name and arity and allocate storage space for its arguments.
            int arity = functorName.getArity();
            Term[] arguments = new Term[arity];

            // Loop over all of the functors arguments, recursively decoding them.
            for (int i = 0; i < arity; i++)
            {
                arguments[i] = decodeHeap(val + 1 + i, variableContext);
            }

            // Create a new functor to hold the decoded data.
            result = new Functor(f, arguments);

            break;
        }

        case WAMInstruction.CON:
        {
            //Decode f/n from the CON data.
            int f = val & 0x3fffffff;

            /*log.fine("f = " + f);*/

            // Create a new functor to hold the decoded data.
            result = new Functor(f, null);

            break;
        }

        case WAMInstruction.LIS:
        {
            FunctorName functorName = new FunctorName("cons", 2);
            int f = internFunctorName(functorName);

            // Fill in this functors name and arity and allocate storage space for its arguments.
            int arity = functorName.getArity();
            Term[] arguments = new Term[arity];

            // Loop over all of the functors arguments, recursively decoding them.
            for (int i = 0; i < arity; i++)
            {
                arguments[i] = decodeHeap(val + i, variableContext);
            }

            // Create a new functor to hold the decoded data.
            result = new Functor(f, arguments);

            break;
        }

        default:
            throw new IllegalStateException("Encountered unknown tag type on the heap.");
        }

        return result;
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.common.reflect;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.thesett.common.error.NotImplementedException;
import com.thesett.common.util.TypeConverter;

/**
 * A DirectMemento provides indirect access to the fields of an object, allowing them all to be accessed by name. This
 * allows other objects to access all of an object fields even if they have protect, private or package modifiers. This
 * enables the state of an object to be decoupled from the object itself. Through this mechanism any object can have a
 * snapshot of its state externalized or its state restored from such a snapshot.
 *
 *
To give examples of some of the possible uses of this: in a persistence framework object snapshots can be
 * externalized into persistent storage and restored from there (like entity beans), in a GUI program the state of the
 * data or document model can pushed onto a stack from where it can be restored if the user requests an 'undo'
 * operation.
 *
 *
This DirectMemento class provides the methods to access another objects internal state as well as providing the
 * storage into which to place the snapshot of the state. This class is also serializable, allowing the state to be
 * externalized.
 *
 *
A class is just one step in an inheritance chain; an object is actually composed from many classes that layer on
 * top of each other. The only objects that are built from just one class are objects built from the Object class
 * itself. Every other object has at least one top level class, possibly many middle layers and always the Object class
 * at the root of its inheritence chain. Classes higher up the chain may replace the fields of classes further down the
 * chain with fields that have the same name. This means that a field name may not uniquely identify a field of an
 * object because it is possible for an object to have more than one field with the same name. For this reason, the
 * fields of an object ar always stored against the class in the hierarchy to which the field belongs. When objects are
 * captured the entire inheritence chain is scanned and all fields right back to the root Object are captured.
 *
 *
The {@link #get}, {@link Memento#put(Class, String, TypeConverter.MultiTypeData)},
 * {@link #put(Class, String, Object)}, and {@link #getAllFieldNames} methods are only valid after an object has been
 * captured. Capture an object by using the appropriate constructor or by calling the {@link #capture} method directly
 * before using these methods.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Create a memento from an object.
 *  {@link Class}, {@link Field}
 *
Write to an objects fields from a memento. < {@link Class}, {@link Field}
 *
Read field values.
 *
Modifiy field values.
 *
Get list of all fields.
 *

 *
 * @author Rupert Smith
 * @todo   Make this a base class into which different serialization methods can be plugged. For example, serialize to
 *         binary data, to a SQL update or create, to an XML document etc. This would provide greater flexibility over
 *         how state can be marshalled into external forms.
 * @todo   Consider the threading issue of this. Should obtain a lock on an objects monitor and read and write all its
 *         fields atomically.
 */
public class DirectMemento implements Memento, Serializable
{
    /** Used for logging. */
    /* private static final Logger log = Logger.getLogger(DirectMemento.class.getName()); */

    /** The bitmask of attribute modifiers indicating that a field should not be saved into the memento. */
    public static final int NOSAVE = (Modifier.FINAL | Modifier.STATIC | Modifier.TRANSIENT);

    /** Holds the fields of the object that this memento has captured. */
    private final Map values = new HashMap();

    /** The object that this is a memento for. */
    Object ob;

    /**
     * Builds a memento on the specified object. The object is not captured by this constructor, a seperate call to the
     * {@link #capture()} method must be made to do that.
     *
     * @param ob The object to create a memento on.
     */
    public DirectMemento(Object ob)
    {
        this.ob = ob;
    }

    /**
     * Helper method to determine whether or not a given field should be saved into this memento. A field should not be
     * saved if it is final, static, or transient.
     *
     * @param  f The field to be tested.
     *
     * @return True if the field should be saved.
     */
    public static boolean shouldBeSaved(Member f)
    {
        // Get the fields modifiers
        int mod = f.getModifiers();

        // Check that the field has no modifiers that mean it should not be saved.
        return (mod & DirectMemento.NOSAVE) == 0;
    }

    /** Captures an objects state in this memento. */
    public void capture()
    {
        // Get the class of the object to build a memento for.
        Class cls = ob.getClass();

        // Iterate through the classes whole inheritence chain.
        while (!cls.equals(Object.class))
        {
            // Get the classes fields.
            Field[] attrs = cls.getDeclaredFields();

            // Build a new map to put the fields in for the current class.
            HashMap map = new HashMap();

            // Cache the field values by the class name.
            values.put(cls, map);

            // Loop over all the fields in the current class.
            for (Field attr : attrs)
            {
                // Make the field accessible (it may be protected or private).
                attr.setAccessible(true);

                // Check that the field should be captured.
                if (shouldBeSaved(attr))
                {
                    // Use a try block as access to the field may fail, although this should not happen because
                    // even private, protected and package fields have been made accessible.
                    try
                    {
                        // Cache the field by its name.
                        map.put(attr.getName(), attr.get(ob));
                    }
                    catch (IllegalAccessException e)
                    {
                        // The field could not be accessed but all fields have been made accessible so this should
                        // not happen.
                        throw new IllegalStateException("Field '" + attr.getName() +
                            "' could not be accessed but the 'setAccessible(true)' method was invoked on it.", e);
                    }
                }
            }

            // Get the superclass for the next step of the iteration over the whole inheritence chain.
            cls = cls.getSuperclass();
        }
    }

    /** {@inheritDoc} */
    public void captureNonNull()
    {
        throw new IllegalStateException("Not implemented.");
    }

    /**
     * Restores the values currently in this memento to the specified object.
     *
     * @param  ob The object to which the values from this memento should be restored.
     *
     * @throws NoSuchFieldException If the object in question does not have a field for one of the memento values.
     */
    public void restore(Object ob) throws NoSuchFieldException
    {
        /*log.fine("public void map(Object ob): called");*/
        /*log.fine("class is " + ob.getClass());*/

        // Iterate over the whole inheritence chain.
        for (Object key : values.keySet())
        {
            // Get the next class from the cache.
            Class cls = (Class) key;

            // Get the cache of field values for the class.
            Map vals = (HashMap) values.get(cls);

            // Loop over all fields in the class.
            for (Object o : vals.keySet())
            {
                // Get the next field name.
                String attr = (String) o;

                // Get the next field value.
                Object val = vals.get(attr);

                // Get a reference to the field in the object.
                Field f = cls.getDeclaredField(attr);

                // Make the field accessible (it may be protected, package or private).
                f.setAccessible(true);

                // Use a try block as writing to the field may fail.
                try
                {
                    // Write to the field.
                    f.set(ob, val);
                }
                catch (IllegalAccessException e)
                {
                    // The field could not be written to but all fields have been made accessible so this should
                    // not happen.
                    throw new IllegalStateException("Field '" + f.getName() +
                        "' could not be accessed but the 'setAccessible(true)' method was invoked on it.", e);
                }
            }
        }
    }

    /**
     * Gets the value of the named field of the specified class.
     *
     * @param  cls  The class in which the field to get is declared.
     * @param  attr The name of the field.
     *
     * @return The object value of the attribute.
     */
    public Object get(Class cls, String attr)
    {
        HashMap map;

        // See if the class exists in the cache.
        if (!values.containsKey(cls))
        {
            // Class not in cache so return null.
            return null;
        }

        // Get the cache of field values for the class.
        map = (HashMap) values.get(cls);

        // Extract the specified field from the cache.
        return map.get(attr);
    }

    /**
     * Sets the value of the named property as a multi type object.
     *
     * @param cls      The class in which the property is declared.
     * @param property The name of the property to set.
     * @param value    The multi type object to set that value from.
     */
    public void put(Class cls, String property, TypeConverter.MultiTypeData value)
    {
        throw new NotImplementedException();
    }

    /**
     * Places the specified value into the memento based on the field's declaring class and name.
     *
     * @param cls  The class in which the field is declared.
     * @param attr The name of the attribute.
     * @param val  The value to store into this memento.
     */
    public void put(Class cls, String attr, Object val)
    {
        /*log.fine("public void put(Class cls, String attr, Object val): called");*/
        /*log.fine("class name is " + cls.getName());*/
        /*log.fine("attribute is " + attr);*/
        /*log.fine("value to set is " + val);*/

        HashMap map;

        // Check that the cache for the class exists in the cache.
        if (values.containsKey(cls))
        {
            // Get the cache of field for the class.
            map = (HashMap) values.get(cls);
        }
        else
        {
            // The class does not already exist in the cache to create a new cache for its fields.
            map = new HashMap();

            // Cache the new field cache against the class.
            values.put(cls, map);
        }

        // Store the attribute in the field cache for the class.
        map.put(attr, val);
    }

    /**
     * Generates a list of all the fields of the object that this memento maps for a given class.
     *
     * @param  cls The class to get all field names for.
     *
     * @return A collection of the field names or null if the specified class is not part of the objects class hierarchy
     *         chain.
     */
    public Collection getAllFieldNames(Class cls)
    {
        /*log.fine("public Collection getAllFieldNames(Class cls): called");*/

        // See if the class exists in the cache
        if (!values.containsKey(cls))
        {
            // Class not in cache so return null
            return null;
        }

        // Get the cache of fields for the class
        Map map = (HashMap) values.get(cls);

        // Return all the keys from cache of fields
        return map.keySet();
    }
}