/*
 * 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(XPATHPROCESSORIMPL_HEADER_GUARD_1357924680)
#define XPATHPROCESSORIMPL_HEADER_GUARD_1357924680



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



#include <cstdlib>



#include <xalanc/XalanDOM/XalanDOMString.hpp>



#include <xalanc/Include/XalanVector.hpp>
#include <xalanc/Include/XalanMap.hpp>



#include "xalanc/PlatformSupport/XalanMessageLoader.hpp"



// Base class header file...
#include <xalanc/XPath/XPathProcessor.hpp>



#include <xalanc/XPath/XPath.hpp>



XALAN_CPP_NAMESPACE_BEGIN



class XalanNode;



/**
 * The XPathProcessorImpl class responsibilities include tokenizing and
 * parsing the XPath expression, and acting as a general interface to XPaths.
 */
class XALAN_XPATH_EXPORT XPathProcessorImpl : public XPathProcessor
{
public:

    typedef XalanMap<XalanDOMString,
                const XalanDOMString*>  StringToStringMapType;

    typedef XalanVector<bool>               BoolVectorType;

    typedef XalanDOMString::size_type   t_size_type;

    XPathProcessorImpl(MemoryManager& theManager XALAN_DEFAULT_MEMMGR);

    virtual
    ~XPathProcessorImpl();


    static XPathProcessorImpl*
    create(MemoryManager& theManager);
    // These are inherited from XPathProcessor...

    virtual void
    initXPath(
            XPath&                      pathObj,
            XPathConstructionContext&   constructionContext,
            const XalanDOMString&       expression,
            const PrefixResolver&       resolver,
            const Locator*              locator = 0,
            bool                        allowVariableReferences = true,
            bool                        allowKeyFunction = true);

    virtual void
    initMatchPattern(
            XPath&                      pathObj,
            XPathConstructionContext&   constructionContext,
            const XalanDOMString&       expression,
            const PrefixResolver&       resolver,
            const Locator*              locator = 0,
            bool                        allowVariableReferences = true,
            bool                        allowKeyFunction = true);

private:

    /**
     * Walk through the expression and build a token queue, and a map of the
     * top-level elements.
     *
     * @param pat XSLT Expression.
     */
    void
    tokenize(const XalanDOMString&  pat);
  
    void
    addToTokenQueue(const XalanDOMString&   s) const;

    void
    replaceTokenWithNamespaceToken() const;

    /**
     * When a separator token is found, see if there's a element name or the
     * like to map.
     */
    t_size_type
    mapNSTokens(
            const XalanDOMString&   pat,
            t_size_type             startSubstring,
            t_size_type             posOfNSSep,
            t_size_type             posOfScan);

    /**
     * Check if m_token==s. If m_token is null, this won't throw
     * an exception, instead it just returns false (or true
     * if s is also null).
     */
    bool
    tokenIs(const XalanDOMString&   s) const;

    /**
     * Check if m_token==s. If m_token is null, this won't throw
     * an exception, instead it just returns false (or true
     * if s is also null).
     */
    bool
    tokenIs(const XalanDOMChar*     s) const;

    /**
     * Check if m_token==s. If m_token is null, this won't throw
     * an exception, instead it just returns false (or true
     * if s is also null).
     */
    bool
    tokenIs(XalanDOMChar    c) const;

    /**
     * Lookahead of the current token in order to 
     * make a branching decision.
     * @param s the string to compare it to.
     * @param n number of tokens to lookahead.  Must be 
     * greater than 1.
     */
    bool
    lookahead(
            XalanDOMChar    c,
            int             n) const;

    /**
     * Lookahead of the current token in order to 
     * make a branching decision.
     * @param s the string to compare it to.
     * @param n number of tokens to lookahead.  Must be 
     * greater than 1.
     */
    bool
    lookahead(
            const XalanDOMChar*     s,
            int                     n) const;

    /**
     * Lookahead of the current token in order to 
     * make a branching decision.
     * @param s the string to compare it to.
     * @param n number of tokens to lookahead.  Must be 
     * greater than 1.
     */
    bool
    lookahead(
            const XalanDOMString&   s,
            int                     n) const;

    /**
     * Lookbehind the first character of the current token in order to 
     * make a branching decision.
     * @param c the character to compare it to.
     * @param n number of tokens to lookbehind.  Must be 
     * greater than 1.  Note that the lookbehind terminates 
     * at either the beginning of the string or on a '|' 
     * character.  Because of this, this method should only
     * be used for pattern matching.
     */
    bool
    lookbehind( 
            char    c,
            int     n) const;

    /**
     * look behind the current token in order to 
     * see if there is a useable token.
     * @param n number of tokens to lookahead.  Must be 
     * greater than 1.  Note that the lookbehind terminates 
     * at either the beginning of the string or on a '|' 
     * character.  Because of this, this method should only
     * be used for pattern matching.
     * @return true if lookbehind has a token, false otherwise.
     */
    bool
    lookbehindHasToken(int  n) const;

    /**
     * Retrieve the next token from the command and
     * store it in m_token string.
     */
    bool
    nextToken();

    /**
     * Retrieve the next token from the command and
     * store it in m_token string.
     */
    const XalanDOMString&
    getTokenRelative(int    theOffset) const;

    /**
     * Retrieve the previous token from the command and
     * store it in m_token string.
     */
    void
    prevToken();

    /**
     * Consume an expected token, throwing an exception if it 
     * isn't there.
     */
    void
    consumeExpected(XalanDOMChar    expected);

    bool
    isCurrentLiteral() const;

    /**
     * Determine if the token is an axis
     *
     * @param theToken The token to test
     * @return true if the token is a valid axis, false if not.
     */
    static bool
    isAxis(const XalanDOMString&    theToken);

    /**
     * Determine if the token could be a node test
     *
     * @param theToken The token to test
     * @return true if the token is a valid node test, false if not.
     */
    static bool
    isNodeTest(const XalanDOMString&    theToken);

    /**
     * Throw an exception using the provided message text.
     */
    void
    error(const XalanDOMString&     msg) const;

    /**
     * Throw an exception using the provided message text.
     */
    void
    error(XalanMessages::Codes  theCode) const;

    void
    error(
            XalanMessages::Codes    theCode,
            const XalanDOMString&   theToken) const;

    void
    error(
            XalanMessages::Codes    theCode,
            const XalanDOMChar*     theToken) const;

    void
    error(
            XalanMessages::Codes    theCode,
            XalanDOMChar            theToken1,
            const XalanDOMString&   theToken2) const;

    /**
     * Given a string, return the corresponding token.
     */
    static XPathExpression::eOpCodes
    getFunctionToken(const XalanDOMString&  key)
    {
        return searchTable(s_functionTable, s_functionTableSize, key).m_opCode;
    }

    /**
     * Given a string, return the corresponding token.
     */
    static XPathExpression::eOpCodes
    getNodeTypeToken(const XalanDOMString&  key)
    {
        return searchTable(s_nodeTypeTable, s_nodeTypeTableSize, key).m_opCode;
    }

    /**
     * Given a string, return the corresponding token.
     */
    static XPathExpression::eOpCodes
    getAxisToken(const XalanDOMString&  key)
    {
        return searchTable(s_axisTable, s_axisTableSize, key).m_opCode;
    }

    /**
     * 
     * --------------------------------------------------------------------------------
    Expr  ::=  OrExpr   
     * --------------------------------------------------------------------------------
     */
    void
    Expr();


    /**
     * 
     * --------------------------------------------------------------------------------
     OrExpr  ::=    AndExpr   
     | OrExpr 'or' AndExpr   
     * --------------------------------------------------------------------------------
     */
    void
    OrExpr();

    /**
     * 
     * --------------------------------------------------------------------------------
     AndExpr    ::=  EqualityExpr   
     | AndExpr 'and' EqualityExpr   
     * --------------------------------------------------------------------------------
     */
    void
    AndExpr() ;
  
    /**
     * XXXX.
     * @returns an Object which is either a String, a Number, a Boolean, or a vector 
     * of nodes.
     * --------------------------------------------------------------------------------
     EqualityExpr  ::=  RelationalExpr   
     | EqualityExpr '=' RelationalExpr   
     * --------------------------------------------------------------------------------
     */
    int
    EqualityExpr(int    opCodePos = -1);

    /**
     * XXXX.
     * @returns an Object which is either a String, a Number, a Boolean, or a vector 
     * of nodes.
     * --------------------------------------------------------------------------------
     RelationalExpr  ::=    AdditiveExpr   
     | RelationalExpr '<' AdditiveExpr   
     | RelationalExpr '>' AdditiveExpr   
     | RelationalExpr '<=' AdditiveExpr 
     | RelationalExpr '>=' AdditiveExpr 
     * --------------------------------------------------------------------------------
     */
    int
    RelationalExpr(int  opCodePos = -1);

    /**
     * XXXX.
     * @returns an Object which is either a String, a Number, a Boolean, or a vector 
     * of nodes.
     * --------------------------------------------------------------------------------
     AdditiveExpr  ::=  MultiplicativeExpr   
     | AdditiveExpr '+' MultiplicativeExpr   
     | AdditiveExpr '-' MultiplicativeExpr   
     * --------------------------------------------------------------------------------
     */
    int
    AdditiveExpr(int    opCodePos = -1);

    /**
     * XXXX.
     * @returns an Object which is either a String, a Number, a Boolean, or a vector 
     * of nodes.
     * --------------------------------------------------------------------------------
     MultiplicativeExpr  ::=    UnaryExpr   
     | MultiplicativeExpr MultiplyOperator UnaryExpr     
     | MultiplicativeExpr 'div' UnaryExpr   
     | MultiplicativeExpr 'mod' UnaryExpr   
     | MultiplicativeExpr 'quo' UnaryExpr   
     * --------------------------------------------------------------------------------
     */
    int
    MultiplicativeExpr(int  opCodePos = -1);

    /**
     * XXXX.
     * @returns an Object which is either a String, a Number, a Boolean, or a vector 
     * of nodes.
     * --------------------------------------------------------------------------------
     UnaryExpr  ::=  UnionExpr   
     | '-' UnaryExpr     
     * --------------------------------------------------------------------------------
     */
    void
    UnaryExpr();

    /**
     * The context of the right hand side expressions is the context of the 
     * left hand side expression. The results of the right hand side expressions 
     * are node sets. The result of the left hand side UnionExpr is the union 
     * of the results of the right hand side expressions.
     * 
     * --------------------------------------------------------------------------------
     UnionExpr  ::=    PathExpr   
     | UnionExpr '|' PathExpr   
     * --------------------------------------------------------------------------------
     */
    void
    UnionExpr();

    /**
     * 
     * --------------------------------------------------------------------------------
     PathExpr  ::=  LocationPath     
     | FilterExpr   
     | FilterExpr '/' RelativeLocationPath   
     | FilterExpr '//' RelativeLocationPath 
     * --------------------------------------------------------------------------------
     * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide 
     * the error condition is severe enough to halt processing.
     */
    void
    PathExpr();

    /**
     * 
     * --------------------------------------------------------------------------------
     FilterExpr  ::=    PrimaryExpr   
     | FilterExpr Predicate 
     * --------------------------------------------------------------------------------
     * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide 
     * the error condition is severe enough to halt processing.
     */
    void
    FilterExpr();
  
    /**
     * --------------------------------------------------------------------------------
     PrimaryExpr    ::=  VariableReference   
     | '(' Expr ')' 
     | Literal   
     | Number   
     | FunctionCall 
     * --------------------------------------------------------------------------------
     */
    void
    PrimaryExpr();


    /**
     * --------------------------------------------------------------------------------
     Argument    ::=      Expr     
     * --------------------------------------------------------------------------------
     */
    void
    Argument();
  
    /**
     * --------------------------------------------------------------------------------
     FunctionCall    ::=      FunctionName '(' ( Argument ( ',' Argument)*)? ')'    
     * --------------------------------------------------------------------------------
     */
    void
    FunctionCall();

    void
    FunctionPosition();

    void
    FunctionLast();

    void
    FunctionCount();

    void
    FunctionNot();

    void
    FunctionTrue();

    void
    FunctionFalse();

    void
    FunctionBoolean();

    void
    FunctionName(int    opPos);

    void
    FunctionLocalName(int   opPos);

    void
    FunctionNumber(int  opPos);

    void
    FunctionFloor();

    void
    FunctionCeiling();

    void
    FunctionRound();

    void
    FunctionString(int  opPos);

    void
    FunctionStringLength(int    opPos);

    void
    FunctionSum();

    void
    FunctionNamespaceURI(int    opPos);

    /**
     * --------------------------------------------------------------------------------
     LocationPath ::= RelativeLocationPath 
     | AbsoluteLocationPath 
     * --------------------------------------------------------------------------------
     */
    void
    LocationPath();
  
    /**
     * --------------------------------------------------------------------------------
     RelativeLocationPath ::= Step 
     | RelativeLocationPath '/' Step 
     | AbbreviatedRelativeLocationPath 
     * --------------------------------------------------------------------------------
     */
    void
    RelativeLocationPath();
  
    /**
     * --------------------------------------------------------------------------------
     Step    ::=      Basis Predicate*  
     | AbbreviatedStep
     */
    void
    Step();
  
    /**
     * --------------------------------------------------------------------------------
     Basis  ::=    AxisName '::' NodeTest   
     | AbbreviatedBasis  
     */
    void
    Basis();
  
    /**
     * --------------------------------------------------------------------------------
     Basis  ::=    AxisName '::' NodeTest   
     | AbbreviatedBasis  
     */
    XPathExpression::eOpCodes
    AxisName();
  
    /**
     * --------------------------------------------------------------------------------
     NodeTest    ::=      WildcardName   
     | NodeType '(' ')'  
     | 'processing-instruction' '(' Literal ')' 
     */
    int
    NodeTest();

    /**
     * --------------------------------------------------------------------------------
     Predicate ::= '[' PredicateExpr ']' 
     * --------------------------------------------------------------------------------
     */
    void
    Predicate();

    /**
     *--------------------------------------------------------------------------------
     PredicateExpr ::= Expr 
     *--------------------------------------------------------------------------------
     */
    void
    PredicateExpr();
  
    /**
     * QName ::=    (Prefix ':')? LocalPart 
     * Prefix ::=  NCName 
     * LocalPart ::=    NCName 
     */
    void
    QName();

    /**
     * NCName ::=  (Letter | '_') (NCNameChar)*
     * NCNameChar ::=  Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender 
     */
    void
    NCName();
  
    /**
     * The value of the Literal is the sequence of characters inside 
     * the " or ' characters>.
     * --------------------------------------------------------------------------------
     Literal    ::=  '"' [^"]* '"'   
     | "'" [^']* "'"     
     * --------------------------------------------------------------------------------
     */
    void
    Literal();
  
    /**
     * --------------------------------------------------------------------------------
     * Number ::= [0-9]+('.'[0-9]+)? | '.'[0-9]+
     * --------------------------------------------------------------------------------
     */
    void
    Number();

    /**
     * --------------------------------------------------------------------------------
     Pattern    ::=  LocationPathPattern   
     | Pattern '|' LocationPathPattern   
     * --------------------------------------------------------------------------------
     */
    void
    Pattern();

    /**
     * 
     * --------------------------------------------------------------------------------
     LocationPathPattern    ::=  '/' RelativePathPattern?   
     | IdKeyPattern (('/' | '//') RelativePathPattern)? 
     | '//'? RelativePathPattern     
     * --------------------------------------------------------------------------------
     */
    void
    LocationPathPattern();

    /**
     * --------------------------------------------------------------------------------
     IdKeyPattern  ::=  'id' '(' Literal ')'     
     | 'key' '(' Literal ',' Literal ')'     
     * (Also handle doc())
     * --------------------------------------------------------------------------------
     */
    void
    IdKeyPattern();

    /**
     * --------------------------------------------------------------------------------
     RelativePathPattern    ::=  StepPattern   
     | RelativePathPattern '/' StepPattern   
     | RelativePathPattern '//' StepPattern 
     * --------------------------------------------------------------------------------
     */
    void
    RelativePathPattern();

    /**
     * --------------------------------------------------------------------------------
     StepPattern    ::=  AbbreviatedNodeTestStep  
     * --------------------------------------------------------------------------------
     */
    void
    StepPattern();

    /**
     * --------------------------------------------------------------------------------
     AbbreviatedNodeTestStep      ::=    '@'? NodeTest Predicate*   
     * --------------------------------------------------------------------------------
     */
    void
    AbbreviatedNodeTestStep();

    static bool
    isValidFunction(const XalanDOMString&   key);

private:

    int
    FunctionCallArguments();

    struct TableEntry
    {
        const XalanDOMChar*         m_string;

        XPathExpression::eOpCodes   m_opCode;
    };

#if defined(XALAN_STRICT_ANSI_HEADERS)
    typedef std::size_t             size_type;
#else
    typedef size_t                  size_type;
#endif

    static const TableEntry&
    searchTable(
        const TableEntry        theTable[],
        size_type               theTableSize,
        const XalanDOMString&   theString);

    /**
     * The current input token.
     */
    XalanDOMString                  m_token;
  
    /**
     * The first char in m_token, the theory being that this 
     * is an optimization because we won't have to do index
     * into the string as often.
     */
    XalanDOMChar                    m_tokenChar;

    /**
     * A pointer to the current XPath.
     */
    XPath*                          m_xpath;

    /**
     * A pointer to the current XPathConstructionContext.
     */
    XPathConstructionContext*       m_constructionContext;

    /**
     * A pointer to the current XPath's expression.
     */
    XPathExpression*                m_expression;

    /**
     * A pointer to the current executionContext.
     */
    const PrefixResolver*           m_prefixResolver;

    bool                            m_requireLiterals;

    bool                            m_isMatchPattern;

    const Locator*                  m_locator;

    BoolVectorType                  m_positionPredicateStack;

    StringToStringMapType           m_namespaces;

    bool                            m_allowVariableReferences;

    bool                            m_allowKeyFunction;

    // Static stuff here...
    static const XalanDOMString     s_emptyString;

    static const XalanDOMChar       s_functionIDString[];

    // This shouldn't really be here, since it's not part of the XPath standard,
    // but rather a part ofthe XSLT standard.
    static const XalanDOMChar       s_functionKeyString[];

    static const XalanDOMChar       s_orString[];

    static const XalanDOMChar       s_andString[];

    static const XalanDOMChar       s_divString[];

    static const XalanDOMChar       s_modString[];

    static const XalanDOMChar       s_dotString[];

    static const XalanDOMChar       s_dotDotString[];

    static const XalanDOMChar       s_axisString[];

    static const XalanDOMChar       s_attributeString[];

    static const XalanDOMChar       s_childString[];

    static const XalanDOMChar       s_lastString[];

    static const XalanDOMChar       s_positionString[];

    static const XalanDOMChar       s_asteriskString[];

    static const XalanDOMChar       s_commentString[];

    static const XalanDOMChar       s_piString[];

    static const XalanDOMChar       s_nodeString[];

    static const XalanDOMChar       s_textString[];

    static const XalanDOMChar       s_ancestorString[];

    static const XalanDOMChar       s_ancestorOrSelfString[];

    static const XalanDOMChar       s_descendantString[];

    static const XalanDOMChar       s_descendantOrSelfString[];

    static const XalanDOMChar       s_followingString[];

    static const XalanDOMChar       s_followingSiblingString[];

    static const XalanDOMChar       s_parentString[];

    static const XalanDOMChar       s_precedingString[];

    static const XalanDOMChar       s_precedingSiblingString[];

    static const XalanDOMChar       s_selfString[];

    static const XalanDOMChar       s_namespaceString[];

    static const TableEntry         s_functionTable[];

    static const size_type          s_functionTableSize;

    static const TableEntry         s_nodeTypeTable[];

    static const size_type          s_nodeTypeTableSize;

    static const TableEntry         s_axisTable[];

    static const size_type          s_axisTableSize;

    static const TableEntry         s_dummyEntry;
};



XALAN_CPP_NAMESPACE_END



#endif  // XPATHPROCESSORIMPL_HEADER_GUARD_1357924680
