You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xerces.apache.org by an...@apache.org on 2001/01/18 08:10:56 UTC

cvs commit: xml-xerces/java/src/org/apache/xerces/validators/schema/identity Field.java FieldActivator.java IdentityConstraint.java Key.java KeyRef.java Makefile Selector.java Unique.java ValueStore.java XPath.java XPathException.java XPathMatcher.java

andyc       01/01/17 23:10:56

  Modified:    java/src/org/apache/xerces/framework XMLParser.java
               java/src/org/apache/xerces/parsers SAXParser.java
               java/src/org/apache/xerces/validators/common Grammar.java
                        XMLElementDecl.java
               java/src/org/apache/xerces/validators/schema
                        SchemaSymbols.java TraverseSchema.java XUtil.java
  Added:       java/src/org/apache/xerces/validators/schema/identity
                        Field.java FieldActivator.java
                        IdentityConstraint.java Key.java KeyRef.java
                        Makefile Selector.java Unique.java ValueStore.java
                        XPath.java XPathException.java XPathMatcher.java
  Log:
  Adding support for Schema identity constraints.
  
  1) Ported the limited XPath code from Xerces2 to Xerces 1.x.
  2) Added identity constraint info to grammar and element decl.
  3) Updated TraverseSchema to populate the grammar with the
     identity constraint information.
  
  NEXT: Modifying the validator to use this information. Yay!
  
  Revision  Changes    Path
  1.26      +6 -2      xml-xerces/java/src/org/apache/xerces/framework/XMLParser.java
  
  Index: XMLParser.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/framework/XMLParser.java,v
  retrieving revision 1.25
  retrieving revision 1.26
  diff -u -r1.25 -r1.26
  --- XMLParser.java	2000/12/06 22:14:35	1.25
  +++ XMLParser.java	2001/01/18 07:10:49	1.26
  @@ -90,7 +90,7 @@
   /**
    * This is the base class of all standard parsers.
    *
  - * @version $Id: XMLParser.java,v 1.25 2000/12/06 22:14:35 lehors Exp $
  + * @version $Id: XMLParser.java,v 1.26 2001/01/18 07:10:49 andyc Exp $
    */
   public abstract class XMLParser 
       implements XMLErrorReporter, XMLDocumentHandler.DTDHandler {
  @@ -197,7 +197,11 @@
        * Constructor
        */
       protected XMLParser() {
  -        fStringPool = new StringPool();
  +        this(new StringPool());
  +    }
  +
  +    protected XMLParser(StringPool stringPool) {
  +        fStringPool = stringPool;
           fErrorReporter = this;
           fEntityHandler = new DefaultEntityHandler(fStringPool, fErrorReporter);
           fScanner = new XMLDocumentScanner(fStringPool, fErrorReporter, fEntityHandler, new ChunkyCharArray(fStringPool));
  
  
  
  1.19      +6 -1      xml-xerces/java/src/org/apache/xerces/parsers/SAXParser.java
  
  Index: SAXParser.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/parsers/SAXParser.java,v
  retrieving revision 1.18
  retrieving revision 1.19
  diff -u -r1.18 -r1.19
  --- SAXParser.java	2000/12/27 09:49:43	1.18
  +++ SAXParser.java	2001/01/18 07:10:50	1.19
  @@ -88,7 +88,7 @@
    * SAXParser provides a parser which implements the SAX1 and SAX2
    * parser APIs.
    *
  - * @version $Id: SAXParser.java,v 1.18 2000/12/27 09:49:43 andyc Exp $
  + * @version $Id: SAXParser.java,v 1.19 2001/01/18 07:10:50 andyc Exp $
    */
   public class SAXParser
       extends XMLParser
  @@ -162,6 +162,11 @@
   
       /** Default constructor. */
       public SAXParser() {
  +        initHandlers(true, this, this);
  +    }
  +
  +    protected SAXParser(StringPool stringPool) {
  +        super(stringPool);
           initHandlers(true, this, this);
       }
   
  
  
  
  1.16      +75 -1     xml-xerces/java/src/org/apache/xerces/validators/common/Grammar.java
  
  Index: Grammar.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/validators/common/Grammar.java,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- Grammar.java	2001/01/11 05:09:31	1.15
  +++ Grammar.java	2001/01/18 07:10:51	1.16
  @@ -63,13 +63,14 @@
   import org.apache.xerces.validators.datatype.DatatypeValidator;
   import org.apache.xerces.validators.common.XMLContentModel;
   import org.apache.xerces.validators.common.CMException;
  +import org.apache.xerces.validators.schema.identity.IdentityConstraint;
   import org.apache.xerces.utils.ImplementationMessages;
   import org.w3c.dom.Document;
   import java.util.Vector;
   
   
   /**
  - * @version $Id: Grammar.java,v 1.15 2001/01/11 05:09:31 andyc Exp $
  + * @version $Id: Grammar.java,v 1.16 2001/01/18 07:10:51 andyc Exp $
    */
   public class Grammar
   implements XMLContentSpec.Provider {
  @@ -106,6 +107,9 @@
       private XMLContentModel fElementDeclContentModelValidator[][] = new XMLContentModel[INITIAL_CHUNK_COUNT][];
       private int fElementDeclFirstAttributeDeclIndex[][] = new int[INITIAL_CHUNK_COUNT][];
       private int fElementDeclLastAttributeDeclIndex[][] = new int[INITIAL_CHUNK_COUNT][];
  +    private Vector fElementDeclUnique[][] = new Vector[INITIAL_CHUNK_COUNT][];
  +    private Vector fElementDeclKey[][] = new Vector[INITIAL_CHUNK_COUNT][];
  +    private Vector fElementDeclKeyRef[][] = new Vector[INITIAL_CHUNK_COUNT][];
   
       // content spec tables
   
  @@ -183,6 +187,26 @@
           elementDecl.datatypeValidator       = fElementDeclDatatypeValidator[chunk][index];       
           elementDecl.contentSpecIndex        = fElementDeclContentSpecIndex[chunk][index];        
   
  +        // copy identity constraints
  +        elementDecl.unique.removeAllElements();
  +        int ucount = fElementDeclUnique[chunk][index] != null
  +                   ? fElementDeclUnique[chunk][index].size() : 0;
  +        for (int i = 0; i < ucount; i++) {
  +            elementDecl.unique.addElement(fElementDeclUnique[chunk][index].elementAt(i));
  +        }
  +        elementDecl.key.removeAllElements();
  +        int kcount = fElementDeclKey[chunk][index] != null
  +                   ? fElementDeclKey[chunk][index].size() : 0;
  +        for (int i = 0; i < kcount; i++) {
  +            elementDecl.key.addElement(fElementDeclKey[chunk][index].elementAt(i));
  +        }
  +        elementDecl.keyRef.removeAllElements();
  +        int krcount = fElementDeclKeyRef[chunk][index] != null
  +                    ? fElementDeclKeyRef[chunk][index].size() : 0;
  +        for (int i = 0; i < krcount; i++) {
  +            elementDecl.keyRef.addElement(fElementDeclKeyRef[chunk][index].elementAt(i));
  +        }
  +
           return true;
       }
   
  @@ -353,6 +377,44 @@
           fElementDeclDatatypeValidator[chunk][index]       = elementDecl.datatypeValidator;
           fElementDeclContentSpecIndex[chunk][index]        = elementDecl.contentSpecIndex;
   
  +        // copy identity constraints
  +        int ucount = elementDecl.unique.size();
  +        if (ucount > 0) {
  +            if (fElementDeclUnique[chunk][index] == null) {
  +                fElementDeclUnique[chunk][index] = (Vector)elementDecl.unique.clone();
  +            }
  +            else {
  +                fElementDeclUnique[chunk][index].removeAllElements();
  +                for (int i = 0; i < ucount; i++) {
  +                    fElementDeclUnique[chunk][index].addElement(elementDecl.unique.elementAt(i));
  +                }
  +            }
  +        }
  +        int kcount = elementDecl.key.size();
  +        if (kcount > 0) {
  +            if (fElementDeclKey[chunk][index] == null) {
  +                fElementDeclKey[chunk][index] = (Vector)elementDecl.key.clone();
  +            }
  +            else {
  +                fElementDeclKey[chunk][index].removeAllElements();
  +                for (int i = 0; i < kcount; i++) {
  +                    fElementDeclKey[chunk][index].addElement(elementDecl.key.elementAt(i));
  +                }
  +            }
  +        }
  +        int krcount = elementDecl.keyRef.size();
  +        if (krcount > 0) {
  +            if (fElementDeclKeyRef[chunk][index] == null) {
  +                fElementDeclKeyRef[chunk][index] = (Vector)elementDecl.keyRef.clone();
  +            }
  +            else {
  +                fElementDeclKeyRef[chunk][index].removeAllElements();
  +                for (int i = 0; i < krcount; i++) {
  +                    fElementDeclKeyRef[chunk][index].addElement(elementDecl.keyRef.elementAt(i));
  +                }
  +            }
  +        }
  +
           // add the mapping information to the 
           putElementNameMapping(elementDecl.name,
                                 elementDecl.enclosingScope, 
  @@ -805,6 +867,9 @@
               fElementDeclContentModelValidator = resize(fElementDeclContentModelValidator, fElementDeclContentModelValidator.length * 2);
               fElementDeclFirstAttributeDeclIndex = resize(fElementDeclFirstAttributeDeclIndex, fElementDeclFirstAttributeDeclIndex.length * 2);
               fElementDeclLastAttributeDeclIndex = resize(fElementDeclLastAttributeDeclIndex, fElementDeclLastAttributeDeclIndex.length * 2);
  +            fElementDeclUnique = resize(fElementDeclUnique, fElementDeclUnique.length * 2);
  +            fElementDeclKey = resize(fElementDeclKey, fElementDeclKey.length * 2);
  +            fElementDeclKeyRef = resize(fElementDeclKeyRef, fElementDeclKeyRef.length * 2);
           } catch (NullPointerException ex) {
               // ignore
           }
  @@ -815,6 +880,9 @@
           fElementDeclContentModelValidator[chunk] = new XMLContentModel[CHUNK_SIZE];
           fElementDeclFirstAttributeDeclIndex[chunk] = new int[CHUNK_SIZE];
           fElementDeclLastAttributeDeclIndex[chunk] = new int[CHUNK_SIZE];
  +        fElementDeclUnique[chunk] = new Vector[CHUNK_SIZE];
  +        fElementDeclKey[chunk] = new Vector[CHUNK_SIZE];
  +        fElementDeclKeyRef[chunk] = new Vector[CHUNK_SIZE];
           return true;
       }
   
  @@ -886,6 +954,12 @@
   
       private String[][] resize(String array[][], int newsize) {
           String newarray[][] = new String[newsize][];
  +        System.arraycopy(array, 0, newarray, 0, array.length);
  +        return newarray;
  +    }
  +
  +    private Vector[][] resize(Vector array[][], int newsize) {
  +        Vector newarray[][] = new Vector[newsize][];
           System.arraycopy(array, 0, newarray, 0, array.length);
           return newarray;
       }
  
  
  
  1.3       +14 -1     xml-xerces/java/src/org/apache/xerces/validators/common/XMLElementDecl.java
  
  Index: XMLElementDecl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/validators/common/XMLElementDecl.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- XMLElementDecl.java	2000/05/17 18:33:06	1.2
  +++ XMLElementDecl.java	2001/01/18 07:10:51	1.3
  @@ -57,11 +57,13 @@
   
   package org.apache.xerces.validators.common;
   
  +import java.util.Vector;
  +
   import org.apache.xerces.utils.QName;
   import org.apache.xerces.validators.datatype.DatatypeValidator;
   
   /**
  - * @version $Id: XMLElementDecl.java,v 1.2 2000/05/17 18:33:06 jeffreyr Exp $
  + * @version $Id: XMLElementDecl.java,v 1.3 2001/01/18 07:10:51 andyc Exp $
    */
   public class XMLElementDecl {
   
  @@ -98,6 +100,14 @@
       // enclosingScope where this element is declared, should always be -1 with DTD Validation.
       public int enclosingScope;
   
  +    // identity constraints
  +
  +    public final Vector unique = new Vector();
  +
  +    public final Vector key = new Vector();
  +
  +    public final Vector keyRef = new Vector();
  +
       //
       // Constructors
       //
  @@ -121,6 +131,9 @@
           datatypeValidator = null;
           contentSpecIndex = -1;
           enclosingScope = -1;
  +        unique.removeAllElements();
  +        key.removeAllElements();
  +        keyRef.removeAllElements();
       }
   
       public void setValues(XMLElementDecl elementDecl) {
  
  
  
  1.9       +2 -0      xml-xerces/java/src/org/apache/xerces/validators/schema/SchemaSymbols.java
  
  Index: SchemaSymbols.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/validators/schema/SchemaSymbols.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- SchemaSymbols.java	2001/01/15 23:15:29	1.8
  +++ SchemaSymbols.java	2001/01/18 07:10:52	1.9
  @@ -88,6 +88,7 @@
       public static final String ELT_ELEMENT =  "element";
       public static final String ELT_ENCODING =  "encoding";
       public static final String ELT_ENUMERATION =  "enumeration";
  +    public static final String ELT_FIELD = "field";
       public static final String ELT_WHITESPACE =  "whiteSpace";
       public static final String ELT_GROUP =  "group";
       public static final String ELT_IMPORT =  "import";
  @@ -146,6 +147,7 @@
       public static final String ATT_USE =  "use";
       public static final String ATT_VALUE = "value";
       public static final String ATT_MIXED = "mixed";
  +    public static final String ATT_XPATH = "xpath";
       public static final String ATTVAL_TWOPOUNDANY =  "##any";
       public static final String ATTVAL_TWOPOUNDLOCAL =  "##local";
       public static final String ATTVAL_TWOPOUNDOTHER =  "##other";
  
  
  
  1.73      +165 -52   xml-xerces/java/src/org/apache/xerces/validators/schema/TraverseSchema.java
  
  Index: TraverseSchema.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/validators/schema/TraverseSchema.java,v
  retrieving revision 1.72
  retrieving revision 1.73
  diff -u -r1.72 -r1.73
  --- TraverseSchema.java	2001/01/17 21:29:58	1.72
  +++ TraverseSchema.java	2001/01/18 07:10:52	1.73
  @@ -1,10 +1,9 @@
  -
   /*
    * The Apache Software License, Version 1.1
    *
    *
  - * Copyright (c) 2000 The Apache Software Foundation.  All rights 
  - * reserved.
  + * Copyright (c) 2000,2001 The Apache Software Foundation.  
  + * All rights reserved.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions
  @@ -65,6 +64,13 @@
   import  org.apache.xerces.validators.common.XMLAttributeDecl;
   import  org.apache.xerces.validators.schema.SchemaSymbols;
   import  org.apache.xerces.validators.schema.XUtil;
  +import  org.apache.xerces.validators.schema.identity.Field;
  +import  org.apache.xerces.validators.schema.identity.IdentityConstraint;
  +import  org.apache.xerces.validators.schema.identity.Key;
  +import  org.apache.xerces.validators.schema.identity.KeyRef;
  +import  org.apache.xerces.validators.schema.identity.Selector;
  +import  org.apache.xerces.validators.schema.identity.Unique;
  +import  org.apache.xerces.validators.schema.identity.XPathException;
   import  org.apache.xerces.validators.datatype.DatatypeValidator;
   import  org.apache.xerces.validators.datatype.DatatypeValidatorFactoryImpl;
   import  org.apache.xerces.validators.datatype.UnionDatatypeValidator;  //CR implementation
  @@ -72,12 +78,14 @@
   import  org.apache.xerces.utils.StringPool;
   import  org.w3c.dom.Element;
   
  -//REVISIT: for now, import everything in the DOM package
  -import  org.w3c.dom.*;
  +import java.io.IOException;
   import java.util.*;
   import java.net.URL;
   import java.net.MalformedURLException;
   
  +//REVISIT: for now, import everything in the DOM package
  +import  org.w3c.dom.*;
  +
   //Unit Test 
   import  org.apache.xerces.parsers.DOMParser;
   import  org.apache.xerces.validators.common.XMLValidator;
  @@ -95,15 +103,11 @@
   import  org.xml.sax.EntityResolver;
   import  org.xml.sax.ErrorHandler;
   import  org.xml.sax.SAXException;
  -import  java.io.IOException;
   import  org.w3c.dom.Document;
   import  org.apache.xml.serialize.OutputFormat;
   import  org.apache.xml.serialize.XMLSerializer;
   import  org.apache.xerces.validators.schema.SchemaSymbols;
   
  -
  -
  -
   /**
    * Instances of this class get delegated to Traverse the Schema and
    * to populate the Grammar internal representation by
  @@ -391,7 +395,7 @@
    *  
    * @see                  org.apache.xerces.validators.common.Grammar
    *
  - * @version $Id: TraverseSchema.java,v 1.72 2001/01/17 21:29:58 elena Exp $
  + * @version $Id: TraverseSchema.java,v 1.73 2001/01/18 07:10:52 andyc Exp $
    */
   
   public class TraverseSchema implements 
  @@ -401,9 +405,17 @@
       //CONSTANTS
       private static final int TOP_LEVEL_SCOPE = -1;
   
  +    /** Identity constraint keywords. */
  +    private static final String[] IDENTITY_CONSTRAINTS = {
  +        SchemaSymbols.ELT_UNIQUE, 
  +        SchemaSymbols.ELT_KEY, SchemaSymbols.ELT_KEYREF
  +    };
  +
       //debuggin
       private static boolean DEBUGGING = false;
   
  +    /** Compile to true to debug identity constraints. */
  +    private static boolean DEBUG_IDENTITY_CONSTRAINTS = false;
       
   	//CR Implementation
   	private static boolean DEBUG_UNION = false;
  @@ -835,10 +847,7 @@
           }
           fIncludeLocations.addElement((Object)location);
   
  -        DOMParser parser = new DOMParser() {
  -            public void ignorableWhitespace(char ch[], int start, int length) {}
  -            public void ignorableWhitespace(int dataIdx) {}
  -        };
  +        DOMParser parser = new IgnoreWhitespaceParser();
           parser.setEntityResolver( new Resolver() );
           parser.setErrorHandler(  new ErrorHandler() );
   
  @@ -1009,10 +1018,7 @@
                importedGrammar = new SchemaGrammar();
            }
   
  -         DOMParser parser = new DOMParser() {
  -                public void ignorableWhitespace(char ch[], int start, int length) {}
  -                public void ignorableWhitespace(int dataIdx) {}
  -         };
  +         DOMParser parser = new IgnoreWhitespaceParser();
            parser.setEntityResolver( new Resolver() );
            parser.setErrorHandler(  new ErrorHandler() );
   
  @@ -4554,36 +4560,6 @@
           }
   
           //
  -        // key/keyref/unique processing\
  -        //
  -
  -        child = XUtil.getFirstChildElement(elementDecl);
  -        Vector idConstraints = null;
  -        
  -        while (child != null){
  -            String childName = child.getLocalName();
  -           /**** 
  -            if ( childName.equals(SchemaSymbols.ELT_KEY) ) { 
  -                traverseKey(child, idCnstrt);
  -            }
  -            else if ( childName.equals(SchemaSymbols.ELT_KEYREF) ) {
  -                traverseKeyRef(child, idCnstrt);
  -            }
  -            else if ( childName.equals(SchemaSymbols.ELT_UNIQUE) ) {
  -                traverseUnique(child, idCnstrt);
  -            }
  -
  -            if (idCnstrt!= null) {
  -                if (idConstraints != null) {
  -                    idConstraints = new Vector();
  -                }
  -                idConstraints.addElement(idCnstrt);
  -            }
  -            /****/
  -            child = XUtil.getNextSiblingElement(child);
  -        }
  -        
  -        //
           // Create element decl
           //
   
  @@ -4663,11 +4639,146 @@
           // setEquivClassElementFullName
           fSchemaGrammar.setElementDeclEquivClassElementFullName(elementIndex, equivClassFullName);
   
  +        //
  +        // key/keyref/unique processing\
  +        //
  +
  +        Element ic = XUtil.getFirstChildElement(elementDecl, IDENTITY_CONSTRAINTS);
  +        Vector idConstraints = null;
  +        
  +        if (ic != null) {
  +            // REVISIT: Use cached copy. -Ac
  +            XMLElementDecl edecl = new XMLElementDecl();
  +            fSchemaGrammar.getElementDecl(elementIndex, edecl);
  +            while (ic != null){
  +                String icName = ic.getLocalName();
  +                if ( icName.equals(SchemaSymbols.ELT_KEY) ) { 
  +                    traverseKey(ic, edecl);
  +                }
  +                else if ( icName.equals(SchemaSymbols.ELT_KEYREF) ) {
  +                    traverseKeyRef(ic, edecl);
  +                }
  +                else if ( icName.equals(SchemaSymbols.ELT_UNIQUE) ) {
  +                    traverseUnique(ic, edecl);
  +                }
  +                else {
  +                    // should never get here
  +                    throw new RuntimeException("identity constraint must be one of "+
  +                                               "\""+SchemaSymbols.ELT_UNIQUE+"\", "+
  +                                               "\""+SchemaSymbols.ELT_KEY+"\", or "+
  +                                               "\""+SchemaSymbols.ELT_KEYREF+'"');
  +                }
  +                fSchemaGrammar.setElementDecl(elementIndex, edecl);
  +                ic = XUtil.getNextSiblingElement(ic, IDENTITY_CONSTRAINTS);
  +            }
  +        }
  +        
           return eltQName;
   
       }// end of method traverseElementDecl(Element)
   
  +    private void traverseUnique(Element uelem, XMLElementDecl edecl) 
  +        throws Exception {
  +
  +        // create identity constraint
  +        if (DEBUG_IDENTITY_CONSTRAINTS) {
  +            System.out.println("<IC>: traverseUnique(\""+uelem.getNodeName()+"\")");
  +        }
  +        Unique unique = new Unique();
  +
  +        // get selector and fields
  +        traverseIdentityConstraint(unique, uelem);
  +
  +        // add to element decl
  +        edecl.unique.addElement(unique);
  +
  +    } // traverseUnique(Element,XMLElementDecl)
  +
  +    private void traverseKey(Element kelem, XMLElementDecl edecl)
  +        throws Exception {
  +
  +        // create identity constraint
  +        String kname = kelem.getAttribute(SchemaSymbols.ATT_NAME);
  +        if (DEBUG_IDENTITY_CONSTRAINTS) {
  +            System.out.println("<IC>: traverseKey(\""+kelem.getNodeName()+"\") ["+kname+']');
  +        }
  +        Key key = new Key(kname);
  +
  +        // get selector and fields
  +        traverseIdentityConstraint(key, kelem);
  +
  +        // add to element decl
  +        edecl.key.addElement(key);
   
  +    } // traverseKey(Element,XMLElementDecl)
  +
  +    private void traverseKeyRef(Element krelem, XMLElementDecl edecl) 
  +        throws Exception {
  +
  +        // create identity constraint
  +        String krname = krelem.getAttribute(SchemaSymbols.ATT_NAME);
  +        if (DEBUG_IDENTITY_CONSTRAINTS) {
  +            System.out.println("<IC>: traverseKeyRef(\""+krelem.getNodeName()+"\") ["+krname+']');
  +        }
  +        KeyRef keyRef = new KeyRef(krname);
  +
  +        // add to element decl
  +        traverseIdentityConstraint(keyRef, krelem);
  +
  +        // add key reference to element decl
  +        edecl.keyRef.addElement(keyRef);
  +
  +    } // traverseKeyRef(Element,XMLElementDecl)
  +
  +    private void traverseIdentityConstraint(IdentityConstraint ic, 
  +                                            Element icelem) throws Exception {
  +        
  +        // get selector
  +        Element selem = XUtil.getFirstChildElement(icelem, SchemaSymbols.ELT_SELECTOR);
  +        String stext = CR_IMPL
  +                     ? selem.getAttribute(SchemaSymbols.ATT_XPATH) 
  +                     : XUtil.getChildText(selem);
  +        stext = stext.trim();
  +        try {
  +            // REVISIT: Get namespace context! -Ac
  +            Selector.XPath sxpath = new Selector.XPath(stext, fStringPool, null);
  +            Selector selector = new Selector(sxpath, ic);
  +            if (DEBUG_IDENTITY_CONSTRAINTS) {
  +                System.out.println("<IC>:   selector: "+selector);
  +            }
  +            ic.setSelector(selector);
  +        }
  +        catch (XPathException e) {
  +            // REVISIT: Add error message.
  +            throw new SAXException(e.getMessage());
  +        }
  +
  +        // get fields
  +        Element felem = XUtil.getNextSiblingElement(selem, SchemaSymbols.ELT_FIELD);
  +        while (felem != null) {
  +            String ftext = CR_IMPL
  +                         ? felem.getAttribute(SchemaSymbols.ATT_XPATH) 
  +                         : XUtil.getChildText(felem);
  +            ftext = ftext.trim();
  +            try {
  +                // REVISIT: Get namespace context! -Ac
  +                Field.XPath fxpath = new Field.XPath(ftext, fStringPool, null);
  +                // REVISIT: Get datatype validator. -Ac
  +                Field field = new Field(fxpath, null, ic);
  +                if (DEBUG_IDENTITY_CONSTRAINTS) {
  +                    System.out.println("<IC>:   field:    "+field);
  +                }
  +                ic.addField(field);
  +            }
  +            catch (XPathException e) {
  +                // REVISIT: Add error message.
  +                throw new SAXException(e.getMessage());
  +            }
  +            felem = XUtil.getNextSiblingElement(felem, SchemaSymbols.ELT_FIELD);
  +        }
  +
  +    } // traverseIdentityConstraint(IdentityConstraint,Element)
  +
       int getLocalPartIndex(String fullName){
           int colonAt = fullName.indexOf(":"); 
           String localpart = fullName;
  @@ -5966,10 +6077,7 @@
               System.exit(0);
           }
   
  -        DOMParser parser = new DOMParser() {
  -            public void ignorableWhitespace(char ch[], int start, int length) {}
  -            public void ignorableWhitespace(int dataIdx) {}
  -        };
  +        DOMParser parser = new IgnoreWhitespaceParser();
           parser.setEntityResolver( new Resolver() );
           parser.setErrorHandler(  new ErrorHandler() );
   
  @@ -6092,6 +6200,11 @@
           } // getLocationString(SAXParseException):String
       }
   
  +    static class IgnoreWhitespaceParser
  +        extends DOMParser {
  +        public void ignorableWhitespace(char ch[], int start, int length) {}
  +        public void ignorableWhitespace(int dataIdx) {}
  +    } // class IgnoreWhitespaceParser
   
   }
   
  
  
  
  1.3       +35 -0     xml-xerces/java/src/org/apache/xerces/validators/schema/XUtil.java
  
  Index: XUtil.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/validators/schema/XUtil.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- XUtil.java	2000/05/26 18:59:16	1.2
  +++ XUtil.java	2001/01/18 07:10:53	1.3
  @@ -427,4 +427,39 @@
   
       } // getNextSiblingElement(Node,String,String,String):Element
   
  +    /**
  +     * Returns the concatenated child text of the specified node.
  +     * This method only looks at the immediate children of type
  +     * <code>Node.TEXT_NODE</code> or the children of any child
  +     * node that is of type <code>Node.CDATA_SECTION_NODE</code> 
  +     * for the concatenation.
  +     *
  +     * @param node The node to look at.
  +     */
  +    public static String getChildText(Node node) {
  +
  +        // is there anything to do?
  +        if (node == null) {
  +            return null;
  +        }
  +
  +        // concatenate children text
  +        StringBuffer str = new StringBuffer();
  +        Node child = node.getFirstChild();
  +        while (child != null) {
  +            short type = child.getNodeType();
  +            if (type == Node.TEXT_NODE) {
  +                str.append(child.getNodeValue());
  +            }
  +            else if (type == Node.CDATA_SECTION_NODE) {
  +                str.append(getChildText(child));
  +            }
  +            child = child.getNextSibling();
  +        }
  +
  +        // return text value
  +        return str.toString();
  +
  +    } // getChildText(Node):String
  +
   } // class XUtil
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/Field.java
  
  Index: Field.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001 The Apache Software Foundation.  
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  import org.apache.xerces.utils.NamespacesScope;
  import org.apache.xerces.utils.StringPool;
  import org.apache.xerces.validators.datatype.DatatypeValidator;
  
  import org.xml.sax.SAXException;
  
  /**
   * Schema identity constraint field.
   *
   * @author Andy Clark, IBM
   * @version $Id: Field.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public class Field {
  
      //
      // Data
      //
  
      /** Field XPath. */
      private Field.XPath fXPath;
  
      /** Datatype. */
      private DatatypeValidator fDatatypeValidator;
  
      /** Identity constraint. */
      private IdentityConstraint fIdentityConstraint;
  
      //
      // Constructors
      //
  
      /** Constructs a selector. */
      public Field(Field.XPath xpath, DatatypeValidator datatypeValidator,
                   IdentityConstraint identityConstraint) {
          fXPath = xpath;
          fDatatypeValidator = datatypeValidator;
          fIdentityConstraint = identityConstraint;
      } // <init>(Field.XPath,DatatypeValidator,IdentityConstraint)
  
      //
      // Public methods
      //
  
      /** Returns the field XPath. */
      public org.apache.xerces.validators.schema.identity.XPath getXPath() {
          return fXPath;
      } // getXPath():org.apache.xerces.validators.schema.identity.XPath
  
      /** Returns the datatype validator. */
      public DatatypeValidator getDatatypeValidator() {
          return fDatatypeValidator;
      } // getDatatypeValidator():DatatypeValidator
  
      /** Returns the identity constraint. */
      public IdentityConstraint getIdentityConstraint() {
          return fIdentityConstraint;
      } // getIdentityConstraint():IdentityConstraint
  
      // factory method
  
      /** Creates a field matcher. */
      public XPathMatcher createMatcher(ValueStore store) {
          return new Field.Matcher(fXPath, store);
      } // createMatcher(ValueStore):XPathMatcher
  
      //
      // Object methods
      //
  
      /** Returns a string representation of this object. */
      public String toString() {
          return fXPath.toString();
      } // toString():String
  
      //
      // Classes
      //
  
      /**
       * Field XPath.
       *
       * @author Andy Clark, IBM
       */
      public static class XPath
          extends org.apache.xerces.validators.schema.identity.XPath {
  
          //
          // Constructors
          //
  
          /** Constructs a field XPath expression. */
          public XPath(String xpath, StringPool stringPool,
                       NamespacesScope context) throws XPathException {
              // NOTE: We have to prefix the field XPath with "./" in
              //       order to handle selectors such as "@attr" that 
              //       select the attribute because the fields could be
              //       relative to the selector element. -Ac
              super("./"+xpath, stringPool, context);
          } // <init>(String,StringPool,NamespacesScope)
  
      } // class XPath
  
      /**
       * Field matcher.
       *
       * @author Andy Clark, IBM
       */
      protected class Matcher
          extends XPathMatcher {
  
          //
          // Data
          //
  
          /** Value store for data values. */
          protected ValueStore fStore;
  
          //
          // Constructors
          //
  
          /** Constructs a field matcher. */
          public Matcher(Field.XPath xpath, ValueStore store) {
              super(xpath);
              fStore = store;
          } // <init>(Field.XPath,ValueStore)
  
          //
          // XPathHandler methods
          //
  
          /**
           * This method is called when the XPath handler matches the
           * XPath expression.
           */
          protected void matched(String content) throws SAXException {
              fStore.addValue(content, Field.this);
          } // matched(String)
  
      } // class Matcher
  
  } // class Field
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/FieldActivator.java
  
  Index: FieldActivator.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001 The Apache Software Foundation.  
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  import org.xml.sax.SAXException;
  
  /**
   * Interface for a field activator. The field activator is responsible
   * for activating fields within a specific scope; the caller merely
   * requests the fields to be activated.
   *
   * @author Andy Clark, IBM
   *
   * @version $Id: FieldActivator.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public interface FieldActivator {
      
      //
      // FieldActivator methods
      //
  
      /** 
       * Request to activate the specified field. This method returns the
       * matcher for the field.
       *
       * @param field The field to activate.
       */
      public XPathMatcher activateField(Field field) throws SAXException;
  
  } // interface FieldActivator
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/IdentityConstraint.java
  
  Index: IdentityConstraint.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001 The Apache Software Foundation.  
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  /**
   * Base class of Schema identity constraint.
   *
   * @author Andy Clark, IBM
   * @version $Id: IdentityConstraint.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public abstract class IdentityConstraint {
  
      //
      // Data
      //
  
      /** Selector. */
      private Selector fSelector;
  
      /** Field count. */
      private int fFieldCount;
  
      /** Fields. */
      private Field[] fFields;
  
      //
      // Constructors
      //
  
      /** Default constructor. */
      protected IdentityConstraint() {
      } // <init>()
  
      //
      // Public methods
      //
  
      /** Sets the selector. */
      public void setSelector(Selector selector) {
          fSelector = selector;
      } // setSelector(Selector)
  
      /** Returns the selector. */
      public Selector getSelector() {
          return fSelector;
      } // getSelector():Selector
  
      /** Adds a field. */
      public void addField(Field field) {
          try {
              fFields[fFieldCount] = null;
          }
          catch (NullPointerException e) {
              fFields = new Field[4];
          }
          catch (ArrayIndexOutOfBoundsException e) {
              Field[] newfields = new Field[fFields.length * 2];
              System.arraycopy(fFields, 0, newfields, 0, fFields.length);
              fFields = newfields;
          }
          fFields[fFieldCount++] = field;
      } // addField(Field)
  
      /** Returns the field count. */
      public int getFieldCount() {
          return fFieldCount;
      } // getFieldCount():int
  
      /** Returns the field at the specified index. */
      public Field getFieldAt(int index) {
          return fFields[index];
      } // getFieldAt(int):Field
  
  } // class IdentityConstraint
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/Key.java
  
  Index: Key.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  /**
   * Schema key identity constraint.
   *
   * @author Andy Clark, IBM
   * @version $Id: Key.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public class Key 
      extends IdentityConstraint {
  
      //
      // Data
      //
  
      /** Name. */
      private String fName;
  
      //
      // Constructors
      //
  
      /** Constructs a key with the specified name. */
      public Key(String name) {
          fName = name;
      } // <init>(String)
  
      //
      // Public methods
      //
  
      /** Returns the name. */
      public String getName() {
          return fName;
      } // getName():String
  
  } // class Key
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/KeyRef.java
  
  Index: KeyRef.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  /**
   * Schema key reference identity constraint.
   *
   * @author Andy Clark, IBM
   * @version $Id: KeyRef.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public class KeyRef 
      extends IdentityConstraint {
  
      //
      // Data
      //
  
      /** Key name. */
      private String fName;
  
      //
      // Constructors
      //
  
      /** Constructs a keyref with the specified name. */
      public KeyRef(String name) {
          fName = name;
      } // <init>(String)
  
      //
      // Public methods
      //
  
      /** Returns the name. */
      public String getName() {
          return fName;
      } // getName():String
  
  } // class KeyRef
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/Makefile
  
  Index: Makefile
  ===================================================================
  # Makefile for directory ./org/apache/xerces/validators/schema/identity
  
  TARGETS=\
  	Field.class\
  	FieldActivator.class\
  	IdentityConstraint.class\
  	Key.class\
  	KeyRef.class\
  	Selector.class\
  	Unique.class\
  	ValueStore.class\
  	XPath.class\
  	XPathException.class\
  	XPathMatcher.class
  
  DIRS = 
  
  TOP = ../../../../../../..
  include $(TOP)/src/Makefile.incl
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/Selector.java
  
  Index: Selector.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001 The Apache Software Foundation.  
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  import org.apache.xerces.framework.XMLAttrList;
  import org.apache.xerces.utils.NamespacesScope;
  import org.apache.xerces.utils.QName;
  import org.apache.xerces.utils.StringPool;
  
  import org.xml.sax.SAXException;
  
  /**
   * Schema identity constraint selector.
   *
   * @author Andy Clark, IBM
   * @version $Id: Selector.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public class Selector {
  
      //
      // Data
      //
  
      /** XPath. */
      private Selector.XPath fXPath;
  
      /** Identity constraint. */
      private IdentityConstraint fIdentityConstraint;
  
      //
      // Constructors
      //
  
      /** Constructs a selector. */
      public Selector(Selector.XPath xpath, 
                      IdentityConstraint identityConstraint) {
          fXPath = xpath;
          fIdentityConstraint = identityConstraint;
      } // <init>(Selector.XPath,IdentityConstraint)
  
      //
      // Public methods
      //
  
      /** Returns the selector XPath. */
      public org.apache.xerces.validators.schema.identity.XPath getXPath() {
          return fXPath;
      } // getXPath():org.apache.xerces.impl.xpath.XPath
  
      /** Returns the identity constraint. */
      public IdentityConstraint getIdentityConstraint() {
          return fIdentityConstraint;
      } // getIdentityConstraint():IdentityConstraint
  
      // factory method
  
      /** Creates a selector matcher. */
      public XPathMatcher createMatcher(FieldActivator activator) {
          return new Selector.Matcher(fXPath, activator);
      } // createMatcher(FieldActivator):XPathMatcher
  
      //
      // Object methods
      //
  
      /** Returns a string representation of this object. */
      public String toString() {
          return fXPath.toString();
      } // toString():String
  
      //
      // Classes
      //
  
      /**
       * Schema identity constraint selector XPath expression.
       *
       * @author Andy Clark, IBM
       * @version $Id: Selector.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
       */
      public static class XPath
          extends org.apache.xerces.validators.schema.identity.XPath {
      
          //
          // Constructors
          //
      
          /** Constructs a selector XPath expression. */
          public XPath(String xpath, StringPool stringPool, 
                       NamespacesScope context) throws XPathException {
              // NOTE: We have to prefix the selector XPath with "./" in
              //       order to handle selectors such as "." that select
              //       the element container because the fields could be
              //       relative to that element. -Ac
              super("./"+xpath, stringPool, context);
      
              // verify that an attribute is not selected
              XPath.Axis axis = fLocationPath.steps[fLocationPath.steps.length-1].axis;
              if (axis.type == XPath.Axis.ATTRIBUTE) {
                  throw new XPathException("selectors cannot select attributes");
              }
      
          } // <init>(String,StringPool,NamespacesScope)
      
      } // class XPath
  
      /**
       * Selector matcher.
       *
       * @author Andy Clark, IBM
       */
      protected class Matcher
          extends XPathMatcher {
      
          //
          // Data
          //
  
          /** Field activator. */
          private FieldActivator fFieldActivator;
  
          //
          // Constructors
          //
  
          /** Constructs a selector matcher. */
          public Matcher(Selector.XPath xpath, FieldActivator activator) {
              super(xpath);
              fFieldActivator = activator;
          } // <init>(Selector.XPath,FieldActivator)
  
          //
          // XMLDocumentFragmentHandler methods
          //
      
          /**
           * The start of an element. If the document specifies the start element
           * by using an empty tag, then the startElement method will immediately
           * be followed by the endElement method, with no intervening methods.
           * 
           * @param element    The name of the element.
           * @param attributes The element attributes.
           *
           * @throws SAXException Thrown by handler to signal an error.
           */
          public void startElement(QName element, XMLAttrList attributes, 
                                   int handle) throws Exception {
              super.startElement(element, attributes, handle);
      
              // activate the fields, if selector is matched
              if (isMatched()) {
                  int count = fIdentityConstraint.getFieldCount();
                  for (int i = 0; i < count; i++) {
                      Field field = fIdentityConstraint.getFieldAt(i);
                      XPathMatcher matcher = fFieldActivator.activateField(field);
                      // NOTE: We have to notify the field of *this* element
                      //       to handle fields like "@foo" which is relative
                      //       to the selector matched. -Ac
                      matcher.startElement(element, attributes, handle);
                  }
              }
      
          } // startElement(QName,XMLAttrList,int)
      
      } // class Matcher
  
  } // class Selector
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/Unique.java
  
  Index: Unique.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  /**
   * Schema unique identity constraint.
   *
   * @author Andy Clark, IBM
   * @version $Id: Unique.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public class Unique 
      extends IdentityConstraint {
  } // class Unique
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/ValueStore.java
  
  Index: ValueStore.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001 The Apache Software Foundation.  
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  import org.xml.sax.SAXException;
  
  /**
   * Interface for storing values associated to an identity constraint. 
   * Each value stored corresponds to a field declared for the identity
   * constraint. One instance of an object implementing this interface
   * is created for each identity constraint per element declaration in
   * the instance document to store the information for this identity
   * constraint.
   * <p>
   * <strong>Note:</strong> The component performing identity constraint
   * collection and validation is responsible for providing an 
   * implementation of this interface. The component is also responsible
   * for performing the necessary checks required by each type of identity
   * constraint.
   *
   * @author Andy Clark, IBM
   *
   * @version $Id: ValueStore.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public interface ValueStore {
      
      //
      // ValueStore methods
      //
  
      /** 
       * Adds the specified value to the value store.
       *
       * @param value The value to add.
       * @param field The field associated to the value. This reference
       *              is used to ensure that each field only adds a value
       *              once within a selection scope.
       */
      public void addValue(String value, Field field) throws SAXException;
  
  } // interface ValueStore
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/XPath.java
  
  Index: XPath.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2000,2001 The Apache Software Foundation.  
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  import org.apache.xerces.utils.NamespacesScope;
  import org.apache.xerces.utils.QName;
  import org.apache.xerces.utils.StringPool;
  
  /**
   * Bare minimum XPath parser.
   *
   * @author Andy Clark, IBM
   * @version $Id: XPath.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public class XPath {
  
      //
      // Constants
      //
  
      private static final boolean DEBUG_ALL = false;
  
      private static final boolean DEBUG_XPATH_PARSE = DEBUG_ALL || false;
  
      private static final boolean DEBUG_ANY = DEBUG_XPATH_PARSE;
  
      //
      // Data
      //
  
      /** Expression. */
      protected String fExpression;
  
      /** Location path. */
      protected LocationPath fLocationPath;
  
      // Xerces 1.x framework
  
      /** String pool. */
      protected StringPool fStringPool;
  
      //
      // Constructors
      //
  
      /** Constructs an XPath object from the specified expression. */
      public XPath(String xpath, StringPool stringPool, NamespacesScope context) 
          throws XPathException {
          fExpression = xpath;
          fStringPool = stringPool;
          //System.out.println("@@@ fStringPool:     "+fStringPool);
          parseExpression(context);
      } // <init>(String,StringPool,NamespacesScope)
  
      //
      // Public methods
      //
  
      /** Returns a representation of the location path for this XPath. */
      public LocationPath getLocationPath() {
          return (LocationPath)fLocationPath.clone();
      } // getLocationPath(LocationPath)
  
      //
      // Object methods
      //
  
      /** Returns a string representation of this object. */
      public String toString() {
          return fLocationPath.toString();
      } // toString():String
  
      //
      // Private methods
      //
  
      /**
       * This method is implemented by using the XPathExprScanner and
       * examining the list of tokens that it returns.
       */
      private void parseExpression(final NamespacesScope context) 
          throws XPathException {
  
          // tokens
          final XPath.Tokens xtokens = new XPath.Tokens(fStringPool);
  
          // scanner
          XPath.Scanner scanner = new XPath.Scanner(fStringPool) {
              protected void addToken(XPath.Tokens tokens, int token) 
                  throws XPathException {
                  if (
                      token == XPath.Tokens.EXPRTOKEN_ATSIGN ||
                      token == XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE ||
                      token == XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD ||
                      //token == XPath.Tokens.EXPRTOKEN_AXISNAME_SELF ||
                      token == XPath.Tokens.EXPRTOKEN_DOUBLE_COLON ||
                      //token == XPath.Tokens.EXPRTOKEN_NAMETEST_ANY ||
                      token == XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME ||
                      //token == XPath.Tokens.EXPRTOKEN_NODETYPE_NODE ||
                      token == XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH ||
                      token == XPath.Tokens.EXPRTOKEN_PERIOD
                      ) {
                      super.addToken(tokens, token);
                      return;
                  }
                  StringBuffer str = new StringBuffer();
                  str.append("token not supported: ");
                  String tokenName = tokens.getTokenName(token);
                  if (tokenName != null) {
                      str.append('"');
                      str.append(tokenName);
                      str.append('"');
                  }
                  else {
                      str.append('(');
                      str.append(token);
                      str.append(')');
                  }
                  String message = str.toString();
                  throw new XPathException(message);
              }
          };
  
          int length = fExpression.length();
          /***/
          boolean success = scanner.scanExpr(fStringPool, 
                                             xtokens, fExpression, 0, length);
          //fTokens.dumpTokens();
          java.util.Vector stepsVector = new java.util.Vector();
          int tokenCount = xtokens.getTokenCount();
          for (int i = 0; i < tokenCount; i++) {
              int token = xtokens.getToken(i);
              switch (token) {
                  case XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE: {
                      // consume "::" token and drop through
                      i++;
                  }
                  case XPath.Tokens.EXPRTOKEN_ATSIGN: {
                      // consume QName token
                      if (i == tokenCount - 1) {
                          throw new XPathException("missing attribute name");
                      }
                      token = xtokens.getToken(++i);
                      if (token != XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME) {
                          throw new XPathException("expected "+xtokens.getTokenName(XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME)+
                                                   ", found "+xtokens.getTokenName(token));
                      }
                      token = xtokens.getToken(++i);
                      int prefix = xtokens.getTokenString(token);
                      int uri = context != null
                              ? context.getNamespaceForPrefix(prefix) : -1;
                      if (prefix != -1 && context != null && uri == -1) {
                          throw new XPathException("prefix "+fStringPool.toString(prefix)+" not bound to namespace URI");
                      }
                      token = xtokens.getToken(++i);
                      int localpart = xtokens.getTokenString(token);
                      int rawname = prefix != -1
                                  ? fStringPool.addSymbol(fStringPool.toString(prefix) + ':' + fStringPool.toString(localpart)) 
                                  : localpart;
                      
                      // build step
                      Axis axis = new Axis(Axis.ATTRIBUTE);
                      NodeTest nodeTest = new NodeTest(new QName(prefix, localpart, rawname, uri));
                      Step step = new Step(axis, nodeTest);
                      stepsVector.addElement(step);
                      break;
                  }
                  /***
                  case XPath.Tokens.EXPRTOKEN_AXISNAME_SELF: {
                      break;
                  }
                  /***/
                  case XPath.Tokens.EXPRTOKEN_DOUBLE_COLON: {
                      // should never have a bare double colon
                      throw new XPathException("Not allowed to have double colon here");
                  }
                  /***
                  case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY: {
                      break;
                  }
                  /***/
                  case XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD: {
                      // consume "::" token and drop through
                      i++;
                  }
                  case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME: {
                      // consume QName token
                      token = xtokens.getToken(++i);
                      int prefix = xtokens.getTokenString(token);
                      int uri = context != null
                              ? context.getNamespaceForPrefix(prefix) : -1;
                      if (prefix != -1 && context != null && uri == -1) {
                          throw new XPathException("prefix "+fStringPool.toString(prefix)+" not bound to namespace URI");
                      }
                      token = xtokens.getToken(++i);
                      int localpart = xtokens.getTokenString(token);
                      int rawname = prefix != -1
                                  ? fStringPool.addSymbol(fStringPool.toString(prefix) + ':' + fStringPool.toString(localpart)) 
                                  : localpart;
                      
                      // build step
                      Axis axis = new Axis(Axis.CHILD);
                      NodeTest nodeTest = new NodeTest(new QName(prefix, localpart, rawname, uri));
                      Step step = new Step(axis, nodeTest);
                      stepsVector.addElement(step);
                      break;
                  }
                  /***
                  case XPath.Tokens.EXPRTOKEN_NODETYPE_NODE: {
                      break;
                  }
                  /***/
                  case XPath.Tokens.EXPRTOKEN_PERIOD: {
                      // build step
                      Axis axis = new Axis(Axis.SELF);
                      NodeTest nodeTest = new NodeTest(NodeTest.NODE);
                      Step step = new Step(axis, nodeTest);
                      stepsVector.addElement(step);
                      break;
                  }
                  case XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH: {
                      // keep on truckin'
                      if (i == 0) {
                          throw new XPathException("not allowed to select the root");
                      }
                      if (i == tokenCount - 1) {
                          throw new XPathException("expected step following '/'");
                      }
                      break;
                  }
              }
          }
  
          int size = stepsVector.size();
          if (size == 0) {
              throw new XPathException("empty xpath expression");
          }
          Step[] steps = new Step[size];
          stepsVector.copyInto(steps);
  
          // save location path
          fLocationPath = new LocationPath(steps);
          if (DEBUG_XPATH_PARSE) {
              System.out.println(">>> "+fLocationPath);
          }
  
      } // parseExpression(SymbolTable,NamespaceContext)
  
      //
      // Classes
      //
  
      // location path information
  
      /**
       * A location path representation for an XPath expression.
       *
       * @author Andy Clark, IBM
       */
      public class LocationPath 
          implements Cloneable {
  
          //
          // Data
          //
          
          /** List of steps. */
          public Step[] steps;
  
          //
          // Constructors
          //
  
          /** Creates a location path from a series of steps. */
          public LocationPath(Step[] steps) {
              this.steps = steps;
          } // <init>(Step[])
  
          /** Copy constructor. */
          protected LocationPath(LocationPath path) {
              steps = new Step[path.steps.length];
              for (int i = 0; i < steps.length; i++) {
                  steps[i] = (Step)path.steps[i].clone();
              }
          } // <init>(LocationPath)
  
          //
          // Object methods
          //
  
          /** Returns a string representation of this object. */
          public String toString() {
              StringBuffer str = new StringBuffer();
              for (int i = 0; i < steps.length; i++) {
                  if (i > 0) {
                      str.append('/');
                  }
                  str.append(steps[i].toString());
              }
              return str.toString();
          } // toString():String
  
          /** Returns a clone of this object. */
          public Object clone() {
              return new LocationPath(this);
          } // clone():Object
  
      } // class LocationPath
  
      /**
       * A location path step comprised of an axis and node test.
       *
       * @author Andy Clark, IBM
       */
      public class Step 
          implements Cloneable {
  
          //
          // Data
          //
  
          /** Axis. */
          public Axis axis;
  
          /** Node test. */
          public NodeTest nodeTest;
          
          //
          // Constructors
          //
  
          /** Constructs a step from an axis and node test. */
          public Step(Axis axis, NodeTest nodeTest) {
              this.axis = axis;
              this.nodeTest = nodeTest;
          } // <init>(Axis,NodeTest)
  
          /** Copy constructor. */
          protected Step(Step step) {
              axis = (Axis)step.axis.clone();
              nodeTest = (NodeTest)step.nodeTest.clone();
          } // <init>(Step)
  
          //
          // Object methods
          //
  
          /** Returns a string representation of this object. */
          public String toString() {
              return axis.toString() + "::" + nodeTest.toString();
          } // toString():String
  
          /** Returns a clone of this object. */
          public Object clone() {
              return new Step(this);
          } // clone():Object
  
      } // class Step
  
      /**
       * Axis.
       *
       * @author Andy Clark, IBM
       */
      public class Axis 
          implements Cloneable {
  
          //
          // Constants
          //
  
          /** Type: child. */
          public static final short CHILD = 1;
  
          /** Type: attribute. */
          public static final short ATTRIBUTE = 2;
  
          /** Type: self. */
          public static final short SELF = 3;
  
          //
          // Data
          //
  
          /** Axis type. */
          public short type;
  
          //
          // Constructors
          //
  
          /** Constructs an axis with the specified type. */
          public Axis(short type) {
              this.type = type;
          } // <init>(short)
  
          /** Copy constructor. */
          protected Axis(Axis axis) {
              type = axis.type;
          } // <init>(Axis)
  
          //
          // Object methods
          //
  
          /** Returns a string representation of this object. */
          public String toString() {
              switch (type) {
                  case CHILD: return "child";
                  case ATTRIBUTE: return "attribute";
                  case SELF: return "self";
              }
              return "???";
          } // toString():String
  
          /** Returns a clone of this object. */
          public Object clone() {
              return new Axis(this);
          } // clone():Object
  
      } // class Axis
  
      /**
       * Node test.
       *
       * @author Andy Clark, IBM
       */
      public class NodeTest 
          implements Cloneable {
  
          //
          // Constants
          //
  
          /** Type: qualified name. */
          public static final short QNAME = 1;
  
          /** Type: wildcard. */
          public static final short WILDCARD = 2;
  
          /** Type: node. */
          public static final short NODE = 3;
  
          //
          // Data
          //
  
          /** Node test type. */
          public short type;
  
          /** Node qualified name. */
          public final QName name = new QName();
  
          //
          // Constructors
          //
  
          /** Constructs a node test of type WILDCARD or NODE. */
          public NodeTest(short type) {
              this.type = type;
          } // <init>(int)
  
          /** Constructs a node test of type QName. */
          public NodeTest(QName name) {
              this.type = QNAME;
              this.name.setValues(name);
          } // <init>(QName)
  
          /** Copy constructor. */
          public NodeTest(NodeTest nodeTest) {
              type = nodeTest.type;
              name.setValues(nodeTest.name);
          } // <init>(NodeTest)
  
          //
          // Object methods
          //
  
          /** Returns a string representation of this object. */
          public String toString() {
  
              switch (type) {
                  case QNAME: {
                      if (name.prefix != -1) {
                          if (name.uri == -1) {
                              return fStringPool.toString(name.prefix) + ':' + fStringPool.toString(name.localpart);
                          }
                          return "{" + fStringPool.toString(name.uri) + '}' + fStringPool.toString(name.prefix) + ':' + fStringPool.toString(name.localpart);
                      }
                      return fStringPool.toString(name.localpart);
                  }
                  case WILDCARD: {
                      return "*";
                  }
                  case NODE: {
                      return "node()";
                  }
              }
              return "???";
  
          } // toString():String
  
          /** Returns a clone of this object. */
          public Object clone() {
              return new NodeTest(this);
          } // clone():Object
  
      } // class NodeTest
  
      // xpath implementation
  
      // NOTE: The XPath implementation classes are kept internal because
      //       this implementation is just a temporary hack until a better
      //       and/or more appropriate implementation can be written.
      //       keeping the code in separate source files would "muddy" the
      //       CVS directory when it's not needed. -Ac
  
      /**
       * @author Glenn Marcy, IBM
       * @author Andy Clark, IBM
       *
       * @version $Id: XPath.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
       */
      private static final class Tokens {
      
          static final boolean DUMP_TOKENS = false;
      
          /**
           * [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
           *                  | NameTest | NodeType | Operator | FunctionName
           *                  | AxisName | Literal | Number | VariableReference
           */
          public static final int
              EXPRTOKEN_OPEN_PAREN                    = -1000,
              EXPRTOKEN_CLOSE_PAREN                   = -1001,
              EXPRTOKEN_OPEN_BRACKET                  = -1002,
              EXPRTOKEN_CLOSE_BRACKET                 = -1003,
              EXPRTOKEN_PERIOD                        = -1004,
              EXPRTOKEN_DOUBLE_PERIOD                 = -1005,
              EXPRTOKEN_ATSIGN                        = -1006,
              EXPRTOKEN_COMMA                         = -1007,
              EXPRTOKEN_DOUBLE_COLON                  = -1008,
              //
              // [37] NameTest ::= '*' | NCName ':' '*' | QName
              //
              // followed by symbol handle of NCName or QName
              //
              EXPRTOKEN_NAMETEST_ANY                  = -1009,
              EXPRTOKEN_NAMETEST_NAMESPACE            = -1010,
              EXPRTOKEN_NAMETEST_QNAME                = -1011,
              //
              // [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
              //
              EXPRTOKEN_NODETYPE_COMMENT              = -1012,
              EXPRTOKEN_NODETYPE_TEXT                 = -1013,
              EXPRTOKEN_NODETYPE_PI                   = -1014,
              EXPRTOKEN_NODETYPE_NODE                 = -1015,
              //
              // [32] Operator ::= OperatorName
              //                 | MultiplyOperator
              //                 | '/' | '//' | '|' | '+' | '-' | '=' | '!=' | '<' | '<=' | '>' | '>='
              // [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
              // [34] MultiplyOperator ::= '*'
              //
              EXPRTOKEN_OPERATOR_AND                  = -1016,
              EXPRTOKEN_OPERATOR_OR                   = -1017,
              EXPRTOKEN_OPERATOR_MOD                  = -1018,
              EXPRTOKEN_OPERATOR_DIV                  = -1019,
              EXPRTOKEN_OPERATOR_MULT                 = -1020,
              EXPRTOKEN_OPERATOR_SLASH                = -1021,
              EXPRTOKEN_OPERATOR_DOUBLE_SLASH         = -1022,
              EXPRTOKEN_OPERATOR_UNION                = -1023,
              EXPRTOKEN_OPERATOR_PLUS                 = -1024,
              EXPRTOKEN_OPERATOR_MINUS                = -1025,
              EXPRTOKEN_OPERATOR_EQUAL                = -1026,
              EXPRTOKEN_OPERATOR_NOT_EQUAL            = -1027,
              EXPRTOKEN_OPERATOR_LESS                 = -1028,
              EXPRTOKEN_OPERATOR_LESS_EQUAL           = -1029,
              EXPRTOKEN_OPERATOR_GREATER              = -1030,
              EXPRTOKEN_OPERATOR_GREATER_EQUAL        = -1031,
      
              EXPRTOKEN_FIRST_OPERATOR                = EXPRTOKEN_OPERATOR_AND,
              EXPRTOKEN_LAST_OPERATOR                 = EXPRTOKEN_OPERATOR_GREATER_EQUAL,
              //
              // [35] FunctionName ::= QName - NodeType
              //
              // followed by symbol handle
              //
              EXPRTOKEN_FUNCTION_NAME                 = -1032,
              //
              // [6] AxisName ::= 'ancestor' | 'ancestor-or-self'
              //                | 'attribute'
              //                | 'child'
              //                | 'descendant' | 'descendant-or-self'
              //                | 'following' | 'following-sibling'
              //                | 'namespace'
              //                | 'parent'
              //                | 'preceding' | 'preceding-sibling'
              //                | 'self'
              //
              EXPRTOKEN_AXISNAME_ANCESTOR             = -1033,
              EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF     = -1034,
              EXPRTOKEN_AXISNAME_ATTRIBUTE            = -1035,
              EXPRTOKEN_AXISNAME_CHILD                = -1036,
              EXPRTOKEN_AXISNAME_DESCENDANT           = -1037,
              EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF   = -1038,
              EXPRTOKEN_AXISNAME_FOLLOWING            = -1039,
              EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING    = -1040,
              EXPRTOKEN_AXISNAME_NAMESPACE            = -1041,
              EXPRTOKEN_AXISNAME_PARENT               = -1042,
              EXPRTOKEN_AXISNAME_PRECEDING            = -1043,
              EXPRTOKEN_AXISNAME_PRECEDING_SIBLING    = -1044,
              EXPRTOKEN_AXISNAME_SELF                 = -1045,
              //
              // [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
              //
              // followed by symbol handle for literal
              //
              EXPRTOKEN_LITERAL                       = -1046,
              //
              // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
              // [31] Digits ::= [0-9]+
              //
              // followed by number handle
              //
              EXPRTOKEN_NUMBER                        = -1047,
              //
              // [36] VariableReference ::= '$' QName
              //
              // followed by symbol handle for QName
              //
              EXPRTOKEN_VARIABLE_REFERENCE            = -1048;
      
          /**
           *
           */
          private static final int INITIAL_TOKEN_COUNT = 1 << 8;
          private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
          private int fTokenCount = 0;    // for writing
      
          private StringPool fStringPool;
      
          // REVISIT: Code something better here. -Ac
          private java.util.Hashtable fSymbolMapping = new java.util.Hashtable();
      
          // REVISIT: Code something better here. -Ac
          private java.util.Hashtable fTokenNames = new java.util.Hashtable();
      
          //
          // Constructors
          //
      
          /***
          public XPath.Tokens(SymbolTable symbolTable) {
              fSymbolTable = symbolTable;
          }
          /***/
          public Tokens(StringPool stringPool) {
              fStringPool = stringPool;
              /***
              final String[] symbols = {
                  "ancestor",     "ancestor-or-self",     "attribute",
                  "child",        "descendant",           "descendant-or-self",
                  "following",    "following-sibling",    "namespace",
                  "parent",       "preceding",            "preceding-sibling",
                  "self",
              };
              for (int i = 0; i < symbols.length; i++) {
                  fSymbolMapping.put(fSymbolTable.addSymbol(symbols[i]), new Integer(i));
              }
              /***/
              fTokenNames.put(new Integer(EXPRTOKEN_OPEN_PAREN), "EXPRTOKEN_OPEN_PAREN");
              fTokenNames.put(new Integer(EXPRTOKEN_CLOSE_PAREN), "EXPRTOKEN_CLOSE_PAREN");
              fTokenNames.put(new Integer(EXPRTOKEN_OPEN_BRACKET), "EXPRTOKEN_OPEN_BRACKET");
              fTokenNames.put(new Integer(EXPRTOKEN_CLOSE_BRACKET), "EXPRTOKEN_CLOSE_BRACKET");
              fTokenNames.put(new Integer(EXPRTOKEN_PERIOD), "EXPRTOKEN_PERIOD");
              fTokenNames.put(new Integer(EXPRTOKEN_DOUBLE_PERIOD), "EXPRTOKEN_DOUBLE_PERIOD");
              fTokenNames.put(new Integer(EXPRTOKEN_ATSIGN), "EXPRTOKEN_ATSIGN");
              fTokenNames.put(new Integer(EXPRTOKEN_COMMA), "EXPRTOKEN_COMMA");
              fTokenNames.put(new Integer(EXPRTOKEN_DOUBLE_COLON), "EXPRTOKEN_DOUBLE_COLON");
              fTokenNames.put(new Integer(EXPRTOKEN_NAMETEST_ANY), "EXPRTOKEN_NAMETEST_ANY");
              fTokenNames.put(new Integer(EXPRTOKEN_NAMETEST_NAMESPACE), "EXPRTOKEN_NAMETEST_NAMESPACE");
              fTokenNames.put(new Integer(EXPRTOKEN_NAMETEST_QNAME), "EXPRTOKEN_NAMETEST_QNAME");
              fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_COMMENT), "EXPRTOKEN_NODETYPE_COMMENT");
              fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_TEXT), "EXPRTOKEN_NODETYPE_TEXT");
              fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_PI), "EXPRTOKEN_NODETYPE_PI");
              fTokenNames.put(new Integer(EXPRTOKEN_NODETYPE_NODE), "EXPRTOKEN_NODETYPE_NODE");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_AND), "EXPRTOKEN_OPERATOR_AND");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_OR), "EXPRTOKEN_OPERATOR_OR");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_MOD), "EXPRTOKEN_OPERATOR_MOD");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_DIV), "EXPRTOKEN_OPERATOR_DIV");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_MULT), "EXPRTOKEN_OPERATOR_MULT");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_SLASH), "EXPRTOKEN_OPERATOR_SLASH");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_DOUBLE_SLASH), "EXPRTOKEN_OPERATOR_DOUBLE_SLASH");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_UNION), "EXPRTOKEN_OPERATOR_UNION");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_PLUS), "EXPRTOKEN_OPERATOR_PLUS");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_MINUS), "EXPRTOKEN_OPERATOR_MINUS");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_EQUAL), "EXPRTOKEN_OPERATOR_EQUAL");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_NOT_EQUAL), "EXPRTOKEN_OPERATOR_NOT_EQUAL");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_LESS), "EXPRTOKEN_OPERATOR_LESS");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_LESS_EQUAL), "EXPRTOKEN_OPERATOR_LESS_EQUAL");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_GREATER), "EXPRTOKEN_OPERATOR_GREATER");
              fTokenNames.put(new Integer(EXPRTOKEN_OPERATOR_GREATER_EQUAL), "EXPRTOKEN_OPERATOR_GREATER_EQUAL");
              fTokenNames.put(new Integer(EXPRTOKEN_FUNCTION_NAME), "EXPRTOKEN_FUNCTION_NAME");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_ANCESTOR), "EXPRTOKEN_AXISNAME_ANCESTOR");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF), "EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_ATTRIBUTE), "EXPRTOKEN_AXISNAME_ATTRIBUTE");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_CHILD), "EXPRTOKEN_AXISNAME_CHILD");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_DESCENDANT), "EXPRTOKEN_AXISNAME_DESCENDANT");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF), "EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_FOLLOWING), "EXPRTOKEN_AXISNAME_FOLLOWING");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING), "EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_NAMESPACE), "EXPRTOKEN_AXISNAME_NAMESPACE");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_PARENT), "EXPRTOKEN_AXISNAME_PARENT");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_PRECEDING), "EXPRTOKEN_AXISNAME_PRECEDING");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_PRECEDING_SIBLING), "EXPRTOKEN_AXISNAME_PRECEDING_SIBLING");
              fTokenNames.put(new Integer(EXPRTOKEN_AXISNAME_SELF), "EXPRTOKEN_AXISNAME_SELF");
              fTokenNames.put(new Integer(EXPRTOKEN_LITERAL), "EXPRTOKEN_LITERAL");
              fTokenNames.put(new Integer(EXPRTOKEN_NUMBER), "EXPRTOKEN_NUMBER");
              fTokenNames.put(new Integer(EXPRTOKEN_VARIABLE_REFERENCE), "EXPRTOKEN_VARIABLE_REFERENCE");
          }
          /***/
          
          //
          // Public methods
          //
      
          /***
          public int addSymbol(byte[] data, int offset, int length, EncodingMap encoding) {
              //return fSymbolTable.addSymbol(data, offset, length, encoding);
              return fSymbolTable.addSymbol(new String(data, offset, length));
          }
          /***/
      
          public String getTokenName(int token) { 
              return (String)fTokenNames.get(new Integer(token));
          }
      
          public int getTokenString(int token) {
              return token;
          }
      
          public void addToken(int token) {
              try {
                  fTokens[fTokenCount] = token;
              } catch (ArrayIndexOutOfBoundsException ex) {
                  int[] oldList = fTokens;
                  fTokens = new int[fTokenCount << 1];
                  System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
                  fTokens[fTokenCount] = token;
              }
              fTokenCount++;
          }
          public int getTokenCount() {
              return fTokenCount;
          }
          public int getToken(int tokenIndex) {
              return fTokens[tokenIndex];
          }
          public void dumpTokens() {
              //if (DUMP_TOKENS) {
                  for (int i = 0; i < fTokenCount; i++) {
                      switch (fTokens[i]) {
                      case EXPRTOKEN_OPEN_PAREN:
                          System.out.print("<OPEN_PAREN/>");
                          break;
                      case EXPRTOKEN_CLOSE_PAREN:
                          System.out.print("<CLOSE_PAREN/>");
                          break;
                      case EXPRTOKEN_OPEN_BRACKET:
                          System.out.print("<OPEN_BRACKET/>");
                          break;
                      case EXPRTOKEN_CLOSE_BRACKET:
                          System.out.print("<CLOSE_BRACKET/>");
                          break;
                      case EXPRTOKEN_PERIOD:
                          System.out.print("<PERIOD/>");
                          break;
                      case EXPRTOKEN_DOUBLE_PERIOD:
                          System.out.print("<DOUBLE_PERIOD/>");
                          break;
                      case EXPRTOKEN_ATSIGN:
                          System.out.print("<ATSIGN/>");
                          break;
                      case EXPRTOKEN_COMMA:
                          System.out.print("<COMMA/>");
                          break;
                      case EXPRTOKEN_DOUBLE_COLON:
                          System.out.print("<DOUBLE_COLON/>");
                          break;
                      case EXPRTOKEN_NAMETEST_ANY:
                          System.out.print("<NAMETEST_ANY/>");
                          break;
                      case EXPRTOKEN_NAMETEST_NAMESPACE:
                          System.out.print("<NAMETEST_NAMESPACE");
                          /***
                          System.out.print(" prefix=\"" + fSymbolTable.toString(fTokens[++i]) + "\"");
                          /***/
                          System.out.print(" prefix=\"" + getTokenString(fTokens[++i]) + "\"");
                          /***/
                          System.out.print("/>");
                          break;
                      case EXPRTOKEN_NAMETEST_QNAME:
                          System.out.print("<NAMETEST_QNAME");
                          if (fTokens[++i] != -1)
                              /***
                              System.out.print(" prefix=\"" + fSymbolTable.toString(fTokens[i]) + "\"");
                              /***/
                              System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\"");
                              /***/
                          /***
                          System.out.print(" localpart=\"" + fSymbolTable.toString(fTokens[++i]) + "\"");
                          /***/
                          System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\"");
                          /***/
                          System.out.print("/>");
                          break;
                      case EXPRTOKEN_NODETYPE_COMMENT:
                          System.out.print("<NODETYPE_COMMENT/>");
                          break;
                      case EXPRTOKEN_NODETYPE_TEXT:
                          System.out.print("<NODETYPE_TEXT/>");
                          break;
                      case EXPRTOKEN_NODETYPE_PI:
                          System.out.print("<NODETYPE_PI/>");
                          break;
                      case EXPRTOKEN_NODETYPE_NODE:
                          System.out.print("<NODETYPE_NODE/>");
                          break;
                      case EXPRTOKEN_OPERATOR_AND:
                          System.out.print("<OPERATOR_AND/>");
                          break;
                      case EXPRTOKEN_OPERATOR_OR:
                          System.out.print("<OPERATOR_OR/>");
                          break;
                      case EXPRTOKEN_OPERATOR_MOD:
                          System.out.print("<OPERATOR_MOD/>");
                          break;
                      case EXPRTOKEN_OPERATOR_DIV:
                          System.out.print("<OPERATOR_DIV/>");
                          break;
                      case EXPRTOKEN_OPERATOR_MULT:
                          System.out.print("<OPERATOR_MULT/>");
                          break;
                      case EXPRTOKEN_OPERATOR_SLASH:
                          System.out.print("<OPERATOR_SLASH/>");
                          if (i + 1 < fTokenCount) {
                              System.out.println();
                              System.out.print("  ");
                          }
                          break;
                      case EXPRTOKEN_OPERATOR_DOUBLE_SLASH:
                          System.out.print("<OPERATOR_DOUBLE_SLASH/>");
                          break;
                      case EXPRTOKEN_OPERATOR_UNION:
                          System.out.print("<OPERATOR_UNION/>");
                          break;
                      case EXPRTOKEN_OPERATOR_PLUS:
                          System.out.print("<OPERATOR_PLUS/>");
                          break;
                      case EXPRTOKEN_OPERATOR_MINUS:
                          System.out.print("<OPERATOR_MINUS/>");
                          break;
                      case EXPRTOKEN_OPERATOR_EQUAL:
                          System.out.print("<OPERATOR_EQUAL/>");
                          break;
                      case EXPRTOKEN_OPERATOR_NOT_EQUAL:
                          System.out.print("<OPERATOR_NOT_EQUAL/>");
                          break;
                      case EXPRTOKEN_OPERATOR_LESS:
                          System.out.print("<OPERATOR_LESS/>");
                          break;
                      case EXPRTOKEN_OPERATOR_LESS_EQUAL:
                          System.out.print("<OPERATOR_LESS_EQUAL/>");
                          break;
                      case EXPRTOKEN_OPERATOR_GREATER:
                          System.out.print("<OPERATOR_GREATER/>");
                          break;
                      case EXPRTOKEN_OPERATOR_GREATER_EQUAL:
                          System.out.print("<OPERATOR_GREATER_EQUAL/>");
                          break;
                      case EXPRTOKEN_FUNCTION_NAME:
                          System.out.print("<FUNCTION_NAME");
                          if (fTokens[++i] != -1)
                              /***
                              System.out.print(" prefix=\"" + fSymbolTable.toString(fTokens[i]) + "\"");
                              /***/
                              System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\"");
                              /***/
                          /***
                          System.out.print(" localpart=\"" + fSymbolTable.toString(fTokens[++i]) + "\"");
                          /***/
                          System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\"");
                          /***/
                          System.out.print("/>");
                          break;
                      case EXPRTOKEN_AXISNAME_ANCESTOR:
                          System.out.print("<AXISNAME_ANCESTOR/>");
                          break;
                      case EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF:
                          System.out.print("<AXISNAME_ANCESTOR_OR_SELF/>");
                          break;
                      case EXPRTOKEN_AXISNAME_ATTRIBUTE:
                          System.out.print("<AXISNAME_ATTRIBUTE/>");
                          break;
                      case EXPRTOKEN_AXISNAME_CHILD:
                          System.out.print("<AXISNAME_CHILD/>");
                          break;
                      case EXPRTOKEN_AXISNAME_DESCENDANT:
                          System.out.print("<AXISNAME_DESCENDANT/>");
                          break;
                      case EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF:
                          System.out.print("<AXISNAME_DESCENDANT_OR_SELF/>");
                          break;
                      case EXPRTOKEN_AXISNAME_FOLLOWING:
                          System.out.print("<AXISNAME_FOLLOWING/>");
                          break;
                      case EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING:
                          System.out.print("<AXISNAME_FOLLOWING_SIBLING/>");
                          break;
                      case EXPRTOKEN_AXISNAME_NAMESPACE:
                          System.out.print("<AXISNAME_NAMESPACE/>");
                          break;
                      case EXPRTOKEN_AXISNAME_PARENT:
                          System.out.print("<AXISNAME_PARENT/>");
                          break;
                      case EXPRTOKEN_AXISNAME_PRECEDING:
                          System.out.print("<AXISNAME_PRECEDING/>");
                          break;
                      case EXPRTOKEN_AXISNAME_PRECEDING_SIBLING:
                          System.out.print("<AXISNAME_PRECEDING_SIBLING/>");
                          break;
                      case EXPRTOKEN_AXISNAME_SELF:
                          System.out.print("<AXISNAME_SELF/>");
                          break;
                      case EXPRTOKEN_LITERAL:
                          System.out.print("<LITERAL");
                          /***
                          System.out.print(" value=\"" + fSymbolTable.toString(fTokens[++i]) + "\"");
                          /***/
                          System.out.print(" value=\"" + getTokenString(fTokens[++i]) + "\"");
                          /***/
                          System.out.print("/>");
                          break;
                      case EXPRTOKEN_NUMBER:
                          System.out.print("<NUMBER");
                          System.out.print(" whole=\"" + getTokenString(fTokens[++i]) + "\"");
                          System.out.print(" part=\"" + getTokenString(fTokens[++i]) + "\"");
                          System.out.print("/>");
                          break;
                      case EXPRTOKEN_VARIABLE_REFERENCE:
                          System.out.print("<VARIABLE_REFERENCE");
                          if (fTokens[++i] != -1)
                              /***
                              System.out.print(" prefix=\"" + fSymbolTable.toString(fTokens[i]) + "\"");
                              /***/
                              System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\"");
                              /***/
                          /***
                          System.out.print(" localpart=\"" + fSymbolTable.toString(fTokens[++i]) + "\"");
                          /***/
                          System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\"");
                          /***/
                          System.out.print("/>");
                          break;
                      default:
                          System.out.println("<???/>");
                      }
                  }
                  System.out.println();
              //}
          }
  
      } // class Tokens
      
      /**
       * @author Glenn Marcy, IBM
       * @author Andy Clark, IBM
       *
       * @version $Id: XPath.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
       */
      private static class Scanner {
      
          /**
           * 7-bit ASCII subset
           *
           *  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
           *  0,  0,  0,  0,  0,  0,  0,  0,  0, HT, LF,  0,  0, CR,  0,  0,  // 0
           *  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 1
           * SP,  !,  ",  #,  $,  %,  &,  ',  (,  ),  *,  +,  ,,  -,  .,  /,  // 2
           *  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  :,  ;,  <,  =,  >,  ?,  // 3
           *  @,  A,  B,  C,  D,  E,  F,  G,  H,  I,  J,  K,  L,  M,  N,  O,  // 4
           *  P,  Q,  R,  S,  T,  U,  V,  W,  X,  Y,  Z,  [,  \,  ],  ^,  _,  // 5
           *  `,  a,  b,  c,  d,  e,  f,  g,  h,  i,  j,  k,  l,  m,  n,  o,  // 6
           *  p,  q,  r,  s,  t,  u,  v,  w,  x,  y,  z,  {,  |,  },  ~, DEL  // 7
           */
          private static final byte
              CHARTYPE_INVALID            =  0,   // invalid XML character
              CHARTYPE_OTHER              =  1,   // not special - one of "#%&;?\^`{}~" or DEL
              CHARTYPE_WHITESPACE         =  2,   // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20)
              CHARTYPE_EXCLAMATION        =  3,   // '!' (0x21)
              CHARTYPE_QUOTE              =  4,   // '\"' or '\'' (0x22 and 0x27)
              CHARTYPE_DOLLAR             =  5,   // '$' (0x24)
              CHARTYPE_OPEN_PAREN         =  6,   // '(' (0x28)
              CHARTYPE_CLOSE_PAREN        =  7,   // ')' (0x29)
              CHARTYPE_STAR               =  8,   // '*' (0x2A)
              CHARTYPE_PLUS               =  9,   // '+' (0x2B)
              CHARTYPE_COMMA              = 10,   // ',' (0x2C)
              CHARTYPE_MINUS              = 11,   // '-' (0x2D)
              CHARTYPE_PERIOD             = 12,   // '.' (0x2E)
              CHARTYPE_SLASH              = 13,   // '/' (0x2F)
              CHARTYPE_DIGIT              = 14,   // '0'-'9' (0x30 to 0x39)
              CHARTYPE_COLON              = 15,   // ':' (0x3A)
              CHARTYPE_LESS               = 16,   // '<' (0x3C)
              CHARTYPE_EQUAL              = 17,   // '=' (0x3D)
              CHARTYPE_GREATER            = 18,   // '>' (0x3E)
              CHARTYPE_ATSIGN             = 19,   // '@' (0x40)
              CHARTYPE_LETTER             = 20,   // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
              CHARTYPE_OPEN_BRACKET       = 21,   // '[' (0x5B)
              CHARTYPE_CLOSE_BRACKET      = 22,   // ']' (0x5D)
              CHARTYPE_UNDERSCORE         = 23,   // '_' (0x5F)
              CHARTYPE_UNION              = 24,   // '|' (0x7C)
              CHARTYPE_NONASCII           = 25;   // Multi-byte or 8-bit codepoint (>= 0x80)
          
          private static byte[] fASCIICharMap = {
              0,  0,  0,  0,  0,  0,  0,  0,  0,  2,  2,  0,  0,  2,  0,  0,
              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
              2,  3,  4,  1,  5,  1,  1,  4,  6,  7,  8,  9, 10, 11, 12, 13,
             14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15,  1, 16, 17, 18,  1,
             19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
             20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,  1, 22,  1, 23,
              1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
             20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,  1, 24,  1,  1,  1
          };
          
          /**
           * Symbol literals
           */
          /***
          private static int fgAndSymbol = -1;                // 'and'
          private static int fgOrSymbol = -1;                 // 'or'
          private static int fgModSymbol = -1;                // 'mod'
          private static int fgDivSymbol = -1;                // 'div'
          
          private static int fgCommentSymbol = -1;            // 'comment'
          private static int fgTextSymbol = -1;               // 'text'
          private static int fgPISymbol = -1;                 // 'processing-instruction'
          private static int fgNodeSymbol = -1;               // 'node'
          
          private static int fgAncestorSymbol = -1;           // 'ancestor'
          private static int fgAncestorOrSelfSymbol = -1;     // 'ancestor-or-self'
          private static int fgAttributeSymbol = -1;          // 'attribute'
          private static int fgChildSymbol = -1;              // 'child'
          private static int fgDescendantSymbol = -1;         // 'descendant'
          private static int fgDescendantOrSelfSymbol = -1;   // 'descendant-or-self'
          private static int fgFollowingSymbol = -1;          // 'following'
          private static int fgFollowingSiblingSymbol = -1;   // 'following-sibling'
          private static int fgNamespaceSymbol = -1;          // 'namespace'
          private static int fgParentSymbol = -1;             // 'parent'
          private static int fgPrecedingSymbol = -1;          // 'preceding'
          private static int fgPrecedingSiblingSymbol = -1;   // 'preceding-sibling'
          private static int fgSelfSymbol = -1;               // 'self'
          
          private static SymbolTable fgSymbolTable = new SymbolTable();
          
          static {
              fgAndSymbol = fgSymbolTable.addSymbol("and");
              fgOrSymbol = fgSymbolTable.addSymbol("or");
              fgModSymbol = fgSymbolTable.addSymbol("mod");
              fgDivSymbol = fgSymbolTable.addSymbol("div");
              fgCommentSymbol = fgSymbolTable.addSymbol("comment");
              fgTextSymbol = fgSymbolTable.addSymbol("text");
              fgPISymbol = fgSymbolTable.addSymbol("processing-instruction");
              fgNodeSymbol = fgSymbolTable.addSymbol("node");
              fgAncestorSymbol = fgSymbolTable.addSymbol("ancestor");
              fgAncestorOrSelfSymbol = fgSymbolTable.addSymbol("ancestor-or-self");
              fgAttributeSymbol = fgSymbolTable.addSymbol("attribute");
              fgChildSymbol = fgSymbolTable.addSymbol("child");
              fgDescendantSymbol = fgSymbolTable.addSymbol("descendant");
              fgDescendantOrSelfSymbol = fgSymbolTable.addSymbol("descendant-or-self");
              fgFollowingSymbol = fgSymbolTable.addSymbol("following");
              fgFollowingSiblingSymbol = fgSymbolTable.addSymbol("following-sibling");
              fgNamespaceSymbol = fgSymbolTable.addSymbol("namespace");
              fgParentSymbol = fgSymbolTable.addSymbol("parent");
              fgPrecedingSymbol = fgSymbolTable.addSymbol("preceding");
              fgPrecedingSiblingSymbol = fgSymbolTable.addSymbol("preceding-sibling");
              fgSelfSymbol = fgSymbolTable.addSymbol("self");
          }
          /***/
      
          //
          // Data
          //
      
          /** String pool. */
          private StringPool fStringPool;
      
          // symbols
      
          private int fAndSymbol;
          private int fOrSymbol;
          private int fModSymbol;
          private int fDivSymbol;
          
          private int fCommentSymbol;
          private int fTextSymbol;
          private int fPISymbol;
          private int fNodeSymbol;
          
          private int fAncestorSymbol;
          private int fAncestorOrSelfSymbol;
          private int fAttributeSymbol;
          private int fChildSymbol;
          private int fDescendantSymbol;
          private int fDescendantOrSelfSymbol;
          private int fFollowingSymbol;
          private int fFollowingSiblingSymbol;
          private int fNamespaceSymbol;
          private int fParentSymbol;
          private int fPrecedingSymbol;
          private int fPrecedingSiblingSymbol;
          private int fSelfSymbol;
          
          //
          // Constructors
          //
      
          /** Constructs an XPath expression scanner. */
          public Scanner(StringPool stringPool) {
      
              // save pool and tokens
              fStringPool = stringPool;
      
              // create symbols
              fAndSymbol = fStringPool.addSymbol("and");
              fOrSymbol = fStringPool.addSymbol("or");
              fModSymbol = fStringPool.addSymbol("mod");
              fDivSymbol = fStringPool.addSymbol("div");
              fCommentSymbol = fStringPool.addSymbol("comment");
              fTextSymbol = fStringPool.addSymbol("text");
              fPISymbol = fStringPool.addSymbol("processing-instruction");
              fNodeSymbol = fStringPool.addSymbol("node");
              fAncestorSymbol = fStringPool.addSymbol("ancestor");
              fAncestorOrSelfSymbol = fStringPool.addSymbol("ancestor-or-self");
              fAttributeSymbol = fStringPool.addSymbol("attribute");
              fChildSymbol = fStringPool.addSymbol("child");
              fDescendantSymbol = fStringPool.addSymbol("descendant");
              fDescendantOrSelfSymbol = fStringPool.addSymbol("descendant-or-self");
              fFollowingSymbol = fStringPool.addSymbol("following");
              fFollowingSiblingSymbol = fStringPool.addSymbol("following-sibling");
              fNamespaceSymbol = fStringPool.addSymbol("namespace");
              fParentSymbol = fStringPool.addSymbol("parent");
              fPrecedingSymbol = fStringPool.addSymbol("preceding");
              fPrecedingSiblingSymbol = fStringPool.addSymbol("preceding-sibling");
              fSelfSymbol = fStringPool.addSymbol("self");
      
          } // <init>(StringPool)
      
          /**
           *
           */
          public boolean scanExpr(StringPool stringPool,
                                  XPath.Tokens tokens, String data, 
                                  int currentOffset, int endOffset)
              throws XPathException {
              
              int nameOffset;
              int nameHandle, prefixHandle;
              boolean starIsMultiplyOperator = false;
              int ch;
      
              /***
              if (XPath.Tokens.DUMP_TOKENS) {
                  System.out.println("  <test>");
                  System.out.println("    <expression>");
                  System.out.println("      " + encoding.createString(data, currentOffset, endOffset - currentOffset));
                  System.out.println("    </expression>");
              }
              /***/
              while (true) {
                  if (currentOffset == endOffset) {
                      break;
                  }
                  /***
                  ch = (data[currentOffset] & 0xFF);
                  /***/
                  ch = data.charAt(currentOffset);
                  /***/
                  //
                  // [39] ExprWhitespace ::= S
                  //
                  while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      /***
                      ch = (data[currentOffset] & 0xFF);
                      /***/
                      ch = data.charAt(currentOffset);
                      /***/
                  }
                  if (currentOffset == endOffset) {
                      break;
                  }
                  //
                  // [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
                  //                  | NameTest | NodeType | Operator | FunctionName
                  //                  | AxisName | Literal | Number | VariableReference
                  //
                  byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII : fASCIICharMap[ch];
                  switch (chartype) {
                  case CHARTYPE_OPEN_PAREN:       // '('
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
                      starIsMultiplyOperator = false;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_CLOSE_PAREN:      // ')'
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_PAREN);
                      starIsMultiplyOperator = true;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_OPEN_BRACKET:     // '['
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_BRACKET);
                      starIsMultiplyOperator = false;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_CLOSE_BRACKET:    // ']'
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_BRACKET);
                      starIsMultiplyOperator = true;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  //
                  // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
                  //                                         ^^^^^^^^^^
                  //
                  case CHARTYPE_PERIOD:           // '.', '..' or '.' Digits
                      if (currentOffset + 1 == endOffset) {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
                          starIsMultiplyOperator = true;
                          currentOffset++;
                          break;
                      }
                      /***
                      ch = (data[currentOffset + 1] & 0xFF);
                      /***/
                      ch = data.charAt(currentOffset + 1);
                      /***/
                      if (ch == '.') {            // '..'
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_PERIOD);
                          starIsMultiplyOperator = true;
                          currentOffset += 2;
                      } else if (ch >= '0' && ch <= '9') {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
                          starIsMultiplyOperator = true;
                          currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/);
                      } else {                    // '.'
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD);
                          starIsMultiplyOperator = true;
                          currentOffset++;
                      }
                      if (currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_ATSIGN:           // '@'
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_ATSIGN);
                      starIsMultiplyOperator = false;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_COMMA:            // ','
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_COMMA);
                      starIsMultiplyOperator = false;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_COLON:            // '::'
                      if (++currentOffset == endOffset) {
          System.out.println("abort 1a");
                          return false; // REVISIT
                      }
                      /***
                      ch = (data[currentOffset] & 0xFF);
                      /***/
                      ch = data.charAt(currentOffset);
                      /***/
                      if (ch != ':') {
          System.out.println("abort 1b");
                          return false; // REVISIT
                      }
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
                      starIsMultiplyOperator = false;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_SLASH:            // '/' and '//'
                      if (++currentOffset == endOffset) {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
                          starIsMultiplyOperator = false;
                          break;
                      }
                      /***
                      ch = (data[currentOffset] & 0xFF);
                      /***/
                      ch = data.charAt(currentOffset);
                      /***/
                      if (ch == '/') { // '//'
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH);
                          starIsMultiplyOperator = false;
                          if (++currentOffset == endOffset) {
                              break;
                          }
                      } else {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH);
                          starIsMultiplyOperator = false;
                      }
                      break;
                  case CHARTYPE_UNION:            // '|'
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_UNION);
                      starIsMultiplyOperator = false;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_PLUS:             // '+'
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_PLUS);
                      starIsMultiplyOperator = false;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_MINUS:            // '-'
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MINUS);
                      starIsMultiplyOperator = false;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_EQUAL:            // '='
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_EQUAL);
                      starIsMultiplyOperator = false;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_EXCLAMATION:      // '!='
                      if (++currentOffset == endOffset) {
          System.out.println("abort 2a");
                          return false; // REVISIT
                      }
                      /***
                      ch = (data[currentOffset] & 0xFF);
                      /***/
                      ch = data.charAt(currentOffset);
                      /***/
                      if (ch != '=') {
          System.out.println("abort 2b");
                          return false; // REVISIT
                      }
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_NOT_EQUAL);
                      starIsMultiplyOperator = false;
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  case CHARTYPE_LESS: // '<' and '<='
                      if (++currentOffset == endOffset) {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
                          starIsMultiplyOperator = false;
                          break;
                      }
                      /***
                      ch = (data[currentOffset] & 0xFF);
                      /***/
                      ch = data.charAt(currentOffset);
                      /***/
                      if (ch == '=') { // '<='
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS_EQUAL);
                          starIsMultiplyOperator = false;
                          if (++currentOffset == endOffset) {
                              break;
                          }
                      } else {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS);
                          starIsMultiplyOperator = false;
                      }
                      break;
                  case CHARTYPE_GREATER: // '>' and '>='
                      if (++currentOffset == endOffset) {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
                          starIsMultiplyOperator = false;
                          break;
                      }
                      /***
                      ch = (data[currentOffset] & 0xFF);
                      /***/
                      ch = data.charAt(currentOffset);
                      /***/
                      if (ch == '=') { // '>='
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER_EQUAL);
                          starIsMultiplyOperator = false;
                          if (++currentOffset == endOffset) {
                              break;
                          }
                      } else {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER);
                          starIsMultiplyOperator = false;
                      }
                      break;
                  //
                  // [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
                  //
                  case CHARTYPE_QUOTE:            // '\"' or '\''
                      int qchar = ch;
                      if (++currentOffset == endOffset) {
          System.out.println("abort 2c");
                          return false; // REVISIT
                      }
                      /***
                      ch = (data[currentOffset] & 0xFF);
                      /***/
                      ch = data.charAt(currentOffset);
                      /***/
                      int litOffset = currentOffset;
                      while (ch != qchar) {
                          if (++currentOffset == endOffset) {
          System.out.println("abort 2d");
                              return false; // REVISIT
                          }
                          /***
                          ch = (data[currentOffset] & 0xFF);
                          /***/
                          ch = data.charAt(currentOffset);
                          /***/
                      }
                      int litLength = currentOffset - litOffset;
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_LITERAL);
                      starIsMultiplyOperator = true;
                      /***
                      addToken(tokens, tokens.addSymbol(data, litOffset, litLength, encoding));
                      /***/
                      tokens.addToken(stringPool.addSymbol(data.substring(litOffset, litOffset + litLength)));
                      /***/
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  //
                  // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
                  // [31] Digits ::= [0-9]+
                  //
                  case CHARTYPE_DIGIT:
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER);
                      starIsMultiplyOperator = true;
                      currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/);
                      break;
                  //
                  // [36] VariableReference ::= '$' QName
                  //
                  case CHARTYPE_DOLLAR:
                      if (++currentOffset == endOffset) {
          System.out.println("abort 3a");
                          return false; // REVISIT
                      }
                      /***
                      ch = (data[currentOffset] & 0xFF);
                      /***/
                      ch = data.charAt(currentOffset);
                      /***/
                      nameOffset = currentOffset;
                      if (ch >= 0x80) {
                          throw new RuntimeException("need encoding support");
                      }
                      chartype = fASCIICharMap[ch];
                      if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_UNDERSCORE) {
          System.out.println("abort 3b");
                          return false; // REVISIT
                      }
                      while (++currentOffset < endOffset) {
                          /***
                          ch = (data[currentOffset] & 0xFF);
                          /***/
                          ch = data.charAt(currentOffset);
                          /***/
                          if (ch >= 0x80) {
                              throw new RuntimeException("need encoding support");
                          }
                          chartype = fASCIICharMap[ch];
                          if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_DIGIT &&
                              chartype != CHARTYPE_PERIOD && chartype != CHARTYPE_MINUS &&
                              chartype != CHARTYPE_UNDERSCORE)
                          {
                              break;
                          }
                      }
                      /***
                      nameHandle = tokens.addSymbol(data, nameOffset, currentOffset - nameOffset, encoding);
                      /***/
                      nameHandle = stringPool.addSymbol(data.substring(nameOffset, currentOffset));
                      /***/
                      if (ch != ':') {
                          prefixHandle = -1;
                      } else {
                          prefixHandle = nameHandle;
                          if (++currentOffset == endOffset) {
          System.out.println("abort 4a");
                              return false; // REVISIT
                          }
                          /***
                          ch = (data[currentOffset] & 0xFF);
                          /***/
                          ch = data.charAt(currentOffset);
                          /***/
                          nameOffset = currentOffset;
                          if (ch >= 0x80) {
                              throw new RuntimeException("need encoding support");
                          }
                          chartype = fASCIICharMap[ch];
                          if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_UNDERSCORE) {
          System.out.println("abort 4b");
                              return false; // REVISIT
                          }
                          while (++currentOffset < endOffset) {
                              /***
                              ch = (data[currentOffset] & 0xFF);
                              /***/
                              ch = data.charAt(currentOffset);
                              /***/
                              if (ch >= 0x80) {
                                  throw new RuntimeException("need encoding support");
                              }
                              chartype = fASCIICharMap[ch];
                              if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_DIGIT &&
                                  chartype != CHARTYPE_PERIOD && chartype != CHARTYPE_MINUS &&
                                  chartype != CHARTYPE_UNDERSCORE)
                              {
                                  break;
                              }
                          }
                          /***
                          nameHandle = tokens.addSymbol(data, nameOffset, currentOffset - nameOffset, encoding);
                          /***/
                          nameHandle = stringPool.addSymbol(data.substring(nameOffset, currentOffset));
                          /***/
                      }
                      addToken(tokens, XPath.Tokens.EXPRTOKEN_VARIABLE_REFERENCE);
                      starIsMultiplyOperator = true;
                      tokens.addToken(prefixHandle);
                      tokens.addToken(nameHandle);
                      break;
                  //
                  // [37] NameTest ::= '*' | NCName ':' '*' | QName
                  // [34] MultiplyOperator ::= '*'
                  //
                  case CHARTYPE_STAR:             // '*'
                      //
                      // 3.7 Lexical Structure
                      //
                      //  If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
                      //  an Operator, then a * must be recognized as a MultiplyOperator.
                      //
                      // Otherwise, the token must not be recognized as a MultiplyOperator.
                      //
                      if (starIsMultiplyOperator) {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MULT);
                          starIsMultiplyOperator = false;
                      } else {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_ANY);
                          starIsMultiplyOperator = true;
                      }
                      if (++currentOffset == endOffset) {
                          break;
                      }
                      break;
                  //
                  // NCName, QName and non-terminals
                  //
                  case CHARTYPE_NONASCII: // possibly a valid non-ascii 'Letter' (BaseChar | Ideographic)
                      throw new RuntimeException("need encoding support");
                  case CHARTYPE_LETTER:
                  case CHARTYPE_UNDERSCORE:
                      //
                      // 3.7 Lexical Structure
                      //
                      //  If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
                      //  an Operator, then an NCName must be recognized as an OperatorName.
                      //
                      //  If the character following an NCName (possibly after intervening ExprWhitespace) is (,
                      //  then the token must be recognized as a NodeType or a FunctionName.
                      //
                      //  If the two characters following an NCName (possibly after intervening ExprWhitespace)
                      //  are ::, then the token must be recognized as an AxisName.
                      //
                      //  Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
                      //  FunctionName, or an AxisName.
                      //
                      // [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
                      // [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
                      // [35] FunctionName ::= QName - NodeType
                      // [6] AxisName ::= (see above)
                      //
                      // [37] NameTest ::= '*' | NCName ':' '*' | QName
                      // [5] NCName ::= (Letter | '_') (NCNameChar)*
                      // [?] NCNameChar ::= Letter | Digit | '.' | '-' | '_'  (ascii subset of 'NCNameChar')
                      // [?] QName ::= (NCName ':')? NCName
                      // [?] Letter ::= [A-Za-z]                              (ascii subset of 'Letter')
                      // [?] Digit ::= [0-9]                                  (ascii subset of 'Digit')
                      //
                      nameOffset = currentOffset;
                      while (true) {
                          if (++currentOffset == endOffset) {
                              break;
                          }
                          /***
                          ch = (data[currentOffset] & 0xFF);
                          /***/
                          ch = data.charAt(currentOffset);
                          /***/
                          if (ch >= 0x80) {
                              throw new RuntimeException("need encoding support");
                          }
                          chartype = fASCIICharMap[ch];
                          if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_DIGIT &&
                              chartype != CHARTYPE_PERIOD && chartype != CHARTYPE_MINUS &&
                              chartype != CHARTYPE_UNDERSCORE)
                          {
                              break;
                          }
                      }
                      /***
                      nameHandle = tokens.addSymbol(data, nameOffset, currentOffset - nameOffset, encoding);
                      /***/
                      nameHandle = stringPool.addSymbol(data.substring(nameOffset, currentOffset));
                      /***/
                      boolean isNameTestNCName = false;
                      boolean isAxisName = false;
                      prefixHandle = -1;
                      if (ch == ':') {
                          if (++currentOffset == endOffset) {
          System.out.println("abort 5");
                              return false; // REVISIT
                          }
                          /***
                          ch = (data[currentOffset] & 0xFF);
                          /***/
                          ch = data.charAt(currentOffset);
                          /***/
                          if (ch == '*') {
                              if (++currentOffset < endOffset) {
                                  /***
                                  ch = (data[currentOffset] & 0xFF);
                                  /***/
                                  ch = data.charAt(currentOffset);
                                  /***/
                              }
                              isNameTestNCName = true;
                          } else if (ch == ':') {
                              if (++currentOffset < endOffset) {
                                  /***
                                  ch = (data[currentOffset] & 0xFF);
                                  /***/
                                  ch = data.charAt(currentOffset);
                                  /***/
                              }
                              isAxisName = true;
                          } else {
                              prefixHandle = nameHandle;
                              nameOffset = currentOffset;
                              if (ch >= 0x80) {
                                  throw new RuntimeException("need encoding support");
                              }
                              chartype = fASCIICharMap[ch];
                              if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_UNDERSCORE) {
          System.out.println("abort 5b");
                                  return false; // REVISIT
                              }
                              while (++currentOffset < endOffset) {
                                  /***
                                  ch = (data[currentOffset] & 0xFF);
                                  /***/
                                  ch = data.charAt(currentOffset);
                                  /***/
                                  if (ch >= 0x80) {
                                      throw new RuntimeException("need encoding support");
                                  }
                                  chartype = fASCIICharMap[ch];
                                  if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_DIGIT &&
                                      chartype != CHARTYPE_PERIOD && chartype != CHARTYPE_MINUS &&
                                      chartype != CHARTYPE_UNDERSCORE)
                                  {
                                      break;
                                  }
                              }
                              /***
                              nameHandle = tokens.addSymbol(data, nameOffset, currentOffset - nameOffset, encoding);
                              /***/
                              nameHandle = stringPool.addSymbol(data.substring(nameOffset, currentOffset));
                              /***/
                          }
                      }
                      //
                      // [39] ExprWhitespace ::= S
                      //
                      while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
                          if (++currentOffset == endOffset) {
                              break;
                          }
                          /***
                          ch = (data[currentOffset] & 0xFF);
                          /***/
                          ch = data.charAt(currentOffset);
                          /***/
                      }
                      //
                      //  If there is a preceding token and the preceding token is not one of @, ::, (, [, , or
                      //  an Operator, then an NCName must be recognized as an OperatorName.
                      //
                      if (starIsMultiplyOperator) {
                          if (nameHandle == fAndSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_AND);
                              starIsMultiplyOperator = false;
                          } else if (nameHandle == fOrSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_OR);
                              starIsMultiplyOperator = false;
                          } else if (nameHandle == fModSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MOD);
                              starIsMultiplyOperator = false;
                          } else if (nameHandle == fDivSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DIV);
                              starIsMultiplyOperator = false;
                          } else {
          System.out.println("abort 6");
                              return false; // REVISIT
                          }
                          if (isNameTestNCName) {
          System.out.println("abort 7");
                              return false; // REVISIT - NCName:* where an OperatorName is required
                          } else if (isAxisName) {
          System.out.println("abort 8");
                              return false; // REVISIT - AxisName:: where an OperatorName is required
                          }
                          break;
                      }
                      //
                      //  If the character following an NCName (possibly after intervening ExprWhitespace) is (,
                      //  then the token must be recognized as a NodeType or a FunctionName.
                      //
                      if (ch == '(' && !isNameTestNCName && !isAxisName) {
                          if (nameHandle == fCommentSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_COMMENT);
                          } else if (nameHandle == fTextSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_TEXT);
                          } else if (nameHandle == fPISymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_PI);
                          } else if (nameHandle == fNodeSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_NODE);
                          } else {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_FUNCTION_NAME);
                              tokens.addToken(prefixHandle);
                              tokens.addToken(nameHandle);
                          }
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN);
                          starIsMultiplyOperator = false;
                          if (++currentOffset == endOffset) {
                              break;
                          }
                          break;
                      }
                      //
                      //  If the two characters following an NCName (possibly after intervening ExprWhitespace)
                      //  are ::, then the token must be recognized as an AxisName.
                      //
                      if (isAxisName ||
                          (ch == ':' && currentOffset + 1 < endOffset &&
                           /***
                           (data[currentOffset + 1] & 0xFF) == ':')) {
                           /***/
                           data.charAt(currentOffset + 1) == ':')) {
                           /***/
                          if (nameHandle == fAncestorSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR);
                          } else if (nameHandle == fAncestorOrSelfSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF);
                          } else if (nameHandle == fAttributeSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE);
                          } else if (nameHandle == fChildSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD);
                          } else if (nameHandle == fDescendantSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT);
                          } else if (nameHandle == fDescendantOrSelfSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF);
                          } else if (nameHandle == fFollowingSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING);
                          } else if (nameHandle == fFollowingSiblingSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING);
                          } else if (nameHandle == fNamespaceSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_NAMESPACE);
                          } else if (nameHandle == fParentSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PARENT);
                          } else if (nameHandle == fPrecedingSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING);
                          } else if (nameHandle == fPrecedingSiblingSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING_SIBLING);
                          } else if (nameHandle == fSelfSymbol) {
                              addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_SELF);
                          } else {
          System.out.println("abort 9");
                              return false; // REVISIT
                          }
                          if (isNameTestNCName) {
          System.out.println("abort 10");
                              return false; // REVISIT - "NCName:* ::" where "AxisName ::" is required
                          }
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON);
                          starIsMultiplyOperator = false;
                          if (!isAxisName) {
                              currentOffset++;
                              if (++currentOffset == endOffset) {
                                  break;
                              }
                          }
                          break;
                      }
                      //
                      //  Otherwise, the token must not be recognized as an OperatorName, a NodeType, a
                      //  FunctionName, or an AxisName.
                      //
                      if (isNameTestNCName) {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE);
                          starIsMultiplyOperator = true;
                          tokens.addToken(nameHandle);
                      } else {
                          addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME);
                          starIsMultiplyOperator = true;
                          tokens.addToken(prefixHandle);
                          tokens.addToken(nameHandle);
                      }
                      break;
                  }
              }
              if (XPath.Tokens.DUMP_TOKENS) {
                  tokens.dumpTokens();
              }
              return true;
          }
          /*
          void allocateProducer(byte[] data, int offset, int length, EncodingSupport encoding) {
              XPathStringProducer sp = fStringProducers[fStringProducerCount];
              if (sp != null) {
                  sp = sp.setState(data, offset, length, encoding);
              } else {
                  sp = new XPathStringProducer(data, offset, length, encoding);
              }
              fStringProducers[fStringProducerCount++] = sp;
          }
          void finalizeProducer(byte[] data) {
              fStringProducerCount--;
          }
          class XPathStringProducer {
              byte[] fData = null;
              int fOffset = -1;
              int fLength = -1;
              String fEncoding = null;
              XPathStringProducer(byte[] data, int offset, int length, EncodingSupport encoding) {
                  init(data, offset, length, encoding);
              }
              XPathStringProducer setState(byte[] bytes, int offset, int length, EncodingSupport encoding) {
                  init(bytes, offset, length, encoding);
                  return this;
              }
              void init(byte[] data, int offset, int length, EncodingSupport encoding) {
                  fData = data;
                  fOffset = offset;
                  fLength = length;
                  fEncoding = encoding;
              }
              String getEncoding() {
                  return fEncoding;
              }
              void finalizeProducer() {
                  // do nothing
              }
              int addSymbol(int offset, int length) {
                  return fSymbolTable.addSymbol(fData, offset, length, fEncoding);
              }
          }
          private XPathStringProducer[] fStringProducers = new XPathStringProducer[8];
          private int fStringProducerCount = 0;
          private XPathStringProducer getStringProducer(byte[] data) {
              XPathStringProducer sp = null;
              for (int i = 0; i < fStringProducerCount; i++) {
                  if (fStringProducers[i].fData == data) {
                      return fStringProducers[i];
                  }
              }
              throw new RuntimeException("No StringProducer");
          }
          */
          //
          // [30] Number ::= Digits ('.' Digits?)? | '.' Digits
          // [31] Digits ::= [0-9]+
          //
          private int scanNumber(XPath.Tokens tokens, String/*byte[]*/ data, int endOffset, int currentOffset/*, EncodingSupport encoding*/) {
              /***
              int ch = (data[currentOffset] & 0xFF);
              /***/
              int ch = data.charAt(currentOffset);
              /***/
              int whole = 0;
              int part = 0;
              while (ch >= '0' && ch <= '9') {
                  whole = (whole * 10) + (ch - '0');
                  if (++currentOffset == endOffset) {
                      break;
                  }
                  /***
                  ch = (data[currentOffset] & 0xFF);
                  /***/
                  ch = data.charAt(currentOffset);
                  /***/
              }
              if (ch == '.') {
                  if (++currentOffset < endOffset) {
                      int start = currentOffset;
                      /***
                      ch = (data[currentOffset] & 0xFF);
                      /***/
                      ch = data.charAt(currentOffset);
                      /***/
                      while (ch >= '0' && ch <= '9') {
                          part = (part * 10) + (ch - '0');
                          if (++currentOffset == endOffset) {
                              break;
                          }
                          /***
                          ch = (data[currentOffset] & 0xFF);
                          /***/
                          ch = data.charAt(currentOffset);
                          /***/
                      }
                      if (part != 0) {
                          /***
                          part = tokens.addSymbol(data, start, currentOffset - start, encoding);
                          /***/
                          throw new RuntimeException("find a solution!");
                          //part = fStringPool.addSymbol(data.substring(start, currentOffset));
                          /***/
                      }
                  }
              }
              tokens.addToken(whole);
              tokens.addToken(part);
              return currentOffset;
          }
          
          /***
          public static SymbolTable getGlobalTokens() {
              return fgSymbolTable;
          }
          
          public static void main(String argv[]) {
              try {
                  SymbolTable symbols = new SymbolTable(XPathExprScanner.getGlobalTokens());
                  XPath.Tokens tokens = new XPath.Tokens(symbols);
                  byte[] bytes = new byte[1 << 8]; // 256
                  int i = 0;
                  if (XPath.Tokens.DUMP_TOKENS) {
                      System.out.println("<output>");
                      System.out.println();
                  }
                  while (i < argv.length) {
                      String uri = argv[i++];
                      // System.out.println("uri: "+uri);
                      FileInputStream is = new FileInputStream(uri);
                      int lineOffset = 0;
                      int offset = 0;
                      while (true) {
                          int avail = bytes.length - offset;
                          if (avail == 0) {
                              avail = bytes.length;
                              byte[] newBytes = new byte[avail << 1];
                              System.arraycopy(bytes, 0, newBytes, 0, avail);
                              bytes = newBytes;
                          }
                          int result = is.read(bytes, offset, avail);
                          if (result == -1) {
                              bytes[offset] = 0;
                              break;
                          }
                          result += offset;
                          for (int j = offset; j < result; j++) {
                              int ch = bytes[j];
                              if (ch == 0x0A || ch == 0x0D) {
                                  if (lineOffset < offset) {
                                      XPathExprScanner.scanExpr(tokens, bytes, lineOffset, offset, UTF8EncodingSupport.getInstance());
                                  }
                                  while (ch == 0x0A || ch == 0x0D) {
                                      if (++j == result) {
                                          j = result;
                                          break;
                                      }
                                      ch = bytes[j];
                                  }
                                  lineOffset = offset;
                                  if (j == result) {
                                      break;
                                  }
                              }
                              bytes[offset++] = (byte)ch;
                          }
                      }
                      is.close();
                  }
                  if (XPath.Tokens.DUMP_TOKENS) {
                      System.out.println("</output>");
                  }
              }
              catch (Exception e) {
                  e.printStackTrace();
              }
          }
          /***/
      
          //
          // Protected methods
          //
      
          /**
           * This method adds the specified token to the token list. By
           * default, this method allows all tokens. However, subclasses
           * of the XPathExprScanner can override this method in order
           * to disallow certain tokens from being used in the scanned
           * XPath expression. This is a convenient way of allowing only
           * a subset of XPath.
           */
          protected void addToken(XPath.Tokens tokens, int token) 
              throws XPathException {
              tokens.addToken(token);
          } // addToken(int)
      
      } // class Scanner
      
      /**
       * @author Glenn Marcy, IBM
       * @author Andy Clark, IBM
       *
       * @version $Id: XPath.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
       */
      /***
      public static class XPathExprParser {
          
          //
          // Constants
          //
      
          private static final boolean DUMP_PARSE_TREE = false;
          
          private static final boolean DEBUG_PUSH_PARSEOPS = false;
      
          /** Parse Tree operations * /
          public static final int
              PARSEOP_OR                              = -5000,
              PARSEOP_AND                             = -5001,
              PARSEOP_EQUAL                           = -5002,
              PARSEOP_NOT_EQUAL                       = -5003,
              PARSEOP_PLUS                            = -5004,
              PARSEOP_MINUS                           = -5005,
              PARSEOP_MULT                            = -5006,
              PARSEOP_DIV                             = -5007,
              PARSEOP_MOD                             = -5008,
              PARSEOP_LESS                            = -5009,
              PARSEOP_GREATER                         = -5010,
              PARSEOP_LESS_EQUAL                      = -5011,
              PARSEOP_GREATER_EQUAL                   = -5012,
              PARSEOP_NEGATE                          = -5013,
              PARSEOP_UNION                           = -5014,
              PARSEOP_SELECT_ROOT                     = -5015,
              PARSEOP_STEPS                           = -5016,
              PARSEOP_STEP                            = -5017,
              PARSEOP_AXIS_ANCESTOR                   = -5018,
              PARSEOP_AXIS_ANCESTOR_OR_SELF           = -5019,
              PARSEOP_AXIS_ATTRIBUTE                  = -5020,
              PARSEOP_AXIS_CHILD                      = -5021,
              PARSEOP_AXIS_DESCENDANT                 = -5022,
              PARSEOP_AXIS_DESCENDANT_OR_SELF         = -5023,
              PARSEOP_AXIS_FOLLOWING                  = -5024,
              PARSEOP_AXIS_FOLLOWING_SIBLING          = -5025,
              PARSEOP_AXIS_NAMESPACE                  = -5026,
              PARSEOP_AXIS_PARENT                     = -5027,
              PARSEOP_AXIS_PRECEDING                  = -5028,
              PARSEOP_AXIS_PRECEDING_SIBLING          = -5029,
              PARSEOP_AXIS_SELF                       = -5030,
              PARSEOP_NODETEST_ANY                    = -5031,
              PARSEOP_NODETEST_NAMESPACE              = -5032,
              PARSEOP_NODETEST_QNAME                  = -5033,
              PARSEOP_NODETEST_COMMENT                = -5034,
              PARSEOP_NODETEST_TEXT                   = -5035,
              PARSEOP_NODETEST_PI                     = -5036,
              PARSEOP_NODETEST_PI_TARGET              = -5037,
              PARSEOP_NODETEST_NODE                   = -5038,
              PARSEOP_FILTER                          = -5039,
              PARSEOP_PREDICATES                      = -5040,
              PARSEOP_PREDICATE                       = -5041,
              PARSEOP_VARIABLE_REFERENCE              = -5042,
              PARSEOP_GROUPING                        = -5043,
              PARSEOP_LITERAL                         = -5044,
              PARSEOP_NUMBER                          = -5045,
              PARSEOP_FUNCTION                        = -5046,
              PARSEOP_FUNCTION_NAME                   = -5047,
              PARSEOP_FUNCTION_ARGUMENTS              = -5048;
          
          //
          // Data
          //
      
          private int fTokenCount = 0;  // for reading
          private int fCurrentToken = 0;  // for reading
          
          private static final int INITIAL_PARSEOP_COUNT = 1 << 8;
          private int[] fParseTree = new int[INITIAL_PARSEOP_COUNT];
          private int fParseOpCount = 0;    // for writing
          private int fCurrentParseOp = 0;  // for reading
          
          /** Symbol table. * /
          private SymbolTable fSymbolTable;
      
          /** Tokens. * /
          private XPath.Tokens fTokens;
      
          /** Scanner. * /
          private XPathExprScanner fScanner;
      
          //
          // Constructors
          //
          
          public XPathExprParser() {
              this(new SymbolTable());
          }
      
          public XPathExprParser(SymbolTable symbolTable) {
              this(symbolTable, new XPathExprScanner(symbolTable));
          }
      
          public XPathExprParser(SymbolTable symbolTable, XPathExprScanner scanner) {
      
              fSymbolTable = symbolTable;
              fScanner = scanner;
      
          } // <init>(SymbolTable,XPathExprScanner)
      
          //
          // Public methods
          //
      
          // init
      
          public void reset() {
              fParseOpCount = 0;
              fCurrentParseOp = 0;
          }
          
          // parsing
      
          public int parseExpr(String data) throws XPathException {
              return parseExpr(data, 0, data.length());
          }
      
          public int parseExpr(String data, int currentOffset, int endOffset) 
              throws XPathException {
              return parseExprOrPattern(data, currentOffset, endOffset, false);
          }
      
          public int parsePattern(String data, int currentOffset, int endOffset)
              throws XPathException {
              return parseExprOrPattern(data, currentOffset, endOffset, true);
          }
      
          public int parseExprOrPattern(String data, int currentOffset, 
                                        int endOffset, boolean isPattern) 
              throws XPathException {
              fTokenCount = 0;
          
              if (DUMP_PARSE_TREE) {
                  System.out.println("  <test>");
                  System.out.println("    <expression>");
                  System.out.println("      " + data.substring(currentOffset, endOffset));
                  System.out.println("    </expression>");
              }
              fTokens = new XPath.Tokens(fSymbolTable);
              fScanner.scanExpr(fTokens, data, currentOffset, endOffset);
              //
              // Build parse tree
              //
              fTokenCount = fTokens.getTokenCount();
              fCurrentToken = 0;
              if (!parseExpr()) {
          System.out.println("abort 12");
                  return -1; // REVISIT
              }
              if (fCurrentToken < fTokenCount) {
          System.out.println("abort 13");
                  return -1; // REVISIT
              }
              if (DUMP_PARSE_TREE) {
                  dumpParseTree(fCurrentParseOp, 4);
              }
              if (DUMP_PARSE_TREE) {
                  System.out.println("  </test>");
                  System.out.println();
              }
              return fCurrentParseOp;
          }
      
          // parse tree ops
      
          public int getCurrentParseOp() {
              return fCurrentParseOp;
          }
      
          public int getParseTreeOp(int handle) {
              return fParseTree[handle];
          }
      
          public int getParseTreeSubHandle(int handle, int subHandleIndex) {
              return fParseTree[handle - 1 - subHandleIndex];
          }
      
          public String getParseTreeSymbol(int handle, int subHandleIndex) {
              int symHandle = fParseTree[handle - 1 - subHandleIndex];
              return fTokens.getTokenString(symHandle);
          }
      
          public int getParseTreeIntValue(int handle, int subHandleIndex) {
              return fParseTree[handle - 1 - subHandleIndex];
          }
      
          // debugging
      
          public void dumpParseTree(int rootParseOp, int indent) {
                  indentPrint(indent);
                  System.out.println("<parse-tree>");
                  dumpParseTreeNode(rootParseOp, indent + 2);
                  indentPrint(indent);
                  System.out.println("</parse-tree>");
          }
          
          public String getParseOpName(int parseOp) {
              switch (parseOp) {
                  case PARSEOP_OR: return "PARSEOP_OR";
                  case PARSEOP_AND: return "PARSEOP_AND";
                  case PARSEOP_EQUAL: return "PARSEOP_EQUAL";
                  case PARSEOP_NOT_EQUAL: return "PARSEOP_NOT_EQUAL";
                  case PARSEOP_PLUS: return "PARSEOP_PLUS";
                  case PARSEOP_MINUS: return "PARSEOP_MINUS";
                  case PARSEOP_MULT: return "PARSEOP_MULT";
                  case PARSEOP_DIV: return "PARSEOP_DIV";
                  case PARSEOP_MOD: return "PARSEOP_MOD";
                  case PARSEOP_LESS: return "PARSEOP_LESS";
                  case PARSEOP_GREATER: return "PARSEOP_GREATER";
                  case PARSEOP_LESS_EQUAL: return "PARSEOP_LESS_EQUAL";
                  case PARSEOP_GREATER_EQUAL: return "PARSEOP_GREATER_EQUAL";
                  case PARSEOP_NEGATE: return "PARSEOP_NEGATE";
                  case PARSEOP_UNION: return "PARSEOP_UNION";
                  case PARSEOP_SELECT_ROOT: return "PARSEOP_SELECT_ROOT";
                  case PARSEOP_STEPS: return "PARSEOP_STEPS";
                  case PARSEOP_STEP: return "PARSEOP_STEP";
                  case PARSEOP_AXIS_ANCESTOR: return "PARSEOP_AXIS_ANCESTOR";
                  case PARSEOP_AXIS_ANCESTOR_OR_SELF: return "PARSEOP_AXIS_ANCESTOR_OR_SELF";
                  case PARSEOP_AXIS_ATTRIBUTE: return "PARSEOP_AXIS_ATTRIBUTE";
                  case PARSEOP_AXIS_CHILD: return "PARSEOP_AXIS_CHILD";
                  case PARSEOP_AXIS_DESCENDANT: return "PARSEOP_AXIS_DESCENDANT";
                  case PARSEOP_AXIS_DESCENDANT_OR_SELF: return "PARSEOP_AXIS_DESCENDANT_OR_SELF";
                  case PARSEOP_AXIS_FOLLOWING: return "PARSEOP_AXIS_FOLLOWING";
                  case PARSEOP_AXIS_FOLLOWING_SIBLING: return "PARSEOP_AXIS_FOLLOWING_SIBLING";
                  case PARSEOP_AXIS_NAMESPACE: return "PARSEOP_AXIS_NAMESPACE";
                  case PARSEOP_AXIS_PARENT: return "PARSEOP_AXIS_PARENT";
                  case PARSEOP_AXIS_PRECEDING: return "PARSEOP_AXIS_PRECEDING";
                  case PARSEOP_AXIS_PRECEDING_SIBLING: return "PARSEOP_AXIS_PRECEDING_SIBLING";
                  case PARSEOP_AXIS_SELF: return "PARSEOP_AXIS_SELF";
                  case PARSEOP_NODETEST_ANY: return "PARSEOP_NODETEST_ANY";
                  case PARSEOP_NODETEST_NAMESPACE: return "PARSEOP_NODETEST_NAMESPACE";
                  case PARSEOP_NODETEST_QNAME: return "PARSEOP_NODETEST_QNAME";
                  case PARSEOP_NODETEST_COMMENT: return "PARSEOP_NODETEST_COMMENT";
                  case PARSEOP_NODETEST_TEXT: return "PARSEOP_NODETEST_TEXT";
                  case PARSEOP_NODETEST_PI: return "PARSEOP_NODETEST_PI";
                  case PARSEOP_NODETEST_PI_TARGET: return "PARSEOP_NODETEST_PI_TARGET";
                  case PARSEOP_NODETEST_NODE: return "PARSEOP_NODETEST_NODE";
                  case PARSEOP_FILTER: return "PARSEOP_FILTER";
                  case PARSEOP_PREDICATES: return "PARSEOP_PREDICATES";
                  case PARSEOP_PREDICATE: return "PARSEOP_PREDICATE";
                  case PARSEOP_VARIABLE_REFERENCE: return "PARSEOP_VARIABLE_REFERENCE";
                  case PARSEOP_GROUPING: return "PARSEOP_GROUPING";
                  case PARSEOP_LITERAL: return "PARSEOP_LITERAL";
                  case PARSEOP_NUMBER: return "PARSEOP_NUMBER";
                  case PARSEOP_FUNCTION: return "PARSEOP_FUNCTION";
                  case PARSEOP_FUNCTION_NAME: return "PARSEOP_FUNCTION_NAME";
                  case PARSEOP_FUNCTION_ARGUMENTS: return "PARSEOP_FUNCTION_ARGUMENTS";
              }
              return "??? ("+parseOp+')';
          }
      
          //
          // Protected methods
          //
      
          protected void checkParseOp(int parseOp) throws XPathException {
              // do nothing; all parse ops allowed in the class
          }
      
          //
          // Private methods
          //
      
          private void pushParseOp(int parseOp) throws XPathException {
              if (DEBUG_PUSH_PARSEOPS) {
                  System.out.println("pushParseOp: "+getParseOpName(parseOp));
              }
              checkParseOp(parseOp);
              try {
                  fParseTree[fParseOpCount] = parseOp;
              } catch (ArrayIndexOutOfBoundsException ex) {
                  int[] oldList = fParseTree;
                  fParseTree = new int[fParseOpCount << 1];
                  System.arraycopy(oldList, 0, fParseTree, 0, fParseOpCount);
                  fParseTree[fParseOpCount] = parseOp;
              }
              fCurrentParseOp = fParseOpCount++;
          }
      
          private void pushParseOp2(int parseOp, int arg) throws XPathException {
              if (DEBUG_PUSH_PARSEOPS) {
                  System.out.println("pushParseOp2: "+getParseOpName(parseOp)+", "+arg);
              }
              checkParseOp(parseOp);
              try {
                  fParseTree[fParseOpCount + 1] = parseOp;
              } catch (ArrayIndexOutOfBoundsException ex) {
                  int[] oldList = fParseTree;
                  fParseTree = new int[fParseOpCount << 1];
                  System.arraycopy(oldList, 0, fParseTree, 0, fParseOpCount);
                  fParseTree[fParseOpCount + 1] = parseOp;
              }
              fParseTree[fParseOpCount++] = arg;
              fCurrentParseOp = fParseOpCount++;
          }
      
          private void pushParseOp3(int parseOp, int arg1, int arg2) 
              throws XPathException {
              if (DEBUG_PUSH_PARSEOPS) {
                  System.out.println("pushParseOp3: "+getParseOpName(parseOp)+", "+arg1+", "+arg2);
              }
              checkParseOp(parseOp);
              try {
                  fParseTree[fParseOpCount + 2] = parseOp;
              } catch (ArrayIndexOutOfBoundsException ex) {
                  int[] oldList = fParseTree;
                  fParseTree = new int[fParseOpCount << 1];
                  System.arraycopy(oldList, 0, fParseTree, 0, fParseOpCount);
                  fParseTree[fParseOpCount + 2] = parseOp;
              }
              fParseTree[fParseOpCount++] = arg2;
              fParseTree[fParseOpCount++] = arg1;
              fCurrentParseOp = fParseOpCount++;
          }
      
          private void indentPrint(int indent) {
              for (int i = 0; i < indent; i++) {
                  System.out.print(" ");
              }
          }
      
          private void dumpParseTreeNode(int parseOp, int indent) {
                  switch (fParseTree[parseOp]) {
                  case PARSEOP_OR:
                      indentPrint(indent);
                      System.out.println("<or>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</or>");
                      break;
                  case PARSEOP_AND:
                      indentPrint(indent);
                      System.out.println("<and>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</and>");
                      break;
                  case PARSEOP_EQUAL:
                      indentPrint(indent);
                      System.out.println("<equal>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</equal>");
                      break;
                  case PARSEOP_NOT_EQUAL:
                      indentPrint(indent);
                      System.out.println("<not-equal>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</not-equal>");
                      break;
                  case PARSEOP_PLUS:
                      indentPrint(indent);
                      System.out.println("<plus>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</plus>");
                      break;
                  case PARSEOP_MINUS:
                      indentPrint(indent);
                      System.out.println("<minus>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</minus>");
                      break;
                  case PARSEOP_MULT:
                      indentPrint(indent);
                      System.out.println("<mult>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</mult>");
                      break;
                  case PARSEOP_DIV:
                      indentPrint(indent);
                      System.out.println("<div>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</div>");
                      break;
                  case PARSEOP_MOD:
                      indentPrint(indent);
                      System.out.println("<mod>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</mod>");
                      break;
                  case PARSEOP_LESS:
                      indentPrint(indent);
                      System.out.println("<less>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</less>");
                      break;
                  case PARSEOP_GREATER:
                      indentPrint(indent);
                      System.out.println("<greater>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</greater>");
                      break;
                  case PARSEOP_LESS_EQUAL:
                      indentPrint(indent);
                      System.out.println("<less-equal>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</less-equal>");
                      break;
                  case PARSEOP_GREATER_EQUAL:
                      indentPrint(indent);
                      System.out.println("<greater-equal>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</greater-equal>");
                      break;
                  case PARSEOP_NEGATE:
                      indentPrint(indent);
                      System.out.println("<negate>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      indentPrint(indent);
                      System.out.println("</negate>");
                      break;
                  case PARSEOP_UNION:
                      indentPrint(indent);
                      System.out.println("<union>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</union>");
                      break;
                  case PARSEOP_SELECT_ROOT:
                      indentPrint(indent);
                      if (fParseTree[parseOp - 1] == -1) {
                          System.out.println("<select-root/>");
                      } else {
                          System.out.println("<select-root>");
                          dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                          indentPrint(indent);
                          System.out.println("</select-root>");
                      }
                      break;
                  case PARSEOP_STEPS:
                      indentPrint(indent);
                      System.out.println("<relative-location-path>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</relative-location-path>");
                      break;
                  case PARSEOP_STEP:
                      indentPrint(indent);
                      System.out.println("<step>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</step>");
                      break;
                  case PARSEOP_AXIS_ANCESTOR:
                      indentPrint(indent);
                      System.out.println("<axis name=\"ancestor\"/>");
                      break;
                  case PARSEOP_AXIS_ANCESTOR_OR_SELF:
                      indentPrint(indent);
                      System.out.println("<axis name=\"ancestor-or-self\"/>");
                      break;
                  case PARSEOP_AXIS_ATTRIBUTE:
                      indentPrint(indent);
                      System.out.println("<axis name=\"attribute\"/>");
                      break;
                  case PARSEOP_AXIS_CHILD:
                      indentPrint(indent);
                      System.out.println("<axis name=\"child\"/>");
                      break;
                  case PARSEOP_AXIS_DESCENDANT:
                      indentPrint(indent);
                      System.out.println("<axis name=\"descendant\"/>");
                      break;
                  case PARSEOP_AXIS_DESCENDANT_OR_SELF:
                      indentPrint(indent);
                      System.out.println("<axis name=\"descendant-or-self\"/>");
                      break;
                  case PARSEOP_AXIS_FOLLOWING:
                      indentPrint(indent);
                      System.out.println("<axis name=\"following\"/>");
                      break;
                  case PARSEOP_AXIS_FOLLOWING_SIBLING:
                      indentPrint(indent);
                      System.out.println("<axis name=\"following-sibling\"/>");
                      break;
                  case PARSEOP_AXIS_NAMESPACE:
                      indentPrint(indent);
                      System.out.println("<axis name=\"namespace\"/>");
                      break;
                  case PARSEOP_AXIS_PARENT:
                      indentPrint(indent);
                      System.out.println("<axis name=\"parent\"/>");
                      break;
                  case PARSEOP_AXIS_PRECEDING:
                      indentPrint(indent);
                      System.out.println("<axis name=\"preceding\"/>");
                      break;
                  case PARSEOP_AXIS_PRECEDING_SIBLING:
                      indentPrint(indent);
                      System.out.println("<axis name=\"preceding-sibling\"/>");
                      break;
                  case PARSEOP_AXIS_SELF:
                      indentPrint(indent);
                      System.out.println("<axis name=\"self\"/>");
                      break;
                  case PARSEOP_NODETEST_ANY:
                      indentPrint(indent);
                      System.out.println("<nodetest type=\"any\"/>");
                      break;
                  case PARSEOP_NODETEST_NAMESPACE:
                      indentPrint(indent);
                      System.out.print("<nodetest type=\"namespace\"");
                      System.out.print(" prefix=\"" + fParseTree[parseOp - 1] + "\"");
                      System.out.println("/>");
                      break;
                  case PARSEOP_NODETEST_QNAME:
                      indentPrint(indent);
                      System.out.print("<nodetest type=\"qname\"");
                      if (fParseTree[parseOp - 1] != -1) {
                          System.out.print(" prefix=\"" + fParseTree[parseOp - 1] + "\"");
                      }
                      System.out.print(" localpart=\"" + fParseTree[parseOp - 2] + "\"");
                      System.out.println("/>");
                      break;
                  case PARSEOP_NODETEST_COMMENT:
                      indentPrint(indent);
                      System.out.println("<nodetest type=\"comment\"/>");
                      break;
                  case PARSEOP_NODETEST_TEXT:
                      indentPrint(indent);
                      System.out.println("<nodetest type=\"text\"/>");
                      break;
                  case PARSEOP_NODETEST_PI:
                      indentPrint(indent);
                      System.out.println("<nodetest type=\"processing-instruction\"/>");
                      break;
                  case PARSEOP_NODETEST_PI_TARGET:
                      indentPrint(indent);
                      System.out.print("<nodetest type=\"processing-instruction\" target=\"");
                      System.out.print(fParseTree[parseOp - 1]);
                      System.out.println("\"/>");
                      break;
                  case PARSEOP_NODETEST_NODE:
                      indentPrint(indent);
                      System.out.println("<nodetest type=\"node\"/>");
                      break;
                  case PARSEOP_FILTER:
                      indentPrint(indent);
                      System.out.println("<step-with-predicate>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</step-with-predicate>");
                      break;
                  case PARSEOP_PREDICATES:
                      indentPrint(indent);
                      System.out.println("<predicates>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</predicates>");
                      break;
                  case PARSEOP_PREDICATE:
                      indentPrint(indent);
                      System.out.println("<predicate>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      indentPrint(indent);
                      System.out.println("</predicate>");
                      break;
                  case PARSEOP_VARIABLE_REFERENCE:
                      indentPrint(indent);
                      System.out.print("<variable-reference");
                      if (fParseTree[parseOp - 1] != -1) {
                          System.out.print(" prefix=\"" + fParseTree[parseOp - 1] + "\"");
                      }
                      System.out.print(" localpart=\"" + fParseTree[parseOp - 2] + "\"");
                      System.out.println("/>");
                      break;
                  case PARSEOP_GROUPING:
                      indentPrint(indent);
                      System.out.println("<grouping>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      indentPrint(indent);
                      System.out.println("</grouping>");
                      break;
                  case PARSEOP_LITERAL:
                      indentPrint(indent);
                      System.out.print("<literal");
                      System.out.print(" value=\"" + fParseTree[parseOp - 1] + "\"");
                      System.out.println("/>");
                      break;
                  case PARSEOP_NUMBER:
                      indentPrint(indent);
                      System.out.print("<number");
                      System.out.print(" whole=\"" + fParseTree[parseOp - 1] + "\"");
                      System.out.print(" part=\"" + fParseTree[parseOp - 2] + "\"");
                      System.out.println("/>");
                      break;
                  case PARSEOP_FUNCTION:
                      indentPrint(indent);
                      System.out.println("<function-call>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      if (fParseTree[parseOp - 2] != -1) {
                          dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      }
                      indentPrint(indent);
                      System.out.println("</function-call>");
                      break;
                  case PARSEOP_FUNCTION_NAME:
                      indentPrint(indent);
                      System.out.print("<function-name");
                      if (fParseTree[parseOp - 1] != -1) {
                          System.out.print(" prefix=\"" + fParseTree[parseOp - 1] + "\"");
                      }
                      System.out.print(" localpart=\"" + fParseTree[parseOp - 2] + "\"");
                      System.out.println("/>");
                      break;
                  case PARSEOP_FUNCTION_ARGUMENTS:
                      indentPrint(indent);
                      System.out.println("<function-args>");
                      dumpParseTreeNode(fParseTree[parseOp - 1], indent + 2);
                      dumpParseTreeNode(fParseTree[parseOp - 2], indent + 2);
                      indentPrint(indent);
                      System.out.println("</function-args>");
                      break;
                  default:
                      throw new RuntimeException("dumpParseTreeNode("+parseOp+")");
                  }
          }
          
          //
          // Package methods
          //
      
          /**
           * [14] Expr ::= OrExpr
           * /
          boolean parseExpr() throws XPathException {
              return parseOrExpr();
          }
      
          /**
           * [21] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
           *
           * also: OrExpr ::= (AndExpr 'or')* AndExpr
           * /
          boolean parseOrExpr() throws XPathException {
              if (!parseAndExpr()) {
                  return false;
              }
              while (fCurrentToken < fTokenCount) {
                  if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_OPERATOR_OR) {
                      break;
                  }
                  int saveToken = fCurrentToken;
                  int saveParseOp = fCurrentParseOp;
                  int left = fCurrentParseOp;
                  if (++fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  if (!parseAndExpr()) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  int right = fCurrentParseOp;
                  pushParseOp3(PARSEOP_OR, left, right);
              }
              return true;
          }
      
          /**
           * [22] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
           * /
          boolean parseAndExpr() throws XPathException {
              if (!parseEqualityExpr()) {
                  return false;
              }
              while (fCurrentToken < fTokenCount) {
                  if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_OPERATOR_AND) {
                      break;
                  }
                  int saveToken = fCurrentToken;
                  int saveParseOp = fCurrentParseOp;
                  int left = fCurrentParseOp;
                  if (++fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  if (!parseEqualityExpr()) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  int right = fCurrentParseOp;
                  pushParseOp3(PARSEOP_AND, left, right);
              }
              return true;
          }
      
          /**
           * [23] EqualityExpr ::= RelationalExpr
           *                     | EqualityExpr '=' RelationalExpr
           *                     | EqualityExpr '!=' RelationalExpr
           * /
          boolean parseEqualityExpr() throws XPathException {
              if (!parseRelationalExpr()) {
                  return false;
              }
              while (fCurrentToken < fTokenCount) {
                  int parseOp;
                  if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_EQUAL) {
                      parseOp = PARSEOP_EQUAL;
                  } else if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_NOT_EQUAL) {
                      parseOp = PARSEOP_NOT_EQUAL;
                  } else {
                      break;
                  }
                  int saveToken = fCurrentToken;
                  int saveParseOp = fCurrentParseOp;
                  int left = fCurrentParseOp;
                  if (++fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  if (!parseRelationalExpr()) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  int right = fCurrentParseOp;
                  pushParseOp3(parseOp, left, right);
              }
              return true;
          }
      
          /**
           * [24] RelationalExpr ::= AdditiveExpr
           *                       | RelationalExpr '<' AdditiveExpr
           *                       | RelationalExpr '>' AdditiveExpr
           *                       | RelationalExpr '<=' AdditiveExpr
           *                       | RelationalExpr '>=' AdditiveExpr
           * /
          boolean parseRelationalExpr() throws XPathException {
              if (!parseAdditiveExpr()) {
                  return false;
              }
              while (fCurrentToken < fTokenCount) {
                  int parseOp;
                  if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_LESS) {
                      parseOp = PARSEOP_LESS;
                  } else if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER) {
                      parseOp = PARSEOP_GREATER;
                  } else if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_LESS_EQUAL) {
                      parseOp = PARSEOP_LESS_EQUAL;
                  } else if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER_EQUAL) {
                      parseOp = PARSEOP_GREATER_EQUAL;
                  } else {
                      break;
                  }
                  int saveToken = fCurrentToken;
                  int saveParseOp = fCurrentParseOp;
                  int left = fCurrentParseOp;
                  if (++fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  if (!parseAdditiveExpr()) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  int right = fCurrentParseOp;
                  pushParseOp3(parseOp, left, right);
              }
              return true;
          }
      
          /**
           * [25] AdditiveExpr ::= MultiplicativeExpr
           *                     | AdditiveExpr '+' MultiplicativeExpr
           *                     | AdditiveExpr '-' MultiplicativeExpr
           * /
          boolean parseAdditiveExpr() throws XPathException {
              if (!parseMultiplicativeExpr()) {
                  return false;
              }
              while (fCurrentToken < fTokenCount) {
                  int parseOp;
                  if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_PLUS) {
                      parseOp = PARSEOP_PLUS;
                  } else if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_MINUS) {
                      parseOp = PARSEOP_MINUS;
                  } else {
                      break;
                  }
                  int saveToken = fCurrentToken;
                  int saveParseOp = fCurrentParseOp;
                  int left = fCurrentParseOp;
                  if (++fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  if (!parseMultiplicativeExpr()) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  int right = fCurrentParseOp;
                  pushParseOp3(parseOp, left, right);
              }
              return true;
          }
      
          /**
           * [26] MultiplicativeExpr ::= UnaryExpr
           *                           | MultiplicativeExpr MultiplyOperator UnaryExpr
           *                           | MultiplicativeExpr 'div' UnaryExpr
           *                           | MultiplicativeExpr 'mod' UnaryExpr
           * [34] MultiplyOperator ::= '*'
           * /
          boolean parseMultiplicativeExpr() throws XPathException {
              if (!parseUnaryExpr()) {
                  return false;
              }
              while (fCurrentToken < fTokenCount) {
                  int parseOp;
                  if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_MULT) {
                      parseOp = PARSEOP_MULT;
                  } else if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_DIV) {
                      parseOp = PARSEOP_DIV;
                  } else if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_MOD) {
                      parseOp = PARSEOP_MOD;
                  } else {
                      break;
                  }
                  int saveToken = fCurrentToken;
                  int saveParseOp = fCurrentParseOp;
                  int left = fCurrentParseOp;
                  if (++fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  if (!parseUnaryExpr()) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  int right = fCurrentParseOp;
                  pushParseOp3(parseOp, left, right);
              }
              return true;
          }
      
          /**
           * [27] UnaryExpr ::= UnionExpr | '-' UnaryExpr
           *
           * Note: "--UnionExpr" == "-(-UnionExpr)"
           * /
          boolean parseUnaryExpr() throws XPathException {
              if (parseUnionExpr()) {
                  return true;
              }
              int saveToken = fCurrentToken;
              boolean negate = false;
              while (fCurrentToken < fTokenCount) {
                  if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_OPERATOR_MINUS) {
                      break;
                  }
                  fCurrentToken++;
                  negate = !negate;
              }
              if (fCurrentToken == fTokenCount) {
                  fCurrentToken = saveToken;
                  return false;
              }
              if (!parseUnionExpr()) {
                  fCurrentToken = saveToken;
                  return false;
              }
              if (negate) {
                  pushParseOp2(PARSEOP_NEGATE, fCurrentParseOp);
              }
              return true;
          }
      
          /**
           * [18] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
           * /
          boolean parseUnionExpr() throws XPathException {
              if (!parsePathExpr()) {
                  return false;
              }
              while (fCurrentToken < fTokenCount) {
                  if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_OPERATOR_UNION) {
                      break;
                  }
                  int saveToken = fCurrentToken;
                  int saveParseOp = fCurrentParseOp;
                  int left = fCurrentParseOp;
                  if (++fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  if (!parsePathExpr()) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  int right = fCurrentParseOp;
                  pushParseOp3(PARSEOP_UNION, left, right);
              }
              return true;
          }
      
          /**
           * [19] PathExpr ::= RelativeLocationPath
           *                 | '/' RelativeLocationPath?
           *                 | '//' RelativeLocationPath
           *                 | PrimaryExpr Predicate*
           *                 | PrimaryExpr Predicate* '/' RelativeLocationPath
           *                 | PrimaryExpr Predicate* '//' RelativeLocationPath
           * /
          boolean parsePathExpr() throws XPathException {
              if (parseRelativeLocationPath()) {
                  return true;
              } else {
                  if (fCurrentToken == fTokenCount) {
                      return false;
                  }
                  if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH) {
                      if (++fCurrentToken < fTokenCount && parseRelativeLocationPath()) {
                          pushParseOp2(PARSEOP_SELECT_ROOT, fCurrentParseOp);
                      } else {
                          pushParseOp2(PARSEOP_SELECT_ROOT, -1);
                      }
                      return true;
                  } else if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH) {
                      if (++fCurrentToken == fTokenCount) {
                          return false; // REVISIT - backup index
                      }
                      if (!parseRelativeLocationPath()) {
                          return false;
                      }
                      int left = fCurrentParseOp;
                      pushParseOp(PARSEOP_AXIS_DESCENDANT_OR_SELF);
                      int left2 = fCurrentParseOp;
                      pushParseOp(PARSEOP_NODETEST_NODE);
                      pushParseOp3(PARSEOP_STEP, left2, fCurrentParseOp);
                      pushParseOp3(PARSEOP_STEPS, fCurrentParseOp, left);
                      pushParseOp2(PARSEOP_SELECT_ROOT, fCurrentParseOp);
                      return true;
                  }
              }
              if (!parsePrimaryExpr()) {
                  return false;
              }
              int left = fCurrentParseOp;
              if (parsePredicates()) {
                  pushParseOp3(PARSEOP_FILTER, left, fCurrentParseOp);
              }
              if (fCurrentToken == fTokenCount) {
                  return true;
              }
              left = fCurrentParseOp;
              if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH) {
                  if (++fCurrentToken == fTokenCount) {
                      return false; // REVISIT
                  }
                  if (!parseRelativeLocationPath()) {
                      return false; // REVISIT
                  }
                  pushParseOp3(PARSEOP_STEPS, left, fCurrentParseOp);
              } else if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH) {
                  if (++fCurrentToken == fTokenCount) {
                      return false;
                  }
                  if (!parseRelativeLocationPath()) {
                      return false;
                  }
                  int left2 = fCurrentParseOp;
                  pushParseOp(PARSEOP_AXIS_DESCENDANT_OR_SELF);
                  int left3 = fCurrentParseOp;
                  pushParseOp(PARSEOP_NODETEST_NODE);
                  pushParseOp3(PARSEOP_STEP, left3, fCurrentParseOp);
                  pushParseOp3(PARSEOP_STEPS, fCurrentParseOp, left2);
                  pushParseOp3(PARSEOP_STEPS, left, fCurrentParseOp);
              }
              return true;
          }
      
          /**
           * [3] RelativeLocationPath ::= Step
           *                            | RelativeLocationPath '/' Step
           *                            | RelativeLocationPath '//' Step
           * /
          boolean parseRelativeLocationPath() throws XPathException {
              if (!parseStep()) {
                  return false;
              }
              while (fCurrentToken < fTokenCount) {
                  boolean descendantOrSelf;
                  if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH) {
                      descendantOrSelf = false;
                  } else if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH) {
                      descendantOrSelf = true;
                  } else {
                      break;
                  }
                  int saveToken = fCurrentToken;
                  int saveParseOp = fCurrentParseOp;
                  int left = fCurrentParseOp;
                  if (++fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  if (!parseStep()) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return true;
                  }
                  if (descendantOrSelf) {
                      int left2 = fCurrentParseOp;
                      pushParseOp(PARSEOP_AXIS_DESCENDANT_OR_SELF);
                      int left3 = fCurrentParseOp;
                      pushParseOp(PARSEOP_NODETEST_NODE);
                      pushParseOp3(PARSEOP_STEP, left3, fCurrentParseOp);
                      pushParseOp3(PARSEOP_STEPS, left, fCurrentParseOp);
                      pushParseOp3(PARSEOP_STEPS, fCurrentParseOp, left2);
                  } else {
                      pushParseOp3(PARSEOP_STEPS, left, fCurrentParseOp);
                  }
              }
              return true;
          }
      
          /**
           * [4] Step ::=  (AxisName '::' | '@'?) NodeTest Predicate* | '.' | '..'
           * [6] AxisName ::= 'ancestor' | 'ancestor-or-self'
           *                | 'attribute'
           *                | 'child'
           *                | 'descendant' | 'descendant-or-self'
           *                | 'following' | 'following-sibling'
           *                | 'namespace'
           *                | 'parent'
           *                | 'preceding' | 'preceding-sibling'
           *                | 'self'
           * /
          boolean parseStep() throws XPathException {
              int parseOp;
              int left;
              boolean checkDoubleColon = true;
              int saveToken = fCurrentToken;
              int saveParseOp = fCurrentParseOp;
              if (fCurrentToken == fTokenCount) {
                  return false;
              }
              switch (fTokens.getToken(fCurrentToken)) {
              case XPath.Tokens.EXPRTOKEN_PERIOD:
                  fCurrentToken++;
                  pushParseOp(PARSEOP_AXIS_SELF);
                  left = fCurrentParseOp;
                  pushParseOp(PARSEOP_NODETEST_NODE);
                  pushParseOp3(PARSEOP_STEP, left, fCurrentParseOp);
                  return true;
              case XPath.Tokens.EXPRTOKEN_DOUBLE_PERIOD:
                  fCurrentToken++;
                  pushParseOp(PARSEOP_AXIS_PARENT);
                  left = fCurrentParseOp;
                  pushParseOp(PARSEOP_NODETEST_NODE);
                  pushParseOp3(PARSEOP_STEP, left, fCurrentParseOp);
                  return true;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_ANCESTOR;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_ANCESTOR_OR_SELF;
                  break;
              case XPath.Tokens.EXPRTOKEN_ATSIGN:
                  checkDoubleColon = false;
                  // fall through
              case XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_ATTRIBUTE;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_DESCENDANT;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_DESCENDANT_OR_SELF;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_FOLLOWING;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_FOLLOWING_SIBLING;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_NAMESPACE:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_NAMESPACE;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_PARENT:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_PARENT;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_PRECEDING;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING_SIBLING:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_PRECEDING_SIBLING;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_SELF:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_SELF;
                  break;
              case XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD:
                  fCurrentToken++;
                  parseOp = PARSEOP_AXIS_CHILD;
                  break;
              default:
                  checkDoubleColon = false;
                  parseOp = PARSEOP_AXIS_CHILD;
                  break;
              }
              pushParseOp(parseOp);
              left = fCurrentParseOp;
              if (checkDoubleColon) {
                  if (fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return false;
                  }
                  if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return false;
                  }
                  fCurrentToken++;
              }
              if (fCurrentToken == fTokenCount) {
                  fCurrentToken = saveToken;
                  fCurrentParseOp = saveParseOp;
                  return false;
              }
              if (!parseNodeTest()) {
                  fCurrentToken = saveToken;
                  fCurrentParseOp = saveParseOp;
                  return false;
              }
              pushParseOp3(PARSEOP_STEP, left, fCurrentParseOp);
              left = fCurrentParseOp;
              if (parsePredicates()) {
                  pushParseOp3(PARSEOP_FILTER, left, fCurrentParseOp);
              }
              return true;
          }
      
          /**
           * [7] NodeTest ::= '*'
           *                | NCName ':' '*'
           *                | QName
           *                | 'comment' '(' ')'
           *                | 'text' '(' ')'
           *                | 'processing-instruction' '(' Literal? ')'
           *                | 'node' '(' ')'
           * [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'"
           * /
          boolean parseNodeTest() throws XPathException {
              int parseOp;
              int prefix;
              int name;
              if (fCurrentToken == fTokenCount) {
                  return false;
              }
              switch (fTokens.getToken(fCurrentToken)) {
              case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY:
                  fCurrentToken++;
                  pushParseOp(PARSEOP_NODETEST_ANY);
                  return true;
              case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE:
                  prefix = fTokens.getToken(++fCurrentToken);
                  fCurrentToken++;
                  pushParseOp2(PARSEOP_NODETEST_NAMESPACE, prefix);
                  return true;
              case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME:
                  prefix = fTokens.getToken(++fCurrentToken);
                  name = fTokens.getToken(++fCurrentToken);
                  fCurrentToken++;
                  pushParseOp3(PARSEOP_NODETEST_QNAME, prefix, name);
                  return true;
              case XPath.Tokens.EXPRTOKEN_NODETYPE_COMMENT:
                  parseOp = PARSEOP_NODETEST_COMMENT;
                  break;
              case XPath.Tokens.EXPRTOKEN_NODETYPE_TEXT:
                  parseOp = PARSEOP_NODETEST_TEXT;
                  break;
              case XPath.Tokens.EXPRTOKEN_NODETYPE_PI:
                  parseOp = PARSEOP_NODETEST_PI;
                  break;
              case XPath.Tokens.EXPRTOKEN_NODETYPE_NODE:
                  parseOp = PARSEOP_NODETEST_NODE;
                  break;
              default:
                  return false;
              }
              int saveToken = fCurrentToken;
              int saveParseOp = fCurrentParseOp;
              int left = fCurrentParseOp;
              if (++fCurrentToken == fTokenCount) {
                  fCurrentToken = saveToken;
                  fCurrentParseOp = saveParseOp;
                  return false;
              }
              if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_OPEN_PAREN) {
                  fCurrentToken = saveToken;
                  fCurrentParseOp = saveParseOp;
                  return false;
              }
              if (++fCurrentToken == fTokenCount) {
                  fCurrentToken = saveToken;
                  fCurrentParseOp = saveParseOp;
                  return false;
              }
              if (parseOp == PARSEOP_NODETEST_PI && fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_LITERAL) {
                  int target = fTokens.getToken(++fCurrentToken);
                  if (++fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return false;
                  }
                  if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_CLOSE_PAREN) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return false;
                  }
                  fCurrentToken++;
                  pushParseOp2(PARSEOP_NODETEST_PI_TARGET, target);
              } else {
                  if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_CLOSE_PAREN) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return false;
                  }
                  fCurrentToken++;
                  pushParseOp(parseOp);
              }
              return true;
          }
      
          /**
           * [8] Predicate ::= '[' PredicateExpr ']'
           * [9] PredicateExpr ::= Expr
           * [4] Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep
           * [20] FilterExpr ::= PrimaryExpr Predicate*
           * /
          boolean parsePredicates() throws XPathException {
              int left = -1;
              boolean found = false;
              while (true) {
                  if (fCurrentToken == fTokenCount || fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_OPEN_BRACKET) {
                      return found;
                  }
                  int saveToken = fCurrentToken;
                  int saveParseOp = fCurrentParseOp;
                  if (++fCurrentToken == fTokenCount || !parseExpr() ||
                      fCurrentToken == fTokenCount || fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_CLOSE_BRACKET) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return found;
                  }
                  fCurrentToken++;
                  found = true;
                  pushParseOp2(PARSEOP_PREDICATE, fCurrentParseOp);
                  if (left != -1) {
                      pushParseOp3(PARSEOP_PREDICATES, left, fCurrentParseOp);
                  }
                  left = fCurrentParseOp;
              }
          }
      
          /**
           * [15] PrimaryExpr ::= '$' QName
           *                    | '(' Expr ')'
           *                    | '"' [^"]* '"' | "'" [^']* "'"
           *                    | ([0-9]+) ('.' ([0-9]+)?)? | '.' Digits
           *                    | (QName - NodeType) '(' ( Expr ( ',' Expr )* )? ')'
           * /
          boolean parsePrimaryExpr() throws XPathException {
              int prefix;
              int handle;
              int saveToken;
              int saveParseOp;
              if (fCurrentToken == fTokenCount) {
                  return false;
              }
              switch (fTokens.getToken(fCurrentToken)) {
              case XPath.Tokens.EXPRTOKEN_VARIABLE_REFERENCE:
                  prefix = fTokens.getToken(++fCurrentToken);
                  handle = fTokens.getToken(++fCurrentToken); // localpart
                  fCurrentToken++;
                  pushParseOp3(PARSEOP_VARIABLE_REFERENCE, prefix, handle);
                  break;
              case XPath.Tokens.EXPRTOKEN_OPEN_PAREN:
                  saveToken = fCurrentToken;
                  saveParseOp = fCurrentParseOp;
                  if (++fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return false;
                  }
                  if (!parseExpr()) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return false;
                  }
                  if (fCurrentToken == fTokenCount) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return false;
                  }
                  if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_CLOSE_PAREN) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return false;
                  }
                  fCurrentToken++;
                  pushParseOp2(PARSEOP_GROUPING, fCurrentParseOp);
                  break;
              case XPath.Tokens.EXPRTOKEN_LITERAL:
                  handle = fTokens.getToken(++fCurrentToken);
                  fCurrentToken++;
                  pushParseOp2(PARSEOP_LITERAL, handle);
                  break;
              case XPath.Tokens.EXPRTOKEN_NUMBER:
                  int whole = fTokens.getToken(++fCurrentToken);
                  int part = fTokens.getToken(++fCurrentToken);
                  fCurrentToken++;
                  pushParseOp3(PARSEOP_NUMBER, whole, part);
                  break;
              case XPath.Tokens.EXPRTOKEN_FUNCTION_NAME:
                  saveToken = fCurrentToken;
                  saveParseOp = fCurrentParseOp;
                  prefix = fTokens.getToken(++fCurrentToken);
                  handle = fTokens.getToken(++fCurrentToken); // localpart
                  fCurrentToken++;
                  pushParseOp3(PARSEOP_FUNCTION_NAME, prefix, handle);
                  if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_OPEN_PAREN) {
                      fCurrentToken = saveToken;
                      fCurrentParseOp = saveParseOp;
                      return false;
                  }
                  fCurrentToken++;
                  int funcName = fCurrentParseOp;
                  int nextArg = -1;
                  if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_CLOSE_PAREN) {
                      fCurrentToken++;
                  } else {
                      while (true) {
                          if (!parseExpr()) {
                              fCurrentToken = saveToken;
                              fCurrentParseOp = saveParseOp;
                              return false;
                          }
                          if (nextArg != -1) {
                              pushParseOp3(PARSEOP_FUNCTION_ARGUMENTS, nextArg, fCurrentParseOp);
                          }
                          nextArg = fCurrentParseOp;
                          if (fTokens.getToken(fCurrentToken) == XPath.Tokens.EXPRTOKEN_CLOSE_PAREN) {
                              fCurrentToken++;
                              break;
                          }
                          if (fTokens.getToken(fCurrentToken) != XPath.Tokens.EXPRTOKEN_COMMA) {
                              fCurrentToken = saveToken;
                              fCurrentParseOp = saveParseOp;
                              return false;
                          }
                          fCurrentToken++;
                      }
                  }
                  pushParseOp3(PARSEOP_FUNCTION, funcName, nextArg);
                  break;
              default:
                  return false;
              }
              return true;
          }
          
          //
          // MAIN
          //
      
          public static void main(String argv[]) {
              for (int i = 0; i < argv.length; i++) {
                  String expression = argv[i];
                  System.out.println("# XPath expression: "+expression);
                  XPathExprParser parser = new XPathExprParser();
                  try {
                      parser.parseExpr(expression);
                  }
                  catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }
      
      } // class XPathExprParser
      /***/
      
      //
      // MAIN
      //
  
      /** Main program entry. */
      public static void main(String[] argv) throws Exception {
  
          for (int i = 0; i < argv.length; i++) {
              final String expression = argv[i];
              System.out.println("# XPath expression: \""+expression+'"');
              try {
                  StringPool stringPool = new StringPool();
                  XPath xpath = new XPath(expression, stringPool, null);
                  System.out.println("expanded xpath: \""+xpath.toString()+'"');
              }
              catch (XPathException e) {
                  System.out.println("error: "+e.getMessage());
              }
          }
  
      } // main(String[])
  
  } // class XPath
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/XPathException.java
  
  Index: XPathException.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001 The Apache Software Foundation.  
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  /**
   * XPath exception.
   *
   * @author Andy Clark, IBM
   *
   * @version $Id: XPathException.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public class XPathException 
      extends Exception {
  
      //
      // Constructors
      //
  
      /** Constructs an exception. */
      public XPathException() {
          super();
      } // <init>()
  
      /** Constructs an exception with the specified message. */
      public XPathException(String message) {
          super(message);
      } // <init>(String)
  
  } // class XPathException
  
  
  
  1.1                  xml-xerces/java/src/org/apache/xerces/validators/schema/identity/XPathMatcher.java
  
  Index: XPathMatcher.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001 The Apache Software Foundation.  
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:  
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.xerces.validators.schema.identity;
  
  import org.apache.xerces.framework.XMLAttrList;
  
  import org.apache.xerces.utils.QName;
  import org.apache.xerces.utils.NamespacesScope;
  import org.apache.xerces.utils.StringPool;
  
  import org.xml.sax.SAXException;
  import org.xml.sax.SAXNotRecognizedException;
  import org.xml.sax.SAXNotSupportedException;
  
  /**
   * XPath matcher.
   *
   * @author Andy Clark, IBM
   *
   * @version $Id: XPathMatcher.java,v 1.1 2001/01/18 07:10:55 andyc Exp $
   */
  public class XPathMatcher {
  
      //
      // Constants
      //
  
      // debugging
  
      /** Compile to true to debug everything. */
      private static final boolean DEBUG_ALL = false;
  
      /** Compile to true to debug method callbacks. */
      private static final boolean DEBUG_METHODS = DEBUG_ALL || false;
  
      /** Compile to true to debug important method callbacks. */
      private static final boolean DEBUG_METHODS2 = DEBUG_ALL || DEBUG_METHODS || 
                                                    false;
  
      /** Compile to true to debug match. */
      private static final boolean DEBUG_MATCH = DEBUG_ALL || false;
  
      /** Don't touch this value unless you add more debug constants. */
      private static final boolean DEBUG_ANY = DEBUG_METHODS || 
                                               DEBUG_METHODS2 ||
                                               DEBUG_MATCH;
  
      //
      // Data
      //
  
      /** XPath location path. */
      private XPath.LocationPath fLocationPath;
  
      /** Application preference to buffer content or not. */
      private boolean fShouldBufferContent;
  
      /** True if should buffer character content <em>at this time</em>. */
      private boolean fBufferContent;
  
      /** Buffer to hold match text. */
      private StringBuffer fMatchedBuffer = new StringBuffer();
  
      /** True if XPath has been matched. */
      private boolean fMatched;
  
      /** The matching string. */
      private String fMatchedString;
  
      /** Integer stack of step indexes. */
      private IntegerStack fStepIndexes = new IntegerStack();
  
      /** Current step. */
      private int fCurrentStep;
  
      /** 
       * No match depth. The value of this field will be zero while
       * matching is successful.
       */
      private int fNoMatchDepth;
  
      // Xerces 1.x framework
  
      /** String pool. */
      protected StringPool fStringPool;
  
      /** Namespace scope. */
      protected NamespacesScope fNamespacesScope;
  
      //
      // Constructors
      //
  
      /** 
       * Constructs an XPath matcher that implements a document fragment 
       * handler. 
       *
       * @param xpath   The xpath.
       * @param symbols The symbol table.
       */
      public XPathMatcher(XPath xpath) {
          this(xpath, false);
      } // <init>(Stringm,SymbolTable,NamespaceContext)
  
      /** 
       * Constructs an XPath matcher that implements a document fragment 
       * handler. 
       *
       * @param xpath   The xpath.
       * @param symbols The symbol table.
       * @param shouldBufferContent True if the matcher should buffer the
       *                            matched content.
       */
      public XPathMatcher(XPath xpath, boolean shouldBufferContent) {
          fLocationPath = xpath.getLocationPath();
          fShouldBufferContent = shouldBufferContent;
          if (DEBUG_METHODS) {
              System.out.println("XPATH["+toString()+"]: <init>()");
          }
      } // <init>(String,SymbolTable,NamespaceContext,boolean)
  
      //
      // Public methods
      //
  
      /** Returns true if XPath has been matched. */
      public boolean isMatched() {
          return fMatched;
      } // isMatched():boolean
  
      /** Returns the matched string. */
      public String getMatchedString() {
          return fMatchedString;
      } // getMatchedString():String
  
      //
      // Protected methods
      //
  
      /**
       * This method is called when the XPath handler matches the
       * XPath expression. Subclasses can override this method to
       * provide default handling upon a match.
       */
      protected void matched(String content) throws SAXException {
          if (DEBUG_METHODS || DEBUG_METHODS2) {
              System.out.println("XPATH["+toString()+"]: matched \""+content+'"');
          }
      } // matched(String content)
  
      //
      // XMLDocumentFragmentHandler methods
      //
  
      /**
       * The start of the document fragment.
       *
       * @param namespaceContext The namespace context in effect at the
       *                         start of this document fragment. This
       *                         object only represents the current context.
       *                         Implementors of this class are responsible
       *                         for copying the namespace bindings from the
       *                         the current context (and its parent contexts)
       *                         if that information is important.
       *
       * @throws SAXException Thrown by handler to signal an error.
       */
      public void startDocumentFragment(StringPool stringPool,
                                        NamespacesScope namespacesScope) 
          throws Exception {
          if (DEBUG_METHODS) {
              System.out.println("XPATH["+toString()+"]: startDocumentFragment("+
                                 "stringPool="+stringPool+','+
                                 "namespacesScope="+namespacesScope+
                                 ")");
          }
  
          // reset state
          clear();
          fMatchedBuffer.setLength(0);
          fStepIndexes.clear();
          fCurrentStep = 0;
          fNoMatchDepth = 0;
  
          // keep values
          fStringPool = stringPool;
          fNamespacesScope = namespacesScope;
          if (namespacesScope == null) {
              NamespacesScope.NamespacesHandler handler =
                  new NamespacesScope.NamespacesHandler() {
                  public void startNamespaceDeclScope(int prefix, int uri) throws Exception {
                  }
                  public void endNamespaceDeclScope(int prefix) throws Exception {
                  }
              };
              fNamespacesScope = new NamespacesScope(handler);
          }
  
      } // startDocumentFragment(StringPool,NamespacesScope)
  
      /**
       * The start of an element. If the document specifies the start element
       * by using an empty tag, then the startElement method will immediately
       * be followed by the endElement method, with no intervening methods.
       * 
       * @param element    The name of the element.
       * @param attributes The element attributes.
       *
       * @throws SAXException Thrown by handler to signal an error.
       */
      public void startElement(QName element, XMLAttrList attributes, int handle)
          throws Exception {
          if (DEBUG_METHODS || DEBUG_METHODS2) {
              System.out.println("XPATH["+toString()+"]: startElement("+
                                 "element={"+
                                 "prefix="+fStringPool.toString(element.prefix)+','+
                                 "localpart="+fStringPool.toString(element.localpart)+','+
                                 "rawname="+fStringPool.toString(element.rawname)+','+
                                 "uri="+fStringPool.toString(element.uri)+
                                 "},"+
                                 "attributes="+attributes+
                                 ")");
          }
  
          // return, if not matching
          if (fNoMatchDepth > 0) {
              fNoMatchDepth++;
              return;
          }
  
          // check match
          int startStepIndex = fCurrentStep;
          while (fCurrentStep < fLocationPath.steps.length) {
              XPath.Step step = fLocationPath.steps[fCurrentStep++];
              if (DEBUG_MATCH) {
                  System.out.println("XPATH["+toString()+"]: "+
                                     "attempting match at step["+(fCurrentStep - 1)+"]: \""+step+'"');
              }
              XPath.Axis axis = step.axis;
              switch (axis.type) {
                  case XPath.Axis.SELF: {
                      if (DEBUG_MATCH) {
                          System.out.println("XPATH["+toString()+"]: "+
                                             "axis: SELF");
                      }
                      if (DEBUG_MATCH) {
                          System.out.println("XPATH["+toString()+"]: "+
                                             "STEP MATCHED");
                      }
                      if (fCurrentStep == fLocationPath.steps.length) {
                          if (DEBUG_MATCH) {
                              System.out.println("XPATH["+toString()+"]: "+
                                                 "PATH MATCHED");
                          }
                          fMatched = true;
                          fBufferContent = true && fShouldBufferContent;
                          break;
                      }
                      continue;
                  }
                  case XPath.Axis.CHILD: {
                      int elementStep = fCurrentStep + 1;
                      if (DEBUG_MATCH) {
                          System.out.println("XPATH["+toString()+"]: "+
                                             "axis: CHILD");
                      }
                      // check element match
                      XPath.NodeTest nodeTest = step.nodeTest;
                      if (nodeTest.type == XPath.NodeTest.QNAME) {
                          if (DEBUG_MATCH) {
                              System.out.println("XPATH["+toString()+"]: "+
                                                 "nodeTest: QNAME");
                          }
                          boolean matched = true;
                          QName name = nodeTest.name;
                          if (name.uri == -1) {
                              if (element.rawname != name.rawname) {
                                  //System.out.println(">>> fStringPool:     "+fStringPool);
                                  //System.out.println(">>> element.rawname: "+fStringPool.toString(element.rawname));
                                  //System.out.println(">>> name.rawname:    "+fStringPool.toString(name.rawname));
                                  matched = false;
                              }
                          }
                          else {
                              if (element.uri != name.uri ||
                                  element.localpart != name.localpart) {
                                  matched = false;
                              }
                          }
                          if (!matched) {
                              if (DEBUG_MATCH) {
                                  System.out.println("XPATH["+toString()+"]: "+
                                                     "STEP *NOT* MATCHED");
                              }
                              fNoMatchDepth++;
                              return;
                          }
                          if (DEBUG_MATCH) {
                              System.out.println("XPATH["+toString()+"]: "+
                                                 "STEP MATCHED");
                          }
                          if (fCurrentStep == fLocationPath.steps.length) {
                              if (DEBUG_MATCH) {
                                  System.out.println("XPATH["+toString()+"]: "+
                                                     "PATH MATCHED");
                              }
                              fMatched = true;
                              fBufferContent = true && fShouldBufferContent;
                          }
                      }
                      /***
                      // REVISIT: [Q] Is self:: axis needed? -Ac
                      else if (axis.type == XPath.Axis.SELF) {
                          // let pass
                      }
                      /***/
                      else {
                          throw new SAXException("axis \""+axis+"\" not allowed");
                      }
  
                      // check for attribute match
                      if (fCurrentStep < fLocationPath.steps.length) {
                          step = fLocationPath.steps[fCurrentStep];
                          axis = step.axis;
                          if (axis.type == XPath.Axis.ATTRIBUTE) {
                              fCurrentStep++;
                              nodeTest = step.nodeTest;
                              if (nodeTest.type == XPath.NodeTest.QNAME) {
                                  boolean matched = true;
                                  QName name = nodeTest.name;
                                  if (name.uri == -1) {
                                      fMatchedString = attributes.getValue(name.rawname);
                                      if (fMatchedString == null) {
                                          matched = false;
                                      }
                                  }
                                  else {
                                      int aindex = attributes.getFirstAttr(handle);
                                      while (aindex != -1) {
                                          int auri = attributes.getAttrURI(aindex);
                                          int alocalpart = attributes.getAttrLocalpart(aindex);
                                          if (auri != -1 && alocalpart != -1 &&
                                              auri == name.uri && alocalpart == name.localpart) {
                                              fMatchedString = fStringPool.toString(attributes.getAttValue(aindex));
                                              break;
                                          }
                                          aindex = attributes.getNextAttr(aindex);
                                      }
                                      if (fMatchedString == null) {
                                          matched = false;
                                      }
                                  }
                                  if (!matched) {
                                      if (DEBUG_MATCH) {
                                          System.out.println("XPATH["+toString()+"]: "+
                                                             "ATTRIBUTE *NOT* MATCHED");
                                      }
                                      fNoMatchDepth++;
                                      return;
                                  }
                                  if (DEBUG_MATCH) {
                                      System.out.println("XPATH["+toString()+"]: "+
                                                         "STEP MATCHED");
                                  }
                                  if (fCurrentStep == fLocationPath.steps.length) {
                                      if (DEBUG_MATCH) {
                                          System.out.println("XPATH["+toString()+"]: "+
                                                             "PATH MATCHED");
                                      }
                                  }
                                  fBufferContent = false;
                                  fCurrentStep++;
                                  fMatched = fMatchedString != null;
                                  matched(fMatchedString);
                              }
                              else {
                                  throw new SAXException("node test \""+nodeTest+"\" not allowed");
                              }
                          }
                      }
                      break;
                  }
                  default: {
                      throw new SAXException("step \""+step+"\" not allowed");
                  }
              }
              break;
          }
  
          // push context
          fNamespacesScope.increaseDepth();
          fStepIndexes.push(startStepIndex);
  
      } // startElement(QName,XMLAttrList,int)
  
      /** Character content. */
      public void characters(char[] ch, int offset, int length) 
          throws Exception {
          if (DEBUG_METHODS) {
              System.out.println("XPATH["+toString()+"]: characters("+
                                 "text="+new String(ch, offset, length)+
                                 ")");
          }
  
          // collect match content
          if (fBufferContent && fNoMatchDepth == 0) {
              if (!DEBUG_METHODS && DEBUG_METHODS2) {
                  System.out.println("XPATH["+toString()+"]: characters("+
                                     "text="+new String(ch, offset, length)+
                                     ")");
              }
              fMatchedBuffer.append(ch, offset, length);
          }
          
      } // characters(char[],int,int)
  
      /**
       * The end of an element.
       * 
       * @param element The name of the element.
       *
       * @throws SAXException Thrown by handler to signal an error.
       */
      public void endElement(QName element) throws Exception {
          if (DEBUG_METHODS || DEBUG_METHODS2) {
              System.out.println("XPATH["+toString()+"]: endElement("+
                                 "element={"+
                                 "prefix="+fStringPool.toString(element.prefix)+','+
                                 "localpart="+fStringPool.toString(element.localpart)+','+
                                 "rawname="+fStringPool.toString(element.rawname)+','+
                                 "uri="+fStringPool.toString(element.uri)+
                                 "})");
          }
          
          // return, if not matching
          if (fNoMatchDepth > 0) {
              fNoMatchDepth--;
              return;
          }
  
          // signal match, if appropriate
          if (fBufferContent) {
              fBufferContent = false;
              fMatchedString = fMatchedBuffer.toString();
              matched(fMatchedString);
          }
  
          // go back a step
          fCurrentStep = fStepIndexes.pop();
          clear();
  
      } // endElement(QName)
  
      /**
       * The end of the document fragment.
       *
       * @throws SAXException Thrown by handler to signal an error.
       */
      public void endDocumentFragment() throws Exception {
          if (DEBUG_METHODS) {
              System.out.println("XPATH["+toString()+"]: endDocumentFragment()");
          }
          clear();
      } // endDocumentFragment()
  
      //
      // Object methods
      //
      
      /** Returns a string representation of this object. */
      public String toString() {
          return fLocationPath.toString();
      } // toString():String
  
      //
      // Private methods
      //
  
      /** Clears the match values. */
      private void clear() {
          fBufferContent = false;
          fMatched = false;
          fMatchedString = null;
      } // clear()
  
      //
      // Classes
      //
  
      /**
       * A simple integer stack. 
       *
       * @author Andy Clark, IBM
       */
      protected final static class IntegerStack {
  
          //
          // Data
          //
  
          /** Stack top. */
          private int fTop = -1;
  
          /** Stack data. */
          private int[] fData = new int[4];
  
          //
          // Constructors
          //
  
          /** Default constructor. */
          public IntegerStack() {
          } // <init>()
  
          //
          // Public methods
          //
  
          /** Clears the stack. */
          public void clear() {
              fTop = -1;
          } // clear()
  
          /** Pushes an integer onto the stack. */
          public void push(int value) {
              ensureCapacity(++fTop);
              fData[fTop] = value;
          } // push(int)
  
          /** Pops an integer off of the stack. */
          public int pop() {
              return fData[fTop--];
          } // pop():int
  
          //
          // Private methods
          //
  
          /** Ensures data structure can hold data. */
          private void ensureCapacity(int size) {
              if (size >= fData.length) {
                  int[] array = new int[fData.length * 2];
                  System.arraycopy(fData, 0, array, 0, fData.length);
                  fData = array;
              }
          } // ensureCapacity(int)
  
      } // class IntegerStack
  
      //
      // MAIN
      //
  
      /** Main program. */
      public static void main(String[] argv) throws Exception {
  
          if (DEBUG_ANY) {
              for (int i = 0; i < argv.length; i++) {
                  final String expr = argv[i];
                  final StringPool symbols = new StringPool();
                  final XPath xpath = new XPath(expr, symbols, null);
                  final XPathMatcher matcher = new XPathMatcher(xpath, true);
                  org.apache.xerces.parsers.SAXParser parser = 
                      new org.apache.xerces.parsers.SAXParser(symbols) {
                      public void startDocument() throws Exception {
                          matcher.startDocumentFragment(fStringPool, null);
                      }
                      public void startElement(QName element, XMLAttrList attributes, int handle) throws Exception {
                          matcher.startElement(element, attributes, handle);
                      }
                      public void characters(char[] ch, int offset, int length) throws Exception {
                          matcher.characters(ch, offset, length);
                      }
                      public void endElement(QName element) throws Exception {
                          matcher.endElement(element);
                      }
                      public void endDocument() throws Exception {
                          matcher.endDocumentFragment();
                      }
                  };
                  System.out.println("#### argv["+i+"]: \""+expr+"\" -> \""+xpath.toString()+'"');
                  final String uri = argv[++i];
                  System.out.println("#### argv["+i+"]: "+uri);
                  parser.parse(uri);
              }
          }
  
      } // main(String[])
  
  } // class XPathMatcher