/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */
#if !defined(XPATHEXPRESSION_HEADER_GUARD_1357924680)
#define XPATHEXPRESSION_HEADER_GUARD_1357924680



// Base header file.  Must be first.
#include <xalanc/XPath/XPathDefinitions.hpp>



#include <xalanc/Include/XalanVector.hpp>



#include <iosfwd>



#include <xalanc/XalanDOM/XalanDOMString.hpp>



#include <xalanc/PlatformSupport/DOMStringHelper.hpp>
#include <xalanc/PlatformSupport/PrintWriter.hpp>



#include <xalanc/XPath/XToken.hpp>
#include <xalanc/XPath/XalanXPathException.hpp>



namespace XALAN_CPP_NAMESPACE {



using xercesc::MemoryManager;



class XALAN_XPATH_EXPORT XPathExpression
{
public:

    typedef std::ostream         OstreamType;

    typedef XalanVector<int>                    OpCodeMapType;
    typedef XalanVector<XToken>                 TokenQueueType;

    typedef OpCodeMapType::value_type           OpCodeMapValueType;
    typedef OpCodeMapValueType                  OpCodeMapSizeType;

    typedef XalanVector<OpCodeMapValueType>     OpCodeMapValueVectorType;

    typedef XalanVector<double>                 NumberLiteralValueVectorType;

#define XALAN_XPATH_EXPRESSION_USE_ITERATORS

#if defined(XALAN_XPATH_EXPRESSION_USE_ITERATORS)
    typedef OpCodeMapType::const_iterator   OpCodeMapPositionType;
#else
    typedef OpCodeMapSizeType               OpCodeMapPositionType;
#endif
    typedef OpCodeMapType::difference_type  OpCodeMapDifferenceType;
    typedef TokenQueueType::value_type      TokenQueueValueType;
    typedef int                             TokenQueueSizeType;
    typedef TokenQueueSizeType              TokenQueuePositionType;

    /**
     * List of operations codes.
     *
     * Code for the descriptions of the operations codes:
     * [UPPER CASE] indicates a literal value,
     * [lower case] is a description of a value,
     *      ([length] always indicates the length of the operation,
     *       including the operations code and the length integer.)
     * {UPPER CASE} indicates the given production,
     * {description} is the description of a new production,
     *      (For instance, {boolean expression} means some expression 
     *       that should be resolved to a boolean.)
     *  * means that it occurs zero or more times,
     *  + means that it occurs one or more times,
     *  ? means that it is optional.
     *
     * returns: indicates what the production should return.
     */
    enum eOpCodes
    {
        /**
         * [ELEMWILDCARD]
         * Means ELEMWILDCARD ("*"), used instead 
         * of string index in some places.
         */
        eELEMWILDCARD = -3,

        /**
         * [EMPTY]
         * Empty slot to indicate NULL.
         */
        eEMPTY = -2,

        /**
         * [ENDOP]
         * Some operators may like to have a terminator.
         */
        eENDOP = -1,

        /**
         * [OP_XPATH]
         * [length]
         *  {expression}
         * 
         * returns: 
         *  XNodeSet
         *  XNumber
         *  XString
         *  XBoolean
         *  XRTree
         *  XObject
         */
        eOP_XPATH = 1,

        /**
         * [OP_OR]
         * [length]
         *  {boolean expression}
         *  {boolean expression}
         * 
         * returns: 
         *  XBoolean
         */
        eOP_OR = 2,

        /**
         * [OP_AND]
         * [length]
         *  {boolean expression}
         *  {boolean expression}
         * 
         * returns: 
         *  XBoolean
         */
        eOP_AND = 3,

        /**
         * [OP_NOTEQUALS]
         * [length]
         *  {expression}
         *  {expression}
         * 
         * returns: 
         *  XBoolean
         */
        eOP_NOTEQUALS = 4,

        /**
         * [OP_EQUALS]
         * [length]
         *  {expression}
         *  {expression}
         * 
         * returns: 
         *  XBoolean
         */
        eOP_EQUALS = 5,

        /**
         * [OP_LTE] (less-than-or-equals)
         * [length]
         *  {number expression}
         *  {number expression}
         * 
         * returns: 
         *  XBoolean
         */
        eOP_LTE = 6,

        /**
         * [OP_LT] (less-than)
         * [length]
         *  {number expression}
         *  {number expression}
         * 
         * returns: 
         *  XBoolean
         */
        eOP_LT = 7,

        /**
         * [OP_GTE] (greater-than-or-equals)
         * [length]
         *  {number expression}
         *  {number expression}
         * 
         * returns: 
         *  XBoolean
         */
        eOP_GTE = 8,

        /**
         * [OP_GT] (greater-than)
         * [length]
         *  {number expression}
         *  {number expression}
         * 
         * returns: 
         *  XBoolean
         */
        eOP_GT = 9,

        /**
         * [OP_PLUS]
         * [length]
         *  {number expression}
         *  {number expression}
         * 
         * returns: 
         *  XNumber
         */
        eOP_PLUS = 10,

        /**
         * [OP_MINUS]
         * [length]
         *  {number expression}
         *  {number expression}
         * 
         * returns: 
         *  XNumber
         */
        eOP_MINUS = 11,

        /**
         * [OP_MULT]
         * [length]
         *  {number expression}
         *  {number expression}
         * 
         * returns: 
         *  XNumber
         */
        eOP_MULT = 12,

        /**
         * [OP_DIV]
         * [length]
         *  {number expression}
         *  {number expression}
         * 
         * returns: 
         *  XNumber
         */
        eOP_DIV = 13,

        /**
         * [OP_MOD]
         * [length]
         *  {number expression}
         *  {number expression}
         * 
         * returns: 
         *  XNumber
         */
        eOP_MOD = 14,

        /**
         * [OP_NEG]
         * [length]
         *  {number expression}
         * 
         * returns: 
         *  XNumber
         */
        eOP_NEG = 15,

        /**
         * [OP_BOOL] (cast operation)
         * [length]
         *  {expression}
         * 
         * returns: 
         *  XBoolean
         */
        eOP_BOOL = 16,

        /**
         * [OP_UNION]
         * [length]
         *  {PathExpr}+
         * 
         * returns: 
         *  XNodeSet
         */
        eOP_UNION = 17,

        /**
         * [OP_LITERAL]
         * [3]
         * [index to token]
         * 
         * returns: 
         *  XString
         */
        eOP_LITERAL = 18,

        /**
         * [OP_VARIABLE]
         * [3]
         * [index to token]
         * 
         * returns: 
         *  XString
         */
        eOP_VARIABLE = 19,

        /**
         * [OP_GROUP]
         * [length]
         *  {expression}
         * 
         * returns: 
         *  XNodeSet
         *  XNumber
         *  XString
         *  XBoolean
         *  XRTree
         *  XObject
         */
        eOP_GROUP = 20,

        /**
         * [OP_NUMBERLIT] (Number literal.)
         * [3]
         * [index to token]
         * 
         * returns: 
         *  XString
         */
        eOP_NUMBERLIT = 21,

        /**
         * [OP_ARGUMENT] (Function argument.)
         * [length]
         *  {expression}
         * 
         * returns: 
         *  XNodeSet
         *  XNumber
         *  XString
         *  XBoolean
         *  XRTree
         *  XObject
         */
        eOP_ARGUMENT = 22,

        /**
         * [OP_EXTFUNCTION] (Extension function.)
         * [length]
         * [index to namespace token]
         * [index to function name token]
         *  {OP_ARGUMENT}*
         * 
         * returns: 
         *  XNodeSet
         *  XNumber
         *  XString
         *  XBoolean
         *  XRTree
         *  XObject
         */
        eOP_EXTFUNCTION = 23,

        /**
         * [OP_FUNCTION]
         * [length]
         * [FUNC_ID]
         * [arg count]
         *  {OP_ARGUMENT}*
         * [ENDOP]
         * 
         * returns: 
         *  XNodeSet
         *  XNumber
         *  XString
         *  XBoolean
         *  XRTree
         *  XObject
         */
        eOP_FUNCTION = 24,

        /**
         * [OP_LOCATIONPATH]
         * [length]
         *   {FROM_stepType}
         * | {function}{predicate}*
         * [ENDOP]
         * 
         * (Note that element and attribute namespaces and 
         * names can be wildcarded '*'.)
         * 
         * returns: 
         *  XNodeSet
         */
        eOP_LOCATIONPATH = 25,

        /**
         * [OP_PREDICATE]
         * [length]
         *  {expression}
         * [ENDOP] (For safety)
         * 
         * returns: 
         *  XBoolean or XNumber
         */
        eOP_PREDICATE = 26,

        /**
         * [NODETYPE_COMMENT]
         * No size or arguments.
         * 
         * returns: 
         *  XBoolean
         */
        eNODETYPE_COMMENT = 27,
        
        /**
         * [NODETYPE_TEXT]
         * No size or arguments.
         * 
         * returns: 
         *  XBoolean
         */
        eNODETYPE_TEXT = 28,
        
        /**
         * [NODETYPE_PI]
         * [index to token]
         * 
         * returns: 
         *  XBoolean
         */
        eNODETYPE_PI = 29,
        
        /**
         * [NODETYPE_NODE]
         * No size or arguments.
         * 
         * returns: 
         *  XBoolean
         */
        eNODETYPE_NODE = 30,
        
        /**
         * [NODENAME]
         * [index to ns token or EMPTY]
         * [index to name token]
         * 
         * returns: 
         *  XBoolean
         */
        eNODENAME = 31,
        
        /**
         * [NODETYPE_ROOT]
         * No size or arguments.
         * 
         * returns: 
         *  XBoolean
         */
        eNODETYPE_ROOT = 32,
        
        /**
         * [NODETYPE_ANY]
         * No size or arguments.
         * 
         * returns: 
         *  XBoolean
         */
        eNODETYPE_ANYELEMENT = 33,

        /**
         * [FROM_stepType]
         * [length, including predicates]
         * [length of just the step, without the predicates]
         * {node test}
         * {predicates}?
         * 
         * returns: 
         *  XBoolean
         */
        eFROM_ANCESTORS = 34,
        eFROM_ANCESTORS_OR_SELF = 35,
        eFROM_ATTRIBUTES = 36,
        eFROM_CHILDREN = 37,
        eFROM_DESCENDANTS = 38,
        eFROM_DESCENDANTS_OR_SELF = 39,
        eFROM_FOLLOWING = 40,
        eFROM_FOLLOWING_SIBLINGS = 41,
        eFROM_PARENT = 42,
        eFROM_PRECEDING = 43,
        eFROM_PRECEDING_SIBLINGS = 44,
        eFROM_SELF = 45,
        eFROM_NAMESPACE = 46,
        eFROM_ROOT = 47,

        /**
         * [OP_UNION]
         * [length]
         *  {PathExpr}+
         * 
         * returns: 
         *  XNodeSet
         */
        eOP_MATCHPATTERN = 48,

        /**
         * [OP_UNION]
         * [length]
         *  {PathExpr}+
         * 
         * returns: 
         *  XNodeSet
         */
        eOP_LOCATIONPATHPATTERN = 49,

        // For match patterns
        eMATCH_ATTRIBUTE = 50,
        eMATCH_ANY_ANCESTOR = 51,
        eMATCH_IMMEDIATE_ANCESTOR = 52,
        eMATCH_ANY_ANCESTOR_WITH_PREDICATE = 53,
        eMATCH_ANY_ANCESTOR_WITH_FUNCTION_CALL = 54,

        /**
         * [OP_PREDICATE_WITH_POSITION]
         * [length]
         *  {expression}
         * [ENDOP] (For safety)
         * 
         * returns: 
         *  XBoolean or XNumber
         */
        eOP_PREDICATE_WITH_POSITION = 55,
  
        /**
         * These are values for intrinsic functions which
         * have been compiled directly into the op map.
         */
        eOP_FUNCTION_POSITION = 56,
        eOP_FUNCTION_LAST = 57,
        eOP_FUNCTION_COUNT = 58,
        eOP_FUNCTION_NOT = 59,
        eOP_FUNCTION_TRUE = 60,
        eOP_FUNCTION_FALSE = 61,
        eOP_FUNCTION_BOOLEAN = 62,
        eOP_FUNCTION_NAME_0 = 63,
        eOP_FUNCTION_NAME_1 = 64,
        eOP_FUNCTION_LOCALNAME_0 = 65,
        eOP_FUNCTION_LOCALNAME_1 = 66,
        eOP_FUNCTION_FLOOR = 67,
        eOP_FUNCTION_CEILING = 68,
        eOP_FUNCTION_ROUND = 69,
        eOP_FUNCTION_NUMBER_0 = 70,
        eOP_FUNCTION_NUMBER_1 = 71,
        eOP_FUNCTION_STRING_0 = 72,
        eOP_FUNCTION_STRING_1 = 73,
        eOP_FUNCTION_STRINGLENGTH_0 = 74,
        eOP_FUNCTION_STRINGLENGTH_1 = 75,
        eOP_FUNCTION_NAMESPACEURI_0 = 76,
        eOP_FUNCTION_NAMESPACEURI_1 = 77,
        eOP_FUNCTION_SUM = 78,
        eOP_FUNCTION_CONCAT = 79,

        // Always add _before_ this one and update
        // s_opCodeLengthArray.
        eOpCodeNextAvailable
    };  // enum eOpCodes

    /**
     * Exception class thrown when an invalid XPath expression is encountered
     */
    class XALAN_XPATH_EXPORT XPathExpressionException : public XalanXPathException
    {
    public:

        /**
         * Construct an XPathExpressionException object.
         * 
         * @param theMessage string error message
         */
        XPathExpressionException(const XalanDOMString&  theMessage,
                                    MemoryManager& theManager);

        virtual~
        XPathExpressionException();
    };

    /**
     * Exception class thrown when an invalid XPath operation code is encountered
     */
    class XALAN_XPATH_EXPORT InvalidOpCodeException : public XPathExpressionException
    {
    public:

        /**
         * Construct an InvalidOpCodeException object.
         * 
         * @param theOpCode operation code that caused the exception
         */
        InvalidOpCodeException(
                    OpCodeMapValueType  theOpCode,
                    XalanDOMString&     theBuffer);

        virtual~
        InvalidOpCodeException();

    private:

        static XalanDOMString&
        FormatErrorMessage(
                OpCodeMapValueType  theOpCode,
                XalanDOMString&     theBuffer);
    };

    /**
     * Exception class thrown when an invalid number of XPath arguments is
     * encountered
     */
    class XALAN_XPATH_EXPORT InvalidArgumentCountException : public XPathExpressionException
    {
    public:

        /**
         * Construct an InvalidArgumentCountException object.
         * 
         * @param theOpCode operation code that caused the exception
         * @param theExpectedCount the correct number of arguments for "opcode"
         * @param theSuppliedCount the number of arguments supplied
         */
        InvalidArgumentCountException(
            OpCodeMapValueType  theOpCode,
            OpCodeMapValueType  theExpectedCount,
            OpCodeMapValueType  theSuppliedCount,
            XalanDOMString&     theBuffer);

        virtual~
        InvalidArgumentCountException();

    private:

        static XalanDOMString&
        FormatErrorMessage(
            OpCodeMapValueType  theOpCode,
            OpCodeMapValueType  theExpectedCount,
            OpCodeMapValueType  theSuppliedCount,
            XalanDOMString&     theBuffer);
    };

    /**
     * Exception class thrown when an invalid XPath argument is encountered
     */
    class XALAN_XPATH_EXPORT InvalidArgumentException : public XPathExpressionException
    {
    public:

        /**
         * Construct an InvalidArgumentException object.
         * 
         * @param theOpCode operation code that caused the exception
         * @param theValue invalid argument value
         */
        InvalidArgumentException(
            OpCodeMapValueType  theOpCode,
            OpCodeMapValueType  theValue,
            XalanDOMString&     theBuffer);

        virtual~
        InvalidArgumentException();

    private:

        static XalanDOMString&
        FormatErrorMessage(
                OpCodeMapValueType  theOpCode,
                OpCodeMapValueType  theValue,
                XalanDOMString&     theBuffer);
    };


    /**
     * The length is always the opcode position + 1. Length is always expressed
     * as the opcode+length bytes, so it is always 2 or greater.  This is the
     * offset from the op code where the length is stored.  It will always
     * remain one.
     */
    enum eDummy
    {
        s_opCodeMapLengthIndex = 1
    };

    explicit
    XPathExpression(MemoryManager& theManager);

    ~XPathExpression();

    MemoryManager&
    getMemoryManager()
    {
        return m_opMap.getMemoryManager();
    }
    /**
     * Reset the expression.
     */
    void
    reset();

    /**
     * Shrink internal tables.
     */
    void
    shrink();

    /**
     * Retrieve number of elements in the operations code map.
     * 
     * @return size of operations code map
     */
    OpCodeMapSizeType
    opCodeMapSize() const
    {
        return OpCodeMapSizeType(m_opMap.size());
    }

    /**
     * Retrieve length of the operations code map stored in the map. The length
     * of the entire map is stored after the first op code.  That offset is
     * determined by this const static member.  Note that as expressions are
     * defined recursively, this is really just the length of the first
     * expression in the map, which is the top of the parse tree. Any
     * subexpression will also have a length entry at the same offset from the
     * beginning of the subexpression.
     * 
     * @return length of operations code map
     */
    OpCodeMapValueType
    opCodeMapLength() const
    {
        const OpCodeMapSizeType     theSize = opCodeMapSize();

        if (theSize > s_opCodeMapLengthIndex)
        {
            assert(theSize == OpCodeMapSizeType(m_opMap[s_opCodeMapLengthIndex]));

            return m_opMap[s_opCodeMapLengthIndex];
        }
        else
        {
            assert(theSize == OpCodeMapValueType(theSize));

            return OpCodeMapValueType(theSize);
        }
    }

    OpCodeMapPositionType
    getInitialOpCodePosition() const
    {
#if defined(XALAN_XPATH_EXPRESSION_USE_ITERATORS)
        return m_opMap.begin();
#else
        return 0;
#endif
    }

    bool
    isValidOpCodePosition(OpCodeMapPositionType     opPos) const
    {
        const OpCodeMapDifferenceType  theDifference =
            OpCodeMapDifferenceType(opPos - getInitialOpCodePosition());

        return theDifference >= 0 && 
               theDifference < opCodeMapSize();
    }

#if defined(XALAN_XPATH_EXPRESSION_USE_ITERATORS)
    bool
    isValidOpCodePosition(OpCodeMapSizeType     theIndex) const
    {
        return theIndex >= 0 && theIndex < opCodeMapSize();
    }

    /**
     * Retrieve the value of an operation code at a specified index in the
     * op code map.
     * 
     * @param theIndex The index in list
     * @return value of operation code
     */
    OpCodeMapValueType
    getOpCodeMapValue(OpCodeMapSizeType     theIndex) const
    {
        assert(theIndex < opCodeMapLength());

        return m_opMap[theIndex];
    }
#endif

    /**
     * Retrieve the value of an operation code at a specified position in the
     * list.
     * 
     * @param opPos position in list
     * @return value of operation code
     */
    OpCodeMapValueType
    getOpCodeMapValue(OpCodeMapPositionType     opPos) const
    {
        assert(opPos < getInitialOpCodePosition() + opCodeMapLength());

#if defined(XALAN_XPATH_EXPRESSION_USE_ITERATORS)
        return *opPos;
#else

        return m_opMap[opPos];
#endif
    }

    /**
     * Set the value of an operation code at a specified index in the
     * OpCode map.
     * 
     * @param theOpCodeMapIndex The index in the OpCode map
     * @param theValue value of operation code
     */
    void
    setOpCodeMapValue(
            OpCodeMapSizeType           theOpCodeMapIndex,
            const OpCodeMapValueType&   theValue)
    {
        assert(theOpCodeMapIndex < opCodeMapLength());

        m_opMap[theOpCodeMapIndex] = theValue;
    }

    OpCodeMapValueType
    getOpCodeArgumentLength(OpCodeMapPositionType   opPos) const
    {
        return getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
    }

    /**
     * Retrieve the length of an operation code at a specified position in the
     * op map.
     * 
     * @param opPos position in the op map
     * @return length of operation code
     */
    OpCodeMapValueType
    getOpCodeLengthFromOpMap(OpCodeMapPositionType  opPos,
                             MemoryManager&     theManager) const;

#if defined(XALAN_XPATH_EXPRESSION_USE_ITERATORS)
    /**
     * Retrieve the length of an operation code at a specified index in the
     * op map.
     * 
     * @param theIndex The index in the op map
     * @return length of operation code
     */
    OpCodeMapValueType
    getOpCodeLengthFromOpMap(OpCodeMapSizeType      theIndex,
                                MemoryManager& theManager) const;
#endif

#if defined(XALAN_XPATH_EXPRESSION_USE_ITERATORS)
    /**
     * Retrieve the position of the next operation code at a specified position
     * in the list.
     * 
     * @param opPos position in list
     * @return position of next operation code
     */
    OpCodeMapPositionType
    getNextOpCodePosition(OpCodeMapPositionType     opPos) const
    {
        assert(opPos < getInitialOpCodePosition() + opCodeMapLength());

        return opPos + *(opPos + s_opCodeMapLengthIndex);
    }
#endif

    /**
     * Retrieve the position of the next operation code at a specified index
     * in the list.
     * 
     * @param theIndex theIndex in list
     * @return position of next operation code
     */
    OpCodeMapSizeType
#if defined(XALAN_XPATH_EXPRESSION_USE_ITERATORS)
    getNextOpCodePosition(OpCodeMapSizeType     theIndex) const
#else
    getNextOpCodePosition(OpCodeMapPositionType     theIndex) const
#endif
    {
        assert(theIndex < opCodeMapLength());

        assert(theIndex + m_opMap[theIndex + s_opCodeMapLengthIndex] ==
               OpCodeMapSizeType(theIndex + m_opMap[theIndex + s_opCodeMapLengthIndex]));

        return OpCodeMapSizeType(theIndex + m_opMap[theIndex + s_opCodeMapLengthIndex]);
    }

    /**
     * Set the arguments for an operation code at a specified index in the
     * list.
     * 
     * @param opPos position in list
     * @param theOpCode operation code
     * @param theIndex  index in list
     * @param theArgs   vector or arguments to supply
     */
    void
    setOpCodeArgs(
            eOpCodes                            theOpCode,
            OpCodeMapSizeType                   theIndex,
            const OpCodeMapValueVectorType&     theArgs);

    /**
     * Add an operation code to the list.
     * 
     * @param theOpCode operation code
     * @return the position of the op code
     */
    OpCodeMapSizeType
    appendOpCode(eOpCodes   theOpCode);

    /**
     * Add an operation code with supplied arguments to the list.
     * 
     * @param theOpCode operation code
     * @param theArgs   vector or arguments to supply
     */
    OpCodeMapSizeType
    appendOpCode(
            eOpCodes                            theOpCode,
            const OpCodeMapValueVectorType&     theArgs)
    {
        const OpCodeMapSizeType     thePosition = appendOpCode(theOpCode);

        setOpCodeArgs(theOpCode,
                      thePosition,
                      theArgs);

        return thePosition;
    }

    /**
     * Replace an operation code with supplied code.
     * 
     * @param theIndex  The index of the old operation code
     * @param theOldOpCode The old operation code
     * @param theNewOpCode The new operation code
     */
    void
    replaceOpCode(
            OpCodeMapSizeType   theIndex,
            eOpCodes            theOldOpCode,
            eOpCodes            theNewOpCode);

    /**
     * Insert an operation code at a specified index in the list.
     * 
     * @param theOpCode operation code
     * @param theIndex  index in list
     */
    OpCodeMapValueType
    insertOpCode(
            eOpCodes            theOpCode,
            OpCodeMapSizeType   theIndex);

    /**
     * Update the length of an operation code at a specified index in the list.
     * This presumes that the other opcodes have been appended to the
     * expression, and that the specified op code's length needs to be set.
     * The size includes the normal length of the opcode, plus the length of
     * its subexpressions.
     * 
     * @param theIndex  index in list
     */
    void
    updateOpCodeLength(OpCodeMapSizeType    theIndex)
    {
        assert(theIndex < opCodeMapSize());

        updateOpCodeLength(m_opMap[theIndex], theIndex);
    }

    /**
     * Update the length of an operation code that has moved to a new index in
     * the list.
     * 
     * @param theOpCode        operation code
     * @param theOriginalIndex original index in list
     * @param theNewIndex      new index in list
     */
    void
    updateShiftedOpCodeLength(
            OpCodeMapValueType  theOpCode,
            OpCodeMapSizeType   theOriginalIndex,
            OpCodeMapSizeType   theNewIndex);

    /**
     * Update the length of an operation code at a specified index in the list.
     * This presumes that the other opcodes have been appended to the
     * expression, and that the specified op code's length needs to be set.
     * The size includes the normal length of the opcode, plus the length of
     * its subexpressions.
     * 
     * @param theOpCode operation code at specified index
     * @param theIndex  index in list
     */
    void
    updateOpCodeLength(
            OpCodeMapValueType  theOpCode,
            OpCodeMapSizeType   theIndex);

    /**
     * Whether the operation code is one of the node test types, for example,
     * "ancestor::" or "child::"
     *
     * @param theOpCode operation code
     * @return true if code represents a node test
     */
    static bool
    isNodeTestOpCode(OpCodeMapValueType     theOpCode);

    /**
     * Update the length of an operation code after a node test code.
     * 
     * @param theIndex  index in list
     */
    void
    updateOpCodeLengthAfterNodeTest(OpCodeMapSizeType   theIndex);

    /**
     * Whether there are any more tokens in the token queue.
     *
     * @return true if there are more tokens
     */
    bool
    hasMoreTokens() const
    {
        return tokenQueueSize() > m_currentPosition ? true : false;
    }

    /**
     * Retrieve number of elements in the token queue.
     * 
     * @return size of token queue
     */
    TokenQueueSizeType
    tokenQueueSize() const
    {
        return TokenQueueSizeType(m_tokenQueue.size());
    }

    bool
    isValidTokenQueuePosition(TokenQueueSizeType    thePosition) const
    {
        return thePosition < tokenQueueSize();
    }

    /**
     * Retrieve the current position in the token queue.
     *
     * @return position in queue
     */
    TokenQueueSizeType
    getTokenPosition() const
    {
        return m_currentPosition;
    }

    /**
     * Set the current position in the token queue to zero.
     */
    void
    resetTokenPosition()
    {
        m_currentPosition = 0;
    }

    /**
     * Retrieve a token at the specified position in the token queue.
     * 
     * @param thePosition position in queue
     * @return pointer to XObject token
     */
    const XToken*
    getToken(TokenQueuePositionType     thePosition) const
    {
        assert(thePosition < tokenQueueSize());

        return &m_tokenQueue[thePosition];
    }

    /**
     * Retrieve the next token in the token queue.
     * 
     * @return pointer to XObject token
     */
    const XToken*
    getNextToken()
    {
        if (hasMoreTokens() == true)
        {
            return getToken(m_currentPosition++);
        }
        else
        {
            return 0;
        }
    }

    /**
     * Retrieve the previous token in the token queue.
     * 
     * @return pointer to XObject token
     */
    const XToken*
    getPreviousToken()
    {
        if (m_currentPosition > 0)
        {
            return getToken(--m_currentPosition);
        }
        else
        {
            return 0;
        }
    }

    enum eRelativeDirection
    {
        eRelativeBackward,
        eRelativeForward
    };

    /**
     * Retrieve a token at the specified offset relative to the current
     * position in the token queue.
     * 
     * @param theOffset offset from current position
     * @param theDirection the direction in which to move
     * @return pointer to XObject token
     */
    const XToken*
    getRelativeToken(
        TokenQueuePositionType  theOffset,
        eRelativeDirection      theDirection) const
    {
        const TokenQueuePositionType    thePosition =
            calculateRelativePosition(theOffset, theDirection);

        if (thePosition == tokenQueueSize())
        {
            return 0;
        }
        else
        {
            return getToken(thePosition);
        }
    }

    /**
     * Push a token onto the token queue.
     * 
     * @param theToken the string value to push
     */
    void
    pushToken(const XalanDOMString&     theToken)
    {
        m_tokenQueue.push_back(
            XToken(

                DoubleSupport::toDouble(theToken, getMemoryManager()),
                theToken,
                getMemoryManager()));
    }

    /**
     * Push a token onto the token queue.
     * 
     * @param theNumber the number value to push
     * @param theString the string value to push
     */
    void
    pushToken(
            double                  theNumber,
            const XalanDOMString&   theString)
    {
        m_tokenQueue.push_back(
            XToken(
                theNumber,
                theString,
                getMemoryManager()));
    }

    /**
     * Insert a token onto the token queue at the
     * current position.
     *
     * @param theToken the string value to push
     */
    void
    insertToken(const XalanDOMString&   theToken)
    {
        m_tokenQueue.insert(
            m_tokenQueue.begin() + (m_currentPosition - 1),
            XToken(
                theToken,
                DoubleSupport::toDouble(theToken, getMemoryManager()),
                getMemoryManager()));
    }

    /**
     * Insert a token onto the token queue at the
     * current position.
     *
     * @param theNumber the number value to push
     * @param theString the string value to push
     */
    void
    insertToken(
            double                  theNumber,
            const XalanDOMString&   theString)
    {
        m_tokenQueue.insert(
            m_tokenQueue.begin() + (m_currentPosition - 1),
            XToken(
                theNumber,
                theString,
                getMemoryManager()));
    }

    /**
     * Replace a token in the token queue.
     * 
     * @param theOffset the offset at which to replace the token.
     * @param theString The string data for the token.  The instance will keep a pointer to this string, so it must be persistent.
     */
    void
    replaceRelativeToken(
            TokenQueuePositionType  theOffset,
            eRelativeDirection      theDirection,
            const XalanDOMString&   theString)
    {
        const TokenQueuePositionType    thePosition =
            calculateRelativePosition(theOffset, theDirection);
        assert(thePosition < tokenQueueSize());

        m_tokenQueue[thePosition].set(
            theString,
            DoubleSupport::toDouble(theString, getMemoryManager()));
    }

    /**
     * Diagnostic function to output the operation code map.
     * 
     * @param thePrintWriter   output device
     * @param theStartPosition starting position in map
     */
    void
    dumpOpCodeMap(
            PrintWriter&        thePrintWriter,
            OpCodeMapSizeType   theStartPosition = 0) const;

    /**
     * Diagnostic function to output the operation code map.
     * 
     * @param theStream output stream
     * @param theStartPosition starting position in map
     */
    void
    dumpOpCodeMap(
            OstreamType&        theStream,
            OpCodeMapSizeType   theStartPosition = 0) const;

    /**
     * Diagnostic function to output the token queue.
     * 
     * @param thePrintWriter   output device
     * @param theStartPosition starting position in token queue
     */
    void
    dumpTokenQueue(
            PrintWriter&        thePrintWriter,
            TokenQueueSizeType  theStartPosition = 0) const;

    /**
     * Diagnostic function to output the token queue.
     * 
     * @param thePrintWriter   output device
     * @param theStartPosition starting position in token queue
     */
    void
    dumpTokenQueue(
            OstreamType&        theStream,
            TokenQueueSizeType  theStartPosition = 0) const;

    /**
     * Diagnostic function to output the remaining tokens in the token queue.
     * 
     * @param thePrintWriter   output device
     */
    void
    dumpRemainingTokenQueue(PrintWriter&    thePrintWriter) const;

    /**
     * Diagnostic function to output the remaining tokens in the token queue.
     * 
     * @param theStream The output stream
     * @param theMemoryManager The MemoryManager instance.
     */
    void
    dumpRemainingTokenQueue(
            OstreamType&    theStream,
            MemoryManager&  theMemoryManager) const;

    /**
     * Push a value onto the operations code
     * map.
     *
     * @param theToken string value of the token to push
     */
    void
    pushValueOnOpCodeMap(const OpCodeMapType::value_type&   theValue)
    {
        // Push the index onto the op map.
        m_opMap.push_back(theValue);

        // Update the op map length.
        ++m_opMap[s_opCodeMapLengthIndex];
    }

    /**
     * Push a token onto the token queue and its index onto the operations code
     * map.
     *
     * @param theXToken the XToken to push
     */
    void
    pushArgumentOnOpCodeMap(const XToken&   theXToken);

    /**
     * Push a token onto the token queue and its index onto the operations code
     * map.
     *
     * @param theString The string data for the token.  The instance will keep a pointer to this string, so it must be persistent.
     */
    void
    pushArgumentOnOpCodeMap(const XalanDOMString&   theString);

    /**
     * Push a token onto the token queue and its index onto the operations code
     * map.
     *
     * @param theNumber The numeric data for the token.  This must be consistent with the lexical value in theString.
     * @param theString The string data for the token.  The instance will keep a pointer to this string, so it must be persistent.
     */
    void
    pushArgumentOnOpCodeMap(
            double                  theNumber,
            const XalanDOMString&   theString);

    /**
     * Push a number literal onto the vector of number literals and its index onto
     * the operations code map.
     *
     * @param theToken number value of the token to push
     */
    void
    pushNumberLiteralOnOpCodeMap(double     theNumber);

    /**
     * Get a number literal from the vector of number literals.
     *
     * @param theIndex The index of the desired value.
     */
    double
    getNumberLiteral(int    theIndex) const
    {
        assert(theIndex >= 0 &&
               NumberLiteralValueVectorType::size_type(theIndex) < m_numberLiteralValues.size());

        return m_numberLiteralValues[NumberLiteralValueVectorType::size_type(theIndex)];
    }

    /**
     * Push the current position in the token queue onto the operations code
     * map.
     */
    void
    pushCurrentTokenOnOpCodeMap();

    /**
     * Change the current pattern in the pattern map.
     *
     * @param thePattern match pattern to make current
     */
    void
    setCurrentPattern(const XalanDOMString&     thePattern)
    {
        m_currentPattern = &thePattern;
    }

    /**
     * Retrieve the current pattern in the pattern map.
     * 
     * @return string for current match pattern
     */
    const XalanDOMString&
    getCurrentPattern() const
    {
        assert(m_currentPattern != 0);

        return *m_currentPattern;
    }

private:

    /**
     * Calculate the relative token position given the offset
     * and direction.  Returns the size of the token queue
     * if the offset is not valid.
     *
     * @param theOffset offset from current position
     * @param theDirection the direction in which to move
     * @return thePosition
     */
    TokenQueuePositionType
    calculateRelativePosition(
        TokenQueuePositionType  theOffset,
        eRelativeDirection      theDirection) const
    {
        if (theDirection == eRelativeBackward &&
            theOffset <= m_currentPosition)
        {
            return m_currentPosition - theOffset;
        }
        else if (theDirection == eRelativeForward &&
                 m_currentPosition + theOffset < tokenQueueSize())
        {
            return m_currentPosition + theOffset;
        }
        else
        {
            return tokenQueueSize();
        }
    }

    /**
     * An operations map is used instead of a proper parse tree.  It contains
     * operations codes and indexes into the m_tokenQueue. We use an array
     * instead of a full parse tree in order to cut down on the number of
     * objects created.
     */
    OpCodeMapType           m_opMap;

    /**
     * The index of the last opcode that was appended or inserted.
     *
     */
    OpCodeMapSizeType       m_lastOpCodeIndex;

    /**
     * The queue of used tokens. The current token is the token at the end of
     * the m_tokenQueue. The idea is that the queue can be marked and a
     * sequence of tokens can be reused.
     */
    TokenQueueType          m_tokenQueue;

    /**
     *  The current position in the token queue.
     */
    TokenQueueSizeType      m_currentPosition;

    /**
     * The current pattern string, for diagnostics purposes.
     */
    const XalanDOMString*   m_currentPattern;

    // Default vector allocation sizes.
    enum
    {
        eDefaultOpMapSize = 100,
        eDefaultTokenQueueSize = 30
    };

    NumberLiteralValueVectorType    m_numberLiteralValues;
};



}



#endif  // XPATHEXPRESSION_HEADER_GUARD_1357924680
