/*
 * 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.learning.decisiontree.prototype;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import com.thesett.aima.learning.AbstractLearningMethod;
import com.thesett.aima.learning.ClassifyingMachine;
import com.thesett.aima.learning.LearningFailureException;
import com.thesett.aima.math.InformationTheory;
import com.thesett.aima.state.Attribute;
import com.thesett.aima.state.OrdinalAttribute;
import com.thesett.aima.state.State;
import com.thesett.common.util.SimpleTree;
import com.thesett.common.util.Tree;

/**
 * ProtoDTLearningMethod builds decision trees from a set of examples. This is a prototype decision tree learner and is
 * not built to be efficient.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Build a decision tree from input data and return a machine that classifies with it.
 *      {@link DecisionTree}, {@link ProtoDTMachine}, {@link OrdinalAttribute},
 *          {@link Assignment}, {@link Pending}, {@link Decision}
 *

 *
 * @author Rupert Smith
 * @todo   Add method to extract learned machine as a compiled down java program.
 * @todo   How to cope with missing data from the input set.
 * @todo   How to convert continous real values attributes into ranges (optimally)? There are discretization methods in
 *         the literature to use, many are based on maximal information.
 * @todo   Decide how to manage memory and time efficiently during learning. Some ideas: The examples are held in an
 *         array. Every decision partitions this array into disjoint sets. Begin by sorting the array by the property to
 *         split on. This way each sub-set of examples can now be identified as a pair of indexes into the array
 *         identifying where the sub-set begins and ends. This is good because passing examples by value or creating
 *         large data structures containing many references to individual examples will be very wasteful compared with
 *         this approach. Typically the sort contains very few values and many items with the same value. Choose the
 *         sort algorithm that works fastest on this kind of data (range sort in O(n) time). Only sort when needed and
 *         keep the results of a sort so it never needs to be repeated.
 * @todo   Where the example set is very large, might need to manage this array on disk? Create an interface for array
 *         access and a caching mechanism to manage the disk array, bringing bits into memory as they are needed into a
 *         fixed size LRU cache. Don't know if learning sets that big are feasable to learn on anyway.
 * @todo   Manage the whole example set as a two dimensional array, rather than as an array of States. Can easily
 *         provide a State class that takes an index and pretends to be an object holding those properties by looking
 *         them up in the array. Or manage the example set as a bunch of arrays of primitive data values (attribute
 *         lists, rather than one table).
 */
public class ProtoDTLearningMethod extends AbstractLearningMethod
{
    /** Used for logging. */
    /* private static final Logger log = Logger.getLogger(ProtoDTLearningMethod.class.getName()); */

    /**
     * Used to hold the value of the classification that all of a set of examples have. The
     * {@link #allHaveSameClassification} method tests if all of a set of examples have the same classification and if
     * they do, leaves that classification in this variable at the end of the method.
     */
    private OrdinalAttribute allClassification;

    /**
     * Builds a decision tree by repeatedly selecting the best property of the data examples to split on. The best
     * property is chosen to be the one that reveals the most information about the target property to learn.
     *
     *
The constructed decision tree is then used to create a classifying machine for that tree which is returned.
     *
     * @return A classifying machine built from the learned decision tree.
     *
     * @throws LearningFailureException If there is not exactly one output property to learn for. This algorithm can
     *                                  only handle a single output property.
     */
    public ClassifyingMachine learn() throws LearningFailureException
    {
        /*log.fine("public ClassifyingMachine learn(): called");*/

        // Call the initialize method to ensure that input and output properties are correctly set up.
        initialize();

        // Check that there is only one output property to learn for. Decision trees can only output a single
        // property classification (although mutliple trees could be built for multiple properties).
        if (outputProperties.size() != 1)
        {
            throw new LearningFailureException(
                "Decision trees can only learn a classification for a single property, " + "but " +
                outputProperties.size() + " have been set as outputs.", null);
        }

        // Extract the single output property to learn for.
        String outputProperty = outputProperties.iterator().next();

        // Check that the output property to be learnt is one with a finite number of values. Decision trees
        // cannot learn infinite valued attributes, such as real numbers etc.
        int numValues =
            inputExamples.iterator().next().getComponentType().getPropertyType(outputProperty).getNumPossibleValues();

        if (numValues < 1)
        {
            throw new LearningFailureException("Decision trees can only learn a classification for a " +
                "property with a finite number of values. The property, " + outputProperty +
                " can have an infinite number of values and should be " +
                "discretized prior to being learned by decision trees.", null);
        }

        // Used to queue nodes that are pending construction.
        Queue<DecisionTree> pendingNodes = new LinkedList<DecisionTree>();

        // Create a pending node,(an empty tree, the examples, the attributes, the default classification).
        Pending startNode =
            new Pending(inputExamples, inputProperties, getMajorityClassification(outputProperty, inputExamples), null);

        // Create the root of the decision tree out of this start pending node.
        DecisionTree decisionTree = new DecisionTree(startNode);

        // Push this new pending node onto the construction queue.
        pendingNodes.offer(decisionTree);

        // Loop until the queue of nodes pending construction is empty.
        while (!pendingNodes.isEmpty())
        {
            /*log.fine("Got next pending node.");*/

            // Get the tree fragment for the next pending node. The data element should always be safe to cast to a
            // Pending node as this algorithm only pushes pending nodes onto the queue.
            DecisionTree currentTreePendingNode = pendingNodes.remove();
            DecisionTreeElement currentTreePendingNodeElement = currentTreePendingNode.getElement();
            Pending currentNode = (Pending) currentTreePendingNodeElement;

            // Extract the examples, the attributes, the default classification, and the attribute to be matched,
            // for this pending node.
            Collection<State> examples = currentNode.getExamples();
            Collection<String> undecidedProperties = currentNode.getUndecidedProperties();
            OrdinalAttribute defaultAttribute = currentNode.getDefault();
            OrdinalAttribute matchingAttribute = currentNode.getAttributeValue();

            /*log.fine("Pending node corresponds to " + examples.size() + " examples.");*/
            /*log.fine("Pending node has " + undecidedProperties.size() + " undecided properties left.");*/

            // Used to hold the decision tree fragment (decision node, pending node or assignment leaf) that will
            // be inserted into the decision tree to replace the current pending node.
            DecisionTree newTreeFragment = null;

            // If the set of examples is empty then set the default as the leaf.
            if (examples.isEmpty())
            {
                /*log.fine("Examples is empty.");*/
                newTreeFragment = new DecisionTree(new Assignment(outputProperty, defaultAttribute, matchingAttribute));
            }

            // Else if undecided properties is empty then set the majority value of the classification of
            // examples as the leaf.
            else if (undecidedProperties.isEmpty())
            {
                /*log.fine("No undecided properties left.");*/

                // Work out what the majority classification is and create a leaf with that assignment.
                OrdinalAttribute majority = getMajorityClassification(outputProperty, inputExamples);

                newTreeFragment = new DecisionTree(new Assignment(outputProperty, majority, matchingAttribute));
            }

            // Else if all the examples have the same classification then set that classification as the leaf.
            else if (allHaveSameClassification(outputProperty, examples))
            {
                /*log.fine("All examples have the same classification.");*/
                newTreeFragment =
                    new DecisionTree(new Assignment(outputProperty, allClassification, matchingAttribute));
            }

            // Else choose the best attribute (with the largest estimated information gain) on the classification.
            else
            {
                /*log.fine("Choosing the best property to split on.");*/

                String bestProperty = chooseBestPropertyToDecideOn(outputProperty, examples, inputProperties);

                // Check if a best property could not be found, in which case behave as if there are no
                // input properties left to work with.
                if (bestProperty == null)
                {
                    /*log.fine("Couldn't find a best property to split on.");*/

                    // Put the pending node back onto the construction queue but with zero input properties.
                    Pending newPendingNode =
                        new Pending(examples, new ArrayList<String>(), defaultAttribute, matchingAttribute);

                    newTreeFragment = new DecisionTree(newPendingNode);
                    pendingNodes.offer(newTreeFragment);
                }
                else
                {
                    // Extract an attribute with this property name from the first example and use this to get
                    // a listing of all the possible values of that attribute.
                    Attribute bestAttribute =
                        (OrdinalAttribute) examples.iterator().next().getProperty(bestProperty);

                    /*log.fine("bestProperty = " + bestProperty);*/
                    /*log.fine("bestAttribute = " + bestAttribute);*/

                    // Create a decision node that decides on the best property. New pending nodes will be created
                    // as children of this node and added to it.
                    newTreeFragment =
                        new DecisionTree(new Decision(bestProperty, bestAttribute.getType().getNumPossibleValues(),
                                matchingAttribute));

                    // Produce a cut down input property set equal to the old one but with the selected best property
                    // removed as a decision node is to be created for it.
                    Set<String> newInputProperties = new HashSet<String>(inputProperties);

                    newInputProperties.remove(bestProperty);

                    // For each possible value of the best attribute.
                    for (Iterator<? extends Attribute> i = bestAttribute.getType().getAllPossibleValuesIterator();
                            i.hasNext();)
                    {
                        OrdinalAttribute nextAttribute = (OrdinalAttribute) i.next();

                        // Extract just those examples with the current value. To implement this efficiently first sort
                        // the examples by their attribute value for the best property to split on using a range sort
                        // (O(n) time as num possible values is limited and they are already indexed by their order).
                        // Create the new example collection as simply a pair of indexes into the sorted list.

                        Collection<State> matchingExamples = new ArrayList<State>();

                        for (State example : examples)
                        {
                            // Extract the attribute value for the property to decide on.
                            OrdinalAttribute testAttribute = (OrdinalAttribute) example.getProperty(bestProperty);

                            // Check if it matches the current value being extracted and add it to the collection of
                            // extracted examples if so.
                            if (nextAttribute.equals(testAttribute))
                            {
                                matchingExamples.add(example);
                            }
                        }

                        // Push onto the queue, a child node for this value, the subset of examples (or the value to
                        // select that subset on), attributes without the best one (or an exclusion list of attributes
                        // used so far), default majority-value of classification of these examples.
                        Pending newPendingNode =
                            new Pending(matchingExamples, newInputProperties,
                                getMajorityClassification(outputProperty, matchingExamples), nextAttribute);

                        DecisionTree newPendingNodeTreeFragment = new DecisionTree(newPendingNode);

                        pendingNodes.offer(newPendingNodeTreeFragment);

                        /*log.fine("Created new pending node below the split, for " + matchingExamples.size() +
                            " examples and " + newInputProperties.size() + " undecided properties.");*/

                        // Add the new pending node as a child of the parent decision node for the best property.
                        newTreeFragment.addChild(newPendingNodeTreeFragment);
                    }
                }
            }

            // Remove the current pending node from the decision tree and replace it with the new element that the
            // algorithm has selected.
            Tree.Node parentTree = (DecisionTree) currentTreePendingNode.getParent();

            // Check if the current pending node has a parent (if not it is the real tree root and the first pending
            // node created).
            if (parentTree != null)
            {
                // Remove the pending node from its parents child list.
                parentTree.getChildren().remove(currentTreePendingNode);

                // Replace the pending node with the newly built tree fragment in its parent child list.
                parentTree.addChild(newTreeFragment);
            }
            else
            {
                // Update the root tree pointer to point to the newly built tree fragment.
                decisionTree = newTreeFragment;
            }

            /*log.fine("There are now " + pendingNodes.size() + " pending nodes on the construction queue.");*/
        }

        // Loop over the whole decision tree initializing its quick lookup tables for all the decisions it contains.
        for (Iterator<SimpleTree<DecisionTreeElement>> i = decisionTree.iterator(Tree.IterationOrder.PreOrder);
                i.hasNext();)
        {
            DecisionTree nextTreeFragment = (DecisionTree) i.next();
            DecisionTreeElement nextElement = nextTreeFragment.getElement();

            // Check if it is a decision and build its lookup table if so.
            if (nextElement instanceof Decision)
            {
                Decision decision = (Decision) nextElement;

                decision.initializeLookups(nextTreeFragment);
            }
        }

        // Create a new decisition tree machine out of the fully constructed decision tree.
        ProtoDTMachine dtMachine = new ProtoDTMachine();

        dtMachine.setDecisionTree(decisionTree);

        // Return the trained decision tree classifying machine.
        return dtMachine;
    }

    /**
     * This helper method works out how the majority of the specified examples are classified by the named property. The
     * property should always be the goal property that the algorithm is learning and must always take on a finite
     * number of different values.
     *
     * @param  property The name of the property to find the majority classification for.
     * @param  examples A collection of state objects to count over in order to find the majority classification for the
     *                  named property.
     *
     * @return The value of the majority classification for the named property or null if the example collection was
     *         empty so no majority could be found.
     *
     * @throws LearningFailureException If the number of possible value that the specified property can take on is not
     *                                  finite.
     */
    private OrdinalAttribute getMajorityClassification(String property, Iterable<State> examples)
        throws LearningFailureException
    {
        /*log.fine("private OrdinalAttribute getMajorityClassification(String property, Collection<State> examples): called");*/
        /*log.fine("property = " + property);*/

        // Flag used to indicate that the map to hold the value counts in has been initialized.
        Map<OrdinalAttribute, Integer> countMap = null;

        // Used to hold the biggest count found so far.
        int biggestCount = 0;

        // Used to hold the value with the biggest count found so far.
        OrdinalAttribute biggestAttribute = null;

        // Loop over all the examples counting the number of occurences of each possible classification by the
        // named property.
        for (State example : examples)
        {
            OrdinalAttribute nextAttribute = (OrdinalAttribute) example.getProperty(property);
            /*log.fine("nextAttribute = " + nextAttribute);*/

            // If this is the first attribute then find out how many possible values it can take on.
            if (countMap == null)
            {
                // A check has already been performed at the start of the learning method to ensure that the output
                // property only takes on a finite number of values.
                countMap = new HashMap<OrdinalAttribute, Integer>();
            }

            int count;

            // Increment the count for the number of occurences of this classification.
            if (!countMap.containsKey(nextAttribute))
            {
                count = 1;
                countMap.put(nextAttribute, count);
            }
            else
            {
                count = countMap.get(nextAttribute);
                countMap.put(nextAttribute, count++);
            }

            // Compare it to the biggest score found so far to see if it is bigger.
            if (count > biggestCount)
            {
                // Update the biggest score.
                biggestCount = count;

                // Update the value of the majority classification.
                biggestAttribute = nextAttribute;
            }
        }

        // Return the majority classification found.
        return biggestAttribute;
    }

    /**
     * Tests if a property of a set of examples has the same value for all the examples. This algorithm works by
     * iterating through the examples until two different values are found or the end of the collection is reached
     * having found only one value.
     *
     *
As a side effect this method leaves the value of the classification in the member variable
     * {@link #allClassification} when all the examples do match.
     *
     * @param  property The name of the property to test.
     * @param  examples A collection of state objects to iterate over in order to test if they all have the same value
     *                  for the named property.
     *
     * @return true if they all have the same value; false otherwise.
     */
    private boolean allHaveSameClassification(String property, Iterable<State> examples)
    {
        // Used to hold the value of the first attribute seen.
        OrdinalAttribute firstAttribute = null;

        // Flag used to indicate that the test passed successfully.
        boolean success = true;

        // Loop over all the examples.
        for (State example : examples)
        {
            OrdinalAttribute nextAttribute = (OrdinalAttribute) example.getProperty(property);

            // If this is the first example just store its attribute value.
            if (firstAttribute == null)
            {
                firstAttribute = nextAttribute;
            }

            // Otherwise check if the attribute value does not match the first one in which case the test fails.
            else if (!nextAttribute.equals(firstAttribute))
            {
                success = false;

                break;
            }
        }

        // If the test passed then store the matching classification that all the examples have in a memeber variable
        // from where it can be accessed.
        if (success)
        {
            allClassification = firstAttribute;
        }

        return success;
    }

    /**
     * For a given set of examples, input properties and an output property this method chooses the input property that
     * provides the largest information gain on the value of the output property.
     *
     * @param  outputProperty  The name of the property to compare the information gain of the input properties against.
     * @param  examples        A collection of state objects to count over in order to find the highest information gain
     *                         input property.
     * @param  inputProperties A collection of input properties to find the best one amongst.
     *
     * @return The name of the input property with the highest information gain.
     */
    private String chooseBestPropertyToDecideOn(String outputProperty, Iterable<State> examples,
        Iterable<String> inputProperties)
    {
        /*log.fine("private String chooseBestPropertyToDecideOn(String outputProperty, Collection<State> examples, " +
         "Collection<String> inputProperties): called");*/

        // for (State e : examples) /*log.fine(e);*/

        // Determine how many possible values (symbols) the output property can have.
        int numOutputValues =
            examples.iterator().next().getComponentType().getPropertyType(outputProperty).getNumPossibleValues();

        // Used to hold the largest information gain found so far.
        double largestGain = 0.0d;

        // Used to hold the input property that gives the largest gain found so far.
        String largestGainProperty = null;

        // Loop over all the input properties.
        for (String inputProperty : inputProperties)
        {
            // let G = the set of goal property values.
            // let A = the set of property values that the input property can have.

            // Determine how many possible values (symbols) the input property can have.
            int numInputValues =
                examples.iterator().next().getComponentType().getPropertyType(inputProperty).getNumPossibleValues();

            // Create an array to hold the counts of the output symbols.
            int[] outputCounts = new int[numOutputValues];

            // Create arrays to hold the counts of the input symbols and the joint input/output counts.
            int[] inputCounts = new int[numInputValues];
            int[][] jointCounts = new int[numInputValues][numOutputValues];

            // Loop over all the examples.
            for (State example : examples)
            {
                // Extract the output property attribute value.
                OrdinalAttribute outputAttribute = (OrdinalAttribute) example.getProperty(outputProperty);

                // Extract the input property attribute value.
                OrdinalAttribute inputAttribute = (OrdinalAttribute) example.getProperty(inputProperty);

                // Increment the count for the occurence of this value of the output property.
                outputCounts[outputAttribute.ordinal()]++;

                // Increment the count for the occurence of this value of the input property.
                inputCounts[inputAttribute.ordinal()]++;

                // Increment the count for the joint occurrence of this input/output value pair.
                jointCounts[inputAttribute.ordinal()][outputAttribute.ordinal()]++;
            }

            // Calculate the estimated probability distribution of G from the occurrence counts over the examples.
            double[] pForG = InformationTheory.pForDistribution(outputCounts);

            // Calculate the estimated probability distribution of A from the occurrence counts over the examples.
            double[] pForA = InformationTheory.pForDistribution(inputCounts);

            // Calculate the estimated probability distribution p(g|a) from the joint occurrence counts over the
            // examples.
            double[][] pForGGivenA = InformationTheory.pForJointDistribution(jointCounts);

            // Calculate the information gain on G by knowing A.
            double gain = InformationTheory.gain(pForG, pForA, pForGGivenA);

            // Check if the gain is larger than the best found so far and update the best if so.
            if (gain > largestGain)
            {
                largestGain = gain;
                largestGainProperty = inputProperty;
            }
        }

        return largestGainProperty;
    }
}


/*
 * 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.builder;

import com.thesett.aima.logic.fol.FloatLiteral;
import com.thesett.aima.logic.fol.Functor;
import com.thesett.aima.logic.fol.IntLiteral;
import com.thesett.aima.logic.fol.Term;
import com.thesett.aima.logic.fol.Variable;
import com.thesett.aima.logic.fol.VariableAndFunctorInterner;

/**
 * TermBuilder is a helper class for constructing Prolog terms.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *

 *

 *
 * @author Rupert Smith
 */
public class TermBuilder
{
    /** The interner used to intern all names. */
    private final VariableAndFunctorInterner interner;

    /**
     * Creates a term build using the supplied interner to intern functor and variable names.
     *
     * @param interner The interner to use.
     */
    public TermBuilder(VariableAndFunctorInterner interner)
    {
        this.interner = interner;
    }

    /**
     * Creates a source code position to be applied to the next term created.
     *
     * @param startLine   The start line position.
     * @param startColumn The start column position.
     * @param endLine     The end line position.
     * @param endColumn   The end column position.
     */
    public TermBuilder atPosition(int startLine, int startColumn, int endLine, int endColumn)
    {
        throw new UnsupportedOperationException("'atPosition' is not implemented yet.");
    }

    /**
     * Creates a functor.
     *
     * @param  name The name of the functor.
     * @param  args The functors arguments.
     *
     * @return A functor.
     */
    public Functor functor(String name, Term... args)
    {
        int internedName = interner.internFunctorName(name, args.length);

        return new Functor(internedName, args);
    }

    /**
     * Creates an atom (functor with no arguments).
     *
     * @param  name The name of the atom.
     *
     * @return An atom.
     */
    public Functor atom(String name)
    {
        int internedName = interner.internFunctorName(name, 0);

        return new Functor(internedName, null);
    }

    /**
     * Creates a variable. If the variable name begins with an underscore "_", it will be anonymous, otherwise it will
     * be named.
     *
     * @param  name The name of the variable.
     *
     * @return A variable.
     */
    public Variable var(String name)
    {
        boolean isAnonymous = name.startsWith("_");
        int internedName = interner.internVariableName(name);

        return new Variable(internedName, null, isAnonymous);
    }

    /**
     * Creates an integer.
     *
     * @param  value The value.
     *
     * @return An integer.
     */
    public IntLiteral integer(int value)
    {
        return new IntLiteral(value);
    }

    /**
     * Creates a floating point value.
     *
     * @param  value The value.
     *
     * @return A floating point number.
     */
    public FloatLiteral real(float value)
    {
        return new FloatLiteral(value);
    }
}


/*
 * 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.webapp.actions;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.beanutils.DynaBean;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;

/**
 * RedirectAction is a slightly alternative version of j_security_check that can be used to perform a login without
 * trying to access a protected resource. It is intended for use on web sites where users do not have to login to use
 * many of the sites features but can optionally log in for greater privileges/utility. The normal pattern of logging in
 * and protecting web resources works as follows:
 *
 *
User tries to access protected resource -> container redirects to login page -> container redirects to resource.
 *
 *
If the log in is optional it can occur at any time and the resource being accessed could be any page and not just
 * a protected one. This works as follows:
 *
 *
User is on unprotected page and enters log in details and then submits to this action -> container redirects to
 * login page, which can tell from its parameters that the log in details have been entered and automatically submits to
 * j_security check -> container authenticates and redirects to this action -> this action redirects to any desired page
 * passed in the original parameters to this action.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Bounce back to the calling location after a j_security_check log in.
 *

 *
 * @author Rupert Smith
 * @todo   A better name for this may be BounceBackAction. Its purpose is to be a protected resource which can bounce
 *         back to the calling location (or another location) when doing a log in at an arbitrary time. For example when
 *         a log in (or register)/logged in box should be displayed on every page and users can opt to log in whenever
 *         they like to gain greater access priveleges. Normal servlet log in only kicks in when a protected resource is
 *         explicitly accessed and the log in page cannot be navigated to directly or else j_security_check won't know
 *         where to redirect to.
 *
 * @struts.action
 *         name     = "redirectform"
 *         path     = "/protected/loginredirect"
 *         scope    = "request"
 *         validate = "false"
 */
public class RedirectAction extends BaseAction
{
    /** Used for logging. */
    private static final java.util.logging.Logger log =
        java.util.logging.Logger.getLogger(RedirectAction.class.getName());

    /** Constant defining the name of the request parameter that holds the redirect location. */
    private static final String REDIRECT = "redirect";

    /**
     * Handles an HTTP request sent to this action by struts. This simply forwards to the location specified by the
     * "redirect" request parameter.
     *
     * @param  mapping  The Struts action mapping.
     * @param  form     The Struts form associated with this action.
     * @param  request  The HTTP request.
     * @param  response The HTTP response.
     * @param  errors   The Struts action errors object to place any errors in.
     *
     * @return A forward to the "redirect" location.
     */
    public ActionForward executeWithErrorHandling(ActionMapping mapping, ActionForm form, HttpServletRequest request,
        HttpServletResponse response, ActionErrors errors)
    {
        log.fine("public ActionForward performWithErrorHandling(ActionMapping mapping, ActionForm form," +
            "HttpServletRequest request, HttpServletResponse response, " + "ActionErrors errors): called");

        HttpSession session = request.getSession();
        DynaBean dynaForm = (DynaActionForm) form;

        // Redirect to the specified location
        String redirect = (String) dynaForm.get(REDIRECT);

        log.fine("redirect = " + redirect);

        return new ActionForward(redirect, true);
    }
}


/*
 * 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;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * PagedList is a {@link java.util.List} that is built from another list that breaks the orignal list into a list of
 * pages or sub-lists from the original. It also provides a current page index which is useful for keeping track of a
 * current page.
 *
 *
There is also a secondary index which can be used simply as a marker. It is very useful when breaking down a list
 * of items to display into pages of X elements and then displaying a page control that lets a user select from Y to Y +
 * M pages. The secondary index can be used to hold Y to remember which page block the user is currently on. For example
 * a web search may return 1000 hits broken into pages of twenty. A page control will let the user select which page of
 * twenty hits from one to ten to view. A more button will let the user chose from pages 11 to 20 and so on. In this
 * case the secondary index would hold the start of the current block, for example, 10 and the primary index would hold
 * the currently viewed page, for example, 15.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Build from list.
 *
Build from collection.
 *
Extract indexed page.
 *
Calculate size.
 *
Manage current page index.
 *

 *
 * @author Rupert Smith
 */
public class PagedList<E> extends AbstractList<List<E>>
{
    /** Used for logging. */
    /* private static final Logger log = Logger.getLogger(PagedList.class.getName()); */

    /** Used to hold a reference to the list to be broken into pages. */
    private final List<E> original;

    /** Holds the page size. */
    private final int pageSize;

    /** Holds the current page. */
    private int currentPage;

    /** Holds a secondary page index, useful for grouping pages. */
    private int currentIndex;

    /**
     * Builds a paged list from a list. The list is split into pages of the specified size. The current page is
     * initialized to zero.
     *
     * @param list     the list to split into sub-lists
     * @param pageSize the size of the sub-lists
     */
    public PagedList(List<E> list, int pageSize)
    {
        this.original = list;
        this.pageSize = pageSize;
        currentPage = 0;
    }

    /**
     * Builds a paged list from a {@link java.util.Collection}. The collection is split into pages of the specified
     * size. The current page is initialized to zero.
     *
     * @param collection the collection to split into sub-lists
     * @param pageSize   the size of the sub-lists
     */
    public PagedList(Collection<E> collection, int pageSize)
    {
        this.original = new ArrayList<E>(collection);
        this.pageSize = pageSize;
        currentPage = 0;
    }

    /**
     * Extracts the sub-list at the specified page index. The returned list will have size equal to the page size unless
     * it is the last page in which case it may not be a full page.
     *
     * @param  index The sub-list index to extract.
     *
     * @return The sub-list as a {@link java.util.List}.
     *
     * @throws IndexOutOfBoundsException If the index is < 0 or larger that the number of pages (list size / page size +
     *                                   (list size % page size == 0 ? 0 : 1)).
     */
    public List<E> get(int index)
    {
        /*log.fine("public List<E> get(int index): called");*/

        // Check that the index is not to large.
        int originalSize = original.size();
        int size = (originalSize / pageSize) + (((originalSize % pageSize) == 0) ? 0 : 1);

        /*log.fine("originalSize = " + originalSize);*/
        /*log.fine("size = " + size);*/

        // Check if the size of the underlying list is zero, in which case return an empty list, so long as page zero
        // was requested.
        if ((index == 0) && (originalSize == 0))
        {
            return new ArrayList<E>();
        }

        // Check if the requested index exceeds the number of pages, or is an illegal negative value.
        if ((index >= size) || (index < 0))
        {
            /*log.fine("(index >= size) || (index < 0), throwing out of bounds exception.");*/
            throw new IndexOutOfBoundsException("Index " + index +
                " is less than zero or more than the number of pages: " + size);
        }

        // Extract the appropriate sub-list.
        // Note that if this is the last page it may not be a full page. Just up to the last page will be returned.
        /*log.fine("Requesting sublist from, " + (pageSize * index) + ", to ," +
            (((pageSize * (index + 1)) >= originalSize) ? originalSize : (pageSize * (index + 1))) + ".");*/

        List<E> result =
            original.subList(pageSize * index,
                ((pageSize * (index + 1)) >= originalSize) ? originalSize : (pageSize * (index + 1)));

        return result;
    }

    /**
     * Gets the number of pages.
     *
     * @return The number of pages.
     */
    public int size()
    {
        int size = original.size();

        return (size / pageSize) + (((size % pageSize) == 0) ? 0 : 1);
    }

    /**
     * Gets the index of the current page.
     *
     * @return The index of the current page.
     */
    public int getCurrentPage()
    {
        return currentPage;
    }

    /**
     * Sets the index of the current page. The {@link #getCurrent} method will return this page.
     *
     * @param page The current page to set.
     */
    public void setCurrentPage(int page)
    {
        this.currentPage = page;
    }

    /**
     * Gets the secondary index.
     *
     * @return The secondary index.
     */
    public int getCurrentIndex()
    {
        return currentIndex;
    }

    /**
     * Sets the secondary index.
     *
     * @param index The secondary index.
     */
    public void setCurrentIndex(int index)
    {
        this.currentIndex = index;
    }

    /**
     * Gets the current page as a list.
     *
     * @return The list at the current page.
     */
    public List<E> getCurrent()
    {
        return get(currentPage);
    }

    /**
     * Renders as a string for debugging purposes.
     *
     * @return A string for debugging purposes.
     */
    public String toString()
    {
        return "currentPage: " + currentPage + ", currentIndex: " + currentIndex + ", pageSize: " + pageSize +
            ", original: [" + original + "]";
    }
}


/*
 * 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.debugger.swing;

import java.awt.Adjustable;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.plaf.basic.BasicScrollBarUI;

/**
 * DiscreetScrollBarUI implements a new look&feel rendering for swing scroll bars, which is intended to be less
 * obtrusive and chunky.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Render scroll bar track.

 *
 Render scroll bar position indicator.

 *

 *
 * @author Rupert Smith
 */
public class DiscreetScrollBarUI extends BasicScrollBarUI
{
    /** The thickness of the scroll bar. */
    public static final int THICKNESS = 12;

    /** Provides the colors. */
    private final ToolingColorScheme colorScheme;

    /**
     * Creates a discreet scroll bar renderer.
     *
     * @param colorScheme The color scheme to use.
     */
    public DiscreetScrollBarUI(ToolingColorScheme colorScheme)
    {
        this.colorScheme = colorScheme;

    }

    /** {@inheritDoc} */
    public void installUI(JComponent c)
    {
        super.installUI(c);
        scrollbar.setFocusable(false);
    }

    /** {@inheritDoc} */
    public Dimension getMaximumSize(JComponent c)
    {
        int thickness = THICKNESS;

        return new Dimension(thickness, thickness);
    }

    /** {@inheritDoc} */
    public Dimension getMinimumSize(JComponent c)
    {
        return getMaximumSize(c);
    }

    /** {@inheritDoc} */
    public Dimension getPreferredSize(JComponent c)
    {
        return getMaximumSize(c);
    }

    /** {@inheritDoc} */
    public boolean getSupportsAbsolutePositioning()
    {
        return true;
    }

    /** {@inheritDoc} */
    protected void installListeners()
    {
        super.installListeners();
    }

    /** {@inheritDoc} */
    protected void uninstallListeners()
    {
        super.uninstallListeners();
    }

    /** {@inheritDoc} */
    protected void paintTrack(Graphics g, JComponent c, Rectangle bounds)
    {
        g.setColor(colorScheme.getToolingBackground());
        g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
    }

    /** {@inheritDoc} */
    protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
    {
        if (thumbBounds.isEmpty() || !scrollbar.isEnabled())
        {
            return;
        }

        g.translate(thumbBounds.x, thumbBounds.y);

        boolean vertical = isVertical();
        int hgap = vertical ? 2 : 1;
        int vgap = vertical ? 1 : 2;

        int w = thumbBounds.width - (hgap * 2);
        int h = thumbBounds.height - (vgap * 2);

        // leave one pixel between thumb and right or bottom edge
        if (vertical)
        {
            h -= 1;
        }
        else
        {
            w -= 1;
        }

        g.setColor(colorScheme.getToolingActiveBackground());
        g.fillRect(hgap + 1, vgap + 1, w - 1, h - 1);

        g.setColor(colorScheme.getToolingActiveBackground());
        g.drawRoundRect(hgap, vgap, w, h, 3, 3);
        g.translate(-thumbBounds.x, -thumbBounds.y);
    }

    /** {@inheritDoc} */
    protected Dimension getMinimumThumbSize()
    {
        int thickness = THICKNESS;

        return isVertical() ? new Dimension(thickness, thickness * 2) : new Dimension(thickness * 2, thickness);
    }

    /** {@inheritDoc} */
    protected JButton createIncreaseButton(int orientation)
    {
        return new InvisibleButton();
    }

    /** {@inheritDoc} */
    protected JButton createDecreaseButton(int orientation)
    {
        return new InvisibleButton();
    }

    /**
     * Checks if the scroll bar is a vertical one.
     *
     * @return <tt>true</tt> iff the scroll bar is vertical.
     */
    private boolean isVertical()
    {
        return scrollbar.getOrientation() == Adjustable.VERTICAL;
    }

    /**
     * InvisibleButton is a button with zero size. It is used because the {@link BasicScrollBarUI} component expects to
     * have up and down buttons. Replacing these with zero sized buttons allows the layout algorithm to be re-used from
     * there without having to restructure it.
     */
    private static class InvisibleButton extends JButton
    {
        /** Creates an invisible button. */
        private InvisibleButton()
        {
            setFocusable(false);
            setRequestFocusEnabled(false);
        }

        /** {@inheritDoc} */
        public Dimension getMaximumSize()
        {
            return new Dimension(0, 0);
        }

        /** {@inheritDoc} */
        public Dimension getPreferredSize()
        {
            return getMaximumSize();
        }

        /** {@inheritDoc} */
        public Dimension getMinimumSize()
        {
            return getMaximumSize();
        }
    }
}


/*
 * 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.swing.surface;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

import javax.swing.JPanel;
import javax.swing.RepaintManager;

/**
 * Surface is a Panel that generates a Graphics2D context onto which sub classes may render an image to be displayed in
 * the panel. Surface provides different properties and image types to select the exact nature of the image to render.
 * Subclasses need only provide a render method which takes a Graphics2D context as an argument and should draw into
 * this context without being aware of the colour depth and other parameters which are determined by the Surface
 * proprties.
 *
 *
The different image types are:
 *
 *

 *
0 automatic, (on screen for non animating, double buffered for animating)
 *
1 on screen, no buffered image is used, rendering is direct to the screen.
 *
2 to 15 buffered image types
 *
16 custom binary
 *
17 custom binary
 *
18 custom sgi
 *
19 custom sgi
 *

 *
 *
Rendering qaulity may be set or cleared. Higher quality means slower rendering, lower qaulity means faster
 * rendering.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *

 *
 * @author Rupert Smith
 */
public abstract class Surface extends JPanel
{
    /** Used for loging. */
    /* private static final Logger log = Logger.getLogger(Surface.class.getName()); */

    // Image type constants

    /** Automatic image type. */
    public static final int AUTO_SCREEN = 0;

    /** On screen image type, no buffered image is used, rendering is direct to the screen. */
    public static final int ON_SCREEN = 1;

    /** Off screen buffered image type. */
    public static final int OFF_SCREEN = 2;

    /** 24-bit RGB packed into last three bytes of a word. Direct color model. Off screen buffered. */
    public static final int INT_XRGB = 3;

    /** 32-bit Alpha and RGB. Direct color model. Off screen buffered. */
    public static final int INT_ARGB = 4;

    /** 32-bit Alpha and RGB. Direct color model. The RGB is pre-multiplied by the alpha. Off screen buffered. */
    public static final int INT_ARGB_PRE = 5;

    /** 24-bit RGB with direct color model. Off screen buffered. */
    public static final int INT_BGR = 6;

    /** 24-bit RGB with component color mode. Off screen buffered. */
    public static final int THREE_BYTE_BGR = 7;

    /** 32-bit Alpha and RGB. Component color model. Off screen buffered. */
    public static final int FOUR_BYTE_ABGR = 8;

    /** 32-bit Alpha and RGB. Component color model. The RGB is pre-multiplied by the alpha. Off screen buffered. */
    public static final int FOUR_BYTE_ABGR_PRE = 9;

    /** 5-bit red, 6-bit green and 5-bits blue. Direct color model. Off screen buffered. */
    public static final int USHORT_565_RGB = 10;

    /** 5-bit red, 5-bit green and 5-bits blue. Direct color model. Off screen buffered. */
    public static final int USHORT_X555_RGB = 11;

    /** 8-bit grey scale. Component color model. Off screen buffered. */
    public static final int BYTE_GRAY = 12;

    /** 16-bit grey scale. Component color model. Off screen buffered. */
    public static final int USHORT_GRAY = 13;

    /** 1-bit grey scale. Index color model. Off screen buffered. */
    public static final int BYTE_BINARY = 14;

    /** 8-bit grey scale. Index color model. Off screen buffered. */
    public static final int BYTE_INDEXED = 15;

    /** 2-bit grey scale. Index color model. Off screen buffered. */
    public static final int BYTE_BINARY_2_BIT = 16;

    /** 4-bit grey scale. Index color model. Off screen buffered. */
    public static final int BYTE_BINARY_4_BIT = 17;

    /** 32-bit color. Direct color model. Off screen buffered. */
    public static final int INT_RGBX = 18;

    /** 16-bit color. Direct color model. Off screen buffered. */
    public static final int USHORT_555X_RGB = 19;

    // Lookup tables for BYTE_BINARY 1, 2 and 4 bits.

    /** 1-bit intensity look up table. */
    static byte[] lut1Arr = new byte[] { 0, (byte) 255 };

    /** 2-bit intensity look up table. */
    static byte[] lut2Arr = new byte[] { 0, (byte) 85, (byte) 170, (byte) 255 };

    /** 4-bit intensity look up table. */
    static byte[] lut4Arr =
        new byte[]
        {
            0, (byte) 17, (byte) 34, (byte) 51, (byte) 68, (byte) 85, (byte) 102, (byte) 119, (byte) 136, (byte) 153,
            (byte) 170, (byte) 187, (byte) 204, (byte) 221, (byte) 238, (byte) 255
        };

    /** Defines quick reference to the anti aliasing on constant defined in RenderingHints. */
    public Object antiAlias = RenderingHints.VALUE_ANTIALIAS_ON;

    /** Defines quick reference to the fast rendering constant defined in RenderingHints. */
    public Object rendering = RenderingHints.VALUE_RENDER_SPEED;

    /** Compositing algorithm. */
    public AlphaComposite composite;

    /** Background texture to initialize the image with. */
    public Paint texture;

    /** The image buffer that this Surface displays. */
    public BufferedImage bimg;

    /** The type of image to render to. See image type constants. */
    public int imageType;

    /** Sets whether or not the image is cleared before every call to the render method. */
    public boolean clearSurface = true;

    /** Demos using animated gif's that implement ImageObserver set dontThread. */
    public boolean dontThread;

    /** The amount to sleep between animation frames. */
    protected long sleepAmount = 50;

    /** Reference to the AWT implementing Toolkit. */
    private Toolkit toolkit;

    /** Holds the size of teh current buffered image. */
    private int biw, bih;

    /** Flag used by the paint method to signal that the image should initially be cleared. */
    private boolean clearOnce;

    /** Creates a new Surface object. */
    public Surface()
    {
        toolkit = getToolkit();
        setImageType(0);
    }

    /**
     * Subclasses that extend Surface must implement this routine to paint in the graphics context.
     *
     * @param w  The width of the Graphics2D context.
     * @param h  The height of the Graphics2D context.
     * @param g2 The Graphics2D context to draw into.
     */
    public abstract void render(int w, int h, Graphics2D g2);

    /**
     * Returns the image type.
     *
     * @return The image type in use.
     */
    public int getImageType()
    {
        return imageType;
    }

    /**
     * Sets the image type.
     *
     * @param imgType Sets the image type to use.
     */
    public void setImageType(int imgType)
    {
        // Check if auto image type selection is set
        if (imgType == 0)
        {
            // Use direct to screen rendering
            imageType = 1;
        }
        else
        {
            imageType = imgType;
        }

        // Clear any existing buffered image so that the new type will take effect
        bimg = null;
    }

    /**
     * Turn anti-aliasing on or off.
     *
     * @param aa Set to true to use ani-aliasing and false to not use it.
     */
    public void setAntiAlias(boolean aa)
    {
        antiAlias = aa ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF;
    }

    /**
     * Sets the rendering quality flag. Set to false for speed, tru for quality.
     *
     * @param rd Set to false for speed, tru for quality.
     */
    public void setRendering(boolean rd)
    {
        rendering = rd ? RenderingHints.VALUE_RENDER_QUALITY : RenderingHints.VALUE_RENDER_SPEED;
    }

    /**
     * Sets the kind of paint to use. For the image background?
     *
     * @param obj The paint to use.
     */
    public void setTexture(Paint obj)
    {
        if (obj instanceof GradientPaint)
        {
            texture = new GradientPaint(0, 0, Color.white, getSize().width * 2, 0, Color.green);
        }
        else
        {
            texture = obj;
        }
    }

    /**
     * Sets whether or not to alpha composite the image.
     *
     * @param cp Set to true to use alpha compositing, false to not use it.
     */
    public void setComposite(boolean cp)
    {
        composite = cp ? AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f) : null;
    }

    /**
     * Sets amount to sleep between animation frames.
     *
     * @param amount The time in milliseconds to wait between animation frames.
     */
    public void setSleepAmount(long amount)
    {
        sleepAmount = amount;
    }

    /**
     * Gets the amount to sleep between animation frames.
     *
     * @return The amount of sleep time set between animation frames.
     */
    public long getSleepAmount()
    {
        return sleepAmount;
    }

    /**
     * Immediately repaints the surface.
     *
     *
It's possible to turn off double-buffering for just the repaint calls invoked directly on the non double
     * buffered component. This can be done by overriding paintImmediately() (which is called as a result of repaint)
     * and getting the current RepaintManager and turning off double buffering in the RepaintManager before calling
     * super.paintImmediately(g).
     *
     * @param x The X coord to start painting at.
     * @param y The Y coord to start painting at.
     * @param w The width of the region to paint.
     * @param h The height of the region to paint.
     */
    public void paintImmediately(int x, int y, int w, int h)
    {
        RepaintManager repaintManager = null;
        boolean save = true;

        if (!isDoubleBuffered())
        {
            repaintManager = RepaintManager.currentManager(this);
            save = repaintManager.isDoubleBufferingEnabled();
            repaintManager.setDoubleBufferingEnabled(false);
        }

        super.paintImmediately(x, y, w, h);

        if (repaintManager != null)
        {
            repaintManager.setDoubleBufferingEnabled(save);
        }
    }

    /**
     * Renders the surface. This method will create a buffered image to render in if one does not already exist. If
     * rendering is to be done directly to the screen no buffered image will be generated. If the size of the buffered
     * image does not match the Graphics context size then a new buffered image will be generated of the correct size
     * before rendering. The {@link #render} method will be called to do the actual drawing.
     *
     * @param g The graphics context to paint.
     */
    public void paint(Graphics g)
    {
        // Get the size of the surface
        Dimension d = getSize();

        // Check if direct to screen rendering is to be used
        if (imageType == 1)
        {
            // Use no buffered image for direct rendering
            bimg = null;
        }

        // Check if no buffered image has been created yet or if the existing one has the wrong size
        else if ((bimg == null) || (biw != d.width) || (bih != d.height))
        {
            // Create a new bufferd image. Note that imageType has two subtracted for it as type 0 stands for auto
            // selection and type 1 for direct to screen. Subtracting two corresponds to the image types in the
            // BufferedImage class.
            bimg = createBufferedImage(d.width, d.height, imageType - 2);

            // Set clear once flag to clear the new image on the first pass
            clearOnce = true;
        }

        // Build a Graphics2D context for the image
        Graphics2D g2 = createGraphics2D(d.width, d.height, bimg, g);

        // Call the subclass to perform rendering
        render(d.width, d.height, g2);

        // Clear up the Graphics2D context
        g2.dispose();

        // Check if a buffered image was used (and not direct to screen rendering)
        if (bimg != null)
        {
            // Copy the buffered image onto the screen
            g.drawImage(bimg, 0, 0, null);

            // Ensure screen is up to date
            toolkit.sync();
        }
    }

    /**
     * Generates a fresh buffered image of the appropriate type.
     *
     * @param  w       The image width.
     * @param  h       The image height.
     * @param  imgType The image type. Note that this should be two less than the image types defined as constants in
     *                 this class to correspond with the image type constants defined in BufferedImage. This class uses
     *                 0 for auto image type selection and 1 for direct to screen.
     *
     * @return A buffered image of the specifed size and type.
     */
    protected BufferedImage createBufferedImage(int w, int h, int imgType)
    {
        BufferedImage bi = null;

        if (imgType == 0)
        {
            bi = (BufferedImage) createImage(w, h);
        }
        else if ((imgType > 0) && (imgType < 14))
        {
            bi = new BufferedImage(w, h, imgType);
        }
        else if (imgType == 14)
        {
            bi = createBinaryImage(w, h, 2);
        }
        else if (imgType == 15)
        {
            bi = createBinaryImage(w, h, 4);
        }
        else if (imgType == 16)
        {
            bi = createSGISurface(w, h, 32);
        }
        else if (imgType == 17)
        {
            bi = createSGISurface(w, h, 16);
        }

        // Store the buffered image size
        biw = w;
        bih = h;

        return bi;
    }

    /**
     * Creates a Graphics2D drawing context from a BufferedImage or Graphics context. The graphics context is built
     * using the properties defined for the surface. This method is used to generate the Graphics2D context that
     * subclasses will render in. IF the buffered image is null then the passed in Graphics context will be used to
     * generate the Graphics2D context. This is the case when no buffered image is used and the subclass renders
     * straight to the screen.
     *
     * @param  width  The width of the buffered image.
     * @param  height The height of the buffered image.
     * @param  bi     The buffered image to generate a Graphics2D context for, or null if the next param is to be used.
     * @param  g      The Graphics context to generate a Graphics2D context for, only used if bi is null.
     *
     * @return A Graphics2D drawing context of the specified size and type.
     */
    protected Graphics2D createGraphics2D(int width, int height, BufferedImage bi, Graphics g)
    {
        Graphics2D g2 = null;

        // Check if the buffered image is null
        if (bi != null)
        {
            // Create Graphics2D context for the buffered image
            g2 = bi.createGraphics();
        }
        else
        {
            // The buffered image is null so create Graphics2D context for the Graphics context
            g2 = (Graphics2D) g;
        }

        // @todo what is this for?
        g2.setBackground(getBackground());

        // Set the rendering properties of the graphics context
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias);
        g2.setRenderingHint(RenderingHints.KEY_RENDERING, rendering);

        // Check the clear flags to see if the graphics context should be cleared
        if (clearSurface || clearOnce)
        {
            // Clear the image
            g2.clearRect(0, 0, width, height);

            // Reset the clear once flag to show that clearing has been done
            clearOnce = false;
        }

        // Check if a background fill texture is to be used
        if (texture != null)
        {
            // set composite to opaque for texture fills
            g2.setComposite(AlphaComposite.SrcOver);
            g2.setPaint(texture);
            g2.fillRect(0, 0, width, height);
        }

        // Check if alpha compositing is to be used
        if (composite != null)
        {
            // Set the alpha compositing algorithm
            g2.setComposite(composite);
        }

        return g2;
    }

    /**
     * Creates a custom grey-scale binary image format.
     *
     * @param  w         The image width.
     * @param  h         The image height.
     * @param  pixelBits The number of grey-scale bits to use. Must be 1, 2 or 4.
     *
     * @return A buffered image of the specifed size and type.
     */
    private BufferedImage createBinaryImage(int w, int h, int pixelBits)
    {
        int bytesPerRow = w * pixelBits / 8;

        if ((w * pixelBits % 8) != 0)
        {
            bytesPerRow++;
        }

        byte[] imageData = new byte[h * bytesPerRow];
        IndexColorModel cm = null;

        switch (pixelBits)
        {
        case 1:
        {
            cm = new IndexColorModel(pixelBits, lut1Arr.length, lut1Arr, lut1Arr, lut1Arr);
            break;
        }

        case 2:
        {
            cm = new IndexColorModel(pixelBits, lut2Arr.length, lut2Arr, lut2Arr, lut2Arr);
            break;
        }

        case 4:
        {
            cm = new IndexColorModel(pixelBits, lut4Arr.length, lut4Arr, lut4Arr, lut4Arr);
            break;
        }

        default:
        {
            new Exception("Invalid # of bit per pixel").printStackTrace();
        }
        }

        DataBuffer db = new DataBufferByte(imageData, imageData.length);
        WritableRaster r = Raster.createPackedRaster(db, w, h, pixelBits, null);

        return new BufferedImage(cm, r, false, null);
    }

    /**
     * Creates a custom colour image format.
     *
     * @param  w         The image width.
     * @param  h         The image height.
     * @param  pixelBits The number of bits-per-pixel to use. Must be 16 or 32.
     *
     * @return A buffered image of the specifed size and type.
     */
    private BufferedImage createSGISurface(int w, int h, int pixelBits)
    {
        int rMask32 = 0xFF000000;
        int rMask16 = 0xF800;
        int gMask32 = 0x00FF0000;
        int gMask16 = 0x07C0;
        int bMask32 = 0x0000FF00;
        int bMask16 = 0x003E;
        DirectColorModel dcm = null;
        DataBuffer db = null;
        WritableRaster wr = null;

        switch (pixelBits)
        {
        case 16:
        {
            short[] imageDataUShort = new short[w * h];
            dcm = new DirectColorModel(16, rMask16, gMask16, bMask16);
            db = new DataBufferUShort(imageDataUShort, imageDataUShort.length);
            wr = Raster.createPackedRaster(db, w, h, w, new int[] { rMask16, gMask16, bMask16 }, null);

            break;
        }

        case 32:
        {
            int[] imageDataInt = new int[w * h];
            dcm = new DirectColorModel(32, rMask32, gMask32, bMask32);
            db = new DataBufferInt(imageDataInt, imageDataInt.length);
            wr = Raster.createPackedRaster(db, w, h, w, new int[] { rMask32, gMask32, bMask32 }, null);

            break;
        }

        default:
        {
            new Exception("Invalid # of bit per pixel").printStackTrace();
        }

        }

        return new BufferedImage(dcm, wr, false, null);
    }
}


/*
 * 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.printer;

import com.thesett.aima.logic.fol.Predicate;
import com.thesett.aima.logic.fol.VariableAndFunctorInterner;
import com.thesett.common.util.doublemaps.SymbolTable;

/**
 * Pretty printer for compiled predicates.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Pretty print compiled predicates with internal information about the compilation.
 *

 *
 * @author Rupert Smith
 */
public class WAMCompiledPredicatePrintingVisitor extends WAMCompiledTermsPrintingVisitor
{
    /**
     * Creates a pretty printing visitor for clauses being compiled in WAM.
     *
     * @param interner    The symbol name table.
     * @param symbolTable The symbol table for the compilation.
     * @param result      A string buffer to place the results in.
     */
    public WAMCompiledPredicatePrintingVisitor(VariableAndFunctorInterner interner,
        SymbolTable<Integer, String, Object> symbolTable, StringBuffer result)
    {
        super(interner, symbolTable, result);
    }

    /** {@inheritDoc} */
    public void visit(Predicate predicate)
    {
        if (traverser.isEnteringContext())
        {
            initializePrinters();
        }
        else if (traverser.isLeavingContext())
        {
            printTable();
        }

        super.visit(predicate);
    }
}


/*
 * 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.tck.support.mockwebserver;

import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;

/**
 * Utility methods for working with {@code MockWebServer}.
 */
public final class MockWebServerUtils
{
	// ----------------------------------------------------------------------------------------------------------------
	// constructors
	// ----------------------------------------------------------------------------------------------------------------

	private MockWebServerUtils()
	{
		throw new AssertionError();
	}

	// ----------------------------------------------------------------------------------------------------------------
	// public methods
	// ----------------------------------------------------------------------------------------------------------------

	public static String url(MockWebServer server)
	{
		return url(server, "/");
	}

	public static String url(MockWebServer server, String path)
	{
		return server.url(path).toString();
	}

	/**
	 * Workaround MockWebServer issue #11.
	 *
	 * @see Issue #11
	 */
	public static RecordedRequest takeRequest(MockWebServer server) throws InterruptedException
	{
		RecordedRequest request = server.takeRequest();

		while ("GET /favicon.ico HTTP/1.1".equals(request.getRequestLine()))
		{
			request = server.takeRequest();
		}

		return request;
	}
}


/*
 * 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.attribute.validation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import com.thesett.aima.attribute.impl.EnumeratedStringAttribute;

/**
 * ValidEnumValidator checks that a value contains a valid instance of an
 * {@link com.thesett.aima.attribute.impl.EnumeratedStringAttribute}. To be considered valid, the value must have an id
 * on it, that is not <tt>-1</tt>. <tt>null</tt> values are also considered to be valid, as null checking can be handled
 * by other validations.
 *
 *

CRC Card

 *
 Responsibilities 	 Collaborations
 *
 Check that an instance of an enum has a valid id.

 *

 */
public class ValidEnumValidator implements ConstraintValidator<ValidEnum, EnumeratedStringAttribute>
{
    /** The name of the enumeration type the the value must be a valid instance of. */
    private String enumTypeName;

    /** {@inheritDoc} */
    public void initialize(ValidEnum constraintAnnotation)
    {
        this.enumTypeName = constraintAnnotation.value();
    }

    /** {@inheritDoc} */
    public boolean isValid(EnumeratedStringAttribute value, ConstraintValidatorContext context)
    {
        if (value == null)
        {
            return true;
        }

        return value.getId() != -1;
    }
}


/*
Copyright (c) 2013, Groupon, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

Neither the name of GROUPON nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.groupon.novie.internal.controllers;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import com.groupon.novie.internal.validation.*;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.groupon.novie.SchemaDefinition;
import com.groupon.novie.internal.GenericSchemaService;
import com.groupon.novie.internal.SchemaDefinitionRepository;
import com.groupon.novie.internal.exception.InvalidParameterException;
import com.groupon.novie.internal.exception.InvalidSchemaException;
import com.groupon.novie.internal.exception.ServiceException;
import com.groupon.novie.internal.response.Report;
import com.groupon.novie.internal.response.ReportRecord;

/**
 *

 * Generic Controller with the purpose of dispatching every request to the
 * desired {@link SchemaDefinition} implementation.
 *

 *

 * The controller performs runtime validation of parameters based on the config
 * parameter.
 *

 *

 * The controller has one handler for each of the following Accept Headers:
 *

 *
application/json

 *
application/xml

 *
text/csv

 *

 *

 *
 * @author ricardo
 * @author damiano
 * @author thomas
 * @since July 1, 2013
 */
@Controller
@RequestMapping(method = RequestMethod.GET)
public class ApiController {

    /**
     *

     * Repository of available Schemas.
     *

     *

     *

     * A repository is used as the schema is selected at runtime.
     *

     */
    @Autowired
    private SchemaDefinitionRepository schemaRepository;

    /**
     *

     * Generic Schema Service that will call a SQL Generation Engine, based on
     * supplied constraints, and aggregate the result.
     *

     */
    @Autowired
    private GenericSchemaService schemaService;

    /**
     *

     * Logger object for ApiController Class.
     *

     */
    private static final Logger LOG = LoggerFactory.getLogger(ApiController.class);

    /**
     *

     * List of mandatory domain specific parameters.
     *

     *

     *

     * Avoids using magic numbers.
     *

     */
    private static final Set<String> DOMAIN_PARAMETERS;

    /**
     *

     * Specific mandatory field for every Star Schema.
     *

     */
    private static final String[] MANDATORY_FIELDS = {"group"};

    static {
        DOMAIN_PARAMETERS = new HashSet<String>();
        DOMAIN_PARAMETERS.add("group");
        DOMAIN_PARAMETERS.add("sort");
        DOMAIN_PARAMETERS.add("timezone");
        DOMAIN_PARAMETERS.add("pageSize");
        DOMAIN_PARAMETERS.add("page");
        DOMAIN_PARAMETERS.add("pageKey");
    }

    /**
     *

     * Handles requests that accept application/json.
     *

     */
    @RequestMapping(value = {"/{schema:[^\\.]+}", "/{schema}.json"}, produces = "application/json", headers = {"Accept=application/json"})
    @ResponseBody
    public Report processJsonRequest(@PathVariable String schema, HttpServletRequest request) throws ServiceException, InvalidParameterException,
            InvalidSchemaException {
        LOG.info("Processing JSON request for parameters " + request.getQueryString());
        return processRequest(schema, request);
    }

    /**
     *

     * Handles requests that accept application/xml.
     *

     */
    @RequestMapping(value = {"/{schema:[^\\.]+}", "/{schema}.xml"}, produces = "application/xml", headers = {"Accept=application/xml, text/xml"})
    @ResponseBody
    public Report processXmlRequest(@PathVariable String schema, HttpServletRequest request) throws ServiceException, InvalidParameterException,
            InvalidSchemaException {
        LOG.info("Processing XML request for parameters " + request.getQueryString());
        return processRequest(schema, request);
    }

    /**
     *

     * Handles requests that accept text/csv.
     *

     */
    @RequestMapping(value = {"/{schema}:[^\\.]+", "/{schema}.csv"}, produces = "text/csv", headers = {"Accept=text/csv"})
    @ResponseBody
    public
    Report processCsvRequest(@PathVariable String schema, HttpServletRequest request) throws ServiceException, InvalidParameterException,
            InvalidSchemaException {
        LOG.info("Processing CSV request for parameters " + request.getQueryString());
        return processRequest(schema, request);
    }

    /**
     *

     * Generic processor of all types of requests.
     *

     *

     * Validates schema availability, mandatory fields for the specified schema
     * and creates chains of {@link QueryParameterAware} to be processed by the
     * underlying {@link GenericSchemaService}.
     *

     *
     * @param schema  Schema from where to extract the information.
     * @param request Request in generic form so the parameters can be dynamically
     *                extract.
     *
     * @return Returns a {@link Report} Object delivered by the {@link GenericSchemaService}.
     *
     * @throws ServiceException          Exception generated by the underlying Service.
     * @throws InvalidParameterException Exception generated by the controller due to an invalid
     *                                   parameter for the chosen schema.
     * @throws InvalidSchemaException    Exception generated by the controller when an invalid schema
     *                                   is requested.
     */
    @SuppressWarnings("unchecked")
    private Report processRequest(String schema, HttpServletRequest request) throws ServiceException, InvalidParameterException,
            InvalidSchemaException {

        long begin = System.currentTimeMillis();

        SchemaDefinition config = schemaRepository.getStarSchemaConfig(schema);
        if (config == null) {
            LOG.warn("Invalid endpoint \"" + schema + "\"");
            throw new InvalidSchemaException("Invalid endpoint");
        }

        Map<String, String[]> params = request.getParameterMap();

        for (String mandatory : MANDATORY_FIELDS) {
            if (!params.containsKey(mandatory)) {
                // mandatory fields are case sensitive
                LOG.error("Mandatory field \"" + mandatory + "\" missing");
                throw new InvalidParameterException("Missing mandatory field " + mandatory);
            }
        }

        QueryParameterEnvelope queryParameterDto = new QueryParameterEnvelope();

        for (String key : params.keySet()) {

            if (DOMAIN_PARAMETERS.contains(key)) {
                String domainParameter = params.get(key)[0];
                final DomainParserAware systemParameter = SystemParameter.valueOf(key);
                systemParameter.parseValue(domainParameter, config, queryParameterDto);

            } else {

                Pair<String, String> pair = splitKey(key);
                if (!config.isValidDimension(pair.getLeft(), pair.getRight())) {
                    LOG.warn("Invalid parameter \"" + key + "\" for endpoint \"" + schema + "\".");
                    throw new InvalidParameterException("Invalid parameter " + key + " for endpoint " + schema + "");
                }

                String[] multiParam = request.getParameterValues(key);
                for (String param : multiParam) {
                    queryParameterDto.addConstraints(new HttpQueryConstraint(config, key, param));
                }
            }
        }
        final Report report = schemaService.generateReport(config, queryParameterDto);
        report.setRecords(inMemoryPagination(report.getRecords(), queryParameterDto));
        if (LOG.isInfoEnabled()) {
            LOG.info("Query successfully processed for schema " + schema + " in " + (System.currentTimeMillis() - begin) + "s.");
        }
        return report;
    }

    /**
     *

     * Handles {@link ServiceException} generated by the underlying layer.
     *

     *
     * @param exception of type {@link ServiceException}
     *
     * @return A {@link ResponseEntity} with the {@link HttpStatus#INTERNAL_SERVER_ERROR}
     *         code and a message describing the error itself.
     */
    @ExceptionHandler(ServiceException.class)
    @ResponseBody
    public ResponseEntity<String> handleServiceException(ServiceException exception) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.TEXT_PLAIN);
        return new ResponseEntity<String>(exception.getMessage(), headers, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    /**
     *

     * Handles {@link InvalidParameterException} generated by the controller
     * itself when receives an invalid parameter.
     *

     *
     * @param exception of type {@link InvalidParameterException}
     * @return A {@link ResponseEntity} with the {@link HttpStatus#BAD_REQUEST}
     * code and a message describing the error itself.
     */
    @ExceptionHandler(InvalidParameterException.class)
    @ResponseBody
    public ResponseEntity<String> handleControllerException(InvalidParameterException exception) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.TEXT_PLAIN);
        return new ResponseEntity<String>(exception.getMessage(), headers, HttpStatus.BAD_REQUEST);
    }

    /**
     *

     * Handles {@link InvalidSchemaException} generated by the controller itself
     * when receives a request to a non existing schema configuration.
     *

     *
     * @param exception of type {@link InvalidSchemaException}
     * @return A {@link ResponseEntity} with the {@link HttpStatus#NOT_FOUND}
     * code and a message describing the error itself.
     */
    @ExceptionHandler(InvalidSchemaException.class)
    @ResponseBody
    public ResponseEntity<String> handleInvalidSchemaException(InvalidSchemaException exception) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.TEXT_PLAIN);
        return new ResponseEntity<String>(exception.getMessage(), headers, HttpStatus.NOT_FOUND);
    }

    /**
     *

     * Creates a {@link Pair} composed of Dimension and Information, required by
     * the {@link GenericSchemaService}.
     *

     *
     * @param key
     * @return
     */
    private static Pair<String, String> splitKey(String key) {
        String dimension = null;
        String info = null;

        if (!key.contains(".")) {
            dimension = key;
        } else {
            dimension = key.substring(0, key.indexOf('.'));
            info = key.substring(key.indexOf('.') + 1, key.length());
        }
        return Pair.of(dimension, info);
    }

    /**
     * TODO to be removed
     *
     * @param recordList
     * @param queryParameterEnvelope
     * @return
     */
    private List<ReportRecord> inMemoryPagination(final List<ReportRecord> recordList, QueryParameterEnvelope queryParameterEnvelope) {
        final QueryParameterAware pageSize = queryParameterEnvelope.getPageSize();
        final QueryParameterAware pageNumber = queryParameterEnvelope.getPageNumber();
        if (pageSize != null && pageNumber != null) {
            final Integer pageSizeValue = ((PageSizeConstraint) pageSize).getPageSizeValue();
            final Integer pageValue = ((PageNumberConstraint) pageNumber).getPageNumberValue() - 1;
            int offset = pageValue * pageSizeValue;
            return recordList.subList(offset > recordList.size() ? recordList.size() : offset, offset + pageSizeValue > recordList.size()
                    ? recordList.size() : offset + pageSizeValue);
        }
        return recordList;
    }
}


/*
 * 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;

/**
 * UndoStackBase implements a stack of operations with nested save points, and the ability to undo operations up to the
 * most recent save point. This assists in the implementation of arbitrary depth undo operations. Specific
 * implementations need to define and implement a set of operators for the operations they are capable of undoing.
 *
 *
The {@link #save} and {@link #restore} methods are nestable, in that a sequence of saves will place save markers
 * onto the stack, and subsequent restores will back up to subsequent save points.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *

 *
 * @author Rupert Smith
 */
public abstract class UndoStackBase implements UndoStack
{
    /** Holds the stack of undoable operations. */
    protected Queue<Undoable> undoStack = new StackQueue<Undoable>();

    /** {@inheritDoc} */
    public void save()
    {
        undoStack.offer(new SavePoint());
    }

    /** {@inheritDoc} */
    public void restore()
    {
        boolean foundSavePoint = false;

        while (!foundSavePoint && !undoStack.isEmpty())
        {
            Undoable undoable = undoStack.poll();
            undoable.undo();

            foundSavePoint = (undoable instanceof SavePoint);
        }
    }

    /**
     * A save point is a special marker undoable operation that represents a point up to which operations should be
     * undone.
     */
    public static class SavePoint implements Undoable
    {
        /**
         * {@inheritDoc}
         *
         *
This implementation does nothing, as the save point is simply a marker.
         */
        public void undo()
        {
        }
    }
}


/*
Copyright (c) 2013, Groupon, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

Neither the name of GROUPON nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.groupon.novie.internal.engine.schema;

import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.groupon.novie.engine.ColumnDataType;
import org.apache.commons.lang3.StringUtils;

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

/**
 * Object representing an Sql Table should be immutable
 */
public class SqlTable implements SqlElement {

    private String tableName;

    /**
     * Map<Column identifier, Column>
     */
    private Map<String, AbstractSqlColumn> sqlTableColumns;
    /**
     * Map<ForeignTable,Map<Local column,Foreign column>>
     */
    private Map<SqlTable, Map<AbstractSqlColumn, AbstractSqlColumn>> sqlForeignKeys;

    public SqlTable(String tableName) {
        this.tableName = tableName;
        sqlTableColumns = Maps.newLinkedHashMap();
        sqlForeignKeys = Maps.newHashMap();
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    /**
     * Checks if the give column exist (by name)
     *
     * @param columnName    The name of the {@link AbstractSqlColumn SqlColumn}
     */
    public final boolean containsColumn(String columnName) {
        if (StringUtils.isNotBlank(columnName)) {
            return sqlTableColumns.containsKey(columnName);
        }
        return false;
    }

    @Override
    public String getSqlString() {
        return tableName;
    }

    public Optional<AbstractSqlColumn> getColumnByName(String columnName) {
        return Optional.fromNullable(sqlTableColumns.get(columnName));
    }

    void addForeignKey(AbstractSqlColumn localColumn, AbstractSqlColumn foreignColumn) {
        if (!sqlForeignKeys.containsKey(foreignColumn.getSqlTable())) {
            sqlForeignKeys.put(foreignColumn.getSqlTable(), new HashMap<AbstractSqlColumn, AbstractSqlColumn>());
        }
        sqlForeignKeys.get(foreignColumn.getSqlTable()).put(localColumn, foreignColumn);
    }

    public Map<AbstractSqlColumn, AbstractSqlColumn> findForeignKeyForTable(final SqlTable sqlTable) {
        if (sqlForeignKeys.containsKey(sqlTable)) {
            return sqlForeignKeys.get(sqlTable);
        }
        return Collections.emptyMap();
    }

    public List<AbstractSqlColumn> getListOfColumn() {
        return Collections.unmodifiableList(Lists.newArrayList(sqlTableColumns.values()));
    }

    public List<SqlTable> getLinkedTables() {
        List<SqlTable> listForeignTables = Lists.newArrayList(sqlForeignKeys.keySet());
        return Collections.unmodifiableList(listForeignTables);
    }

    public final PhysicalSqlColumn addTableColumn(String columnName, ColumnDataType type) {
        PhysicalSqlColumn returnValue = new PhysicalSqlColumn(this, columnName, type, getNextColumnOrder());
        addTableColumn(returnValue);
        return returnValue;
    }

    protected final void addTableColumn(AbstractSqlColumn col) {
        sqlTableColumns.put(col.getColumnIdentifier(), col);
    }

    protected final int getNextColumnOrder() {
        return sqlTableColumns.size();
    }

    @Override
    public String toString() {
        StringBuilder returnValue = new StringBuilder("Table ");
        returnValue.append(this.getTableName());
        return returnValue.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        SqlTable sqlTable = (SqlTable) o;

        if (!sqlTableColumns.equals(sqlTable.sqlTableColumns)) {
            return false;
        }
        if (!tableName.equals(sqlTable.tableName)) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = tableName != null ? tableName.hashCode() : 0;
        result = 31 * result + (sqlTableColumns != null ? sqlTableColumns.hashCode() : 0);
        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.aima.logic.fol.wam.printer;

import com.thesett.aima.logic.fol.Clause;
import com.thesett.aima.logic.fol.Predicate;
import com.thesett.aima.logic.fol.VariableAndFunctorInterner;
import com.thesett.aima.logic.fol.compiler.PositionalTermTraverser;
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 com.thesett.aima.logic.fol.wam.compiler.WAMLabel;
import com.thesett.aima.logic.fol.wam.compiler.WAMOptimizeableListing;
import com.thesett.common.util.doublemaps.SymbolTable;
import com.thesett.text.api.model.TextTableModel;

/**
 * LabelPrinter prints labels for any bytecode instructions that are labelled.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Print any labels on the bytecode instructions.
 *

 *
 * @author Rupert Smith
 */
public class UnoptimizedLabelPrinter extends BasePrinter
{
    /**
     * Creates a printer.
     *
     * @param interner    The name interner.
     * @param symbolTable The compiler symbol table.
     * @param traverser   The positional context traverser.
     * @param column      The column to print to.
     * @param table       The table to inform of cell sizes and positions.
     */
    public UnoptimizedLabelPrinter(VariableAndFunctorInterner interner,
        SymbolTable<Integer, String, Object> symbolTable, PositionalTermTraverser traverser, int column,
        TextTableModel table)
    {
        super(interner, symbolTable, traverser, column, table);
    }

    /** {@inheritDoc} */
    protected void enterClause(Clause clause)
    {
        if (clause instanceof WAMCompiledQuery)
        {
            WAMOptimizeableListing query = (WAMCompiledQuery) clause;

            for (WAMInstruction instruction : query.getUnoptimizedInstructions())
            {
                WAMLabel label = instruction.getLabel();
                addLineToRow((label != null) ? (label.toPrettyString() + ":") : "");
                nextRow();
            }
        }
    }

    /** {@inheritDoc} */
    protected void enterPredicate(Predicate predicate)
    {
        if (predicate instanceof WAMCompiledPredicate)
        {
            WAMOptimizeableListing compiledPredicate = (WAMCompiledPredicate) predicate;

            for (WAMInstruction instruction : compiledPredicate.getUnoptimizedInstructions())
            {
                WAMLabel label = instruction.getLabel();
                addLineToRow((label != null) ? (label.toPrettyString() + ":") : "");
                nextRow();
            }
        }
    }
}