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

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;

import com.thesett.aima.logic.fol.FunctorName;
import com.thesett.aima.logic.fol.VariableAndFunctorInterner;
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.machine.WAMCodeView;
import com.thesett.common.util.SizeableList;
import com.thesett.common.util.doublemaps.DoubleKeyedMap;
import com.thesett.text.api.model.TextTableModel;

/**
 * ByteCodeMonitor responds to changes in the byte code loaded into the target machine. Byte-code is disassembled and
 * inserted into a {@link TextTableModel}.
 *
 *
The instructions vary in size, so mapping between addresses in the code, and rows in the table model is not
 * straightforward. This monitor maintains this mapping, and provides the methods {@link #getAddressForRow(int)} and
 * {@link #getRowForAddress(int)} to access it. This can be used to work out which instruction has been selected in the
 * table, or which row in the table to highlight when a particular address is being stepped, and so on.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Maintain a table model containing disassembled byte-code.

 *
 Maintain a mapping between code addresses and table rows.

 *

 *
 * @author Rupert Smith
 */
public class ByteCodeMonitor
{
    /** Used for debugging. */
    private static final java.util.logging.Logger log =
        java.util.logging.Logger.getLogger(ByteCodeMonitor.class.getName());

    /** Defines the label for the code address column. */
    public static final String ADDRESS = "address";

    /** Defines the label for the code label column. */
    public static final String LABEL = "label";

    /** Defines the label for the instruction mnemonic column. */
    public static final String MNEMONIC = "mnemonic";

    /** Defines the label for the first instruction arguments column. */
    public static final String ARG_1 = "arg_1";

    /** Defines the label for the second instruction arguments column. */
    public static final String ARG_2 = "arg_2";

    /** Column labels for the code table. */
    private static final String[] BYTE_CODE_COL_LABELS = new String[] { ADDRESS, LABEL, MNEMONIC, ARG_1 };

    /** The table to output the byte code to. */
    private final TextTableModel table;

    /** A column labeled view onto the code table. */
    private final DoubleKeyedMap<String, Integer, String> labeledTable;

    /** A mapping from code table rows to code addresses. */
    private final List<Integer> rowToAddress = new ArrayList<Integer>();

    /** A mapping from code addresses to code table rows. */
    private final ConcurrentSkipListMap<Integer, Integer> addressToRow = new ConcurrentSkipListMap<Integer, Integer>();

    /** A copy of the code buffer, with code update deltas merged into it. */
    private final ByteBuffer codeBuffer = ByteBuffer.allocate(1024);

    /**
     * Constructs a table model with labelled columns to hold byte code.
     *
     * @param table The table to output the byte code to.
     */
    public ByteCodeMonitor(TextTableModel table)
    {
        this.table = table;
        labeledTable = table.withColumnLabels();

        for (int j = 0; j < BYTE_CODE_COL_LABELS.length; j++)
        {
            String label = BYTE_CODE_COL_LABELS[j];
            this.table.labelColumn(label, j);
        }
    }

    /**
     * Provides the code address corresponding to a table row.
     *
     * @param  row The table row.
     *
     * @return The corresponding code address.
     */
    public int getAddressForRow(int row)
    {
        return rowToAddress.get(row);
    }

    /**
     * Provides the table row for a code address.
     *
     * @param  address The code address.
     *
     * @return The corresponding table row.
     */
    public int getRowForAddress(int address)
    {
        return addressToRow.get(address);
    }

    /**
     * Should be notified every time byte-code is added to the machine.
     *
     * @param codeBuffer A buffer containing the byte-code.
     * @param start      The start offset within the buffer of the new code.
     * @param length     The length of the new code.
     * @param interner   The machines interner to lookup interned names with.
     * @param codeView   The machines code view to find interned names matching addresses.
     */
    public void onCodeUpdate(ByteBuffer codeBuffer, int start, int length, VariableAndFunctorInterner interner,
        WAMCodeView codeView)
    {
        log.fine("public void onCodeUpdate(ByteBuffer codeBuffer, int start = " + start + ", int length = " + length +
            ", VariableAndFunctorInterner interner, WAMCodeView codeView): called");

        // Take a copy of the new bytecode.
        copyAndResizeCodeBuffer(codeBuffer, start, length);

        // Disassemble the new area of byte code.
        SizeableList<WAMInstruction> instructions =
            WAMInstruction.disassemble(start, length, this.codeBuffer, interner, codeView);

        // Figure out where to start writing the disassembled code into the table.
        Map.Entry<Integer, Integer> entry = addressToRow.floorEntry(start);
        int firstRow = (entry == null) ? 0 : (entry.getValue() + 1);

        int address = start;
        int row = firstRow;

        // Build the mapping between addresses and rows.
        for (WAMInstruction instruction : instructions)
        {
            addressToRow.put(address, row);
            rowToAddress.add(row, address);

            row++;
            address += instruction.sizeof();
        }

        // Render the instructions into the table to be displayed.
        renderInstructions(instructions, firstRow, start);
    }

    /**
     * Copies code from the specified code buffer, into the internal one, resizing the internal code buffer if necessary
     * to make enough room.
     *
     * @param codeBuffer The code buffer to copy from.
     * @param start      The start offset within the buffer of the new code.
     * @param length     The length of the new code.
     */
    private void copyAndResizeCodeBuffer(ByteBuffer codeBuffer, int start, int length)
    {
        // Check the internal code buffer is large enough or resize it, then copy in the new instructions.
        int max = start + length;

        if (this.codeBuffer.limit() <= max)
        {
            ByteBuffer newCodeBuffer = ByteBuffer.allocate(max * 2);
            newCodeBuffer.put(this.codeBuffer.array(), 0, this.codeBuffer.limit());

            log.fine("Re-sized code buffer to " + (max * 2));
        }

        codeBuffer.position(start);
        codeBuffer.get(this.codeBuffer.array(), start, length);
    }

    /**
     * Renders disassembled instructions into the code table, starting at the specified row and instruction address.
     *
     * @param instructions A list of instructions to render into the table.
     * @param row          The table row to start at.
     * @param address      The code address to start at.
     */
    private void renderInstructions(Iterable<WAMInstruction> instructions, int row, int address)
    {
        for (WAMInstruction instruction : instructions)
        {
            WAMLabel label = instruction.getLabel();

            labeledTable.put(ADDRESS, row, String.format("%08X", address));
            labeledTable.put(LABEL, row, (label == null) ? "" : (label.toPrettyString() + ":"));
            labeledTable.put(MNEMONIC, row, instruction.getMnemonic().getPretty());

            int fieldMask = instruction.getMnemonic().getFieldMask();

            String arg = "";

            for (int i = 2; i < 32; i = i * 2)
            {
                if ((fieldMask & i) != 0)
                {
                    if (!"".equals(arg))
                    {
                        arg += ", ";
                    }

                    switch (i)
                    {
                    case 2:
                        arg += Integer.toString(instruction.getReg1());
                        break;

                    case 4:
                        arg += Integer.toString(instruction.getReg2());
                        break;

                    case 8:

                        FunctorName fn = instruction.getFn();
                        if (fn != null)
                        {
                            arg += fn.getName() + "/" + fn.getArity();
                        }

                        break;

                    case 16:

                        WAMLabel target1 = instruction.getTarget1();
                        if (target1 != null)
                        {
                            arg += target1.getName() + "/" + target1.getArity() + "_" + target1.getId();
                        }

                        break;
                    }
                }
            }

            labeledTable.put(ARG_1, row, arg);

            row++;
            address += instruction.sizeof();
        }
    }
}


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

import com.thesett.aima.logic.fol.Functor;
import com.thesett.aima.logic.fol.FunctorVisitor;
import com.thesett.aima.logic.fol.LiteralType;
import com.thesett.aima.logic.fol.LiteralTypeVisitor;
import com.thesett.aima.logic.fol.Term;
import com.thesett.aima.logic.fol.Variable;
import com.thesett.aima.logic.fol.VariableAndFunctorInterner;
import com.thesett.aima.logic.fol.VariableVisitor;
import com.thesett.common.util.doublemaps.SymbolKey;
import com.thesett.common.util.doublemaps.SymbolTable;

/**
 * AnnotatingVisitor annotates terms within a clause in order to provide more detailed information that can be used to
 * generate more optimal code. Going to perform symbolic evaluation of the program in order to determine as much as can
 * possibly be known about it at compile time. This visitor will probably be split up into more phases and renamed and
 * so on, this is just an experiment to get things started at the moment. Use a postfix search with the backtracking
 * algorithm with this visitor, to ensure that leaves are visited before nodes. This reverses the usual left-right
 * ordering so the clause head is examined after the body, but the body is examined backwards. Want to actually traverse
 * the body forwards, so that terms are evaluated in the order that they would be executed.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Scan for terms that are always fully ground.
 *
 Scan variables usages to determine which ones are temporary (occur in the head only).
 *      {@link PositionalTermTraverserImpl}.
 *

 *
 * @author Rupert Smith
 */
public class AnnotatingVisitor implements FunctorVisitor, VariableVisitor, LiteralTypeVisitor
{
    /** Used for debugging purposes. */
    /* private static final Logger log = Logger.getLogger(AnnotatingVisitor.class.getName()); */

    /** Defines the symbol field for the abstract domain over all terms. */
    public static final String TERM_DOMAIN = "termDomain";

    /** Defines the symbol field for the abstract domain over all variables. */
    public static final String VARIABLE_DOMAIN = "varDomain";

    /** The basic traverser used to provide positional context to visited terms. */
    private final PositionalContext traverser;

    /** The name interner. */
    private final VariableAndFunctorInterner interner;

    /** Holds the symbol table to record the annotations in. */
    private final SymbolTable<Integer, String, Object> symbolTable;

    /**
     * Creates an annotating visitor using the specified basic traverser to provide positional context information about
     * symbols.
     *
     * @param interner    The symbol name interner.
     * @param symbolTable The symbol table to store annotations in.
     * @param traverser   The positional context traverser.
     */
    public AnnotatingVisitor(VariableAndFunctorInterner interner, SymbolTable<Integer, String, Object> symbolTable,
        PositionalContext traverser)
    {
        this.traverser = traverser;
        this.interner = interner;
        this.symbolTable = symbolTable;
    }

    /** {@inheritDoc} */
    public void visit(Functor functor)
    {
        // functor is ground if all of its arguments are ground.
        boolean ground = true;

        if (functor.getArguments() != null)
        {
            for (Term argument : functor.getArguments())
            {
                SymbolKey symbolKey = argument.getSymbolKey();
                TermDomain annotation = (TermDomain) symbolTable.get(symbolKey, TERM_DOMAIN);

                if ((annotation == null) || !annotation.ground)
                {
                    ground = false;

                    break;
                }
            }
        }

        /*log.fine((ground ? "ground " : "non-ground ") + functor.toString(interner, true, false));*/
        symbolTable.put(functor.getSymbolKey(), TERM_DOMAIN, new TermDomain(ground));
    }

    /** {@inheritDoc} */
    public void visit(Variable variable)
    {
        SymbolKey symbolKey = variable.getSymbolKey();

        // Check if the variable has already been annotated.
        TermDomain annotation = (TermDomain) symbolTable.get(symbolKey, TERM_DOMAIN);

        if (annotation == null)
        {
            // variable is ground if it appears in a call to a predicate that always grounds that argument.
            /*log.fine("non-ground " + variable.toString(interner, true, false));*/
            symbolTable.put(symbolKey, TERM_DOMAIN, new TermDomain(false));
        }
        else
        {
            /*log.fine("already seen " + variable.toString(interner, true, false));*/
        }

        // Check if the variable domain has already been annotated for a previous occurrence of the variable.
        VarDomain varDomain = (VarDomain) symbolTable.get(symbolKey, VARIABLE_DOMAIN);

        if (varDomain == null)
        {
            varDomain = new VarDomain(traverser.isInHead());
            symbolTable.put(symbolKey, VARIABLE_DOMAIN, varDomain);
        }
        else
        {
            varDomain.isTemporary = traverser.isInHead() && varDomain.isTemporary();
        }

        /*log.fine(variable.toString(interner, true, false) +
            (varDomain.isTemporary() ? " may be temporary." : " is not temporary."));*/
    }

    /** {@inheritDoc} */
    public void visit(LiteralType literal)
    {
        // literal is ground.
        /*log.fine("ground " + literal.toString(interner, true, false));*/
        symbolTable.put(literal.getSymbolKey(), TERM_DOMAIN, new TermDomain(true));
    }

    /** {@inheritDoc} */
    public void visit(Term term)
    {
    }

    /**
     * Abstract domain over variables.
     */
    public static class VarDomain
    {
        /**
         * Flag used to indicate that a variable occurs in the head or as the argument to determinate predicates only.
         */
        boolean isTemporary;

        /**
         * Creates a variable domain element.
         *
         * @param temporary <tt>true</tt> if the variable can be temporary.
         */
        private VarDomain(boolean temporary)
        {
            isTemporary = temporary;
        }

        /**
         * Reports whether a variable is temporary.
         *
         * @return <tt>true</tt> if the variable is temporary.
         */
        public boolean isTemporary()
        {
            return isTemporary;
        }
    }

    /**
     * Implements the abstract domain to be computed over terms.
     */
    public static class TermDomain
    {
        /** Flag used to indicate that a term is fully ground, that is, cannot contain any free variables. */
        boolean ground;

        /**
         * Creates a domain element.
         *
         * @param ground <tt>true</tt> to indicate that a term is fully ground.
         */
        private TermDomain(boolean ground)
        {
            this.ground = ground;
        }
    }
}


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

import com.thesett.aima.logic.fol.NumericType;
import com.thesett.aima.logic.fol.Term;

/**
 * BinaryArithmeticOperator provides a base implementation for all two-place arithmetic operators on numeric arguments.
 * The {@link #getValue()} method invokes {@link #getValue()} on its arguments to recursively evaluate them, as they may
 * also be arithmetic operators or other {@link BuiltInExpressionOperator}s. It checks that the resulting arguments are
 * fully instantiated and numeric prior to calling the operators implementing {#evaluate} method to compute the result.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Ensure that both arguments evaluate to fully instantiated numbers.  {@link Term}, {@link NumericType}.
 *
 Evaluate the arithmetic operator.
 *

 *
 * @author Rupert Smith
 */
public abstract class BinaryArithmeticOperator extends BuiltInExpressionOperator
{
    /**
     * Creates a binary arithmetic operator on two arguments.
     *
     * @param name      The interned name of the operator.
     * @param arguments The operators arguments. There must be exactly two.
     */
    protected BinaryArithmeticOperator(int name, Term[] arguments)
    {
        super(name, arguments);

        // Ensure that there are exactly two argument.
        if (arguments.length != 2)
        {
            throw new IllegalArgumentException("BinaryArithmeticOperators must take exactly two arguments.");
        }
    }

    /**
     * Gets the actual value of a term, which is a numeric type equal in value to the arithmetic operator applied to its
     * arguments. This method checks that both arguments produce values which are fully instantiated and numeric when
     * their {@link Term#getValue()} methods are invoked.
     *
     * @return A numeric type equal in value to the the arithmetic operator applied to its arguments.
     */
    public Term getValue()
    {
        Term firstArgValue = arguments[0].getValue();
        Term secondArgValue = arguments[1].getValue();

        // Check that the arguments to operate on are both numeric values.
        if (firstArgValue.isNumber() && secondArgValue.isNumber())
        {
            return evaluate((NumericType) firstArgValue, (NumericType) secondArgValue);
        }
        else
        {
            return this;
        }
    }

    /**
     * Evaluates the arithmetic operator on its two numeric arguments.
     *
     * @param  firstNumber  The first argument.
     * @param  secondNumber The second argument.
     *
     * @return The result of performing the arithmetic operator on its arguments.
     */
    protected abstract NumericType evaluate(NumericType firstNumber, NumericType secondNumber);
}


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

import java.util.Properties;

import javax.servlet.http.HttpServlet;

import com.thesett.common.properties.DefaultPropertyReader;

/**
 * PropertyLoaderServlet is a servlet that uses its config method to load a properties file into the application scope.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Load properties and store them in the application scope
 *

 *
 * @author Rupert Smith
 * @todo   Deprecate this functionality to a property config bean to be run by the config bean manager once it has been
 *         written.
 * @todo   Could make this servlet return some status information about the config beans if its service method is
 *         called.
 */
public class PropertyLoaderServlet extends HttpServlet
{
    /** Used for logging. */
    private static final java.util.logging.Logger log =
        java.util.logging.Logger.getLogger(PropertyLoaderServlet.class.getName());

    /** Holds the name of the initialization parameter for the property resource name. */
    private static final String PROPERTY_RESOURCE = "property_resource";

    /** Holds the name of the initialization parameter for the application scope variable name. */
    private static final String APP_VAR_NAME = "app_var_name";

    /**
     * Loads a properties file and stores it in the application context. The property resource name and the application
     * scope variable name are passed as initialization parameters in the servlet config in the web.xml.
     */
    public void init()
    {
        log.fine("public void init(): called");

        // Get the name of the property file resource to load and the application variable name to store it under
        String propertyResource = getInitParameter(PROPERTY_RESOURCE);
        String varName = getInitParameter(APP_VAR_NAME);

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

        // Use the default property reader to load the resource
        Properties properties = DefaultPropertyReader.getProperties(propertyResource);

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

        // Store the properties under the specified variable name in the application scope
        getServletContext().setAttribute(varName, properties);
    }
}


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

import java.util.LinkedList;
import java.util.List;

import com.thesett.aima.logic.fol.Functor;
import com.thesett.aima.logic.fol.Term;
import com.thesett.aima.logic.fol.Variable;

/**
 * Unifies is the ISO Prolog built in operator '='/2. It performs a standard unification (no occurrs check) on its left
 * and right arguments, possibly binding variables as a result of the unification, and succeeds iff the unification
 * succeeds.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Unify the left and right arguments of the unify operator.
 *      {@link PrologUnifier}.
 *
 Add unified variables to the resolution state.  {@link ResolutionState}.
 *

 *
 * @author Rupert Smith
 */
public class Unifies extends BaseBuiltIn
{
    /** Used for providing user readable execution traces. */
    /* private static final Logger trace = Logger.getLogger("TRACE.Prolog." + Unifies.class.getSimpleName()); */

    /** Flag used as a quick check to see if trace is enabled. */
    private static final boolean TRACE = false; //log.isLoggable(Level.FINE)

    /**
     * Creates a unifies built-in to implement the specified functor.
     *
     * @param functor The functor to implement as a built in.
     */
    public Unifies(Functor functor)
    {
        super(functor);
    }

    /** {@inheritDoc} */
    public boolean proofStep(ResolutionState state)
    {
        Functor goalTerm = state.getGoalStack().poll().getFunctor();

        Term leftArg = goalTerm.getArgument(0);
        Term rightArg = goalTerm.getArgument(1);

        // This is used to record variables bound during the unification, so that they may be undone if the resolution
        // state is backtracked over.
        List<Variable> boundVariables = new LinkedList<Variable>();

        // Unify the current query goal with the possibly matching clause, creating variable bindings.
        boolean matched = state.getUnifier().unifyInternal(leftArg, rightArg, boundVariables, boundVariables);

        if (matched)
        {
            if (TRACE)
            {
                /*trace.fine(state.getTraceIndenter().generateTraceIndent() + "Unify " +
                    leftArg.toString(state.getInterner(), true, true) + " against " +
                    rightArg.toString(state.getInterner(), true, true) + ", ok.");*/
            }

            for (Variable binding : boundVariables)
            {
                state.getVariableBindings().offer(binding);
            }
        }

        return matched;
    }
}


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

import java.util.*;

import org.apache.commons.lang3.tuple.Pair;

import com.google.common.collect.Lists;
import com.groupon.novie.internal.engine.constraint.RawConstraint;

/**
 * This class is the whole query constraints/parameters bean.
 * It contains all the required information, decoded from the HTTP query, needed to build the SQL query.
 *
 * @author thomas
 * @author ricardo
 * @author damiano
 */
public class QueryParameter {

    public static enum QueryParameterKind {
        CONSTRAINTS, ORDERS, GROUPS, TIMEZONE, PAGE,
    }

    private List<RawConstraint<?>> rawConstraints;
    // List of Order,DimensionName/Measure,InfoName. InfoName can be null
    private List<QuerySortConstraint> orders;
    // List of DimensionName,InfoName. InfoName can be null
    private List<Pair<String, String>> groups;

    // Timezone name. if null, use the default one
    private String timezoneName = null;

    private int page;
    private int pageSize;

    public QueryParameter() {
        rawConstraints = Lists.newArrayList();
        orders = Lists.newArrayList();
        groups = Lists.newArrayList();
    }

    public <T> void addConstraint(final String dimensionName, final String informationName, final List<Pair<QueryOperator, T>> operatorValues) {
        if (dimensionName == null) {
            throw new IllegalArgumentException("dimensionName can not be null");
        }
        rawConstraints.add(new RawConstraint<T>(dimensionName.toUpperCase(), (informationName == null) ? null : informationName.toUpperCase(),
                operatorValues));
    }

    public void addConstraint(final String dimensionName, final String informationName, final QueryOperator op, final Object value) {
        if (dimensionName == null) {
            throw new IllegalArgumentException("dimensionName can not be null");
        }
        rawConstraints.add(new RawConstraint<Object>(dimensionName.toUpperCase(), (informationName == null) ? null : informationName.toUpperCase(),
                op, value));
    }

    /**
     * Add list of grouping dimension which use the default grouping info
     *
     * @param dimensionName VarArgs list of the dimension names
     */
    public void addGroups(final String... dimensionName) {
        for (String dn : dimensionName) {
            addGroup(dn, null);
        }
    }

    public void addGroup(final String dimensionName) {
        addGroup(dimensionName, null);
    }

    public void addGroup(final String dimensionName, final String informationName) {
        if (dimensionName == null) {
            throw new IllegalArgumentException("dimensionName can not be null");
        }
        groups.add(Pair.of(dimensionName.toUpperCase(), (informationName == null) ? null : informationName.toUpperCase()));
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getPage() {
        return page;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getPageSize() {
        return pageSize;
    }

    // Use unmodifiable list to force to use add method
    public List<Pair<String, String>> getGroups() {
        return Collections.unmodifiableList(groups);
    }

    // Use unmodifiable list to force to use add method
    public List<RawConstraint<?>> getConstraints() {
        return Collections.unmodifiableList(rawConstraints);
    }

    // Use unmodifiable list to force to use add method
    public List<QuerySortConstraint> getOrders() {
        return Collections.unmodifiableList(orders);
    }

    public String getTimezoneName() {
        return timezoneName;
    }

    public void setTimezoneName(String timezoneName) {
        this.timezoneName = timezoneName;
    }

    public void addOrderByConstraint(final OrderByDirection orderByDirection, final String dimensionName) {
        addOrderByConstraint(orderByDirection, dimensionName, null);
    }

    public void addOrderByConstraint(final OrderByDirection orderByDirection, final String dimensionName, final String informationName) {
        orders.add(new QuerySortConstraint(orderByDirection, dimensionName, informationName));
    }

    /**
     * Return a QueryParameter which contains reference to the original elements except for those from the exception list.
     *
     * @param excludedElements list of QueryParameterKind which will NOT be cloned
     * @return
     */
    public QueryParameter partialCopy(final QueryParameterKind... excludedElements) {
        List<QueryParameterKind> excludedList = Arrays.asList(excludedElements);
        QueryParameter returnValue = new QueryParameter();
        if (!excludedList.contains(QueryParameterKind.CONSTRAINTS)) {
            returnValue.rawConstraints = this.rawConstraints;
        }
        if (!excludedList.contains(QueryParameterKind.GROUPS)) {
            returnValue.groups = this.groups;
        }
        if (!excludedList.contains(QueryParameterKind.ORDERS)) {
            returnValue.orders = this.orders;
        }
        if (!excludedList.contains(QueryParameterKind.PAGE)) {
            returnValue.pageSize = this.pageSize;
            returnValue.page = this.page;
        }
        if (!excludedList.contains(QueryParameterKind.TIMEZONE)) {
            returnValue.timezoneName = this.timezoneName;
        }

        return returnValue;
    }

}


/*
 * 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.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

/**
 * SimpleTree provides an implementation of the {@link Tree} interface.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Provide access to a tree as a node or a leaf.
 *
Iterate over all data elements in a tree.
 *
Provide access to the elements of a tree.
 *

 *
 * @author Rupert Smith
 */
public class SimpleTree<E> implements Tree<E>, Tree.Node<E>, Tree.Leaf<E>, Serializable
{
    /** There are two type of tree, nodes and leafs. These enums are used to define which this is. */
    private enum Type
    {
        /** Used to indicate that a point in the tree is a node. */
        Node,

        /** Used to indicate that a point in the tree is a leaf. */
        Leaf
    }

    /** Used to indicate whether this is a node or a leaf. */
    private Type nodeOrLeaf;

    /** Used to hold the data element at this point in the tree. */
    private E element;

    /** Used to hold the parent of this point in the tree. Null if this is a root. */
    private Tree.Node<E> parent;

    /** Used to hold the child trees. */
    private List<Tree<E>> children;

    /** Builds an empty leaf. */
    public SimpleTree()
    {
        // Mark this tree as a leaf.
        nodeOrLeaf = Type.Leaf;

        // Set the data element to null.
        this.element = null;
    }

    /**
     * Builds a leaf.
     *
     * @param element The data element to store at the leaf.
     */
    public SimpleTree(E element)
    {
        // Mark this tree as a leaf.
        nodeOrLeaf = Type.Leaf;

        // Keep the data element.
        this.element = element;
    }

    /**
     * Builds a node.
     *
     * @param element  The data element to store at the node.
     * @param children A collection of sub-trees to place below the node.
     */
    public SimpleTree(E element, Collection<? extends Tree<E>> children)
    {
        // Check if children is empty in which case this should be a leaf and not a node.
        if (children.isEmpty())
        {
            nodeOrLeaf = Type.Leaf;
        }
        else
        {
            // Mark this tree as a node.
            nodeOrLeaf = Type.Node;
        }

        // Keep the data element.
        this.element = element;

        // Keep all the children.
        this.children = new ArrayList<Tree<E>>(children);
        // this.children = children;
    }

    /**
     * Returns an iterator over all the children of this node. This is different to the {@link #iterator} method which
     * returns an iterator over the whole tree from this point downwards. This iterator is only over the child nodes and
     * does not recurse over their children.
     *
     * @return An iterator over the children of this node, if it is a node and has children.
     */
    public Iterator<Tree<E>> getChildIterator()
    {
        initChildren();

        return new ChildIterator();
    }

    /**
     * Gets all the child nodes of this one.
     *
     * @return All the child nodes as a collection.
     */
    public Collection<Tree<E>> getChildren()
    {
        initChildren();

        return children;
    }

    /**
     * Gets the ith child of this node.
     *
     * @param  i The index of the child tree to get.
     *
     * @return The ith child of this node.
     */
    public Tree<E> getChild(int i)
    {
        initChildren();

        return children.get(i);
    }

    /**
     * Reports the number of children that a node has.
     *
     * @return The number of children that a node has.
     */
    public int numChildren()
    {
        initChildren();

        return children.size();
    }

    /**
     * Returns true if this tree has no children and is therefore a leaf in the tree.
     *
     * @return True if this node has no children, false if it does.
     */
    public boolean isLeaf()
    {
        return nodeOrLeaf.equals(Type.Leaf);
    }

    /**
     * Returns the tree which is the parent of this tree. If this tree's root is the real root node of the whole tree
     * then this returns null.
     *
     * @return The tree which this tree is a sub-tree of. If this tree if a root tree then this returns null.
     */
    public Node<E> getParent()
    {
        return parent;
    }

    /**
     * Set the node which is the parent of this tree.
     *
     * @param parent The parent tree which this tree is to be added as a sub-tree of.
     */
    public void setParent(Node<E> parent)
    {
        this.parent = parent;
    }

    /**
     * If this is a leaf, then returns the tree as a Leaf. If it is not a leaf then this method should return null.
     *
     * @return This point in the tree as a leaf, or null if this is a node.
     */
    public Leaf<E> getAsLeaf()
    {
        return nodeOrLeaf.equals(Type.Leaf) ? this : null;
    }

    /**
     * If this is a node, then returns the tree as a Node. If it is not a node then this method should return null.
     *
     * @return This point in the tree as a node, or null if this is a leaf.
     */
    public Node<E> getAsNode()
    {
        return nodeOrLeaf.equals(Type.Node) ? this : null;
    }

    /**
     * Returns the data element stored at the current point in this tree.
     *
     * @return The data element stored at this point in the tree.
     */
    public E getElement()
    {
        return element;
    }

    /**
     * Sets the data element stored at the current point in this tree.
     *
     * @param newElement The data element to store in the tree at this point.
     */
    public void setElement(E newElement)
    {
        this.element = newElement;
    }

    /**
     * Adds a child tree to the children of this point in the tree. If this is already a node then it remains as a node.
     * If this is a leaf then adding a child to it must promote it to become a node. This implementation supports
     * turning leaves into nodes.
     *
     * @param child A child tree to add below this point in the tree.
     */
    public void addChild(Tree<E> child)
    {
        initChildren();

        // Add the new child to the collection of children.
        children.add(child);

        // Set the type of this point in the tree to a node as it now has children.
        nodeOrLeaf = Type.Node;

        // Set the new childs parent to this.
        child.setParent(this);
    }

    /**
     * Clears all the children of this point in the tree. If this point is a leaf it will have no children so this
     * operation does nothing. If this point is a node it will be reduced to a leaf by this operation. This
     * implementation supports turning nodes into leaves.
     */
    public void clearChildren()
    {
        // Check that their are children to clear.
        if (children != null)
        {
            // Loop over all the children setting their parent to null.
            for (Tree<E> child : children)
            {
                child.setParent(null);
            }

            // Clear out the children collection.
            children.clear();

            // Mark this as a leaf node.
            nodeOrLeaf = Type.Leaf;
        }
    }

    /**
     * This iterates over the elements stored in this Tree (including all its subtrees). The iteration order must be
     * specified as a parameter to this method.
     *
     * @param  order The iteration order. See {@link Tree} for more information about possible iteration orders.
     *
     * @return An iterator over the tree.
     */
    public Iterator<SimpleTree<E>> iterator(IterationOrder order)
    {
        return new SimpleTreeIterator(order, this);
    }

    /**
     * Returns the entire tree as a string for debugging purposes.
     *
     * @return The entire tree as a string for debugging purposes.
     */
    public String toString()
    {
        String result = "[ ";

        // Loop over the tree preorder. Root first, then children.

        // Print the root.
        E rootElement = getElement();

        result += rootElement + (isLeaf() ? "" : " : ");

        // Iterator<SimpleTree<E>> i = iterator(IterationOrder.PreOrder);
        if (!isLeaf())
        {
            // Print the children.
            for (Iterator<Tree<E>> i = getChildIterator(); i.hasNext();)
            {
                Tree<E> child = i.next();
                result += child.toString() + (i.hasNext() ? ", " : "");
            }
        }

        result += " ]";

        return result;
    }

    /** Ensures that the child list is initialized. */
    private void initChildren()
    {
        // If the children collection is empty then create a new array list to hold the children in.
        if (children == null)
        {
            children = new ArrayList<Tree<E>>();
        }
    }

    /**
     * An iterator that explicitly manages its own queue of elements and tree fragments for later expansion rather than
     * using a recursive scheme which would be too costly on stack space and may cause a StackOverflowError for large
     * trees.
     */
    private class SimpleTreeIterator<E> implements Iterator<SimpleTree<E>>
    {
        /** Used to hold the iteration order. */
        IterationOrder order;

        /** Used to queue the trees for this iterator. */
        Queue<QueueElement<E>> elementVisitationQueue = new StackQueue<QueueElement<E>>();

        /**
         * Creates a new SimpleTreeIterator object.
         *
         * @param order The order in which this iterator is to traverse the tree. See {@link Tree} for more information
         *              about the possible traversal orders.
         * @param tree  The tree to iterate over.
         */
        public SimpleTreeIterator(IterationOrder order, Tree<E> tree)
        {
            // Keep the iteration order.
            this.order = order;

            // Enqueue the initial tree.
            enqueueTree(tree);
        }

        /**
         * Tests if this iterator has more elements.
         *
         * @return True if there are more elements to iterate over, false otherwise.
         */
        public boolean hasNext()
        {
            // Check that the queue is not empty, in which case there are more elements.
            return !elementVisitationQueue.isEmpty();
        }

        /**
         * Extracts the next element from this iterator.
         *
         * @return The next sub-tree in the iteration.
         */
        public SimpleTree<E> next()
        {
            // Throw an exception if the queue is empty.
            if (elementVisitationQueue.isEmpty())
            {
                throw new NoSuchElementException();
            }

            // Loop until the head element of the queue is a simple element.
            while (!elementVisitationQueue.peek().holdsSimpleElement)
            {
                // Take the head element off the queue and expand its tree fragment.
                QueueElement<E> nextElement = elementVisitationQueue.remove();

                enqueueTree(nextElement.treeFragment);
            }

            // Extract the head element of the queue and return its simple element.
            return (SimpleTree<E>) elementVisitationQueue.remove().treeFragment;
        }

        /** Remove is not supported by this iterator. */
        public void remove()
        {
            throw new UnsupportedOperationException();
        }

        /**
         * Expands a tree fragment onto the element visitation queue.
         *
         * @param tree The sub-tree to be expanded onto the iteration queue.
         */
        private void enqueueTree(Tree<E> tree)
        {
            // Get the root element from the tree and build a queue element to contain it.
            QueueElement<E> rootElement = new QueueElement<E>();

            rootElement.treeFragment = tree;
            rootElement.holdsSimpleElement = true;

            // Check if the tree has children and if so then cast it to a node type and get an iterator over its
            // children.
            Tree.Node<E> treeNode = null;
            Iterator<Tree<E>> i = null;

            if (!tree.isLeaf())
            {
                treeNode = tree.getAsNode();
                i = treeNode.getChildIterator();
            }

            // Enqueue the root and its children in an order depending on the type of iteration order requested.
            switch (order)
            {
            case PreOrder:
            {
                // Enqueue all its children and then the root element.
                if (!tree.isLeaf())
                {
                    while (i.hasNext())
                    {
                        QueueElement<E> childElement = new QueueElement<E>();

                        childElement.treeFragment = i.next();
                        childElement.holdsSimpleElement = false;
                        elementVisitationQueue.offer(childElement);
                    }
                }

                elementVisitationQueue.offer(rootElement);

                break;
            }

            case InOrder:
            {
                // Enqueue the rest of the children, the root element and then the first child.
                QueueElement<E> firstChildElement = null;

                if (!tree.isLeaf() && i.hasNext())
                {
                    firstChildElement = new QueueElement<E>();
                    firstChildElement.treeFragment = i.next();
                    firstChildElement.holdsSimpleElement = false;

                    while (i.hasNext())
                    {
                        QueueElement<E> childElement = new QueueElement<E>();

                        childElement.treeFragment = i.next();
                        childElement.holdsSimpleElement = false;
                        elementVisitationQueue.offer(childElement);
                    }
                }

                elementVisitationQueue.offer(rootElement);

                if (!tree.isLeaf())
                {
                    elementVisitationQueue.offer(firstChildElement);
                }

                break;
            }

            case PostOrder:
            {
                // Enqueue the root element and then all its children.
                elementVisitationQueue.offer(rootElement);

                if (!tree.isLeaf())
                {
                    while (i.hasNext())
                    {
                        QueueElement<E> childElement = new QueueElement<E>();

                        childElement.treeFragment = i.next();
                        childElement.holdsSimpleElement = false;
                        elementVisitationQueue.offer(childElement);
                    }
                }

                break;
            }

            default:
            {
                throw new IllegalStateException("Unsupported tree traversal ordering.");
            }
            }
        }

        /**
         * This data structure is used to keep track of trees enqueued for examination by this iterator. Either a tree
         * is enqueued for later expansion, or its element in enqueued for immediate retrieval and removal from the
         * queue.
         */
        private class QueueElement<E>
        {
            /** Used to indicate that this queue element holds a simple element and not a tree fragment. */
            public boolean holdsSimpleElement = true;

            /** Used to hold a tree fragment for later expansion. Null if this is a simple element. */
            public Tree<E> treeFragment;
        }
    }

    /**
     * ChildIterator is an iterator over the child trees/leafs of a node which are held in a list referenced by the
     * node. This iterator simply keeps track of the index of the current position within that list that it is iterating
     * over. As new children are added to the node, they are placed on the end of that list. This iterator will not
     * throw ConcurrentModificationExceptions when the underlying list is modified whilst it is iterating. However, it
     * is not synchronized, so only a single thread should be modifying the list or reading from the iterator where
     * overlapping could occurr.
     */
    private class ChildIterator implements Iterator<Tree<E>>
    {
        /** Holds the current offset within the child list. */
        private int index;

        /**
         * Determines whether this iterator has more elements, depending on the current size of the child list.
         *
         * @return <tt>true</tt> if this iterator has more elements, <tt>false</tt> if not.
         */
        public boolean hasNext()
        {
            return (children.size() > index);
        }

        /**
         * Gets the next element in the child list.
         *
         * @return The next element in the child list.
         */
        public Tree<E> next()
        {
            return (children.get(index++));
        }

        /** Remove is not supported by this iterator. */
        public void remove()
        {
            throw new UnsupportedOperationException();
        }
    }
}


/*
 * 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.Iterator;
import java.util.NoSuchElementException;

/**
 * SequenceIterator is a helper class for writing iterators over lazily generated sequences. This follows a common
 * pattern, different from an iterator over a collection of a known size. This is because the next element in the
 * sequence must be generated before knowing whether or not there is next element. The pattern that this base class
 * follows is to generate and cache the next element when the {@link #hasNext()} method is called, and to consume and
 * return the cached element when the {@link #next()} method is called. The {@link #next()} method will generate the
 * next element in the sequence, if it has not already been created and cached.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Turn a lazily generated sequence into an iterator.
 *

 *
 * @author Rupert Smith
 */
public abstract class SequenceIterator<E> implements Iterator<E>
{
    /** Caches the next lazily generated solution, when it has already been asked for by {@link #hasNext}. */
    private E nextSolution;

    private boolean exhausted;

    /**
     * Generates the next element in the sequence.
     *
     * @return The next element from the sequence if one is available, or <tt>null</tt> if the sequence is complete.
     */
    public abstract E nextInSequence();

    /**
     * Checks if a sequnce has more elements, caching any generated as a result of the check.
     *
     * @return <tt>true</tt> if there are more elements, <tt>false</tt> if not.
     */
    public boolean hasNext()
    {
        boolean hasNext;

        nextInternal();

        hasNext = !exhausted;

        return hasNext;
    }

    /**
     * Gets the next element from the sequence if one is available. The difference between this method and
     * {@link #nextInSequence} is that this method consumes any cached solution, so subsequent calls advance onto
     * subsequent solutions.
     *
     * @return The next solution from the search space if one is available.
     *
     * @throws NoSuchElementException If no solutions are available.
     */
    public E next()
    {
        // Consume the next element in the sequence, if one is available.
        E result = nextInternal();

        if (exhausted)
        {
            throw new NoSuchElementException();
        }

        nextSolution = null;

        return result;
    }

    /**
     * Removes from the underlying collection the last element returned by the iterator (optional operation). This
     * method can be called only once per call to <tt>next</tt>. The behavior of an iterator is unspecified if the
     * underlying collection is modified while the iteration is in progress in any way other than by calling this
     * method.
     *
     * @throws UnsupportedOperationException The <tt>remove</tt> operation is not generally supported by lazy sequences.
     */
    public void remove()
    {
        throw new UnsupportedOperationException("Lazy sequences, in general, do not support removal.");
    }

    /**
     * Gets the next element from the sequence, the cached one if one has already been generated, or creating and
     * caching a new one if not. If the cached element from a previous call has not been consumed, then subsequent calls
     * to this method will not advance the iterator.
     *
     * @return The next solution from the search space if one is available.
     *
     * @throws NoSuchElementException If no solutions are available.
     */
    private E nextInternal()
    {
        // Check if the next soluation has already been cached, because of a call to hasNext.
        if (nextSolution != null)
        {
            return nextSolution;
        }

        // Otherwise, generate the next solution, if possible.
        nextSolution = nextInSequence();

        // Check if the solution was null, which indicates that the search space is exhausted.
        if (nextSolution == null)
        {
            exhausted = true;
        }

        return nextSolution;
    }
}


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

import java.util.Arrays;

import com.thesett.aima.logic.fol.Clause;
import com.thesett.aima.logic.fol.Functor;
import com.thesett.common.util.SizeableList;

/**
 * WAMCompiledClause is a clause, that belongs to an {@link WAMCompiledPredicate}. Compiled instructions added to the
 * clause fall through onto the parent predicate, and are not held on the compiled clause itself. The compiled clause
 * has a label, which identifies its position within the parent predicate.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Forward added instructions onto the parent predicate.
 *
 Hold a label as a position marker for the clause within its containing predicate.
 *

 *
 * @author Rupert Smith
 */
public class WAMCompiledClause extends Clause<Functor>
{
    /** The parent predicate to which this compiled clause belongs. */
    private final WAMCompiledPredicate parent;

    /** The interned name of this clauses label. */
    int label;

    /** Indicates that this has been added to a parent predicate already. */
    private boolean addedToParent;

    /**
     * Creates a clause within a parent predicate.
     *
     * @param parent The parent predicate.
     */
    public WAMCompiledClause(WAMCompiledPredicate parent)
    {
        super(null, null);
        this.parent = parent;
    }

    /**
     * Adds a conjunctive body functor, or head functor, to this clause, along with the instructions that implement it.
     *
     * @param body         A conjunctive body functor to add to this clause.
     * @param instructions A list of instructions to add to the body.
     */
    public void addInstructions(Functor body, SizeableList<WAMInstruction> instructions)
    {
        int oldLength;

        if (this.body == null)
        {
            oldLength = 0;
            this.body = new Functor[1];
        }
        else
        {
            oldLength = this.body.length;
            this.body = Arrays.copyOf(this.body, oldLength + 1);
        }

        this.body[oldLength] = body;

        addInstructionsAndThisToParent(instructions);
    }

    /**
     * Adds some instructions sequentially, after any existing instructions, to the clause.
     *
     * @param instructions The instructions to add to the clause.
     */
    public void addInstructions(SizeableList<WAMInstruction> instructions)
    {
        addInstructionsAndThisToParent(instructions);
    }

    /**
     * Adds some instructions to the parent predicate, and also adds this as a clause on the parent, if it has not
     * already been added.
     *
     * @param instructions The instructions to add.
     */
    private void addInstructionsAndThisToParent(SizeableList<WAMInstruction> instructions)
    {
        if (!addedToParent)
        {
            parent.addInstructions(this, instructions);
            addedToParent = true;
        }
        else
        {
            parent.addInstructions(instructions);
        }
    }
}


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

import com.thesett.aima.logic.fol.DoubleLiteral;
import com.thesett.aima.logic.fol.IntLiteral;
import com.thesett.aima.logic.fol.NumericType;
import com.thesett.aima.logic.fol.Term;

/**
 * Minus implements the subtraction operator '-' in Prolog.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Subtract two numeric values together using integer or floating point arithmetic.
 *      {@link NumericType}.
 *

 *
 * @author Rupert Smith
 */
public class Minus extends BinaryArithmeticOperator
{
    /**
     * Creates a new '-' expression operator.
     *
     * @param name      The interned name of the operator.
     * @param arguments The arguments, of which there must be two.
     */
    public Minus(int name, Term[] arguments)
    {
        super(name, arguments);
    }

    /**
     * Evaluates the arithmetic operator on its two numeric arguments.
     *
     * @param  firstNumber  The first argument.
     * @param  secondNumber The second argument.
     *
     * @return The result of performing the arithmetic operator on its arguments.
     */
    protected NumericType evaluate(NumericType firstNumber, NumericType secondNumber)
    {
        // If either of the arguments is a real number, then use real number arithmetic, otherwise use integer arithmetic.
        if (firstNumber.isInteger() && secondNumber.isInteger())
        {
            return new IntLiteral(firstNumber.intValue() - secondNumber.intValue());
        }
        else
        {
            return new DoubleLiteral(firstNumber.doubleValue() - secondNumber.doubleValue());
        }
    }
}


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

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JButton;
import javax.swing.JPanel;

/**
 * ButtonPanel displays a panel consisting of OK, Cancel and Apply buttons.
 *
 *
An ActionListener can register itself with ButtonPanel and it will be sent all of the Ok, Cancel and Apply actions
 * that the ButtonPanel generates.
 *
 *
ButtonPanel is intended to be used closely with {@link WorkPanel}s. A work panel can be registered with a button
 * panel in which case the work panel will be sent the action events from the buttons and the button panel will listen
 * for changes to the work panels state and enable or disable its buttons accordingly.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Build user interface
 *
Register listener for Ok, Cancel and Apply actions
 *
Enable or disable buttons depending on work panel state  {@link WorkPanelState}
 *

 *
 * @author Rupert Smith
 */
public class ButtonPanel extends JPanel implements PropertyChangeListener
{
    /** Used for logging. */
    /* private static final Logger log = Logger.getLogger(ButtonPanel.class.getName()); */

    /** Holds the OK button. */
    private final JButton okButton = new JButton("OK");

    /** Holds the Cancel button. */
    private final JButton cancelButton = new JButton("Cancel");

    /** Holds the Apply button. */
    private final JButton applyButton = new JButton("Apply");

    /** Creates new ButtonPanel. */
    public ButtonPanel()
    {
        initComponents();
    }

    /**
     * Called when a property in the WorkPanelState is changed. This method calls initPanels to rebuild the user
     * interface to reflect the current application state.
     *
     * @param event The work panel state change event. All other event types are ignored.
     */
    public void propertyChange(PropertyChangeEvent event)
    {
        /*log.fine("void propertyChange(PropertyChangeEvent): called");*/

        // Check that the property change was sent by a WorkPanelState
        if (event.getSource() instanceof WorkPanelState)
        {
            // Get the state
            String state = ((WorkPanelState) event.getSource()).getState();

            // Check what the state to set is
            if (state.equals(WorkPanelState.NOT_SAVED))
            {
                // Set the Cancel and Apply buttons to enabled
                cancelButton.setEnabled(true);
                applyButton.setEnabled(true);
            }
            else if (state.equals(WorkPanelState.READY))
            {
                // Set the Cancel and Apply buttons to disabled
                cancelButton.setEnabled(false);
                applyButton.setEnabled(false);
            }
            else if (state.equals(WorkPanelState.NOT_INITIALIZED))
            {
                // Disable all the buttons
                okButton.setEnabled(false);
                cancelButton.setEnabled(false);
                applyButton.setEnabled(false);
            }
        }
    }

    /**
     * Sets the specified work panel to listen to the button press events for all of the OK, Cancel and Apply buttons.
     * Regisers this object to listen for changes to the work panels state.
     *
     * @param panel The work panel to register with.
     */
    public void registerWorkPanel(WorkPanel panel)
    {
        // Set the work panel to listen for actions generated by the buttons
        okButton.addActionListener(panel);
        cancelButton.addActionListener(panel);
        applyButton.addActionListener(panel);

        // Register this to listen for changes to the work panels state
        panel.getWorkPanelState().addPropertyChangeListener(this);
    }

    /** Creates the buttons. */
    private void initComponents()
    {
        // Add the buttons to this panel
        add(okButton);
        add(cancelButton);
        add(applyButton);
    }
}


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

package com.bazaarvoice.jless;

import com.bazaarvoice.jless.ast.node.Node;
import com.bazaarvoice.jless.ast.node.ScopeNode;
import com.bazaarvoice.jless.ast.visitor.FlattenNestedRuleSets;
import com.bazaarvoice.jless.ast.visitor.NestedMediaQueries;
import com.bazaarvoice.jless.ast.visitor.Printer;
import com.bazaarvoice.jless.exception.LessTranslationException;
import com.bazaarvoice.jless.parser.Parser;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.commons.io.IOUtils;
import org.parboiled.Parboiled;
import org.parboiled.errors.ErrorUtils;
import org.parboiled.parserunners.ParseRunner;
import org.parboiled.parserunners.ReportingParseRunner;
import org.parboiled.support.DefaultValueStack;
import org.parboiled.support.ParsingResult;
import org.parboiled.support.ValueStack;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * LessProcessor is JLESS's parsing and translation engine that is used to convert input files that
 * use the LESS styling framework syntax into standard CSS.
 * This implementation is based on the Ruby version of LESS by Alexis Sellier.
 *
 * At this time, the following LESS features are supported:
 *

 *
Variables

 *
Mixins

 *
Mixin Arguments

 *
Nesting

 *

 *
 * The following LESS features are not currently supported:
 *

 *
Operations

 *
Accessors

 *
Imports

 *

 *
 * This implementation does not attempt to generate the same output as the Ruby version.
 * Translation differences from LESS Ruby include:
 *

 *
Equivalent adjacent rule sets are not grouped

 *
Empty rule sets are preserved

 *
Numbers and colors are not reformatted

 *

 *
 * This list only notes changes in the translation stage. See {@link com.bazaarvoice.jless.parser.Parser} for details
 * on any changes to the parsing stage.
 *
 * @see com.bazaarvoice.jless.parser.Parser
 */
public class LessProcessor {

    // Controls whether only parsing or both parsing and translation are performed.
    private boolean _translationEnabled = true;

    // Controls whether a compressed version of the output is printed.
    // There is no performance penalty for enabling compression.
    private boolean _compressionEnabled = false;

    public boolean isTranslationEnabled() {
        return _translationEnabled;
    }

    public void setTranslationEnabled(boolean translationEnabled) {
        _translationEnabled = translationEnabled;
    }

    public boolean isCompressionEnabled() {
        return _compressionEnabled;
    }

    public void setCompressionEnabled(boolean compressionEnabled) {
        _compressionEnabled = compressionEnabled;
    }

    public Result process(InputStream input) throws IOException {
        return process(null, input);
    }

    /**
     * The {@link ScopeNode} from a parent's result is placed on the parser's {@link ValueStack} and joined
     * together to allow for variable and mixin resolution across scopes.
     * @return A printable {@link Result} of processing the given input.
     */
    public Result process(Result parent, InputStream input) throws IOException {
        ValueStack<Node> stack = new DefaultValueStack<Node>();

        // Make the scope of each parent result accessible for variable and mixin resolution during parsing
        ScopeNode parentScope = null;
        if (parent != null) {
            parentScope = parent.getScope();
            stack.push(parentScope);
        }

        // Parse the input
        ParseRunner<Node> parseRunner = new ReportingParseRunner<Node>(Parboiled.createParser(Parser.class, _translationEnabled).Document()).withValueStack(stack);
        ParsingResult<Node> result = parseRunner.run(IOUtils.toString(input, "UTF-8"));

        if (result.hasErrors()) {
            throw new LessTranslationException("An error occurred while parsing a LESS input file:\n" +
                    ErrorUtils.printParseErrors(result));
        }

        // Retrieve the processed result
        ScopeNode scope = (ScopeNode) stack.pop();

        // Link the new scope to the last parent for later variable resolution
        if (parentScope != null) {
            scope.setParentScope(parentScope);
        }

        return new Result(scope);
    }

    public static void main(String[] args) {
        if (args.length == 0) {
            System.err.println("You must specify an input file.");
            System.exit(1);
        }

        LessProcessor translator = new LessProcessor();
        String inputPath = null;

        for (String arg : args) {
            if (arg.equals("-c")) {
                translator.setCompressionEnabled(true);
            } else {
                if (inputPath != null) {
                    System.err.println("Only one input file can be used.");
                    System.exit(1);
                }
                inputPath = arg;
            }
        }

        try {
            System.out.println(translator.process(new FileInputStream(inputPath)));
        } catch (IOException e) {
            System.err.println("Unable to read input file.");
        }
    }

    public class Result {
        private final ScopeNode _scope;
        private final Supplier<String> _toStringSupplier;

        public Result(ScopeNode scope) {
            _scope = scope;
            _toStringSupplier = Suppliers.memoize(new Supplier<String>() {
                @Override
                public String get() {
                    // Perform additional translation steps if needed
                    if (_translationEnabled) {
                        _scope.traverse(new NestedMediaQueries());
                        _scope.traverse(new FlattenNestedRuleSets());
                    }

                    // Print the output nodes
                    Printer printer = new Printer(_compressionEnabled);
                    _scope.traverse(printer);
                    return printer.toString();
                }
            });
        }

        public ScopeNode getScope() {
            return _scope;
        }

        @Override
        public String toString() {
            return _toStringSupplier.get();
        }
    }
}


/*
 * 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.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * DesktopAppLayout is a custom LayoutManager for a set of window panels in a typical arrangement for a desktop
 * application. It divides the window up in a fairly standard way for a desktop GUI type tool. There are left and right
 * tool panels, a status bar, a console panel at the bottom, and a main area in the window centre.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Add/remove components to the layout in the available positions.

 *
 Calculate layout dimensions.

 *
 Provide motion receivers for the resizable console and left and right panels.
 *      {@link MotionDelta}

 *
 Layout the components to the positioning defined.

 *

 *
 * @author Rupert Smith
 */
public class DesktopAppLayout implements LayoutManager
{
    /** Label identifying the center position. */
    public static final String CENTER = "center";

    /** Label identifying the status bar position. */
    public static final String STATUS_BAR = "status";

    /** Label identifying the left grip-bar position. */
    public static final String LEFT_VERTICAL_BAR = "left_vertical_bar";

    /** Label identifying the right grip-bar position. */
    public static final String RIGHT_VERTICAL_BAR = "right_vertical_bar";

    /** Label identifying the right position. */
    public static final String RIGHT_PANE = "right_pane";

    /** Label identifying the left position. */
    public static final String LEFT_PANE = "left_pane";

    /** Label identifying the console position. */
    public static final String CONSOLE = "console";

    /** Defines the default console height. */
    public static final int DEFAULT_CONSOLE_HEIGHT = 90;

    /** Defines the default left or right panel width. */
    public static final int DEFAULT_PANE_WIDTH = 140;

    /** Defines the default status bar height. */
    public static final int DEFAULT_STATUS_BAR_HEIGHT = 20;

    /** Defines the default vertical grip-bar width. */
    public static final int DEFAULT_VBAR_WIDTH = 3;

    /** Defines the default scroll bar width in pixels. */
    public static final int DEFAULT_SCROLL_BAR_SIZE = 25;

    /** Map of all components in the layout by position label. */
    private final Map<String, Component> componentMap = new HashMap<String, Component>();

    /** Map from components to position label, in order to identify position when presented with a component. */
    private final Map<Component, String> reverseMap = new HashMap<Component, String>();

    /** Flags indicating which components in the layout have vertical scroll bars. */
    private final Set<String> verticalScrollBars = new HashSet<String>();

    /** Flags indicating which components in the layout have horizontal scroll bars. */
    private final Set<String> horizontalScrollBars = new HashSet<String>();

    /** The current console height. */
    private int consoleHeight = DEFAULT_CONSOLE_HEIGHT;

    /** The current left panel width. */
    private int leftPaneWidth = DEFAULT_PANE_WIDTH;

    /** The current right panel width. */
    private int rightPaneWidth = DEFAULT_PANE_WIDTH;

    /** Indicates console is in the layout. */
    private boolean hasConsole;

    /** Indicates status bar is in the layout. */
    private boolean hasStatusBar;

    /** Indicates left grip-bar is in the layout. */
    private boolean hasLeftBar;

    /** Indicates left panel is in the layout. */
    private boolean hasLeftPane;

    /** Indicates right grip-bar is in the layout. */
    private boolean hasRightBar;

    /** Indicates right panel is in the layout. */
    private boolean hasRightPane;

    /** {@inheritDoc} */
    public void addLayoutComponent(String name, Component comp)
    {
        componentMap.put(name, comp);
        reverseMap.put(comp, name);
    }

    /** {@inheritDoc} */
    public void removeLayoutComponent(Component comp)
    {
        String name = reverseMap.remove(comp);

        if (name != null)
        {
            componentMap.remove(name);
        }
    }

    /** {@inheritDoc} */
    public Dimension preferredLayoutSize(Container parent)
    {
        Insets insets = parent.getInsets();

        return new Dimension(insets.left + insets.right, insets.top + insets.bottom);
    }

    /** {@inheritDoc} */
    public Dimension minimumLayoutSize(Container parent)
    {
        Insets insets = parent.getInsets();

        return new Dimension(insets.left + insets.right, insets.top + insets.bottom);
    }

    /**
     * Provides a MotionDelta to capture console height resizing through.
     *
     * @return A MotionDelta to capture console height resizing through.
     */
    public MotionDelta getConsoleHeightResizer()
    {
        return new ConsoleHeightResizer();
    }

    /**
     * Provides a MotionDelta to capture left pane width resizing through.
     *
     * @return A MotionDelta to capture left pane width resizing through.
     */
    public MotionDelta getLeftPaneWidthResizer()
    {
        return new LeftPaneWidthResizer();
    }

    /**
     * Provides a MotionDelta to capture right pane width resizing through.
     *
     * @return A MotionDelta to capture right pane width resizing through.
     */
    public MotionDelta getRightPaneWidthResizer()
    {
        return new RightPaneWidthResizer();
    }

    /** {@inheritDoc} */
    public void layoutContainer(Container parent)
    {
        // Get the available area to layout within.
        Insets insets = parent.getInsets();
        int maxWidth = parent.getWidth() - (insets.left + insets.right);
        int maxHeight = parent.getHeight() - (insets.top + insets.bottom);

        // Check which optional components are present.
        updatePresentComponentFlags();

        int centerHeight =
            maxHeight - ((hasConsole ? consoleHeight : 0) + (hasStatusBar ? DEFAULT_STATUS_BAR_HEIGHT : 0));
        int statusBarTop = centerHeight;
        int consoleTop = statusBarTop + DEFAULT_STATUS_BAR_HEIGHT;

        int centerLeft = (hasLeftPane ? leftPaneWidth : 0) + (hasLeftBar ? DEFAULT_VBAR_WIDTH : 0);
        int centerRight = maxWidth - (hasRightPane ? rightPaneWidth : 0) - (hasRightBar ? DEFAULT_VBAR_WIDTH : 0);
        int centerWidth = centerRight - centerLeft;

        int leftBarRight = centerLeft;
        int leftBarLeft = leftBarRight - DEFAULT_VBAR_WIDTH;
        int leftPaneRight = leftBarLeft;

        int rightBarLeft = centerRight;
        int rightPaneLeft = rightBarLeft + DEFAULT_VBAR_WIDTH;

        for (Component component : parent.getComponents())
        {
            String type = reverseMap.get(component);

            int left = 0;
            int top = 0;
            int width = maxWidth;
            int height = maxHeight;

            if (CENTER.equals(type))
            {
                left = centerLeft;
                width = centerWidth;
                height = centerHeight;
            }
            else if (STATUS_BAR.equals(type))
            {
                top = statusBarTop;
                height = DEFAULT_STATUS_BAR_HEIGHT;
            }
            else if (CONSOLE.equals(type))
            {
                top = consoleTop;
                height = consoleHeight;
            }
            else if (LEFT_PANE.equals(type))
            {
                width = leftPaneRight;
                height = centerHeight;
            }
            else if (LEFT_VERTICAL_BAR.equals(type))
            {
                left = leftBarLeft;
                width = DEFAULT_VBAR_WIDTH;
                height = centerHeight;
            }
            else if (RIGHT_VERTICAL_BAR.equals(type))
            {
                left = rightBarLeft;
                width = DEFAULT_VBAR_WIDTH;
                height = centerHeight;
            }
            else if (RIGHT_PANE.equals(type))
            {
                left = rightPaneLeft;
                width = rightPaneWidth;
                height = centerHeight;
            }

            component.setBounds(left, top, width, height);
        }
    }

    /** Keeps the set of flags indicating which window components are present, up-to-date. */
    private void updatePresentComponentFlags()
    {
        hasConsole = componentMap.containsKey(CONSOLE);
        hasStatusBar = componentMap.containsKey(STATUS_BAR);
        hasLeftBar = componentMap.containsKey(LEFT_VERTICAL_BAR);
        hasLeftPane = componentMap.containsKey(LEFT_PANE);
        hasRightBar = componentMap.containsKey(RIGHT_VERTICAL_BAR);
        hasRightPane = componentMap.containsKey(RIGHT_PANE);
    }

    /**
     * Provides a motion delta to capture resize events for the console height.
     */
    private class ConsoleHeightResizer implements MotionDelta
    {
        /** {@inheritDoc} */
        public void deltaX(int delta)
        {
        }

        /** {@inheritDoc} */
        public void deltaY(int delta)
        {
            consoleHeight += delta;
        }
    }

    /**
     * Provides a motion delta to capture resize events for the left pane width.
     */
    private class LeftPaneWidthResizer implements MotionDelta
    {
        /** {@inheritDoc} */
        public void deltaX(int delta)
        {
            leftPaneWidth -= delta;
        }

        /** {@inheritDoc} */
        public void deltaY(int delta)
        {
        }
    }

    /**
     * Provides a motion delta to capture resize events for the right pane width.
     */
    private class RightPaneWidthResizer implements MotionDelta
    {
        /** {@inheritDoc} */
        public void deltaX(int delta)
        {
            rightPaneWidth += delta;
        }

        /** {@inheritDoc} */
        public void deltaY(int delta)
        {
        }
    }
}


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

import org.hobsoft.microbrowser.Microbrowser;
import org.hobsoft.microbrowser.MicrodataDocument;
import org.openqa.selenium.WebDriver;

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

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

/**
 * {@code Microbrowser} implementation that uses Selenium.
 */
public class SeleniumMicrobrowser implements Microbrowser
{
	// ----------------------------------------------------------------------------------------------------------------
	// fields
	// ----------------------------------------------------------------------------------------------------------------

	private final WebDriver driver;

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

	public SeleniumMicrobrowser(WebDriver driver)
	{
		this.driver = checkNotNull(driver, "driver");
	}

	// ----------------------------------------------------------------------------------------------------------------
	// Microbrowser methods
	// ----------------------------------------------------------------------------------------------------------------

	public MicrodataDocument get(String url)
	{
		newUrl(url);

		driver.get(url);

		return new SeleniumMicrodataDocument(driver);
	}
}


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

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

import com.thesett.aima.state.BaseType;
import com.thesett.aima.state.InfiniteValuesException;
import com.thesett.aima.state.RandomInstanceFactory;
import com.thesett.aima.state.Type;
import com.thesett.aima.state.TypeVisitor;
import com.thesett.aima.state.restriction.DecimalMaxRestriction;
import com.thesett.aima.state.restriction.DecimalMinRestriction;
import com.thesett.aima.state.restriction.TypeRestriction;
import com.thesett.common.error.NotImplementedException;

/**
 * Implements a decimal type, with precision and scale.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Maintain a set of named decimal types.
 *
 Provide a default value that is an instance of the type.
 *

 *
 * @author Rupert Smith
 */
public class BigDecimalTypeImpl extends BaseType<BigDecimal> implements Type<BigDecimal>,
    RandomInstanceFactory<BigDecimal>, Serializable, BigDecimalType
{
    /** Holds all of the named decimal types that have been defined. */
    private static final Map<String, BigDecimalTypeImpl> DECIMAL_TYPES = new HashMap<String, BigDecimalTypeImpl>();

    /** Used to hold the name of this decimal type. */
    private final String typeName;

    /** Holds the precision of this type. */
    private final int precision;

    /** Holds the scale of this type. */
    private final int scale;

    /** The minimum value in BigDecimal string notation, or <tt>null</tt> if none is specified. */
    private final String min;

    /** The maximum value in BigDecimal string notation, or <tt>null</tt> if none is specified. */
    private final String max;

    /**
     * Creates a named decimal type with the specified precision and scale.
     *
     * @param typeName  The name of the decimal type.
     * @param precision The precision of the decimal type.
     * @param scale     The scale of the decimal type.
     * @param min       The minimum value in BigDecimal string notation, or <tt>null</tt> if none is specified.
     * @param max       The maximum value in BigDecimal string notation, or <tt>null</tt> if none is specified.
     */
    public BigDecimalTypeImpl(String typeName, int precision, int scale, String min, String max)
    {
        this.typeName = typeName;
        this.precision = precision;
        this.scale = scale;
        this.min = min;
        this.max = max;

        restrictions = new LinkedList<TypeRestriction>();

        if (min != null)
        {
            restrictions.add(new DecimalMinRestriction(min));
        }

        if (max != null)
        {
            restrictions.add(new DecimalMaxRestriction(max));
        }
    }

    /**
     * Creates a new decimal type with the specified name, if it does not already exist.
     *
     * @param  name      The name of the decimal type to create.
     * @param  precision The precision of the decimal type.
     * @param  scale     The scale of the decimal type.
     *
     * @return A new decimal type.
     *
     * @throws IllegalArgumentException If the named type already exists.
     */
    public static Type createInstance(String name, int precision, int scale, String min, String max)
    {
        synchronized (DECIMAL_TYPES)
        {
            // Add the newly created type to the map of all types.
            BigDecimalTypeImpl newType = new BigDecimalTypeImpl(name, precision, scale, min, max);

            // Ensure that the named type does not already exist, unless it has an identical definition already, in which
            // case the old definition can be re-used and the new one discarded.
            BigDecimalTypeImpl oldType = DECIMAL_TYPES.get(name);

            if ((oldType != null) && !oldType.equals(newType))
            {
                throw new IllegalArgumentException("The type '" + name + "' already exists and cannot be redefined.");
            }
            else if ((oldType != null) && oldType.equals(newType))
            {
                return oldType;
            }
            else
            {
                DECIMAL_TYPES.put(name, newType);

                return newType;
            }
        }
    }

    /** {@inheritDoc} */
    public BigDecimal getDefaultInstance()
    {
        return BigDecimal.ZERO;
    }

    /** {@inheritDoc} */
    public String getName()
    {
        return typeName;
    }

    /** {@inheritDoc} */
    public Class<BigDecimal> getBaseClass()
    {
        return BigDecimal.class;
    }

    /** {@inheritDoc} */
    public String getBaseClassName()
    {
        return BigDecimal.class.getName();
    }

    /** {@inheritDoc} */
    public int getNumPossibleValues()
    {
        throw new NotImplementedException();
    }

    /** {@inheritDoc} */
    public Set<BigDecimal> getAllPossibleValuesSet() throws InfiniteValuesException
    {
        throw new InfiniteValuesException("DecimalType has too many values to enumerate.", null);
    }

    /** {@inheritDoc} */
    public Iterator<BigDecimal> getAllPossibleValuesIterator() throws InfiniteValuesException
    {
        throw new InfiniteValuesException("DecimalType has too many values to enumerate.", null);
    }

    /** {@inheritDoc} */
    public int getPrecision()
    {
        return precision;
    }

    /** {@inheritDoc} */
    public int getScale()
    {
        return scale;
    }

    /** {@inheritDoc} */
    public void acceptVisitor(TypeVisitor visitor)
    {
        if (visitor instanceof BigDecimalTypeVisitor)
        {
            ((BigDecimalTypeVisitor) visitor).visit(this);
        }
        else
        {
            super.acceptVisitor(visitor);
        }
    }

    /**
     * Checks two decimal types for equality, based on their name, scale and precision.
     *
     * @param  o The object to compare to.
     *
     * @return <tt>true</tt> if the comparator is an decimal type with the same name, scale and precision.
     */
    public boolean equals(Object o)
    {
        if (this == o)
        {
            return true;
        }

        if ((o == null) || (getClass() != o.getClass()))
        {
            return false;
        }

        BigDecimalTypeImpl that = (BigDecimalTypeImpl) o;

        if (precision != that.precision)
        {
            return false;
        }

        if (scale != that.scale)
        {
            return false;
        }

        if ((typeName != null) ? (!typeName.equals(that.typeName)) : (that.typeName != null))
        {
            return false;
        }

        return true;
    }

    /**
     * Provides a hashcode based on the name, scale and precision of this type.
     *
     * @return A hashcode based on the name, scale and precision of this type.
     */
    public int hashCode()
    {
        int result;
        result = ((typeName != null) ? typeName.hashCode() : 0);
        result = (31 * result) + precision;
        result = (31 * result) + scale;

        return result;
    }
}