1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2005 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 package com.sun.org.apache.xerces.internal.xpointer;
  21 
  22 import java.util.Hashtable;
  23 
  24 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  25 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  26 import com.sun.org.apache.xerces.internal.util.XMLChar;
  27 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  28 import com.sun.org.apache.xerces.internal.xni.QName;
  29 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  30 import com.sun.org.apache.xerces.internal.xni.XNIException;
  31 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
  32 
  33 /**
  34  * <p>
  35  * Implements the XPointerPart interface for element() scheme specific processing.
  36  * </p>
  37  *
  38  * @xerces.internal
  39  *
  40  * @version $Id: ElementSchemePointer.java,v 1.4 2009/06/11 23:51:50 joehw Exp $
  41  *
  42  */
  43 class ElementSchemePointer implements XPointerPart {
  44 
  45     // Fields
  46 
  47     // The Scheme Name i.e element
  48     private String fSchemeName;
  49 
  50     // The scheme Data
  51     private String fSchemeData;
  52 
  53     // The scheme Data & child sequence
  54     private String fShortHandPointerName;
  55 
  56     // Should we attempt to resolve the ChildSequence from the
  57     // current element position. If a ShortHand Pointer is present
  58     // attempt to resolve relative to the short hand pointer.
  59     private boolean fIsResolveElement = false;
  60 
  61     // Has the element been found
  62     private boolean fIsElementFound = false;
  63 
  64     // Was only an empty element found
  65     private boolean fWasOnlyEmptyElementFound = false;
  66 
  67     // If a shorthand pointer is present and resolved
  68     boolean fIsShortHand = false;
  69 
  70     // The depth at which the element was found
  71     int fFoundDepth = 0;
  72 
  73     // The XPointer element child sequence
  74     private int fChildSequence[];
  75 
  76     // The current child position
  77     private int fCurrentChildPosition = 1;
  78 
  79     // The current child depth
  80     private int fCurrentChildDepth = 0;
  81 
  82     // The current element's child sequence
  83     private int fCurrentChildSequence[];;
  84 
  85     // Stores if the Fragment was resolved by the pointer
  86     private boolean fIsFragmentResolved = false;
  87 
  88     // Stores if the Fragment was resolved by the pointer
  89     private ShortHandPointer fShortHandPointer;
  90 
  91     // The XPointer Error reporter
  92     protected XMLErrorReporter fErrorReporter;
  93 
  94     // The XPointer Error Handler
  95     protected XMLErrorHandler fErrorHandler;
  96 
  97     //
  98     private SymbolTable fSymbolTable;
  99 
 100     // ************************************************************************
 101     // Constructors
 102     // ************************************************************************
 103     public ElementSchemePointer() {
 104     }
 105 
 106     public ElementSchemePointer(SymbolTable symbolTable) {
 107         fSymbolTable = symbolTable;
 108     }
 109 
 110     public ElementSchemePointer(SymbolTable symbolTable,
 111             XMLErrorReporter errorReporter) {
 112         fSymbolTable = symbolTable;
 113         fErrorReporter = errorReporter;
 114     }
 115 
 116     // ************************************************************************
 117     // XPointerPart implementation
 118     // ************************************************************************
 119 
 120     /**
 121      * Parses the XPointer expression and tokenizes it into Strings
 122      * delimited by whitespace.
 123      *
 124      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#parseXPointer(java.lang.String)
 125      */
 126     public void parseXPointer(String xpointer) throws XNIException {
 127 
 128         //
 129         init();
 130 
 131         // tokens
 132         final Tokens tokens = new Tokens(fSymbolTable);
 133 
 134         // scanner
 135         Scanner scanner = new Scanner(fSymbolTable) {
 136             protected void addToken(Tokens tokens, int token)
 137                     throws XNIException {
 138                 if (token == Tokens.XPTRTOKEN_ELEM_CHILD
 139                         || token == Tokens.XPTRTOKEN_ELEM_NCNAME) {
 140                     super.addToken(tokens, token);
 141                     return;
 142                 }
 143                 reportError("InvalidElementSchemeToken", new Object[] { tokens
 144                         .getTokenString(token) });
 145             }
 146         };
 147 
 148         // scan the element() XPointer expression
 149         int length = xpointer.length();
 150         boolean success = scanner.scanExpr(fSymbolTable, tokens, xpointer, 0,
 151                 length);
 152 
 153         if (!success) {
 154             reportError("InvalidElementSchemeXPointer",
 155                     new Object[] { xpointer });
 156         }
 157 
 158         // Initialize a temp arrays to the size of token count which should
 159         // be atleast twice the size of child sequence, to hold the ChildSequence.
 160         int tmpChildSequence[] = new int[tokens.getTokenCount() / 2 + 1];
 161 
 162         // the element depth
 163         int i = 0;
 164 
 165         // Traverse the scanned tokens
 166         while (tokens.hasMore()) {
 167             int token = tokens.nextToken();
 168 
 169             switch (token) {
 170             case Tokens.XPTRTOKEN_ELEM_NCNAME: {
 171                 // Note:  Only a single ShortHand pointer can be present
 172 
 173                 // The shortHand name
 174                 token = tokens.nextToken();
 175                 fShortHandPointerName = tokens.getTokenString(token);
 176 
 177                 // Create a new ShortHandPointer
 178                 fShortHandPointer = new ShortHandPointer(fSymbolTable);
 179                 fShortHandPointer.setSchemeName(fShortHandPointerName);
 180 
 181                 break;
 182             }
 183             case Tokens.XPTRTOKEN_ELEM_CHILD: {
 184                 tmpChildSequence[i] = tokens.nextToken();
 185                 i++;
 186 
 187                 break;
 188             }
 189             default:
 190                 reportError("InvalidElementSchemeXPointer",
 191                         new Object[] { xpointer });
 192             }
 193         }
 194 
 195         // Initialize the arrays to the number of elements in the ChildSequence.
 196         fChildSequence = new int[i];
 197         fCurrentChildSequence = new int[i];
 198         System.arraycopy(tmpChildSequence, 0, fChildSequence, 0, i);
 199 
 200     }
 201 
 202     /**
 203      * Returns the scheme name i.e element
 204      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#getSchemeName()
 205      */
 206     public String getSchemeName() {
 207         return fSchemeName;
 208     }
 209 
 210     /**
 211      * Returns the scheme data
 212      *
 213      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#getSchemeData()
 214      */
 215     public String getSchemeData() {
 216         return fSchemeData;
 217     }
 218 
 219     /**
 220      * Sets the scheme name
 221      *
 222      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#setSchemeName(java.lang.String)
 223      */
 224     public void setSchemeName(String schemeName) {
 225         fSchemeName = schemeName;
 226 
 227     }
 228 
 229     /**
 230      * Sets the scheme data
 231      *
 232      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#setSchemeData(java.lang.String)
 233      */
 234     public void setSchemeData(String schemeData) {
 235         fSchemeData = schemeData;
 236     }
 237 
 238     /**
 239      * Responsible for resolving the element() scheme XPointer.  If a ShortHand
 240      * Pointer is present and it is successfully resolved and if a child
 241      * sequence is present, the child sequence is resolved relative to it.
 242      *
 243      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#resolveXPointer(com.sun.org.apache.xerces.internal.xni.QName, com.sun.org.apache.xerces.internal.xni.XMLAttributes, com.sun.org.apache.xerces.internal.xni.Augmentations, int event)
 244      */
 245     public boolean resolveXPointer(QName element, XMLAttributes attributes,
 246             Augmentations augs, int event) throws XNIException {
 247 
 248         boolean isShortHandPointerResolved = false;
 249 
 250         // if a ChildSequence exisits, resolve child elements
 251 
 252         // if an element name exists
 253         if (fShortHandPointerName != null) {
 254             // resolve ShortHand Pointer
 255             isShortHandPointerResolved = fShortHandPointer.resolveXPointer(
 256                     element, attributes, augs, event);
 257             if (isShortHandPointerResolved) {
 258                 fIsResolveElement = true;
 259                 fIsShortHand = true;
 260             } else {
 261                 fIsResolveElement = false;
 262             }
 263         } else {
 264             fIsResolveElement = true;
 265         }
 266 
 267         // Added here to skip the ShortHand pointer corresponding to
 268         // an element if one exisits and start searching from its child
 269         if (fChildSequence.length > 0) {
 270             fIsFragmentResolved = matchChildSequence(element, event);
 271         } else if (isShortHandPointerResolved && fChildSequence.length <= 0) {
 272             // if only a resolved shorthand pointer exists
 273             fIsFragmentResolved = isShortHandPointerResolved;
 274         } else {
 275             fIsFragmentResolved = false;
 276         }
 277 
 278         return fIsFragmentResolved;
 279     }
 280 
 281     /**
 282      * Matches the current element position in the document tree with the
 283      * element position specified in the element XPointer scheme.
 284      *
 285      * @param event
 286      * @return boolean - true if the current element position in the document
 287      * tree matches theelement position specified in the element XPointer
 288      * scheme.
 289      */
 290     protected boolean matchChildSequence(QName element, int event)
 291             throws XNIException {
 292 
 293         // need to resize fCurrentChildSequence
 294         if (fCurrentChildDepth >= fCurrentChildSequence.length) {
 295             int tmpCurrentChildSequence[] = new int[fCurrentChildSequence.length];
 296             System.arraycopy(fCurrentChildSequence, 0, tmpCurrentChildSequence,
 297                     0, fCurrentChildSequence.length);
 298 
 299             // Increase the size by a factor of 2 (?)
 300             fCurrentChildSequence = new int[fCurrentChildDepth * 2];
 301             System.arraycopy(tmpCurrentChildSequence, 0, fCurrentChildSequence,
 302                     0, tmpCurrentChildSequence.length);
 303         }
 304 
 305         //
 306         if (fIsResolveElement) {
 307             // start
 308             fWasOnlyEmptyElementFound = false;
 309             if (event == XPointerPart.EVENT_ELEMENT_START) {
 310                 fCurrentChildSequence[fCurrentChildDepth] = fCurrentChildPosition;
 311                 fCurrentChildDepth++;
 312 
 313                 // reset the current child position
 314                 fCurrentChildPosition = 1;
 315 
 316                 //if (!fSchemeNameFound) {
 317                 if ((fCurrentChildDepth <= fFoundDepth) || (fFoundDepth == 0)) {
 318                     if (checkMatch()) {
 319                         fIsElementFound = true;
 320                         fFoundDepth = fCurrentChildDepth;
 321                     } else {
 322                         fIsElementFound = false;
 323                         fFoundDepth = 0;
 324                     }
 325                 }
 326 
 327             } else if (event == XPointerPart.EVENT_ELEMENT_END) {
 328                 if (fCurrentChildDepth == fFoundDepth) {
 329                     fIsElementFound = true;
 330                 } else if (((fCurrentChildDepth < fFoundDepth) && (fFoundDepth != 0))
 331                         || ((fCurrentChildDepth > fFoundDepth) // or empty element found
 332                         && (fFoundDepth == 0))) {
 333                     fIsElementFound = false;
 334                 }
 335 
 336                 // reset array position of last child
 337                 fCurrentChildSequence[fCurrentChildDepth] = 0;
 338 
 339                 fCurrentChildDepth--;
 340                 fCurrentChildPosition = fCurrentChildSequence[fCurrentChildDepth] + 1;
 341 
 342             } else if (event == XPointerPart.EVENT_ELEMENT_EMPTY) {
 343 
 344                 fCurrentChildSequence[fCurrentChildDepth] = fCurrentChildPosition;
 345                 fCurrentChildPosition++;
 346 
 347                 // Donot check for empty elements if the empty element is
 348                 // a child of a found parent element
 349                 //if (!fIsElementFound) {
 350                     if (checkMatch()) {
 351                         fIsElementFound = true;
 352                         fWasOnlyEmptyElementFound = true;
 353                     } else {
 354                         fIsElementFound = false;
 355                     }
 356                 //}
 357 
 358             }
 359         }
 360 
 361         return fIsElementFound;
 362     }
 363 
 364     /**
 365      * Matches the current position of the element being visited by checking
 366      * its position and previous elements against the element XPointer expression.
 367      * If a match is found it return true else false.
 368      *
 369      * @return boolean
 370      */
 371     protected boolean checkMatch() {
 372         // If the number of elements in the ChildSequence is greater than the
 373         // current child depth, there is not point in checking further
 374         if (!fIsShortHand) {
 375             // If a shorthand pointer is not present traverse the children
 376             // and compare
 377             if (fChildSequence.length <= fCurrentChildDepth + 1) {
 378 
 379                 for (int i = 0; i < fChildSequence.length; i++) {
 380                     if (fChildSequence[i] != fCurrentChildSequence[i]) {
 381                         return false;
 382                     }
 383                 }
 384             } else {
 385                 return false;
 386             }
 387         } else {
 388             // If a shorthand pointer is present traverse the children
 389             // ignoring the first element of the CurrenChildSequence which
 390             // contains the ShortHand pointer element and compare
 391             if (fChildSequence.length <= fCurrentChildDepth + 1) {
 392 
 393                 for (int i = 0; i < fChildSequence.length; i++) {
 394                     // ensure fCurrentChildSequence is large enough
 395                     if (fCurrentChildSequence.length < i + 2) {
 396                         return false;
 397                     }
 398 
 399                     // ignore the first element of fCurrentChildSequence
 400                     if (fChildSequence[i] != fCurrentChildSequence[i + 1]) {
 401                         return false;
 402                     }
 403                 }
 404             } else {
 405                 return false;
 406             }
 407 
 408         }
 409 
 410         return true;
 411     }
 412 
 413     /**
 414      * Returns true if the node matches or is a child of a matching element()
 415      * scheme XPointer.
 416      *
 417      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#isFragmentResolved()
 418      */
 419     public boolean isFragmentResolved() throws XNIException {
 420         // Return true if the Fragment was resolved and the current Node depth
 421         // is greater than or equal to the depth at which the element was found
 422         return fIsFragmentResolved ;
 423     }
 424 
 425     /**
 426      * Returns true if the XPointer expression resolves to a non-element child
 427      * of the current resource fragment.
 428      *
 429      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#isChildFragmentResolved()
 430      *
 431      */
 432     public boolean isChildFragmentResolved() {
 433         // if only a shorthand pointer was present
 434         if (fIsShortHand && fShortHandPointer != null && fChildSequence.length <= 0) {
 435                 return fShortHandPointer.isChildFragmentResolved();
 436         } else {
 437                 return fWasOnlyEmptyElementFound ? !fWasOnlyEmptyElementFound
 438                                 : (fIsFragmentResolved && (fCurrentChildDepth >= fFoundDepth));
 439         }
 440     }
 441 
 442     /**
 443          * Reports an XPointer error
 444          */
 445     protected void reportError(String key, Object[] arguments)
 446             throws XNIException {
 447         /*fErrorReporter.reportError(XPointerMessageFormatter.XPOINTER_DOMAIN,
 448          key, arguments, XMLErrorReporter.SEVERITY_ERROR);
 449          */
 450         throw new XNIException((fErrorReporter
 451                         .getMessageFormatter(XPointerMessageFormatter.XPOINTER_DOMAIN))
 452                                 .formatMessage(fErrorReporter.getLocale(), key, arguments));
 453     }
 454 
 455     /**
 456      * Initializes error handling objects
 457      */
 458     protected void initErrorReporter() {
 459         if (fErrorReporter == null) {
 460             fErrorReporter = new XMLErrorReporter();
 461         }
 462         if (fErrorHandler == null) {
 463             fErrorHandler = new XPointerErrorHandler();
 464         }
 465         fErrorReporter.putMessageFormatter(
 466                 XPointerMessageFormatter.XPOINTER_DOMAIN,
 467                 new XPointerMessageFormatter());
 468     }
 469 
 470     /**
 471      * Initializes the element scheme processor
 472      */
 473     protected void init() {
 474         fSchemeName = null;
 475         fSchemeData = null;
 476         fShortHandPointerName = null;
 477         fIsResolveElement = false;
 478         fIsElementFound = false;
 479         fWasOnlyEmptyElementFound = false;
 480         fFoundDepth = 0;
 481         fCurrentChildPosition = 1;
 482         fCurrentChildDepth = 0;
 483         fIsFragmentResolved = false;
 484         fShortHandPointer = null;
 485 
 486         initErrorReporter();
 487     }
 488 
 489     // ************************************************************************
 490     // element() Scheme expression scanner
 491     // ************************************************************************
 492 
 493     /**
 494      * List of XPointer Framework tokens.
 495      *
 496      * @xerces.internal
 497      *
 498      * @author Neil Delima, IBM
 499      * @version $Id: ElementSchemePointer.java,v 1.4 2009/06/11 23:51:50 joehw Exp $
 500      *
 501      */
 502     private final class Tokens {
 503 
 504         /**
 505          * XPointer element() scheme
 506          * [1]    ElementSchemeData    ::=    (NCName ChildSequence?) | ChildSequence
 507          * [2]    ChildSequence    ::=    ('/' [1-9] [0-9]*)+
 508          */
 509         private static final int XPTRTOKEN_ELEM_NCNAME = 0;
 510 
 511         private static final int XPTRTOKEN_ELEM_CHILD = 1;
 512 
 513         // Token names
 514         private final String[] fgTokenNames = { "XPTRTOKEN_ELEM_NCNAME",
 515                 "XPTRTOKEN_ELEM_CHILD" };
 516 
 517         // Token count
 518         private static final int INITIAL_TOKEN_COUNT = 1 << 8;
 519 
 520         private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
 521 
 522         private int fTokenCount = 0;
 523 
 524         // Current token position
 525         private int fCurrentTokenIndex;
 526 
 527         private SymbolTable fSymbolTable;
 528 
 529         private Hashtable fTokenNames = new Hashtable();
 530 
 531         /**
 532          * Constructor
 533          *
 534          * @param symbolTable SymbolTable
 535          */
 536         private Tokens(SymbolTable symbolTable) {
 537             fSymbolTable = symbolTable;
 538 
 539             fTokenNames.put(new Integer(XPTRTOKEN_ELEM_NCNAME),
 540                     "XPTRTOKEN_ELEM_NCNAME");
 541             fTokenNames.put(new Integer(XPTRTOKEN_ELEM_CHILD),
 542                     "XPTRTOKEN_ELEM_CHILD");
 543         }
 544 
 545         /*
 546          * Returns the token String
 547          * @param token The index of the token
 548          * @return String The token string
 549          */
 550         private String getTokenString(int token) {
 551             return (String) fTokenNames.get(new Integer(token));
 552         }
 553 
 554         /**
 555          * Returns the token String
 556          * @param token The index of the token
 557          * @return String The token string
 558          */
 559         private Integer getToken(int token) {
 560             return (Integer) fTokenNames.get(new Integer(token));
 561         }
 562 
 563         /**
 564          * Add the specified string as a token
 565          *
 566          * @param token The token string
 567          */
 568         private void addToken(String tokenStr) {
 569             Integer tokenInt = (Integer) fTokenNames.get(tokenStr);
 570             if (tokenInt == null) {
 571                 tokenInt = new Integer(fTokenNames.size());
 572                 fTokenNames.put(tokenInt, tokenStr);
 573             }
 574             addToken(tokenInt.intValue());
 575         }
 576 
 577         /**
 578          * Add the specified int token
 579          *
 580          * @param token The int specifying the token
 581          */
 582         private void addToken(int token) {
 583             try {
 584                 fTokens[fTokenCount] = token;
 585             } catch (ArrayIndexOutOfBoundsException ex) {
 586                 int[] oldList = fTokens;
 587                 fTokens = new int[fTokenCount << 1];
 588                 System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
 589                 fTokens[fTokenCount] = token;
 590             }
 591             fTokenCount++;
 592         }
 593 
 594         /**
 595          * Resets the current position to the head of the token list.
 596          */
 597         private void rewind() {
 598             fCurrentTokenIndex = 0;
 599         }
 600 
 601         /**
 602          * Returns true if the {@link #getNextToken()} method
 603          * returns a valid token.
 604          */
 605         private boolean hasMore() {
 606             return fCurrentTokenIndex < fTokenCount;
 607         }
 608 
 609         /**
 610          * Obtains the token at the current position, then advance
 611          * the current position by one.
 612          *
 613          * If there's no such next token, this method throws
 614          * <tt>new XNIException("InvalidXPointerExpression");</tt>.
 615          */
 616         private int nextToken() throws XNIException {
 617             if (fCurrentTokenIndex == fTokenCount)
 618                 reportError("XPointerElementSchemeProcessingError", null);
 619             return fTokens[fCurrentTokenIndex++];
 620         }
 621 
 622         /**
 623          * Obtains the token at the current position, without advancing
 624          * the current position.
 625          *
 626          * If there's no such next token, this method throws
 627          * <tt>new XNIException("InvalidXPointerExpression");</tt>.
 628          */
 629         private int peekToken() throws XNIException {
 630             if (fCurrentTokenIndex == fTokenCount)
 631                 reportError("XPointerElementSchemeProcessingError", null);
 632             return fTokens[fCurrentTokenIndex];
 633         }
 634 
 635         /**
 636          * Obtains the token at the current position as a String.
 637          *
 638          * If there's no current token or if the current token
 639          * is not a string token, this method throws
 640          * If there's no such next token, this method throws
 641          * <tt>new XNIException("InvalidXPointerExpression");</tt>.
 642          */
 643         private String nextTokenAsString() throws XNIException {
 644             String s = getTokenString(nextToken());
 645             if (s == null)
 646                 reportError("XPointerElementSchemeProcessingError", null);
 647             return s;
 648         }
 649 
 650         /**
 651          * Returns the number of tokens.
 652          *
 653          */
 654         private int getTokenCount() {
 655             return fTokenCount;
 656         }
 657     }
 658 
 659     /**
 660      *
 661      * The XPointer expression scanner.  Scans the XPointer framework expression.
 662      *
 663      * @xerces.internal
 664      *
 665      * @version $Id: ElementSchemePointer.java,v 1.4 2009/06/11 23:51:50 joehw Exp $
 666      */
 667     private class Scanner {
 668 
 669         /**
 670          * 7-bit ASCII subset
 671          *
 672          *  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
 673          *  0,  0,  0,  0,  0,  0,  0,  0,  0, HT, LF,  0,  0, CR,  0,  0,  // 0
 674          *  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 1
 675          * SP,  !,  ",  #,  $,  %,  &,  ',  (,  ),  *,  +,  ,,  -,  .,  /,  // 2
 676          *  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  :,  ;,  <,  =,  >,  ?,  // 3
 677          *  @,  A,  B,  C,  D,  E,  F,  G,  H,  I,  J,  K,  L,  M,  N,  O,  // 4
 678          *  P,  Q,  R,  S,  T,  U,  V,  W,  X,  Y,  Z,  [,  \,  ],  ^,  _,  // 5
 679          *  `,  a,  b,  c,  d,  e,  f,  g,  h,  i,  j,  k,  l,  m,  n,  o,  // 6
 680          *  p,  q,  r,  s,  t,  u,  v,  w,  x,  y,  z,  {,  |,  },  ~, DEL  // 7
 681          */
 682         private static final byte CHARTYPE_INVALID = 0, // invalid XML characters, control characters and 7F
 683                 CHARTYPE_OTHER = 1, // A valid XML character (possibly invalid NCNameChar) that does not fall in one of the other categories
 684                 CHARTYPE_MINUS = 2, // '-' (0x2D)
 685                 CHARTYPE_PERIOD = 3, // '.' (0x2E)
 686                 CHARTYPE_SLASH = 4, // '/' (0x2F)
 687                 CHARTYPE_DIGIT = 5, // '0'-'9' (0x30 to 0x39)
 688                 CHARTYPE_LETTER = 6, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
 689                 CHARTYPE_UNDERSCORE = 7, // '_' (0x5F)
 690                 CHARTYPE_NONASCII = 8; // Non-ASCII Unicode codepoint (>= 0x80)
 691 
 692         private final byte[] fASCIICharMap = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
 693                 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 694                 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 5, 5, 5, 5, 5,
 695                 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6,
 696                 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1,
 697                 7, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
 698                 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1 };
 699 
 700         /**
 701          * Symbol literals
 702          */
 703 
 704         //
 705         // Data
 706         //
 707         /** Symbol table. */
 708         private SymbolTable fSymbolTable;
 709 
 710         //
 711         // Constructors
 712         //
 713 
 714         /**
 715          * Constructs an XPath expression scanner.
 716          *
 717          * @param symbolTable SymbolTable
 718          */
 719         private Scanner(SymbolTable symbolTable) {
 720             // save pool and tokens
 721             fSymbolTable = symbolTable;
 722 
 723         } // <init>(SymbolTable)
 724 
 725         /**
 726          * Scans the XPointer Expression
 727          *
 728          */
 729         private boolean scanExpr(SymbolTable symbolTable, Tokens tokens,
 730                 String data, int currentOffset, int endOffset)
 731                 throws XNIException {
 732 
 733             int ch;
 734             int nameOffset;
 735             String nameHandle = null;
 736 
 737             while (true) {
 738                 if (currentOffset == endOffset) {
 739                     break;
 740                 }
 741 
 742                 ch = data.charAt(currentOffset);
 743                 byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
 744                         : fASCIICharMap[ch];
 745 
 746                 //
 747                 // [1]    ElementSchemeData    ::=    (NCName ChildSequence?) | ChildSequence
 748                 // [2]    ChildSequence    ::=    ('/' [1-9] [0-9]*)+
 749                 //
 750 
 751                 switch (chartype) {
 752 
 753                 case CHARTYPE_SLASH:
 754                     // if last character is '/', break and report an error
 755                     if (++currentOffset == endOffset) {
 756                         return false;
 757                     }
 758 
 759                     addToken(tokens, Tokens.XPTRTOKEN_ELEM_CHILD);
 760                     ch = data.charAt(currentOffset);
 761 
 762                     // ChildSequence    ::=    ('/' [1-9] [0-9]*)+
 763                     int child = 0;
 764                     while (ch >= '0' && ch <= '9') {
 765                         child = (child * 10) + (ch - '0');
 766                         if (++currentOffset == endOffset) {
 767                             break;
 768                         }
 769                         ch = data.charAt(currentOffset);
 770                     }
 771 
 772                     // An invalid child sequence character
 773                     if (child == 0) {
 774                         reportError("InvalidChildSequenceCharacter",
 775                                 new Object[] { new Character((char) ch) });
 776                         return false;
 777                     }
 778 
 779                     tokens.addToken(child);
 780 
 781                     break;
 782 
 783                 case CHARTYPE_DIGIT:
 784                 case CHARTYPE_LETTER:
 785                 case CHARTYPE_MINUS:
 786                 case CHARTYPE_NONASCII:
 787                 case CHARTYPE_OTHER:
 788                 case CHARTYPE_PERIOD:
 789                 case CHARTYPE_UNDERSCORE:
 790                     // Scan the ShortHand Pointer NCName
 791                     nameOffset = currentOffset;
 792                     currentOffset = scanNCName(data, endOffset, currentOffset);
 793 
 794                     if (currentOffset == nameOffset) {
 795                         //return false;
 796                         reportError("InvalidNCNameInElementSchemeData",
 797                                 new Object[] { data });
 798                         return false;
 799                     }
 800 
 801                     if (currentOffset < endOffset) {
 802                         ch = data.charAt(currentOffset);
 803                     } else {
 804                         ch = -1;
 805                     }
 806 
 807                     nameHandle = symbolTable.addSymbol(data.substring(
 808                             nameOffset, currentOffset));
 809                     addToken(tokens, Tokens.XPTRTOKEN_ELEM_NCNAME);
 810                     tokens.addToken(nameHandle);
 811 
 812                     break;
 813                 }
 814             }
 815             return true;
 816         }
 817 
 818         /**
 819          * Scans a NCName.
 820          * From Namespaces in XML
 821          * [5] NCName ::= (Letter | '_') (NCNameChar)*
 822          * [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
 823          *
 824          * @param data A String containing the XPointer expression
 825          * @param endOffset The int XPointer expression length
 826          * @param currentOffset An int representing the current position of the XPointer expression pointer
 827          */
 828         private int scanNCName(String data, int endOffset, int currentOffset) {
 829             int ch = data.charAt(currentOffset);
 830             if (ch >= 0x80) {
 831                 if (!XMLChar.isNameStart(ch)) {
 832                     return currentOffset;
 833                 }
 834             } else {
 835                 byte chartype = fASCIICharMap[ch];
 836                 if (chartype != CHARTYPE_LETTER
 837                         && chartype != CHARTYPE_UNDERSCORE) {
 838                     return currentOffset;
 839                 }
 840             }
 841             while (++currentOffset < endOffset) {
 842                 ch = data.charAt(currentOffset);
 843                 if (ch >= 0x80) {
 844                     if (!XMLChar.isName(ch)) {
 845                         break;
 846                     }
 847                 } else {
 848                     byte chartype = fASCIICharMap[ch];
 849                     if (chartype != CHARTYPE_LETTER
 850                             && chartype != CHARTYPE_DIGIT
 851                             && chartype != CHARTYPE_PERIOD
 852                             && chartype != CHARTYPE_MINUS
 853                             && chartype != CHARTYPE_UNDERSCORE) {
 854                         break;
 855                     }
 856                 }
 857             }
 858             return currentOffset;
 859         }
 860 
 861         //
 862         // Protected methods
 863         //
 864 
 865         /**
 866          * This method adds the specified token to the token list. By
 867          * default, this method allows all tokens. However, subclasses
 868          * of the XPathExprScanner can override this method in order
 869          * to disallow certain tokens from being used in the scanned
 870          * XPath expression. This is a convenient way of allowing only
 871          * a subset of XPath.
 872          */
 873         protected void addToken(Tokens tokens, int token) throws XNIException {
 874             tokens.addToken(token);
 875         } // addToken(int)
 876 
 877     } // class Scanner
 878 
 879 }