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

import java.net.MalformedURLException;
import java.net.URL;

/**
 * Utility methods for working with {@code URL}s.
 */
public final class Urls
{
	// ----------------------------------------------------------------------------------------------------------------
	// constructors
	// ----------------------------------------------------------------------------------------------------------------

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

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

	/**
	 * Creates a URL for the specified string representation.
	 *
	 * @param spec
	 *            the string representation
	 * @return the URL
	 * @throws IllegalArgumentException
	 *             if the specified string representation is not a valid URL
	 */
	public static URL newUrl(String spec)
	{
		try
		{
			return new URL(spec);
		}
		catch (MalformedURLException exception)
		{
			throw new IllegalArgumentException("Invalid URL: " + spec);
		}
	}

	public static URL newUrlOrNull(String spec)
	{
		try
		{
			return new URL(spec);
		}
		catch (MalformedURLException exception)
		{
			return null;
		}
	}
}


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

import java.io.IOException;
import java.security.Principal;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;

/**
 * DebugTag defines a custom tag that can be used to print out extensive debugging information about a JSP page.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Generate debugging information for a JSP
 *

 *
 * @author Rupert Smith
 *
 * @jsp.tag
 *      name = "debug"
 */
public class DebugTag extends TagSupport
{
    /** Used for logging. */
    private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(DebugTag.class.getName());

    /**
     * Returns a String with all basic request information in an HTML table.
     *
     * @return A String with all basic request information in an HTML table.
     */
    public String getRequestInfo()
    {
        Map info = new TreeMap();
        HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();

        info.put("authType", nullToString(req.getAuthType()));
        info.put("characterEncoding", nullToString(req.getCharacterEncoding()));
        info.put("contentLength", Integer.toString(req.getContentLength()));
        info.put("contentType", nullToString(req.getContentType()));
        info.put("contextPath", nullToString(req.getContextPath()));
        info.put("pathInfo", nullToString(req.getPathInfo()));
        info.put("protocol", nullToString(req.getProtocol()));
        info.put("queryString", nullToString(req.getQueryString()));
        info.put("remoteAddr", nullToString(req.getRemoteAddr()));
        info.put("remoteHost", nullToString(req.getRemoteHost()));
        info.put("remoteUser", nullToString(req.getRemoteUser()));
        info.put("requestURI", nullToString(req.getRequestURI()));
        info.put("scheme", nullToString(req.getScheme()));
        info.put("serverName", nullToString(req.getServerName()));
        info.put("serverPort", Integer.toString(req.getServerPort()));
        info.put("servletPath", nullToString(req.getServletPath()));

        return toHTMLTable("request properties", info);
    }

    /**
     * Returns a String with all header information as an HTML table.
     *
     * @return A String with all header information as an HTML table.
     */
    public String getHeaders()
    {
        Map info = new TreeMap();
        HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
        Enumeration names = req.getHeaderNames();

        while (names.hasMoreElements())
        {
            String name = (String) names.nextElement();
            Enumeration values = req.getHeaders(name);
            StringBuffer sb = new StringBuffer();
            boolean first = true;

            while (values.hasMoreElements())
            {
                if (!first)
                {
                    sb.append(" | ");
                }

                first = false;
                sb.append(values.nextElement());
            }

            info.put(name, sb.toString());
        }

        return toHTMLTable("headers", info);
    }

    /**
     * Returns a String with all cookie information as an HTML table.
     *
     * @return A String with all cookie information as an HTML table.
     */
    public String getCookies()
    {
        Map info = new TreeMap();
        HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
        Cookie[] cookies = req.getCookies();

        // check that cookies is not null which it may be if there are no cookies
        if (cookies != null)
        {
            for (int i = 0; i < cookies.length; i++)
            {
                Cookie cooky = cookies[i];
                info.put(cooky.getName(), cooky.getValue());
            }
        }

        return toHTMLTable("cookies", info);
    }

    /**
     * Returns a String with all request parameter information.
     *
     * @return A String with all request parameter information.
     */
    public String getParameters()
    {
        Map info = new TreeMap();
        ServletRequest req = (HttpServletRequest) pageContext.getRequest();
        Enumeration names = req.getParameterNames();

        while (names.hasMoreElements())
        {
            String name = (String) names.nextElement();
            String[] values = req.getParameterValues(name);
            StringBuffer sb = new StringBuffer();

            for (int i = 0; i < values.length; i++)
            {
                if (i != 0)
                {
                    sb.append(" | ");
                }

                sb.append(values[i]);
            }

            info.put(name, sb.toString());
        }

        return toHTMLTable("request parameters", info);
    }

    /**
     * Returns a String with all request scope variables.
     *
     * @return A String with all request scope variables.
     */
    public String getRequestScope()
    {
        Map info = new TreeMap();

        ServletRequest req = (HttpServletRequest) pageContext.getRequest();

        Enumeration names = req.getAttributeNames();

        while (names.hasMoreElements())
        {
            String name = (String) names.nextElement();

            Object value = req.getAttribute(name);

            info.put(name, toStringValue(value));
        }

        return toHTMLTable("request scope", info);
    }

    /**
     * Returns a String with all page scope variables.
     *
     * @return A String with all page scope variables.
     */
    public String getPageScope()
    {
        Map info = new TreeMap();
        Enumeration names = pageContext.getAttributeNamesInScope(PageContext.PAGE_SCOPE);

        while (names.hasMoreElements())
        {
            String name = (String) names.nextElement();
            Object value = pageContext.getAttribute(name);

            info.put(name, toStringValue(value));
        }

        return toHTMLTable("page scope", info);
    }

    /**
     * Returns a String with all session scope variables.
     *
     * @return A String with all session scope variables.
     */
    public String getSessionScope()
    {
        Map info = new TreeMap();
        HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
        HttpSession session = req.getSession();
        Enumeration names = session.getAttributeNames();

        while (names.hasMoreElements())
        {
            String name = (String) names.nextElement();
            Object value = session.getAttribute(name);

            info.put(name, toStringValue(value));
        }

        return toHTMLTable("session scope", info);
    }

    /**
     * Returns a String with all application scope variables.
     *
     * @return A String with all application scope variables.
     */
    public String getApplicationScope()
    {
        Map info = new TreeMap();
        ServletContext context = pageContext.getServletContext();
        Enumeration names = context.getAttributeNames();

        while (names.hasMoreElements())
        {
            String name = (String) names.nextElement();
            Object value = context.getAttribute(name);

            info.put(name, toStringValue(value));
        }

        return toHTMLTable("application scope", info);
    }

    /**
     * Returns the user principal name.
     *
     * @return The user principal name.
     */
    public String getUserPrincipal()
    {
        // Create a hash table to hold the results in
        Map info = new TreeMap();

        // Extract the request from the page context
        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();

        // Get the principal from the request
        Principal principal = request.getUserPrincipal();

        // Check if there is a principal
        if (principal != null)
        {
            info.put("principal name", principal.getName());
        }
        else
        {
            info.put("principal name", "no principal");
        }

        // Convert the results to an HTML table
        return toHTMLTable("container security", info);
    }

    /**
     * Renders the debugging message.
     *
     * @return The tag return code. Always EVAL_BODY_INCLUDE.
     *
     * @throws JspException If the debug information cannot be written to the page.
     */
    public int doStartTag() throws JspException
    {
        log.fine("doStartTag: called");

        try
        {
            // Write out the beggining of the debug table
            pageContext.getResponse().getWriter().write("
");

            // Write out the debugging info for all categories
            pageContext.getResponse().getWriter().write(getRequestInfo());
            pageContext.getResponse().getWriter().write(getHeaders());
            pageContext.getResponse().getWriter().write(getCookies());
            pageContext.getResponse().getWriter().write(getParameters());
            pageContext.getResponse().getWriter().write(getRequestScope());
            pageContext.getResponse().getWriter().write(getPageScope());
            pageContext.getResponse().getWriter().write(getSessionScope());
            pageContext.getResponse().getWriter().write(getApplicationScope());
            pageContext.getResponse().getWriter().write(getUserPrincipal());

            // Write out the closing of the debug table
            pageContext.getResponse().getWriter().write("
");
        }
        catch (IOException e)
        {
            throw new JspException("Got an IOException whilst writing the debug tag to the page.", e);
        }

        // Continue processing the page
        return (EVAL_BODY_INCLUDE);
    }

    /**
     * Returns the String "null" if the value is null, otherwise the value itself.
     *
     * @param  value The string to check for being null.
     *
     * @return The string "null" or the specified string value.
     */
    private String nullToString(String value)
    {
        if (value == null)
        {
            return "null";
        }
        else
        {
            return value;
        }
    }

    /**
     * Returns an HTML table with all the values of the specified property.
     *
     * @param  propName The header for the table.
     * @param  values   The key/value paris to display in the table.
     *
     * @return The HTML table as a string.
     */
    private String toHTMLTable(String propName, Map values)
    {
        StringBuffer tableSB = new StringBuffer();

        tableSB.append("
").append(propName).append("
");

        for (Iterator it = values.keySet().iterator(); it.hasNext();)
        {
            Object o = it.next();
            String key = (String) o;

            tableSB.append("
").append(key).append("	").append(
                values.get(key)).append("
");
        }

        return tableSB.toString();
    }

    /**
     * Returns a string representation of the specified object, in a format suitable for debug output. If the object is
     * an array all its elements are extracted and displayed seperated by commas. Other objects are converted to strings
     * by their toString methods.
     *
     * @param  value The object value to display as a string.
     *
     * @return A string representation of the specified object.
     */
    private String toStringValue(Object value)
    {
        // Check if the value is null
        if (value == null)
        {
            return "null";
        }

        StringBuffer sb = new StringBuffer();

        Class type = value.getClass();

        if (type.isArray())
        {
            Class componentType = type.getComponentType();

            sb.append(componentType.getName());
            sb.append("[]: {");

            if (!componentType.isPrimitive())
            {
                Object[] arr = (Object[]) value;

                for (int i = 0; i < arr.length; i++)
                {
                    if (i != 0)
                    {
                        sb.append(", ");
                    }

                    sb.append(arr[i]);
                }
            }
            else if (componentType == Boolean.TYPE)
            {
                boolean[] arr = (boolean[]) value;

                for (int i = 0; i < arr.length; i++)
                {
                    if (i != 0)
                    {
                        sb.append(", ");
                    }

                    sb.append(arr[i]);
                }
            }
            else if (componentType == Byte.TYPE)
            {
                byte[] arr = (byte[]) value;

                for (int i = 0; i < arr.length; i++)
                {
                    if (i != 0)
                    {
                        sb.append(", ");
                    }

                    sb.append(arr[i]);
                }
            }
            else if (componentType == Character.TYPE)
            {
                char[] arr = (char[]) value;

                for (int i = 0; i < arr.length; i++)
                {
                    if (i != 0)
                    {
                        sb.append(", ");
                    }

                    sb.append(arr[i]);
                }
            }
            else if (componentType == Double.TYPE)
            {
                double[] arr = (double[]) value;

                for (int i = 0; i < arr.length; i++)
                {
                    if (i != 0)
                    {
                        sb.append(", ");
                    }

                    sb.append(arr[i]);
                }
            }
            else if (componentType == Float.TYPE)
            {
                float[] arr = (float[]) value;

                for (int i = 0; i < arr.length; i++)
                {
                    if (i != 0)
                    {
                        sb.append(", ");
                    }

                    sb.append(arr[i]);
                }
            }
            else if (componentType == Integer.TYPE)
            {
                int[] arr = (int[]) value;

                for (int i = 0; i < arr.length; i++)
                {
                    if (i != 0)
                    {
                        sb.append(", ");
                    }

                    sb.append(arr[i]);
                }
            }
            else if (componentType == Long.TYPE)
            {
                long[] arr = (long[]) value;

                for (int i = 0; i < arr.length; i++)
                {
                    if (i != 0)
                    {
                        sb.append(", ");
                    }

                    sb.append(arr[i]);
                }
            }
            else if (componentType == Short.TYPE)
            {
                short[] arr = (short[]) value;

                for (int i = 0; i < arr.length; i++)
                {
                    if (i != 0)
                    {
                        sb.append(", ");
                    }

                    sb.append(arr[i]);
                }
            }

            sb.append("}");
        }
        else
        {
            // Obtain the objects value using toString, but protect this against null pointer exceptions, to harden
            // this implementation.
            String stringValue = null;

            try
            {
                stringValue = value.toString();
            }
            catch (NullPointerException e)
            {
                stringValue = "";
            }

            sb.append(value.getClass().getName()).append(": ").append(stringValue);
        }

        return sb.toString();
    }
}


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

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

import com.thesett.aima.search.SearchNode;
import com.thesett.aima.search.SearchNotExhaustiveException;
import com.thesett.aima.search.Traversable;
import com.thesett.aima.search.spi.BoundProperty;
import com.thesett.aima.search.spi.QueueSearchState;
import com.thesett.common.util.logic.UnaryPredicate;

/**
 * Implements a bounded search from any queue based search method. This prevents the search from going beyond some
 * bounded property of the search nodes (or underlying states). This is a generalisation of the depth-bounded algorithm
 * where the bounded property is always the search node depth to a bounded algorithm that can work with any property.
 *
 *
If a search exhausts its search space without finding a solution it must return null. If it cannot find a solution
 * but knows that there are more states still to search beyond the boundary then it must fail with a
 * {@link MaxBoundException} to disinguish this from the exhausted search space case. In order to do this the algorithm
 * must peek beyond the fringe set by the bounded property of the search to see if there are successor states there.
 *
 *
Sometimes it is not enough to know that there are search nodes beyond the fringe it is also necessary to know what
 * the minimum value of the bounded property is beyond the fringe. For example, this may be used by an iterative
 * deepening algorithm that steps iteratively onto the next limit of the bounded property. Whilst peeking for nodes
 * beyond the fringe this algorithm also calculates their bound property values and keeps track of the minimum.
 *
 *
BoundedAlgorithm can accept different implementations of the {@link BoundProperty} interface to work with
 * different bound values. It implements this interface itself in order to provide a default behaviour as a depth
 * bounded search.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Transform any {@link BaseQueueSearch} into a bounded search.
 *      {@link BaseQueueSearch}
 *

 *
 * @author Rupert Smith
 */
public class BoundedAlgorithm<O, T extends Traversable<O>> extends BaseQueueSearchAlgorithm<O, T>
    implements BoundProperty<O, T>
{
    /** The maximum bound to search to. */
    protected float maxBound;

    /* Used to hold the switch for the minimum bounded property beyond the fringe tracking operation. */
    // protected boolean minBoundFlag = false;

    /** Used to hold a reference to the bounded property calculation function. */
    BoundProperty<O, T> boundPropertyExtractor = this;

    /** Used to keep track of the minimum bounded property that has been seen beyond the boundary fringe. */
    float minBeyondBound = Float.POSITIVE_INFINITY;

    /**
     * Creates a bounded algorithm with the specified bound.
     *
     * @param maxBound The maximum value that the bounded property is allowed to take for the search to consider a
     *                 search node.
     */
    public BoundedAlgorithm(float maxBound)
    {
        this.maxBound = maxBound;
    }

    /**
     * Provides a default implementation of the {@link BoundProperty} interface to extract the depth as the bound
     * property.
     *
     * @param  searchNode The search node to check the bounded property of.
     *
     * @return The search depth of the node.
     */
    public float getBoundProperty(SearchNode searchNode)
    {
        return (float) searchNode.getDepth();
    }

    /**
     * Accepts a bound property extractor to use to extract the property from a search node that is to be compared with
     * the maximal bound.
     *
     * @param extractor The bound property calculation function to use to work out the bound property value of search
     *                  nodes.
     */
    public void setBoundPropertyExtractor(BoundProperty<O, T> extractor)
    {
        this.boundPropertyExtractor = extractor;
    }

    /**
     * Returns the minimum bound property value that was seen on a node that is beyond the current boundary fringe on
     * the last run of the search method. This is designed to be used by iterative deepening algorithms that take this
     * value to be the next boundary to search under.
     *
     * @return The minimum bound property value seen beyond the current max bound fringe on a search run.
     */
    public float getMinBeyondBound()
    {
        return minBeyondBound;
    }

    /**
     * Searches all SearchNodes less than the maximum bound for some property of the nodes.
     *
     * @param  initSearch  The algorithm to turn the start states into the intial set of search nodes, of the correct
     *                     type to begin the search with.
     * @param  startStates The set of start states to begin the search with.
     * @param  maxSteps    The maximum number of steps to search for before giving up. If this is 0 or less then no
     *                     maximum number is enforced.
     * @param  searchSteps The number of steps taken so far. This may be greater than 0 if this search is run
     *                     iteratively and previous iterations of it have already been run.
     *
     * @return The first {@link SearchNode} corresponding to a goal state that is found or null if none can be found
     *         after exhaustive searching.
     *
     * @throws SearchNotExhaustiveException If the search terminates prematurely because the maximum number of steps is
     *                                      reached or because the maximum bound is reached and no more nodes can be
     *                                      found that are below it.
     */
    public SearchNode search(QueueSearchState<O, T> initSearch, Collection<T> startStates, int maxSteps,
        int searchSteps) throws SearchNotExhaustiveException
    {
        // Initialize the queue with the start states set up in search nodes.
        Queue<SearchNode<O, T>> queue = initSearch.enqueueStartStates(startStates);

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

        // Flag used to indicate whether there are unexplored successor states known to exist beyond the max depth
        // fringe.
        boolean beyondFringe = false;

        // Reset the minimum beyond the fringe boundary value.
        minBeyondBound = Float.POSITIVE_INFINITY;

        // Keep running until the queue becomes empty or a goal state is found.
        while (!queue.isEmpty())
        {
            // Extract the head element from the queue.
            SearchNode<O, T> headNode = peekAtHead ? queue.peek() : queue.remove();

            // Expand the successors into the queue whether the current node is a goal state or not.
            // This prepares the queue for subsequent searches, ensuring that goal states do not block
            // subsequent goal states that exist beyond them.
            // Add the successors to the queue provided that they are below or at the maximum bounded property.

            // Get all the successor states.
            Queue<SearchNode<O, T>> successors = new LinkedList<SearchNode<O, T>>();

            headNode.expandSuccessors(successors, reverseEnqueue);

            // Loop over all the successor states checking how they stand with respect to the bounded property.
            for (SearchNode<O, T> successor : successors)
            {
                // Get the value of the bound property for the successor node.
                float boundProperty = boundPropertyExtractor.getBoundProperty(successor);

                // Check if the successor is below or on the bound.
                if (boundProperty <= maxBound)
                {
                    // Add it to the queue to be searched.
                    queue.offer(successor);
                }

                // The successor state is above the bound.
                else
                {
                    // Set the flag to indicate that there is at least one search node known to exist beyond the
                    // bound.
                    beyondFringe = true;

                    // Compare to the best minimum beyond the bound property found so far to see if
                    // this is a new minimum and update the minimum to the new minimum if so.
                    minBeyondBound = (boundProperty < minBeyondBound) ? boundProperty : minBeyondBound;
                }
            }

            // Get the node to be goal checked, either the head node or the new top of queue, depending on the
            // peek at head flag.
            SearchNode<O, T> currentNode = peekAtHead ? queue.remove() : headNode;

            // Check if the current node is a goal state.
            // Only goal check leaves, or nodes already expanded. (The expanded flag will be set on leaves anyway).
            if (currentNode.isExpanded() && goalPredicate.evaluate(currentNode.getState()))
            {
                return currentNode;
            }

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

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

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

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

        // No goal state was found. Check if there known successors beyond the max depth fringe and if so throw
        // a SearchNotExhaustiveException to indicate that the search failed rather than exhausted the search space.
        if (beyondFringe)
        {
            throw new MaxBoundException("Max bound reached.", null);
        }
        else
        {
            // The search space was exhausted so return null to indicate that no goal could be found.
            return null;
        }
    }
}


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

import java.beans.PropertyChangeEvent;

import com.thesett.common.swing.workpanel.WorkPanelState;

/**
 * WorkFlowState models the possible states of a work flow. A work flow is a series of user inputs (often dialog boxes)
 * that progress through stages of a process interactively. They are frequently used as wizards.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
 *

 * Hold the state of the current work flow screen.
 *
 *

 * Notify listeners of changes of screen state.
 *

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

    /** Holds the state of the current work flow screen. */
    private WorkFlowScreenState currentScreenState;

    /**
     * Sets the new current screen state and notifies all listeners of the change in screen state.
     *
     * @param state The current screen state.
     */
    public void setCurrentScreenState(WorkFlowScreenState state)
    {
        /*log.fine("void setCurrentScreenState(WorkFlowScreenState): called");*/

        WorkFlowScreenState oldState = currentScreenState;

        // Keep the new state.
        currentScreenState = state;

        // Notify all listeners of the change of current screen.
        firePropertyChange(new PropertyChangeEvent(this, "currentScreenState", oldState, state));
    }

    /**
     * Gets the current screen state.
     *
     * @return The current screen state.
     */
    public WorkFlowScreenState getCurrentScreenState()
    {
        return currentScreenState;
    }
}


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

import com.thesett.aima.logic.fol.Term;
import com.thesett.aima.logic.fol.TermTraverser;
import com.thesett.aima.logic.fol.TermVisitor;
import com.thesett.aima.search.QueueBasedSearchMethod;
import com.thesett.aima.search.util.Searches;
import com.thesett.common.util.logic.UnaryPredicate;

/**
 * TermWalker combines together a {@link QueueBasedSearchMethod}, a {@link com.thesett.aima.logic.fol.TermTraverser} and
 * a {@link com.thesett.aima.logic.fol.TermVisitor}, to implement a walk over a recursive term data structure. The queue
 * based search method, controls the order of the walk and the order in which nodes in the term are examined. The
 * traverser allows context information to be embedded in the traversal operators, such that whenever a node is
 * examined, the operators leading to it from the root node, will have been expanded in the order that they are
 * encountered along the path from the root to the current node. The term visitor allows visitor methods to be
 * established on a sub-set of the term nodes in order to effect some function or transformation of the term.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Walk over a term in a specified order, visiting sub-terms that match a search criteria.
 *      {@link Term}, {@link TermTraverser}, {@link TermVisitor}, {@link QueueBasedSearchMethod}
 *

 *
 * @author Rupert Smith
 */
public class TermWalker
{
    /** Holds the search that order the walk. */
    private final QueueBasedSearchMethod<Term, Term> search;

    /** Holds the traverser to expand selected nodes with context. */
    private final TermTraverser traverser;

    /** Holds the visitor to apply to every goal node discovered by the search. */
    private final TermVisitor visitor;

    /** Holds an optional goal predicate. */
    private UnaryPredicate<Term> goalPredicate;

    /**
     * Creates a term walker using the specified search, traverser and visitor.
     *
     * @param search    The search to order the walk by.
     * @param traverser The traverser to expand nodes and supply context with.
     * @param visitor   The visitor to apply to every goal node encountered.
     */
    public TermWalker(QueueBasedSearchMethod<Term, Term> search, TermTraverser traverser, TermVisitor visitor)
    {
        this.search = search;
        this.traverser = traverser;
        this.visitor = visitor;
    }

    /**
     * Sets up a goal predicate to be applied on the underlying queue based search method.
     *
     * @param goalPredicate The predicate that evaluates search states to check if they are goals.
     */
    public void setGoalPredicate(UnaryPredicate<Term> goalPredicate)
    {
        this.goalPredicate = goalPredicate;
    }

    /**
     * Walks over the supplied term.
     *
     * @param term The term to walk over.
     */
    public void walk(Term term)
    {
        // Set up the traverser on the term to walk over.
        term.setTermTraverser(traverser);

        // Create a fresh search starting from the term.
        search.reset();

        if (goalPredicate != null)
        {
            search.setGoalPredicate(goalPredicate);
        }

        search.addStartState(term);

        Iterator<Term> treeWalker = Searches.allSolutions(search);

        // If the traverser is a term visitor, allow it to visit the top-level term in the walk to establish
        // an initial context.
        if (traverser instanceof TermVisitor)
        {
            term.accept((TermVisitor) traverser);
        }

        // Visit every goal node discovered in the walk over the term.
        while (treeWalker.hasNext())
        {
            Term nextTerm = treeWalker.next();
            nextTerm.accept(visitor);
        }

        // Remote the traverser on the term to walk over.
        term.setTermTraverser(null);
    }
}


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

import java.util.Comparator;

import com.thesett.aima.search.SearchNode;

/**
 * AStarComparator is a Comparator that compares the f values (f = heuristic + cost) of heuristic search nodes. This can
 * be used to oder the search nodes in an A* Search.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Compare heuristic search nodes by f value.
 *

 *
 * @author Rupert Smith
 */
public final class AStarComparator implements Comparator<SearchNode>
{
    /**
     * Compares two heuristic search nodes by their f values.
     *
     * @param  object1 The first heuristic search node to compare.
     * @param  object2 The second heuristic search node to compare.
     *
     * @return 1 If the first node has a higher f value than the second, 0 if they have the same f value, -1 if the
     *         second has a higher f value than the first.
     */
    public int compare(SearchNode object1, SearchNode object2)
    {
        float f1 = ((HeuristicSearchNode) object1).getF();
        float f2 = ((HeuristicSearchNode) object2).getF();

        return (f1 > f2) ? 1 : ((f1 < f2) ? -1 : 0);
    }
}


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

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.LinkedList;
import java.util.List;

import com.thesett.aima.logic.fol.wam.machine.WAMInternalRegisters;
import com.thesett.common.util.event.EventListenerSupport;

/**
 * InternalRegisterBean represents WAMInternalRegisters as a Java bean, and adds the ability to attach a
 * {@link PropertyChangeListener} to it. The change listener will be notified when registers or flags change value.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Provide change notification on registers and flags.
 *

 *
 * @author Rupert Smith
 */
public class InternalRegisterBean extends WAMInternalRegisters implements RegisterModel
{
    /** Defines the names of the machine registers. */
    private static final String[] REGISTER_NAMES =
        new String[] { "ip", "hp", "hbp", "sp", "up", "ep", "bp", "b0", "trp" };

    /** Defines the names of the machine flags. */
    private static final String[] FLAG_NAMES = new String[] { "writeMode" };

    /** Holds any property change listeners to notify of register value changes. */
    private final EventListenerSupport<PropertyChangeListener> listeners = new EventListenerSupport<PropertyChangeListener>();

    /**
     * Creates an instance of the WAM machine internal register file and flags.
     *
     * @param ip        The current instruction pointer into the code.
     * @param hp        The heap pointer.
     * @param hbp       The top of heap at the latest choice point.
     * @param sp        The secondary heap pointer, used for the heap address of the next term to match.
     * @param up        The unification stack pointer.
     * @param ep        The environment base pointer.
     * @param bp        The choice point base pointer.
     * @param b0        The last call choice point pointer.
     * @param trp       The trail pointer.
     * @param writeMode The write mode flag.
     */
    public InternalRegisterBean(int ip, int hp, int hbp, int sp, int up, int ep, int bp, int b0, int trp,
        boolean writeMode)
    {
        super(ip, hp, hbp, sp, up, ep, bp, b0, trp, writeMode);
    }

    /**
     * Establishes a PropertyChangeListener to notify of changes to the register file or flags.
     *
     * @param listener A PropertyChangeListener to notify of changes to the register file or flags.
     */
    public void addPropertyChangeListener(PropertyChangeListener listener)
    {
        listeners.addListener(listener);
    }

    /**
     * Updates the register file, with a new set of registers.
     *
     * @param registers The new register values.
     */
    public void updateRegisters(WAMInternalRegisters registers)
    {
        List<PropertyChangeEvent> changes = delta(this, registers);

        ip = registers.ip;
        hp = registers.hp;
        hbp = registers.hbp;
        sp = registers.sp;
        up = registers.up;
        ep = registers.ep;
        bp = registers.bp;
        b0 = registers.b0;
        trp = registers.trp;
        writeMode = registers.writeMode;

        notifyChanges(changes);
    }

    /** Provides the current instruction pointer into the code. */
    public int getIP()
    {
        return ip;
    }

    /** Provides the heap pointer. */
    public int getHP()
    {
        return hp;
    }

    /** Provides the top of heap at the latest choice point. */
    public int getHBP()
    {
        return hbp;
    }

    /** Provides the secondary heap pointer, used for the heap address of the next term to match. */
    public int getSP()
    {
        return sp;
    }

    /** Provides the unification stack pointer. */
    public int getUp()
    {
        return up;
    }

    /** Provides the environment base pointer. */
    public int getEP()
    {
        return ep;
    }

    /** Provides the choice point base pointer. */
    public int getBP()
    {
        return bp;
    }

    /** Provides the last call choice point pointer. */
    public int getB0()
    {
        return b0;
    }

    /** Provides the trail pointer. */
    public int getTRP()
    {
        return trp;
    }

    /** Provides the write mode flag. */
    public boolean getWriteMode()
    {
        return writeMode;
    }

    /** {@inheritDoc} */
    public String[] getRegisterNames()
    {
        return REGISTER_NAMES;
    }

    /** {@inheritDoc} */
    public String[] getFlagNames()
    {
        return FLAG_NAMES;
    }

    /** {@inheritDoc} */
    public int getRegisterSizeBytes(String name)
    {
        return 4;
    }

    /** {@inheritDoc} */
    public byte[] getRegister(String name)
    {
        return new byte[0];
    }

    /** {@inheritDoc} */
    public boolean getFlag(String name)
    {
        return false;
    }

    /**
     * Compares the current register file, with a new set, and creates a list property change notifications for any that
     * have changed value.
     *
     * @param newRegisters The new register values.
     */
    private List<PropertyChangeEvent> delta(WAMInternalRegisters oldRegisters, WAMInternalRegisters newRegisters)
    {
        List<PropertyChangeEvent> result = new LinkedList<PropertyChangeEvent>();

        if (oldRegisters.ip != newRegisters.ip)
        {
            result.add(new PropertyChangeEvent(this, "IP", oldRegisters.ip, newRegisters.ip));
        }

        if (oldRegisters.hp != newRegisters.hp)
        {
            result.add(new PropertyChangeEvent(this, "HP", oldRegisters.hp, newRegisters.hp));
        }

        if (oldRegisters.hbp != newRegisters.hbp)
        {
            result.add(new PropertyChangeEvent(this, "HBP", oldRegisters.hbp, newRegisters.hbp));
        }

        if (oldRegisters.sp != newRegisters.sp)
        {
            result.add(new PropertyChangeEvent(this, "SP", oldRegisters.sp, newRegisters.sp));
        }

        if (oldRegisters.up != newRegisters.up)
        {
            result.add(new PropertyChangeEvent(this, "UP", oldRegisters.up, newRegisters.up));
        }

        if (oldRegisters.ep != newRegisters.ep)
        {
            result.add(new PropertyChangeEvent(this, "EP", oldRegisters.ep, newRegisters.ep));
        }

        if (oldRegisters.bp != newRegisters.bp)
        {
            result.add(new PropertyChangeEvent(this, "BP", oldRegisters.bp, newRegisters.bp));
        }

        if (oldRegisters.b0 != newRegisters.b0)
        {
            result.add(new PropertyChangeEvent(this, "B0", oldRegisters.b0, newRegisters.b0));
        }

        if (oldRegisters.trp != newRegisters.trp)
        {
            result.add(new PropertyChangeEvent(this, "TRP", oldRegisters.trp, newRegisters.trp));
        }

        if (oldRegisters.writeMode != newRegisters.writeMode)
        {
            result.add(new PropertyChangeEvent(this, "writeMode", oldRegisters.writeMode, newRegisters.writeMode));
        }

        return result;
    }

    /**
     * Fires off a list of property change events to any interested listeners.
     *
     * @param changes The list of property changes to fire off.
     */
    private void notifyChanges(Iterable<PropertyChangeEvent> changes)
    {
        List<PropertyChangeListener> activeListeners = listeners.getActiveListeners();

        if (activeListeners != null)
        {
            for (PropertyChangeListener listener : activeListeners)
            {
                for (PropertyChangeEvent event : changes)
                {
                    listener.propertyChange(event);
                }
            }
        }
    }
}


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

import com.thesett.text.api.TextGridListener;
import com.thesett.text.api.model.TextGridModel;
import com.thesett.text.api.model.TextTableModel;

/**
 *

CRC Card

 *
 Responsibilities  Collaborations
 *

 *

 *
 * @author Rupert Smith
 */
public class NestedTextGridImpl implements TextGridModel
{
    /** Start column of this child grid. */
    protected final int column;

    /** Start row of this child grid. */
    protected final int row;

    /** Width of this child grid. */
    protected final int width;

    /** Height of this child grid. */
    protected final int height;

    /** The parent grid. */
    private final TextGridModel parent;

    public NestedTextGridImpl(int column, int row, int width, int height, TextGridModel parent)
    {
        this.column = column;
        this.row = row;
        this.width = width;
        this.height = height;

        this.parent = parent;
    }

    /** {@inheritDoc} */
    public int getWidth()
    {
        return width;
    }

    /** {@inheritDoc} */
    public int getHeight()
    {
        return height;
    }

    /** {@inheritDoc} */
    public void insert(char character, int c, int r)
    {
        parent.insert(character, c + column, r + row);
    }

    /** {@inheritDoc} */
    public void insert(String string, int c, int r)
    {
        parent.insert(string, c + column, r + row);
    }

    /** {@inheritDoc} */
    public char getCharAt(int c, int r)
    {
        return parent.getCharAt(c + column, r + row);
    }

    /** {@inheritDoc} */
    public TextGridModel createInnerGrid(int c, int r, int w, int h)
    {
        return parent.createInnerGrid(c + column, r + row, w, h);
    }

    /** {@inheritDoc} */
    public TextTableModel createTable(int c, int r, int w, int h)
    {
        return parent.createTable(c + column, r + row, w, h);
    }

    /** {@inheritDoc} */
    public void addTextGridListener(TextGridListener listener)
    {
        parent.addTextGridListener(listener);
    }

    /** {@inheritDoc} */
    public void removeTextGridListener(TextGridListener listener)
    {
        parent.removeTextGridListener(listener);
    }
}


/*
 * 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.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.thesett.aima.logic.fol.BasePositionalVisitor;
import com.thesett.aima.logic.fol.Clause;
import com.thesett.aima.logic.fol.Functor;
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.compiler.PositionalContext;
import com.thesett.aima.logic.fol.compiler.PositionalTermTraverser;
import com.thesett.common.util.doublemaps.SymbolKey;
import com.thesett.common.util.doublemaps.SymbolTable;

/**
 * PositionAndOccurrenceVisitor visits a clause to gather information about the positions in which components of the
 * clause appear.
 *
 *
For variables, the following information is gathered:
 *
 *

 *
A count of the number of times the variable occurs in the clause (singleton detection).

 *
A flag indicating that variable only ever appears in non-argument positions.

 *
The last functor body in the clause in which a variable appears, provided it only does so in argument
 * position.

 *

 *
 *
For constants, the following information is gathered:
 *
 *

 *
A flag indicating the constant only ever appears in non-argument positions.

 *

 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Count variable occurrences in a clause.

 *
 Detect variables and constants only appearing in non-argument positions.

 *
 Identify the last functor in which a variable appears, if it only does as an argument.

 *

 *
 * @author Rupert Smith
 */
public class PositionAndOccurrenceVisitor extends BasePositionalVisitor
{
    /** Used for debugging. */
    /* private static final Logger log = Logger.getLogger(PositionAndOccurrenceVisitor.class.getName()); */

    /** Holds the current top-level body functor. <tt>null</tt> when traversing the head. */
    private Functor topLevelBodyFunctor;

    /** Holds a set of all constants encountered. */
    private final Map<Integer, List<SymbolKey>> constants = new HashMap<Integer, List<SymbolKey>>();

    /** Holds a set of all constants found to be in argument positions. */
    private final Collection<Integer> argumentConstants = new HashSet<Integer>();

    /**
     * Creates a positional visitor.
     *
     * @param interner    The name interner.
     * @param symbolTable The compiler symbol table.
     * @param traverser   The positional context traverser.
     */
    public PositionAndOccurrenceVisitor(VariableAndFunctorInterner interner,
        SymbolTable<Integer, String, Object> symbolTable, PositionalTermTraverser traverser)
    {
        super(interner, symbolTable, traverser);
    }

    /**
     * {@inheritDoc}
     *
     *
Counts variable occurrences and detects if the variable ever appears in an argument position.
     */
    protected void enterVariable(Variable variable)
    {
        // Initialize the count to one or add one to an existing count.
        Integer count = (Integer) symbolTable.get(variable.getSymbolKey(), SymbolTableKeys.SYMKEY_VAR_OCCURRENCE_COUNT);
        count = (count == null) ? 1 : (count + 1);
        symbolTable.put(variable.getSymbolKey(), SymbolTableKeys.SYMKEY_VAR_OCCURRENCE_COUNT, count);

        /*log.fine("Variable " + variable + " has count " + count + ".");*/

        // Get the nonArgPosition flag, or initialize it to true.
        Boolean nonArgPositionOnly =
            (Boolean) symbolTable.get(variable.getSymbolKey(), SymbolTableKeys.SYMKEY_VAR_NON_ARG);
        nonArgPositionOnly = (nonArgPositionOnly == null) ? true : nonArgPositionOnly;

        // Clear the nonArgPosition flag if the variable occurs in an argument position.
        nonArgPositionOnly = inTopLevelFunctor(traverser) ? false : nonArgPositionOnly;
        symbolTable.put(variable.getSymbolKey(), SymbolTableKeys.SYMKEY_VAR_NON_ARG, nonArgPositionOnly);

        /*log.fine("Variable " + variable + " nonArgPosition is " + nonArgPositionOnly + ".");*/

        // If in an argument position, record the parent body functor against the variable, as potentially being
        // the last one it occurs in, in a purely argument position.
        // If not in an argument position, clear any parent functor recorded against the variable, as this current
        // last position of occurrence is not purely in argument position.
        if (inTopLevelFunctor(traverser))
        {
            symbolTable.put(variable.getSymbolKey(), SymbolTableKeys.SYMKEY_VAR_LAST_ARG_FUNCTOR, topLevelBodyFunctor);
        }
        else
        {
            symbolTable.put(variable.getSymbolKey(), SymbolTableKeys.SYMKEY_VAR_LAST_ARG_FUNCTOR, null);
        }
    }

    /**
     * {@inheritDoc}
     *
     *
Checks if a constant ever appears in an argument position.
     *
     *
Sets the 'inTopLevelFunctor' flag, whenever the traversal is directly within a top-level functors arguments.
     * This is set at the end, so that subsequent calls to this will pick up the state of this flag at the point
     * immediately below a top-level functor.
     */
    protected void enterFunctor(Functor functor)
    {
        /*log.fine("Functor: " + functor.getName() + " <- " + symbolTable.getSymbolKey(functor.getName()));*/

        // Only check position of occurrence for constants.
        if (functor.getArity() == 0)
        {
            // Add the constant to the set of all constants encountered.
            List<SymbolKey> constantSymKeys = constants.get(functor.getName());

            if (constantSymKeys == null)
            {
                constantSymKeys = new LinkedList<SymbolKey>();
                constants.put(functor.getName(), constantSymKeys);
            }

            constantSymKeys.add(functor.getSymbolKey());

            // If the constant ever appears in argument position, take note of this.
            if (inTopLevelFunctor(traverser))
            {
                argumentConstants.add(functor.getName());
            }
        }

        // Keep track of the current top-level body functor.
        if (isTopLevel(traverser) && !traverser.isInHead())
        {
            topLevelBodyFunctor = functor;
        }
    }

    /**
     * Upon leaving the clause, sets the nonArgPosition flag on any constants that need it.
     *
     * @param clause The clause being left.
     */
    protected void leaveClause(Clause clause)
    {
        // Remove the set of constants appearing in argument positions, from the set of all constants, to derive
        // the set of constants that appear in non-argument positions only.
        constants.keySet().removeAll(argumentConstants);

        // Set the nonArgPosition flag on all symbol keys for all constants that only appear in non-arg positions.
        for (List<SymbolKey> symbolKeys : constants.values())
        {
            for (SymbolKey symbolKey : symbolKeys)
            {
                symbolTable.put(symbolKey, SymbolTableKeys.SYMKEY_FUNCTOR_NON_ARG, true);
            }
        }
    }

    /**
     * Checks if the current position is immediately within a top-level functor.
     *
     * @param  context The position context to examine.
     *
     * @return <tt>true</tt> iff the current position is immediately within a top-level functor.
     */
    private boolean inTopLevelFunctor(PositionalContext context)
    {
        PositionalContext parentContext = context.getParentContext();

        return parentContext.isTopLevel() || isTopLevel(parentContext);
    }

    /**
     * Functors are considered top-level when they appear at the top-level within a clause, or directly beneath a parent
     * conjunction or disjunction that is considered to be top-level.
     *
     * @param  context The position context to examine.
     *
     * @return <tt>true</tt> iff the current position is a top-level functor.
     */
    private boolean isTopLevel(PositionalContext context)
    {
        Term term = context.getTerm();

        if (term.getSymbolKey() == null)
        {
            return false;
        }

        Boolean isTopLevel = (Boolean) symbolTable.get(term.getSymbolKey(), SymbolTableKeys.SYMKEY_TOP_LEVEL_FUNCTOR);

        return (isTopLevel == null) ? false : isTopLevel;
    }
}


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

/**
 * A Heap is a collection of data items that are stored as a tree that obeyes the heap property. The heap property is
 * that there exists an ordering amongst the elements, and the elements are stored in a tree in such a way that the
 * child elements are all ordered the same way with respect to their parent node and that the same direction of ordering
 * applies accross the whole tree. Most often the ordering is "greater than" in which case the minimum element is always
 * at the root of the tree and child elements are always greater than their parents. One of the purposes of heaps is to
 * be able to provide fast access to the smallest element amongst a data collection.
 *
 *
Viewed in this way a heap is a priorty queue of elements. Heaps are often used to store key and value pairs where
 * the keys are ordered as in a heap. This implementation of heaps does not explicitly split the data elements into keys
 * and values because this is unnecessary and easily achieved by providing an ordering comparator that only looks at
 * what is considered to be the 'key' part of a data element. The end user can easily split up the elements just as
 * could be done for storage in any collection. A heap is not a map in the sense of java.util.Map because it does not
 * provide a mapping from any key onto any value, it can contain multiple entries for the same 'key' and it is only
 * designed to provide quick access to the smallest key and not the entire key set.
 *
 *
AbstractHeap provides a skeletal implementation of a heap to be sub-classed with specific implementations.
 *
 *
All methods that are allowed to be optional and throw unsupported operation exceptions do so in this base class if
 * they are not implemented. It is up to sub-class imlpementations to provide these operations if desired.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Add one or many elements to the heap.
 *
Check if the heap contains an element or many elements.
 *
Remove one or many element from the heap.
 *
Get the heap size or check if it is empty.
 *
Convert the heap into an array.
 *

 *
 * @author Rupert Smith
 * @todo   Add the decrease key and merge operations. Extract interface for heaps with these operations.
 */
public abstract class AbstractHeap<E> implements Queue<E>
{
    /**
     * Holds a reference to the element comparator used to order the heap. This will be null if no comparator is set by
     * the constructor, in which case the natural ordering should be used instead.
     */
    protected final Comparator<? super E> entryComparator;

    /** Used to hold the number of elements in the heap. */
    protected int size;

    /**
     * Creates a heap that uses the given comparator to order its elements.
     *
     * @param comparator The comparator to be used to order the heap elements.
     */
    protected AbstractHeap(Comparator<? super E> comparator)
    {
        // Set the size to zero.
        size = 0;

        // Keep the comparator.
        this.entryComparator = comparator;
    }

    /**
     * Returns an iterator over the elements in this heap.
     *
     * @return An iterator over the elements in this heap.
     */
    public abstract Iterator<E> iterator();

    /**
     * Inserts the specified element into this heap.
     *
     * @param  o The data element to add to the heap.
     *
     * @return True if the element was added, false if it was not.
     */
    public abstract boolean offer(E o);

    /**
     * Retrieves, but does not remove, the (head) minimum element of this heap, returning null if this heap is empty.
     *
     * @return The head element of the heap, or null if the heap is empty.
     */
    public abstract E peek();

    /**
     * Retrieves and removes the (head) minimum element of this heap, or null if this heap is empty.
     *
     * @return The head element of the heap, or null if the heap is empty.
     */
    public abstract E poll();

    /**
     * Adds the specified element to this heap. This implementation returns <tt>true</tt> if <tt>offer</tt> succeeds,
     * else throws an IllegalStateException.
     *
     * @param  o the element
     *
     * @return <tt>true</tt> (as per the general contract of <tt>Collection.add</tt>).
     *
     * @throws NullPointerException  if the specified element is <tt>null</tt>
     * @throws IllegalStateException if element cannot be added
     */
    public boolean add(E o)
    {
        if (offer(o))
        {
            return true;
        }
        else
        {
            throw new IllegalStateException("Heap full");
        }
    }

    /**
     * Adds all of the elements in the specified collection to this heap. Attempts to addAll of a queue to itself result
     * in <tt>IllegalArgumentException</tt>. Further, the behavior of this operation is undefined if the specified
     * collection is modified while the operation is in progress.
     *
     *
This implementation iterates over the specified collection, and adds each element returned by the iterator to
     * this collection, in turn. A runtime exception encountered while trying to add an element (including, in
     * particular, a <tt>null</tt> element) may result in only some of the elements having been successfully added when
     * the associated exception is thrown.
     *
     * @param  collection collection whose elements are to be added to this collection.
     *
     * @return <tt>true</tt> if this collection changed as a result of the call.
     *
     * @throws NullPointerException     if the specified collection or any of its elements are null.
     * @throws IllegalArgumentException if collection is this queue.
     *
     * @see    #add(Object)
     */
    public boolean addAll(Collection<? extends E> collection)
    {
        if (collection == null)
        {
            throw new IllegalArgumentException("The 'collection' parameter may not be null.");
        }

        if (collection == this)
        {
            throw new IllegalArgumentException();
        }

        boolean modified = false;

        for (E aC : collection)
        {
            if (add(aC))
            {
                modified = true;
            }
        }

        return modified;
    }

    /**
     * Removes all of the elements from this heap. The heap will be empty after this call returns.
     *
     *
This implementation repeatedly invokes {@link #poll poll} until it returns <tt>null</tt>.
     */
    public void clear()
    {
        while (poll() != null)
        {
            ;
        }
    }

    /**
     * Returns true if this heap contains the specified element. More formally, returns <tt>true</tt> if and only if
     * this collection contains at least one element <tt>e</tt> such that <tt>(o==null ? e==null : o.equals(e))</tt>.
     *
     *
This implementation iterates over the elements in the collection, checking each element in turn for equality
     * with the specified element.
     *
     * @param  o object to be checked for containment in this collection.
     *
     * @return <tt>true</tt> if this collection contains the specified element.
     */
    public boolean contains(Object o)
    {
        // Get an iterator over the whole heap.
        Iterator<E> e = iterator();

        // Check if the element to be scanned for is null.
        if (o == null)
        {
            while (e.hasNext())
            {
                if (e.next() == null)
                {
                    return true;
                }
            }
        }

        // Its not a null element so check for it by the equality method.
        else
        {
            while (e.hasNext())
            {
                if (o.equals(e.next()))
                {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Returns true if this heap contains all of the elements in the specified collection.
     *
     *
This implementation iterates over the specified collection, checking each element returned by the iterator in
     * turn to see if it's contained in this collection. If all elements are so contained <tt>true</tt> is returned,
     * otherwise <tt>false</tt>.
     *
     * @param  c collection to be checked for containment in this collection.
     *
     * @return <tt>true</tt> if this collection contains all of the elements in the specified collection.
     *
     * @throws NullPointerException if the specified collection is null.
     *
     * @see    #contains(Object)
     */
    public boolean containsAll(Collection<?> c)
    {
        for (Object aC : c)
        {
            if (!contains(aC))
            {
                return false;
            }
        }

        return true;
    }

    /**
     * Returns true if this heap contains no elements.
     *
     * @return <tt>true</tt> if this heap contains no elements.
     */
    public boolean isEmpty()
    {
        return size() == 0;
    }

    /**
     * Removes a single instance of the specified element from this heap, if it is present. (optional operation). More
     * formally, removes an element <tt>e</tt> such that <tt>(o==null ? e==null : o.equals(e))</tt>, if the collection
     * contains one or more such elements. Returns <tt>true</tt> if the collection contained the specified element (or
     * equivalently, if the collection changed as a result of the call).
     *
     *
This implementation iterates over the collection looking for the specified element. If it finds the element,
     * it removes the element from the collection using the iterator's remove method.
     *
     *
Note that this implementation throws an <tt>UnsupportedOperationException</tt> if the iterator returned by
     * this collection's iterator method does not implement the <tt>remove</tt> method and this collection contains the
     * specified object.
     *
     * @param  o element to be removed from this collection, if present.
     *
     * @return <tt>true</tt> if the collection contained the specified element.
     *
     * @throws UnsupportedOperationException if the <tt>remove</tt> method is not supported by this collection.
     */
    public boolean remove(Object o)
    {
        Iterator<E> e = iterator();

        if (o == null)
        {
            while (e.hasNext())
            {
                if (e.next() == null)
                {
                    e.remove();

                    return true;
                }
            }
        }
        else
        {
            while (e.hasNext())
            {
                if (o.equals(e.next()))
                {
                    e.remove();

                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Removes all this heap's elements that are also contained in the specified collection.
     *
     *
This implementation iterates over this collection, checking each element returned by the iterator in turn to
     * see if it's contained in the specified collection. If it's so contained, it's removed from this collection with
     * the iterator's <tt>remove</tt> method.
     *
     *
Note that this implementation will throw an <tt>UnsupportedOperationException</tt> if the iterator returned by
     * the <tt>iterator</tt> method does not implement the <tt>remove</tt> method and this collection contains one or
     * more elements in common with the specified collection.
     *
     * @param  c elements to be removed from this collection.
     *
     * @return <tt>true</tt> if this collection changed as a result of the call.
     *
     * @throws UnsupportedOperationException if the <tt>removeAll</tt> method is not supported by this collection.
     * @throws NullPointerException          if the specified collection is null.
     *
     * @see    #remove(Object)
     * @see    #contains(Object)
     */
    public boolean removeAll(Collection<?> c)
    {
        boolean modified = false;
        Iterator<?> e = iterator();

        while (e.hasNext())
        {
            if (c.contains(e.next()))
            {
                e.remove();
                modified = true;
            }
        }

        return modified;
    }

    /**
     * Retains only the elements in this heap that are contained in the specified collection. In other words, removes
     * from this collection all of its elements that are not contained in the specified collection.
     *
     *
This implementation iterates over this collection, checking each element returned by the iterator in turn to
     * see if it's contained in the specified collection. If it's not so contained, it's removed from this collection
     * with the iterator's <tt>remove</tt> method.
     *
     *
Note that this implementation will throw an <tt>UnsupportedOperationException</tt> if the iterator returned by
     * the <tt>iterator</tt> method does not implement the <tt>remove</tt> method and this collection contains one or
     * more elements not present in the specified collection.
     *
     * @param  c elements to be retained in this collection.
     *
     * @return <tt>true</tt> if this collection changed as a result of the call.
     *
     * @throws UnsupportedOperationException if the <tt>retainAll</tt> method is not supported by this Collection.
     * @throws NullPointerException          if the specified collection is null.
     *
     * @see    #remove(Object)
     * @see    #contains(Object)
     */
    public boolean retainAll(Collection<?> c)
    {
        boolean modified = false;
        Iterator<E> e = iterator();

        while (e.hasNext())
        {
            if (!c.contains(e.next()))
            {
                e.remove();
                modified = true;
            }
        }

        return modified;
    }

    /**
     * Returns the number of elements in this heap.
     *
     * @return The number of elements in this heap.
     */
    public int size()
    {
        return size;
    }

    /**
     * Returns an array containing all of the elements in this heap. If the collection makes any guarantees as to what
     * order its elements are returned by its iterator, this method must return the elements in the same order. The
     * returned array will be "safe" in that no references to it are maintained by the collection. (In other words, this
     * method must allocate a new array even if the collection is backed by an Array). The caller is thus free to modify
     * the returned array.
     *
     *
This implementation allocates the array to be returned, and iterates over the elements in the collection,
     * storing each object reference in the next consecutive element of the array, starting with element 0.
     *
     * @return an array containing all of the elements in this collection.
     */
    public Object[] toArray()
    {
        Object[] result = new Object[size()];

        Iterator<E> e = iterator();

        for (int i = 0; e.hasNext(); i++)
        {
            result[i] = e.next();
        }

        return result;
    }

    /**
     * Returns an array containing all of the elements in this heap; the runtime type of the returned array is that of
     * the specified array. If the collection fits in the specified array, it is returned therein. Otherwise, a new
     * array is allocated with the runtime type of the specified array and the size of this collection.
     *
     *
If the collection fits in the specified array with room to spare (i.e., the array has more elements than the
     * collection), the element in the array immediately following the end of the collection is set to <tt>null</tt>.
     * This is useful in determining the length of the collection only if the caller knows that the collection
     * does not contain any <tt>null</tt> elements.)
     *
     *
If this collection makes any guarantees as to what order its elements are returned by its iterator, this
     * method must return the elements in the same order.
     *
     *
This implementation checks if the array is large enough to contain the collection; if not, it allocates a new
     * array of the correct size and type (using reflection). Then, it iterates over the collection, storing each object
     * reference in the next consecutive element of the array, starting with element 0. If the array is larger than the
     * collection, a <tt>null</tt> is stored in the first location after the end of the collection.
     *
     * @param  a   The array into which the elements of the collection are to be stored, if it is big enough; otherwise,
     *             a new array of the same runtime type is allocated for this purpose.
     * @param  <T> The type of the array.
     *
     * @return an array containing the elements of the collection.
     *
     * @throws NullPointerException if the specified array is <tt>null</tt>.
     * @throws ArrayStoreException  if the runtime type of the specified array is not a supertype of the runtime type of
     *                              every element in this collection.
     */
    public <T> T[] toArray(T[] a)
    {
        int size = size();

        if (a.length < size)
        {
            a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
        }

        Iterator<E> it = iterator();
        Object[] result = a;

        for (int i = 0; i < size; i++)
        {
            result[i] = it.next();
        }

        if (a.length > size)
        {
            a[size] = null;
        }

        return a;
    }

    /**
     * Returns the comparator used to order this heap.
     *
     * @return The comparator used to order this heap, or null if this heap is sorted according to its elements natural
     *         ordering (using Comparable).
     */
    public Comparator<? super E> comparator()
    {
        return entryComparator;
    }

    /**
     * Retrieves, but does not remove, the (head) minimum element of this heap. This implementation returns the result
     * of <tt>peek</tt> unless the queue is empty.
     *
     * @return the head of this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public E element()
    {
        E x = peek();

        if (x != null)
        {
            return x;
        }
        else
        {
            throw new NoSuchElementException();
        }
    }

    /**
     * Retrieves and removes the (head) minimum element of this heap. This implementation returns the result of <tt>
     * poll</tt> unless the queue is empty.
     *
     * @return the head of this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public E remove()
    {
        E x = poll();

        if (x != null)
        {
            return x;
        }
        else
        {
            throw new NoSuchElementException();
        }
    }

    /**
     * Returns a string representation of this heap. The string representation consists of a list of the collection's
     * elements in the order they are returned by its iterator, enclosed in square brackets (<tt>"[]"</tt>). Adjacent
     * elements are separated by the characters <tt>", "</tt> (comma and space). Elements are converted to strings as by
     * <tt>String.valueOf(Object)</tt>.
     *
     *
This implementation creates an empty string buffer, appends a left square bracket, and iterates over the
     * collection appending the string representation of each element in turn. After appending each element except the
     * last, the string <tt>", "</tt> is appended. Finally a right bracket is appended. A string is obtained from the
     * string buffer, and returned.
     *
     * @return a string representation of this collection.
     */
    public String toString()
    {
        StringBuffer buf = new StringBuffer();

        buf.append("[");

        Iterator<E> i = iterator();
        boolean hasNext = i.hasNext();

        while (hasNext)
        {
            E o = i.next();

            buf.append((o == this) ? "(this Collection)" : String.valueOf(o));
            hasNext = i.hasNext();

            if (hasNext)
            {
                buf.append(", ");
            }
        }

        buf.append("]");

        return buf.toString();
    }
}


/*
 * 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.ArrayList;
import java.util.Collection;

/**
 * An unsynchronized LIFO stack. This class provides easy access to pushing and popping objects to and from a stack
 * where the rule is that the last object in is the first object out. As with most Java collections, this class is
 * wholly unsynchronized.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Check if stack is empty
 *
Peek at top element
 *
Pop top element from stack
 *
Push element onto stack
 *
Search stack for element location
 *
Calculate size of stack
 *

 *
 * @author Rupert Smith
 */
public class LifoStack<E> extends ArrayList<E> implements Stack<E>
{
    /** Constructs an empty LIFO stack. */
    public LifoStack()
    {
        super();
    }

    /**
     * Builds a LIFO stack from another collection.
     *
     * @param c The collection to copy into this stack.
     */
    public LifoStack(Collection c)
    {
        super(c);
    }

    /**
     * Provides a look at the last object placed on the stack, since it will be the first one out. This method does not
     * change the contents of the stack. Because this class is unsynchronized, applications using this class are
     * responsible for making sure that a peek() followed by a pop() returns the same value.
     *
     * @return the object on the top of the stack
     */
    public E peek()
    {
        int last = size() - 1;
        E ob;

        if (last == -1)
        {
            return null;
        }

        ob = get(last);

        return ob;
    }

    /**
     * Pops the last object placed on the stack off of it and returns it.
     *
     * @return the last object placed on the stack
     */
    public E pop()
    {
        int last = size() - 1;
        E ob;

        if (last == -1)
        {
            return null;
        }

        ob = get(last);
        remove(last);

        return ob;
    }

    /**
     * Pushes a new object onto the stack.
     *
     * @param  ob the new object
     *
     * @return the new object
     */
    public E push(E ob)
    {
        add(ob);

        return ob;
    }

    /**
     * Searches the stack for the specified object. Returns the location of the object with respect to the top of the
     * stack or -1.
     *
     * @param  ob the object being sought
     *
     * @return the index of the object on the stack or -1.
     */
    public int search(E ob)
    {
        int i = lastIndexOf(ob);

        if (i == -1)
        {
            return -1;
        }
        else
        {
            return (size() - i);
        }
    }
}


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

import java.io.IOException;
import java.util.Collections;

import org.hobsoft.microbrowser.Microbrowser;
import org.hobsoft.microbrowser.MicrobrowserException;
import org.hobsoft.microbrowser.MicrodataDocument;
import org.jsoup.Connection.Method;
import org.jsoup.Connection.Response;
import org.jsoup.Jsoup;

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

/**
 * {@code Microbrowser} implementation that uses jsoup.
 */
public class JsoupMicrobrowser implements Microbrowser
{
	// ----------------------------------------------------------------------------------------------------------------
	// Microbrowser methods
	// ----------------------------------------------------------------------------------------------------------------

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

		try
		{
			Response response = Jsoup.connect(url)
				.method(Method.GET)
				.ignoreHttpErrors(true)
				.execute();

			return new JsoupMicrodataDocument(Collections.<String, String>emptyMap(), response);
		}
		catch (IOException exception)
		{
			throw new MicrobrowserException("Error fetching page: " + url, exception);
		}
	}
}


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

import java.util.Iterator;
import java.util.LinkedList;

import com.thesett.aima.search.Operator;
import com.thesett.common.util.StackQueue;

/**
 * A predicate is a collection of clauses, that describe the logical truth of the predicate.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Capture a disjunction of clauses into a predicate.
 *

 *
 * @author Rupert Smith
 */
public class Predicate<T extends Clause> extends BaseTerm implements Term
{
    /** The clauses that make up this predicate. */
    protected T[] body;

    /**
     * Creates a predicate formed from a set of clauses.
     *
     * @param body The clauses that make up the body of the predicate.
     */
    public Predicate(T[] body)
    {
        this.body = body;
    }

    /** {@inheritDoc} */
    public Term getValue()
    {
        return this;
    }

    /**
     * Gets all of the clauses that make up the body of the predicate.
     *
     * @return All of the clauses that make up the body of the predicate.
     */
    public T[] getBody()
    {
        return body;
    }

    /** {@inheritDoc} */
    public void free()
    {
    }

    /** {@inheritDoc} */
    public Iterator<Operator<Term>> getChildren(boolean reverse)
    {
        if ((traverser != null) && (traverser instanceof PredicateTraverser))
        {
            return ((PredicateTraverser) traverser).traverse(this, reverse);
        }
        else
        {
            LinkedList<Operator<Term>> resultList = null;

            if (!reverse)
            {
                resultList = new LinkedList<Operator<Term>>();
            }
            else
            {
                resultList = new StackQueue<Operator<Term>>();
            }

            if (body != null)
            {
                for (Term bodyTerm : body)
                {
                    resultList.add(bodyTerm);
                }
            }

            return resultList.iterator();
        }
    }

    /** {@inheritDoc} */
    public void accept(TermVisitor visitor)
    {
        if (visitor instanceof PredicateVisitor)
        {
            ((PredicateVisitor) visitor).visit(this);
        }
        else
        {
            super.accept(visitor);
        }
    }

    /** {@inheritDoc} */
    public String toString(VariableAndFunctorInterner interner, boolean printVarName, boolean printBindings)
    {
        String result = "";

        if (body != null)
        {
            for (int i = 0; i < body.length; i++)
            {
                result +=
                    body[i].toString(interner, printVarName, printBindings) + ((i < (body.length - 1)) ? "\n" : "");
            }
        }

        return result;
    }

    /** {@inheritDoc} */
    public String toString()
    {
        String bodyString = "[";

        if (body != null)
        {
            for (int i = 0; i < body.length; i++)
            {
                bodyString += body[i].toString() + ((i < (body.length - 1)) ? ", " : "");
            }
        }

        bodyString += "]";

        return "Clause: [ body = " + bodyString + " ]";
    }
}


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

import java.util.HashMap;
import java.util.Map;

import com.thesett.aima.logic.fol.Functor;
import com.thesett.aima.logic.fol.FunctorName;
import com.thesett.aima.logic.fol.VariableAndFunctorInterner;
import com.thesett.aima.logic.fol.wam.compiler.DefaultBuiltIn;
import com.thesett.common.util.Function;
import com.thesett.common.util.ReflectionUtils;

/**
 * BuiltInTransform implements a compilation transformation over term syntax trees, that substitutes for functors that
 * map onto Prolog built-ins, an extension of the functor type that implements the built-in.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Transform functors to built in functors where appropriate.
 *      {@link BuiltInFunctor}, {@link VariableAndFunctorInterner}.
 *

 *
 * @author Rupert Smith
 */
public class BuiltInTransform implements Function<Functor, Functor>
{
    /** Holds a mapping from functor names to built-in implementations. */
    private final Map<FunctorName, Class<? extends BuiltInFunctor>> builtIns =
        new HashMap<FunctorName, Class<? extends BuiltInFunctor>>();

    /** Holds the default built in, for standard compilation and interners and symbol tables. */
    private final DefaultBuiltIn defaultBuiltIn;

    /**
     * Initializes the built-in transformation by population the the table of mappings of functors onto their built-in
     * implementations.
     *
     * @param defaultBuiltIn The default built in, for standard compilation and interners and symbol tables.
     */
    public BuiltInTransform(DefaultBuiltIn defaultBuiltIn)
    {
        this.defaultBuiltIn = defaultBuiltIn;

        builtIns.put(new FunctorName("true", 0), True.class);
        builtIns.put(new FunctorName("fail", 0), Fail.class);
        builtIns.put(new FunctorName("!", 0), Cut.class);

        /*builtIns.put(new FunctorName("=", 2), Unifies.class);
        builtIns.put(new FunctorName("\\=", 2), NonUnifies.class);*/
        builtIns.put(new FunctorName(";", 2), Disjunction.class);
        builtIns.put(new FunctorName(",", 2), Conjunction.class);
        builtIns.put(new FunctorName("call", 1), Call.class);
        /*builtIns.put(new FunctorName("is", 2), Is.class);
        builtIns.put(new FunctorName(">", 2), GreaterThan.class);
        builtIns.put(new FunctorName(">=", 2), GreaterThanOrEqual.class);
        builtIns.put(new FunctorName("<", 2), LessThan.class);
        builtIns.put(new FunctorName("=<", 2), LessThanOrEqual.class);
        builtIns.put(new FunctorName("integer", 1), IntegerCheck.class);
        builtIns.put(new FunctorName("float", 1), FloatCheck.class);*/
    }

    /**
     * Applies a built-in replacement transformation to functors. If the functor matches built-in, a
     * {@link BuiltInFunctor} is created with a mapping to the functors built-in implementation, and the functors
     * arguments are copied into this new functor. If the functor does not match a built-in, it is returned unmodified.
     *
     * @param  functor The functor to attempt to map onto a built-in.
     *
     * @return The functor unmodified, or a {@link BuiltInFunctor} replacement for it.
     */
    public Functor apply(Functor functor)
    {
        FunctorName functorName = defaultBuiltIn.getInterner().getFunctorFunctorName(functor);

        Class<? extends BuiltInFunctor> builtInClass;

        builtInClass = builtIns.get(functorName);

        if (builtInClass != null)
        {
            return ReflectionUtils.newInstance(ReflectionUtils.getConstructor(builtInClass,
                    new Class[] { Functor.class, DefaultBuiltIn.class }), new Object[] { functor, defaultBuiltIn });
        }
        else
        {
            return functor;
        }
    }
}