package org.apache.xpath.parser;

import java.util.Hashtable;

import javax.xml.transform.SourceLocator;

import org.apache.xml.dtm.DTMFilter;
import org.apache.xml.utils.QName;
import org.apache.xpath.Expression;
import org.apache.xpath.ExpressionNode;
import org.apache.xpath.axes.UnionPathIterator;
import org.apache.xpath.axes.WalkerFactory;
import org.apache.xpath.functions.*;
import org.apache.xpath.objects.XDecimal;
import org.apache.xpath.objects.XDouble;
import org.apache.xpath.objects.XInteger;
import org.apache.xpath.operations.Variable;
import org.apache.xpath.patterns.FunctionPattern;
import org.apache.xpath.patterns.StepPattern;
import org.apache.xpath.seqctor.ExprSequence;
import org.apache.xpath.seqctor.FLWRExpr;
import org.apache.xpath.types.InstanceofExpr;

/**
 * This is the most generic syntax node, which implements the JJTree 
 * interface "Node" methods.  This class assumes that it's derived 
 * class is org.apache.xpath.Expression, and so does a fair amount 
 * of downcasting.  The only reason this class exists is to separate 
 * out the jjXXX methods, to act as a more generic interface to 
 * the JavaCC/JJTree parser, and also to act as a factory class for 
 * the concrete implementations of the expressions.
 * 
 * @author sboag
 */
public class SimpleNode implements Node
{

  /**
   * Construct a new SimpleNode.
   * @see java.lang.Object#Object()
   */
  public SimpleNode()
  {
  }

  public static Hashtable m_builtInFunctions;

  static {
    // For user installed functions, can we really have a single static table?
    // Answer: no, each parser instance will need to copy the table.
    // Then the question is, can all instances of a user Function be shared among 
    // processes?  Answer: I think that would cause regressions.
    m_builtInFunctions = new Hashtable();
    m_builtInFunctions.put(new QName("current"), new FuncCurrent());
    m_builtInFunctions.put(
      new QName("last"),
      new org.apache.xpath.functions.FuncLast());
    m_builtInFunctions.put(
      new QName("position"),
      new org.apache.xpath.functions.FuncPosition());
    m_builtInFunctions.put(new QName("count"), new FuncCount());
    m_builtInFunctions.put(new QName("id"), new FuncId());
    m_builtInFunctions.put(
      new QName("key"),
      new org.apache.xalan.templates.FuncKey());
    m_builtInFunctions.put(new QName("local-name"), new FuncLocalPart());
    m_builtInFunctions.put(new QName("namespace-uri"), new FuncNamespace());
    m_builtInFunctions.put(new QName("name"), new FuncQname());
    m_builtInFunctions.put(new QName("generate-id"), new FuncGenerateId());
    m_builtInFunctions.put(new QName("not"), new FuncNot());
    m_builtInFunctions.put(new QName("true"), new FuncTrue());
    m_builtInFunctions.put(new QName("false"), new FuncFalse());
    m_builtInFunctions.put(new QName("boolean"), new FuncBoolean());
    m_builtInFunctions.put(new QName("lang"), new FuncLang());
    m_builtInFunctions.put(new QName("number"), new FuncNumber());
    m_builtInFunctions.put(new QName("floor"), new FuncFloor());
    m_builtInFunctions.put(new QName("ceiling"), new FuncCeiling());
    m_builtInFunctions.put(new QName("round"), new FuncRound());
    m_builtInFunctions.put(new QName("sum"), new FuncSum());
    m_builtInFunctions.put(new QName("string"), new FuncString());
    m_builtInFunctions.put(new QName("starts-with"), new FuncStartsWith());
    m_builtInFunctions.put(new QName("contains"), new FuncContains());
    m_builtInFunctions.put(
      new QName("substring-before"),
      new FuncSubstringBefore());
    m_builtInFunctions.put(
      new QName("substring-after"),
      new FuncSubstringAfter());
    m_builtInFunctions.put(
      new QName("normalize-space"),
      new FuncNormalizeSpace());
    m_builtInFunctions.put(new QName("translate"), new FuncTranslate());
    m_builtInFunctions.put(new QName("concat"), new FuncConcat());
    m_builtInFunctions.put(
      new QName("system-property"),
      new FuncSystemProperty());
    m_builtInFunctions.put(
      new QName("function-available"),
      new FuncExtFunctionAvailable());
    m_builtInFunctions.put(
      new QName("element-available"),
      new FuncExtElementAvailable());
    m_builtInFunctions.put(new QName("substring"), new FuncSubstring());
    m_builtInFunctions.put(new QName("string-length"), new FuncStringLength());
    m_builtInFunctions.put(
      new QName("unparsed-entity-uri"),
      new FuncUnparsedEntityURI());
    m_builtInFunctions.put(
      new QName("document-location"),
      new FuncDoclocation());
    // Proprietary
	// XPATH2 experimental
    m_builtInFunctions.put(
      new QName("data"),
      new FuncData());    
    m_builtInFunctions.put(
      new QName("current-dateTime"),
      new FuncCurrentDateTime());
    m_builtInFunctions.put(
      new QName("current-date"),
      new FuncCurrentDate());
    m_builtInFunctions.put(
      new QName("dateTime"),
      new FuncDateTime());
    m_builtInFunctions.put(
      new QName("date"),
      new FuncDate());
    m_builtInFunctions.put(
      new QName("duration"),
      new FuncDuration());
    m_builtInFunctions.put(
      new QName("time"),
      new FuncTime());
      m_builtInFunctions.put(
      new QName("yearMonthDuration"),
      new FuncYMDuration());
      m_builtInFunctions.put(
      new QName("yearMonthDuration-from-months"),
      new FuncYMDurationFromMonths());
      m_builtInFunctions.put(
      new QName("dayTimeDuration"),
      new FuncDTDuration());
      m_builtInFunctions.put(
      new QName("dayTimeDuration-from-seconds"),
      new FuncDTDurationFromSecs());
      m_builtInFunctions.put(
      new QName("add-dayTimeDuration"),
      new FuncAddDayTimeDuration());
      m_builtInFunctions.put(
      new QName("subtract-dayTimeDuration"),
      new FuncSubDayTimeDuration());
      m_builtInFunctions.put(
      new QName("multiply-dayTimeDuration"),
      new FuncMultDayTimeDuration());
      m_builtInFunctions.put(
      new QName("divide-dayTimeDuration"),
      new FuncDivDayTimeDuration());
      m_builtInFunctions.put(
      new QName("add-yearMonthDuration"),
      new FuncAddYearMonthDuration()); 
      m_builtInFunctions.put(
      new QName("subtract-yearMonthDuration"),
      new FuncSubYearMonthDuration());
      m_builtInFunctions.put(
      new QName("multiply-yearMonthDuration"),
      new FuncMultYearMonthDuration());
      m_builtInFunctions.put(
      new QName("divide-yearMonthDuration"),
      new FuncDivYearMonthDuration());
      m_builtInFunctions.put(
      new QName("get-yearMonthDuration"),
      new FuncGetYMDuration());
      m_builtInFunctions.put(
      new QName("get-dayTimeDuration"),
      new FuncGetDTDuration());
      m_builtInFunctions.put(
      new QName("add-yearMonthDurationToDateTime"),
      new FuncAddYMDurationToDT());
      m_builtInFunctions.put(
      new QName("subtract-yearMonthDurationFromDateTime"),
      new FuncSubtractYMDurationFromDT());
      m_builtInFunctions.put(
      new QName("add-yearMonthDurationToDate"),
      new FuncAddYMDurationToDate());
      m_builtInFunctions.put(
      new QName("subtract-yearMonthDurationFromDate"),
      new FuncSubtractYMDurationFromDate());
      m_builtInFunctions.put(
      new QName("add-dayTimeDurationToDateTime"),
      new FuncAddDTDurationToDT());
      m_builtInFunctions.put(
      new QName("subtract-dayTimeDurationFromDateTime"),
      new FuncSubtractDTDurationFromDT());
      m_builtInFunctions.put(
      new QName("add-dayTimeDurationToDate"),
      new FuncAddDTDurationToDate());
      m_builtInFunctions.put(
      new QName("add-dayTimeDurationToTime"),
      new FuncAddDTDurationToTime());
      m_builtInFunctions.put(
      new QName("subtract-dayTimeDurationFromDate"),
      new FuncSubtractDTDurationFromDate());
      m_builtInFunctions.put(
      new QName("subtract-dayTimeDurationFromTime"),
      new FuncSubtractDTDurationFromTime());
      m_builtInFunctions.put(
      new QName("add-days"),
      new FuncAddDays());
      m_builtInFunctions.put(
      new QName("gYearMonth"),
      new FuncGYearMonth());
      m_builtInFunctions.put(
      new QName("gYear"),
      new FuncGYear());
      m_builtInFunctions.put(
      new QName("gMonth"),
      new FuncGMonth());
      m_builtInFunctions.put(
      new QName("gMonthDay"),
      new FuncGMonthDay());
      m_builtInFunctions.put(
      new QName("gDay"),
      new FuncGDay());
m_builtInFunctions.put(
      new QName("get-day-from-date"),
      new FuncGetDFromDate());
m_builtInFunctions.put(
      new QName("get-day-from-dateTime"),
      new FuncGetDFromDT());
m_builtInFunctions.put(
      new QName("get-days-from-dayTimeDuration"),
      new FuncGetDFromDTDuration());
m_builtInFunctions.put(
      new QName("get-hours-from-dateTime"),
      new FuncGetHFromDT());
m_builtInFunctions.put(
      new QName("get-hours-from-dayTimeDuration"),
      new FuncGetHFromDTDuration());
m_builtInFunctions.put(
      new QName("get-hours-from-time"),
      new FuncGetHFromTime());
m_builtInFunctions.put(
      new QName("get-month-from-date"),
      new FuncGetMFromDate());
m_builtInFunctions.put(
      new QName("get-month-from-dateTime"),
      new FuncGetMFromDT());
m_builtInFunctions.put(
      new QName("get-months-from-yearMonthDuration"),
      new FuncGetMFromYMDuration());
m_builtInFunctions.put(
      new QName("get-minutes-from-dateTime"),
      new FuncGetMnFromDT());
m_builtInFunctions.put(
      new QName("get-minutes-from-dayTimeDuration"),
      new FuncGetMnFromDTDuration());
m_builtInFunctions.put(
      new QName("get-minutes-from-time"),
      new FuncGetMnFromTime());
m_builtInFunctions.put(
      new QName("get-seconds-from-dateTime"),
      new FuncGetSFromDT());
m_builtInFunctions.put(
      new QName("get-seconds-from-dayTimeDuration"),
      new FuncGetSFromDTDuration());
m_builtInFunctions.put(
      new QName("get-seconds-from-time"),
      new FuncGetSFromTime());
m_builtInFunctions.put(
      new QName("get-timezone-from-datetime"),
      new FuncGetTZFromDT());
m_builtInFunctions.put(
      new QName("get-timezone-from-date"),
      new FuncGetTZFromDate());
m_builtInFunctions.put(
      new QName("get-timezone-from-time"),
      new FuncGetTZFromTime());
m_builtInFunctions.put(
      new QName("get-year-from-date"),
      new FuncGetYFromDate());
m_builtInFunctions.put(
      new QName("get-year-from-dateTime"),
      new FuncGetYFromDT());
m_builtInFunctions.put(
      new QName("get-years-from-yearMonthDuration"),
      new FuncGetYFromYMDuration());
m_builtInFunctions.put(
      new QName("duration-equal"),
      new FuncDurationEqual());
m_builtInFunctions.put(
      new QName("yearMonthDuration-equal"),
      new FuncYMDurationEqual());
m_builtInFunctions.put(
      new QName("yearMonthDuration-greater-than"),
      new FuncYMDurationGT());
m_builtInFunctions.put(
      new QName("yearMonthDuration-less-than"),
      new FuncYMDurationLT());
m_builtInFunctions.put(
      new QName("dayTimeDuration-equal"),
      new FuncDTDurationEqual());
m_builtInFunctions.put(
      new QName("dayTimeDuration-greater-than"),
      new FuncDTDurationGT());
m_builtInFunctions.put(
      new QName("dayTimeDuration-less-than"),
      new FuncDTDurationLT());
m_builtInFunctions.put(
      new QName("dateTime-equal"),
      new FuncDateTimeEq());
m_builtInFunctions.put(
      new QName("dateTime-less-than"),
      new FuncDateTimeLT());
m_builtInFunctions.put(
      new QName("dateTime-greater-than"),
      new FuncDateTimeGT());
  }

  /**
   * Factory method for creating parse tree nodes, which is called by 
   * the JJTree created parser.
   * @param p The JJTree created XPath parser.
   * @param id The ID of the node.
   * @return Node The new node, which may not be null.
   */
  public static Node jjtCreate(XPath p, int id)
  {
    SimpleNode newNode = null;
    // To be done:  If function on stack, assume param.
    switch (id)
    {
      case XPathTreeConstants.JJTXPATH2 :
        // When I try to use #void to cancel out this node, 
        // I get an error.  So I guess 
        // the top of the tree can't be void.  So I create a dummy
        // node.  This makes the called have to call 
        // ((SimpleNode)tree.jjtGetChild(0)) to get the real root.
        newNode = new RootOfRoot(p);
        break;

        // === MATCH EXPRESSIONS ===	
      case XPathTreeConstants.JJTMATCHPATTERN :
        // See comment above.
        // TBD: If the pattern is a MatchPattern, then RootOfRoot should 
        // rebuild the children according to the structure needed 
        // by the patterns package.
        newNode = new RootOfRootPattern(p);
        break;
      case XPathTreeConstants.JJTPATTERN :
        // This should be optimized away if there is only one pattern
        newNode = new org.apache.xpath.patterns.UnionPattern();
        break;
      case XPathTreeConstants.JJTPATHPATTERN :
        // This is a temporary node, for construction purposes only,
        // Since the patterns package constructs the StepPatterns as 
        // a linked list.
        newNode = new Pattern(p);
        break;
      case XPathTreeConstants.JJTPATTERNSTEP :
        {
          StepPattern spat = new StepPattern();
          // At this point, the PatternAxis, NodeTest, and Predicates 
          // should be already on the stack?
          newNode = spat;
        }
        break;

      case XPathTreeConstants.JJTROOT :
        {
          if (!p.m_isMatchPattern || p.m_predLevel > 0)
          {
            newNode = new StepExpr(p);
            PatternAxis patAxis =
              new PatternAxis(org.apache.xml.dtm.Axis.ROOT, p);
            patAxis.m_value = "root::"; // for diagnostics
            NodeTest nt =
              new NodeTest(
                org.apache.xml.dtm.DTMFilter.SHOW_DOCUMENT
                  | org.apache.xml.dtm.DTMFilter.SHOW_DOCUMENT_FRAGMENT,
                p);
            Predicates preds = new Predicates(p);
            newNode.jjtAddChild(patAxis, 0);
            newNode.jjtAddChild(nt, 1);
            newNode.jjtAddChild(preds, 2);
          }
          else
          {
            StepPattern spat = new StepPattern();
            spat.setWhatToShow(
              DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT);
            spat.setAxis(org.apache.xml.dtm.Axis.PARENT);
            // spat.setT(org.apache.xpath.patterns.StepPattern.PSEUDONAME_ROOT);
            newNode = spat;
          }
        }
        break;
      case XPathTreeConstants.JJTROOTDESCENDANTS :
        {
          if (!p.m_isMatchPattern || p.m_predLevel > 0)
          {
            newNode = new StepExpr(p);
            PatternAxis patAxis =
              new PatternAxis(
                org.apache.xml.dtm.Axis.DESCENDANTSORSELFFROMROOT,
                p);
            patAxis.m_value = "descendants-from-root::"; // for diagnostics
            NodeTest nt =
              new NodeTest(org.apache.xml.dtm.DTMFilter.SHOW_ALL, p);
            nt.setTotallyWild(true);
            // nt.setLocalName(org.apache.xpath.patterns.StepPattern.PSEUDONAME_ROOT);
            Predicates preds = new Predicates(p);
            newNode.jjtAddChild(patAxis, 0);
            newNode.jjtAddChild(nt, 1);
            newNode.jjtAddChild(preds, 2);
          }
          else
          {
            // see comment for JJTROOT.
            StepPattern spat = new StepPattern();
            spat.setWhatToShow(
              DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT);
            spat.setAxis(org.apache.xml.dtm.Axis.ANCESTOR);
            newNode = spat;
          }
        }
        break;

      case XPathTreeConstants.JJTSLASH :
        newNode = new SlashOrSlashSlash(false, p);
        break;
      case XPathTreeConstants.JJTSLASHSLASH :
        newNode = new SlashOrSlashSlash(true, p);
        break;

        // === NODE TESTS ===	
      case XPathTreeConstants.JJTNODETEST :
        newNode = new NodeTest(p);
        break;
      case XPathTreeConstants.JJTNAMETEST :
        newNode = new NameTest(p);
        break;
      case XPathTreeConstants.JJTQNAME :
        newNode = new org.apache.xpath.parser.QName(p);
        break;
      case XPathTreeConstants.JJTSTAR : // Wildcard
        newNode = new Star(p);
        break;
      case XPathTreeConstants.JJTNCNAMECOLONSTAR :
        newNode = new NCNameColonStar(p);
        break;
      case XPathTreeConstants.JJTSTARCOLONNCNAME :
        newNode = new StarColonNCName(p);
        break;

      case XPathTreeConstants.JJTKINDTEST :
        newNode = new KindTest(p);
        break;
      case XPathTreeConstants.JJTPROCESSINGINSTRUCTIONTEST :
        newNode = new ProcessingInstructionTest(p);
        break;
      case XPathTreeConstants.JJTCOMMENTTEST :
        newNode = new CommentTest(p);
        break;
      case XPathTreeConstants.JJTTEXTTEST :
        newNode = new TextTest(p);
        break;
      case XPathTreeConstants.JJTANYKINDTEST :
        newNode = new AnyKindTest(p);
        break;

      case XPathTreeConstants.JJTLBRACK :
        newNode = new LbrackOrRbrack(p);
        p.m_predLevel++;
        break;
      case XPathTreeConstants.JJTRBRACK :
        newNode = new LbrackOrRbrack(p);
        p.m_predLevel--;
        break;
      case XPathTreeConstants.JJTPREDICATES :
        newNode = new Predicates(p);
        break;

        // === AXES, ETC. ===	
      case XPathTreeConstants.JJTAXISDESCENDANT :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.DESCENDANT, p);
        break;
      case XPathTreeConstants.JJTAXISSELF :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.SELF, p);
        break;
      case XPathTreeConstants.JJTAXISDESCENDANTORSELF :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.DESCENDANTORSELF, p);
        break;
      case XPathTreeConstants.JJTAXISFOLLOWINGSIBLING :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.FOLLOWINGSIBLING, p);
        break;
      case XPathTreeConstants.JJTAXISFOLLOWING :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.FOLLOWING, p);
        break;
      case XPathTreeConstants.JJTAXISNAMESPACE :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.NAMESPACE, p);
        break;
      case XPathTreeConstants.JJTAXISPARENT :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.PARENT, p);
        break;
      case XPathTreeConstants.JJTAXISANCESTOR :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.ANCESTOR, p);
        break;
      case XPathTreeConstants.JJTAXISPRECEDINGSIBLING :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.PRECEDINGSIBLING, p);
        break;
      case XPathTreeConstants.JJTAXISPRECEDING :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.PRECEDING, p);
        break;
      case XPathTreeConstants.JJTAXISANCESTORORSELF :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.ANCESTORORSELF, p);
        break;
      case XPathTreeConstants.JJTAXISCHILD :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.CHILD, p);
        break;
      case XPathTreeConstants.JJTAXISATTRIBUTE :
      case XPathTreeConstants.JJTAT :
        newNode = new PatternAxis(org.apache.xml.dtm.Axis.ATTRIBUTE, p);
        break;

      case XPathTreeConstants.JJTABBREVIATEDFORWARDSTEP :
        {
          PatternAxis patAxis =
            new PatternAxis(org.apache.xml.dtm.Axis.CHILD, p);
          patAxis.m_value = "child::"; // for diagnostics
          newNode = patAxis;
        }
        break;
      case XPathTreeConstants.JJTDOT :
        {
          PatternAxis patAxis =
            new PatternAxis(org.apache.xml.dtm.Axis.SELF, p);
          patAxis.m_value = "self::"; // for diagnostics
          newNode = patAxis;
          NodeTest nt = new NodeTest(org.apache.xml.dtm.DTMFilter.SHOW_ALL, p);
          newNode.jjtAddChild(nt, 0);
        }
        break;
      case XPathTreeConstants.JJTDOTDOT :
        {
          PatternAxis patAxis =
            new PatternAxis(org.apache.xml.dtm.Axis.PARENT, p);
          patAxis.m_value = "parent::"; // for diagnostics
          newNode = patAxis;
          NodeTest nt = new NodeTest(org.apache.xml.dtm.DTMFilter.SHOW_ALL, p);
          newNode.jjtAddChild(nt, 0);
        }
        break;

        // === PATH EXPRESSIONS ===	
      case XPathTreeConstants.JJTPATHEXPR :
        newNode = new PathExpr(p);
        break;
      case XPathTreeConstants.JJTSTEPEXPR :
        newNode = new StepExpr(p);
        break;

        // === SEQUENCE UNIONS AND INTERSECTIONS ===	
      case XPathTreeConstants.JJTUNIONEXPR :
        newNode = new UnionPathIterator();
        break;
      case XPathTreeConstants.JJTINTERSECTEXCEPTEXPR :
        newNode = new NonExecutableExpression(p, "JJTUNIONEXPR");
        break;
        //			case XPathTreeConstants.JJTUNION:
        //				newNode = new SimpleNode();
        //				break;
        //			case XPathTreeConstants.JJTVBAR:
        //				newNode = new SimpleNode();
        //				break;
        //			case XPathTreeConstants.JJTRELATIVEPATHPATTERN:
        //				newNode = new SimpleNode();
        //				break;

        // === FUNCTIONS ===	
      case XPathTreeConstants.JJTIDKEYPATTERN :
        {
          FunctionPattern fpat = new FunctionPattern();
          p.m_matchFunc = fpat; // short lived.
          newNode = fpat;
        }
        break;
      case XPathTreeConstants.JJTFUNCTIONCALL :
        {
          FunctionPattern fpat = new TempFunctionHolder();
          p.m_matchFunc = fpat; // short lived.
          newNode = fpat;
        }
        break;
      case XPathTreeConstants.JJTQNAMELPAR :
        try
        {

          org.apache.xpath.parser.QName qname =
            new org.apache.xpath.parser.QName(p);
          if (null != p.m_matchFunc)
          {
            qname.processToken(p.getToken(0));
            QName funcName = qname.getQName();
            String ns = funcName.getNamespaceURI();
            if (null != ns && ns.length() > 0)
            {
              String uniqueKey =
                String.valueOf(
                  ((SourceLocator) p.m_prefixResolver).getLineNumber())
                  + String.valueOf(funcName.hashCode())
                  + String.valueOf(System.currentTimeMillis());
              Function extension =
                new FuncExtFunction(ns, funcName.getLocalPart(), uniqueKey);
              p.m_matchFunc.setFunctionExpression(extension);
              p.m_matchFunc = null;
            }
            else
            {
              Expression exp = (Expression) m_builtInFunctions.get(funcName);
              if (null != exp)
              {
                exp = (Expression) exp.getClass().newInstance();
                p.m_matchFunc.setFunctionExpression(exp);
                p.m_matchFunc = null;
              }
              else
              {
                // TBD: a proper error throw.  -sb
                throw new RuntimeException(
                  "Function not found: " + funcName + "!");
              }
            }
          }
          newNode = qname;
        }
        catch (InstantiationException iae)
        {
          throw new org.apache.xml.utils.WrappedRuntimeException(iae);
        }
        catch (IllegalAccessException iae)
        {
          throw new org.apache.xml.utils.WrappedRuntimeException(iae);
        }

        break;

        // === BINARY OPERATORS ===	

      case XPathTreeConstants.JJTOREXPR :
        newNode = new org.apache.xpath.operations.Or();
        break;
      case XPathTreeConstants.JJTANDEXPR :
        newNode = new org.apache.xpath.operations.And();
        break;
      case XPathTreeConstants.JJTCOMPARISONEXPR :
        {
          Token operator = (Token) p.binaryTokenStack.peek();
          switch (operator.kind)
          {
            case XPathConstants.Equals :
              newNode = new org.apache.xpath.operations.Equals();
              break;
            case XPathConstants.NotEquals :
              newNode = new org.apache.xpath.operations.NotEquals();
              ;
              break;
            case XPathConstants.Lt :
              newNode = new org.apache.xpath.operations.Lt();
              break;
            case XPathConstants.LtEquals :
              newNode = new org.apache.xpath.operations.Lte();
              break;
            case XPathConstants.Gt :
              newNode = new org.apache.xpath.operations.Gt();
              break;
            case XPathConstants.GtEquals :
              newNode = new org.apache.xpath.operations.Gte();
              break;
            case XPathConstants.FortranEq :
              newNode = new org.apache.xpath.operations.FortranEq();
              break;
            case XPathConstants.FortranNe :
              newNode = new org.apache.xpath.operations.FortranNe();
              break;
            case XPathConstants.FortranLt :
              newNode = new org.apache.xpath.operations.FortranLt();
              break;
            case XPathConstants.FortranLe :
              newNode = new org.apache.xpath.operations.FortranLe();
              break;
            case XPathConstants.FortranGt :
              newNode = new org.apache.xpath.operations.FortranGt();
              break;
            case XPathConstants.FortranGe :
              newNode = new org.apache.xpath.operations.FortranGe();
              break;
            case XPathConstants.Is :
              newNode = new org.apache.xpath.operations.Is();
              break;
            case XPathConstants.IsNot :
              newNode = new org.apache.xpath.operations.IsNot();
              break;
            case XPathConstants.LtLt :
              newNode = new org.apache.xpath.operations.LtLt();
              break;
            case XPathConstants.GtGt :
              newNode = new org.apache.xpath.operations.GtGt();
              break;
            case XPathConstants.Precedes :
              newNode = new org.apache.xpath.operations.Precedes();
              break;
            case XPathConstants.Follows :
              newNode = new org.apache.xpath.operations.Follows();
              break;
          }
        }
        break;
      case XPathTreeConstants.JJTRANGEEXPR :
        newNode = new org.apache.xpath.seqctor.RangeExpr();
        break;
      case XPathTreeConstants.JJTADDITIVEEXPR :
        {
          Token operator = (Token) p.binaryTokenStack.peek();
          switch (operator.kind)
          {
            case XPathConstants.Plus :
              newNode = new org.apache.xpath.operations.Add();
              break;
            case XPathConstants.Minus :
              newNode = new org.apache.xpath.operations.Subtract();
              ;
              break;
          }
        }
        break;
      case XPathTreeConstants.JJTMULTIPLICATIVEEXPR :
        {
          Token operator = (Token) p.binaryTokenStack.peek();
          switch (operator.kind)
          {
            case XPathConstants.Multiply :
              newNode = new org.apache.xpath.operations.Mult();
              break;
            case XPathConstants.Div :
              newNode = new org.apache.xpath.operations.Div();
              ;
              break;
            case XPathConstants.Idiv :
              newNode = new org.apache.xpath.operations.Idiv();
              break;
            case XPathConstants.Mod :
              newNode = new org.apache.xpath.operations.Mod();
              ;
              break;
          }
        }
        break;

        // === UNARY OPERATORS ===	
      case XPathTreeConstants.JJTUNARYEXPR :
        newNode = new UnaryExpr(p);
        break;
      case XPathTreeConstants.JJTPLUS :
        newNode = new org.apache.xpath.operations.Pos();
        break;
      case XPathTreeConstants.JJTMINUS :
        newNode = new org.apache.xpath.operations.Neg();
        break;

        // === VARIABLES ===	
      case XPathTreeConstants.JJTVARNAME :
        {
          // We'll have to check to make sure the node is 
          // really on the stack.
          Variable var = new Variable();
          Token varToken = p.getToken(0);
          String varName = varToken.image;
          QName varQName = new QName(varName, p.m_prefixResolver);
          var.setQName(varQName);
          newNode = var;
        }
        break;

        // === LITERALS ===	
      case XPathTreeConstants.JJTSTRINGLITERAL :
        newNode = new org.apache.xpath.objects.XString();
        break;
      case XPathTreeConstants.JJTINTEGERLITERAL :
        if(p.getVersion() >= 2.0)
          newNode = new XInteger();
        else
          newNode = new XDouble();
        break;
      case XPathTreeConstants.JJTDECIMALLITERAL :
        newNode = new XDecimal();
        break;
      case XPathTreeConstants.JJTDOUBLELITERAL :
        newNode = new XDouble();
        break;

        // === SEQUENCE CONSTRUCTION ===	
      case XPathTreeConstants.JJTEXPRSEQUENCE :
        newNode = new ExprSequence();
        break;

        // === QUANTIFIED EXPRESSIONS ===	
      case XPathTreeConstants.JJTQUANTIFIEDEXPR :
        newNode = new QuantifiedExpr(p);
        break;
      case XPathTreeConstants.JJTSOME :
        newNode = new org.apache.xpath.quantified.Some();
        break;
      case XPathTreeConstants.JJTEVERY :
        newNode = new org.apache.xpath.quantified.Every();
        break;
      case XPathTreeConstants.JJTSATISFIES :
        newNode = new org.apache.xpath.quantified.Satisfies();
        break;
        
      case XPathTreeConstants.JJTIN : // Both for ForClause and Quantified.
        newNode = new In(p);
        break;

        // === CONDITIONAL EXPRESSIONS ===	
      case XPathTreeConstants.JJTIFEXPR :
        newNode = new IfExpr(p);
        break;
      case XPathTreeConstants.JJTIFLPAR :
        newNode = new org.apache.xpath.conditional.If();
        break;
      case XPathTreeConstants.JJTTHEN :
        newNode = new org.apache.xpath.conditional.Then();
        break;
      case XPathTreeConstants.JJTELSE :
        newNode = new org.apache.xpath.conditional.Else();
        break;

        // === ITTERATION ===	
      case XPathTreeConstants.JJTFLWREXPR :
        newNode = new FLWRExpr();
        break;
      case XPathTreeConstants.JJTFORCLAUSE :
        newNode = new org.apache.xpath.parser.ForClause(p);
        break;
      case XPathTreeConstants.JJTRETURN :
        newNode = new org.apache.xpath.parser.Return(p);
        break;

        // === TYPE HANDLING ===	
      case XPathTreeConstants.JJTINSTANCEOFEXPR :
        newNode = new InstanceofExpr();
        break;
      case XPathTreeConstants.JJTINSTANCEOF :
        newNode = new Instanceof(p);
        break;
      case XPathTreeConstants.JJTVALIDATEEXPR :
        newNode = new NonExecutableExpression(p, "JJTVALIDATEEXPR");
        break;
      case XPathTreeConstants.JJTVALIDATE :
        newNode = new NonExecutableExpression(p, "JJTVALIDATE");
        break;
      case XPathTreeConstants.JJTLBRACE :
        newNode = new NonExecutableExpression(p, "JJTLBRACE");
        break;
      case XPathTreeConstants.JJTRBRACE :
        newNode = new NonExecutableExpression(p, "JJTRBRACE");
        break;
      case XPathTreeConstants.JJTCASTEXPR :
        newNode = new NonExecutableExpression(p, "JJTCASTEXPR");
        break;
      case XPathTreeConstants.JJTCASTAS :
        newNode = new NonExecutableExpression(p, "JJTCASTAS");
        break;
      case XPathTreeConstants.JJTTREATAS :
        newNode = new NonExecutableExpression(p, "JJTTREATAS");
        break;
      case XPathTreeConstants.JJTSCHEMACONTEXT :
        newNode = new SchemaContext(p, "JJTSCHEMACONTEXT");
        break;
      case XPathTreeConstants.JJTSCHEMAGLOBALCONTEXT :
        newNode = new SchemaGlobalContext(p, "JJTSCHEMAGLOBALCONTEXT");
        break;
      case XPathTreeConstants.JJTTYPE :
        newNode = new NonExecutableExpression(p, "JJTTYPE");
        break;
      case XPathTreeConstants.JJTSCHEMACONTEXTSTEP :
        newNode = new SchemaContextStep(p);
        break;
      case XPathTreeConstants.JJTSEQUENCETYPE :
        newNode = new SequenceType(p, "JJTSEQUENCETYPE");
        break;
      case XPathTreeConstants.JJTEMPTY :
        newNode = new Empty(p, "JJTEMPTY");
        break;
      case XPathTreeConstants.JJTITEMTYPE :
        newNode = new ItemType(p, "JJTITEMTYPE");
        break;
      case XPathTreeConstants.JJTELEMENTTYPE :
        newNode = new NodeTestType(p, DTMFilter.SHOW_ELEMENT);
        break;
      case XPathTreeConstants.JJTATTRIBUTETYPE :
        newNode = new NodeTestType(p, DTMFilter.SHOW_ATTRIBUTE);
        break;
      case XPathTreeConstants.JJTNODE :
        newNode = new NodeTestType(p, DTMFilter.SHOW_ALL);
        break;
      case XPathTreeConstants.JJTPROCESSINGINSTRUCTION :
        newNode = new NodeTestType(p, DTMFilter.SHOW_PROCESSING_INSTRUCTION);
        break;
      case XPathTreeConstants.JJTCOMMENT :
        newNode = new NodeTestType(p, DTMFilter.SHOW_COMMENT);
        break;
      case XPathTreeConstants.JJTTEXT :
        newNode = new NodeTestType(p, DTMFilter.SHOW_TEXT);
        break;
      case XPathTreeConstants.JJTDOCUMENT :
        newNode = new NodeTestType(p, DTMFilter.SHOW_DOCUMENT);
        break;
      case XPathTreeConstants.JJTITEM :
        newNode = new NodeTestType(p, DTMFilter.SHOW_ITEM);
        break;
      case XPathTreeConstants.JJTUNTYPED :
        newNode = new NodeTestType(p, DTMFilter.SHOW_UNTYPED);
        break;
//      case XPathTreeConstants.JJTATOMICVALUE :
//        newNode = new AtomicType(p);
//        break;
      case XPathTreeConstants.JJTELEMORATTRTYPE :
        newNode = new ElemOrAttrType(p, "JJTELEMORATTRTYPE");
        break;
      case XPathTreeConstants.JJTSCHEMATYPE :
        newNode = new SchemaType(p);
        break;
      case XPathTreeConstants.JJTOFTYPE :
        newNode = new OfType(p, "JJTOFTYPE");
        break;
      case XPathTreeConstants.JJTATOMICTYPE :
        newNode = new AtomicType(p, "JJTATOMICTYPE");
        break;
      case XPathTreeConstants.JJTOCCURRENCEINDICATOR :
        newNode = new OccurrenceIndicator(p, "JJTOCCURRENCEINDICATOR");
        break;
      case XPathTreeConstants.JJTMULTIPLY :
        // Leave as NEE
        newNode = new NonExecutableExpression(p, "JJTMULTIPLY");
        break;
      case XPathTreeConstants.JJTQMARK :
        // Leave as NEE
        newNode = new NonExecutableExpression(p, "JJTQMARK");
        break;

      default :
        newNode = new NonExecutableExpression(p, "(case default: " + id + ")");
    }
    return newNode;
  }

  /**
   * Tell if this node is part of a PathExpr chain.  For instance:
   * <pre>
   * 	|UnaryExpr
   * 	|   PathExpr
   * 	|      StepExpr
   * 	|         AxisChild child::
   * 	|         NodeTest
   * 	|            NameTest
   * 	|               QName foo
   * 	|         Predicates   * 
   * </pre><br/>
   * In this example, UnaryExpr, PathExpr, and StepExpr should all return true.
   */
  public boolean isPathExpr()
  {
    return false;
  }

  /**
   * Tell if this node should have it's PathExpr ancestory reduced.
   */
  public boolean isPathExprReduced()
  {
    return false;
  }

  /**
   * Tell if this node should have it's parent reduced.
   */
  public boolean shouldReduceIfOneChild()
  {
    return false;
  }

  /**
   * This function checks the integrity of the tree, after it has been fully built and 
   * is ready for execution.
   * @return boolean true if the tree has integrity, false otherwise.
   */
  public boolean checkTreeIntegrity()
  {
    return checkTreeIntegrity(0, 0, true);
  }

  /**
   * Tell the user there is a problem with the tree.
   * @param s The string to show the user.
   * @return boolean false always.
   */
  protected boolean flagProblem(String s)
  {
    // System.err.println("checkTreeIntegrity failed: " + s);
    throw new RuntimeException("checkTreeIntegrity failed: " + s);
    // return false;
  }

  /**
   * This function checks the integrity of the tree, after it has been fully built and 
   * is ready for execution.  Derived classes can overload this function to check 
   * their own assumptions.
   * 
   * @param levelCount The current tree level.
   * @param childNumber The current child index.
   * @param parentOK The parent's integrity flag.
   * @return boolean true the node does not have integrity, otherwise 
   *          return the parentOK value.
   */
  public boolean checkTreeIntegrity(
    int levelCount,
    int childNumber,
    boolean parentOK)
  {
    boolean isOK;
//    try
    {
      isOK = parentOK;
      if (levelCount == 0)
      {
        if (!(null == jjtGetParent()))
          isOK =
            flagProblem("The root node has a parent?? parent: " + jjtGetParent());
      }
      else
      {
        Node parent = jjtGetParent();
        if (null == parent)
          isOK =
            flagProblem(
              toString() + " has a null parent and is not the root node! "+
                this.getClass().getName());
        else
        {
          if ( (childNumber >= parent.jjtGetNumChildren())
            || (parent.jjtGetChild(childNumber) != this))
          {
            isOK =
              flagProblem(
                toString() + " has a parent that it is not a child of!");
          }
        }
      
      }
      if (this instanceof NonExecutableExpression)
        isOK = flagProblem(toString() + " is a NonExecutableExpression!");
      
      int childCount = jjtGetNumChildren();
      for (int i = 0; i < childCount; i++)
      {
        Node child = jjtGetChild(i);
        if(null == child)
          isOK = flagProblem(toString() + " has a null child!");
        else
          isOK = ((SimpleNode) child).checkTreeIntegrity(levelCount + 1, i, isOK);
      }
    }
//    catch (RuntimeException e)
//    {
//      isOK = flagProblem(toString() + e.getMessage());
//      e.printStackTrace();
//    }

    return isOK;
  }

  /**
   * This ugly little function strips the extranious PathExpr/RelativePathExpr/StepExpr
   * from XObjects, recursively calling the node's child at index zero, if 
   * the node is a path expression.  It should be called from 
   * jjtAddChild functions.
   * 
   * @param n The node to fix up.
   * @return Node The fixed up node that should be added as a child, which 
   *               may well not be the same node that was passed in.
   */
  protected org.apache.xpath.parser.Node fixupPrimaryRecursive(
    org.apache.xpath.parser.Node n)
  {
    if (((SimpleNode) n).isPathExpr() && n.jjtGetNumChildren() > 0)
    {
      SimpleNode newRoot = (SimpleNode) fixupPrimaryRecursive(n.jjtGetChild(0));

      if (newRoot.isPathExprReduced() && !newRoot.isPathExpr())
      {
        return newRoot;
      }
    }
    return n;
  }

  /**
   * Set this to false if you don't want the PathExprs rewritten to LocationPaths.
   * For Diagnostic purposes.
   */
  public static boolean m_rewritePathExprs = true;

  /**
   * This ugly little function strips the extranious PathExpr/RelativePathExpr/StepExpr
   * from XObjects.  I'm not sure what I'm going to do with the predicates of 
   * these expressions, or with variables, yet.  It should be called from 
   * jjtAddChild functions.
   * 
   * @param n The node to fix up.
   * @return Node The fixed up node that should be added as a child, which 
   *               may well not be the same node that was passed in.
   */
  protected org.apache.xpath.parser.Node fixupPrimarys(
    org.apache.xpath.parser.Node n)
  {
    if (((SimpleNode) n).shouldReduceIfOneChild())
    {
      n = n.jjtGetChild(0);
      n.jjtSetParent(this);
      return n;
    }
    Node orig = n;
    if ((n instanceof NonExecutableExpression) && n.jjtGetNumChildren() > 0)
    {
      n = fixupPrimaryRecursive(n);
    }
    if (n instanceof PathExpr)
    {
      Node mightBeStepExprOrSomethingElse = n.jjtGetChild(0);
      if (!(mightBeStepExprOrSomethingElse instanceof StepExpr))
        n = mightBeStepExprOrSomethingElse; // is something else, so reduce
    }
    if (m_rewritePathExprs && n instanceof PathExpr)
    {
      boolean isTopLevel = (((PathExpr) n).m_parser.m_predLevel == 0);
      try
      {
        n = (Node) WalkerFactory.newDTMIterator((PathExpr) n, isTopLevel);
      }
      catch (javax.xml.transform.TransformerException te)
      {
        throw new org.apache.xml.utils.WrappedRuntimeException(te);
      }
    }
    if (n != orig)
      n.jjtSetParent(this); // because jjtree can't!
    return n;
  }

  /**
   * @see org.apache.xpath.parser.Node#jjtOpen()
   */
  public void jjtOpen()
  {
  }

  /**
   * @see org.apache.xpath.parser.Node#jjtClose()
   */
  public void jjtClose()
  {
  }

  /**
   * @see org.apache.xpath.parser.Node#jjtSetParent(Node)
   */
  public void jjtSetParent(Node n)
  {
    ((Expression) this).exprSetParent((ExpressionNode) n);
  }

  /**
   * @see org.apache.xpath.parser.Node#jjtGetParent()
   */
  public Node jjtGetParent()
  {
    return (Node) ((Expression) this).exprGetParent();
  }

  /**
   * @see org.apache.xpath.parser.Node#jjtAddChild(Node, int)
   */
  public void jjtAddChild(Node n, int i)
  {
    n = fixupPrimarys(n);
    ((Expression) this).exprAddChild((ExpressionNode) n, i);
  }

  /**
   * @see org.apache.xpath.parser.Node#jjtGetChild(int)
   */
  public Node jjtGetChild(int i)
  {
    return (Node) ((Expression) this).exprGetChild(i);
  }

  /**
   * @see org.apache.xpath.parser.Node#jjtGetNumChildren()
   */
  public int jjtGetNumChildren()
  {
    return ((Expression) this).exprGetNumChildren();
  }

  /**
   * @see org.apache.xpath.parser.Node#jjtAccept(XPathVisitor, Object)
   */
  public Object jjtAccept(XPathVisitor visitor, Object data)
  {
    return visitor.visit(this, data);
  }

  /**
   * Call jjtAccept for each child of the node.
   * @param visitor The visitor that will visit the child node.
   * @param data A data object to be handed to that visitor.
   * @return Object The data object handed in, possibly  
   *                 modified by jjtAccept.
   */
  public Object childrenAccept(XPathVisitor visitor, Object data)
  {
    int nChildren = jjtGetNumChildren();
    for (int i = 0; i < nChildren; ++i)
    {
      jjtGetChild(i).jjtAccept(visitor, data);
    }
    return data;
  }

  /**
   * @see java.lang.Object#toString()
   */
  public String toString()
  {
    return this.getClass().getName();
  }

  /**
   * Method toString.
   * @param prefix
   * @return String
   */
  public String toString(String prefix)
  {
    return prefix + toString();
  }

  public void processToken(Token t)
  {
    // abstract
  }

  /**
   * Dump a diagnostic representation of the node to System.out.
   * @param prefix The prefix string to be added before the 
   *                to each line of output.
   */
  public void dump(String prefix)
  {
    dump(prefix, System.out);
  }

  /**
   * Method for subclasses to override to print the diagnostic 
   * <i>value</i> of the node.
   * @param ps The PrintStream to write the value to.
   */
  public void printValue(java.io.PrintStream ps)
  {
  }

  /**
   * Dump a diagnostic representation of the node to a PrintStream.
   * 
   * @param prefix The prefix string to be added before the 
   *                to each line of output.
  
   * @param ps The PrintStream where the representation will 
   *            be written.
   */
  public void dump(String prefix, java.io.PrintStream ps)
  {
    ps.print(toString(prefix));
    printValue(ps);
    ps.println();
    int nChildren = jjtGetNumChildren();
    for (int i = 0; i < nChildren; ++i)
    {
      SimpleNode n = (SimpleNode) jjtGetChild(i);
      if (n != null)
      {
        n.dump(prefix + "   ", ps);
      }
    }
  }

}
