1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 package com.sun.org.apache.xerces.internal.xpointer;
  22 
  23 import java.util.HashMap;
  24 
  25 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  26 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  27 import com.sun.org.apache.xerces.internal.util.XMLChar;
  28 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  29 import com.sun.org.apache.xerces.internal.xni.QName;
  30 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  31 import com.sun.org.apache.xerces.internal.xni.XNIException;
  32 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
  33 
  34 /**
  35  * <p>
  36  * Implements the XPointerPart interface for element() scheme specific processing.
  37  * </p>
  38  *
  39  * @xerces.internal
  40  *
  41  * @version $Id: ElementSchemePointer.java,v 1.4 2009/06/11 23:51:50 joehw Exp $
  42  */
  43 final 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 (checkMatch()) {
 350                     if (!fIsElementFound) {
 351                         fWasOnlyEmptyElementFound = true;
 352                     } else {
 353                         fWasOnlyEmptyElementFound = false;
 354                     }
 355                     fIsElementFound = true;
 356                 } else {
 357                     fIsElementFound = false;
 358                     fWasOnlyEmptyElementFound = false;
 359                 }
 360             }
 361         }
 362 
 363         return fIsElementFound;
 364     }
 365 
 366     /**
 367      * Matches the current position of the element being visited by checking
 368      * its position and previous elements against the element XPointer expression.
 369      * If a match is found it return true else false.
 370      *
 371      * @return boolean
 372      */
 373     protected boolean checkMatch() {
 374         // If the number of elements in the ChildSequence is greater than the
 375         // current child depth, there is not point in checking further
 376         if (!fIsShortHand) {
 377             // If a shorthand pointer is not present traverse the children
 378             // and compare
 379             if (fChildSequence.length <= fCurrentChildDepth + 1) {
 380 
 381                 for (int i = 0; i < fChildSequence.length; i++) {
 382                     if (fChildSequence[i] != fCurrentChildSequence[i]) {
 383                         return false;
 384                     }
 385                 }
 386             } else {
 387                 return false;
 388             }
 389         } else {
 390             // If a shorthand pointer is present traverse the children
 391             // ignoring the first element of the CurrenChildSequence which
 392             // contains the ShortHand pointer element and compare
 393             if (fChildSequence.length <= fCurrentChildDepth + 1) {
 394 
 395                 for (int i = 0; i < fChildSequence.length; i++) {
 396                     // ensure fCurrentChildSequence is large enough
 397                     if (fCurrentChildSequence.length < i + 2) {
 398                         return false;
 399                     }
 400 
 401                     // ignore the first element of fCurrentChildSequence
 402                     if (fChildSequence[i] != fCurrentChildSequence[i + 1]) {
 403                         return false;
 404                     }
 405                 }
 406             } else {
 407                 return false;
 408             }
 409 
 410         }
 411 
 412         return true;
 413     }
 414 
 415     /**
 416      * Returns true if the node matches or is a child of a matching element()
 417      * scheme XPointer.
 418      *
 419      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#isFragmentResolved()
 420      */
 421     public boolean isFragmentResolved() throws XNIException {
 422         // Return true if the Fragment was resolved and the current Node depth
 423         // is greater than or equal to the depth at which the element was found
 424         return fIsFragmentResolved ;
 425     }
 426 
 427     /**
 428      * Returns true if the XPointer expression resolves to a non-element child
 429      * of the current resource fragment.
 430      *
 431      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#isChildFragmentResolved()
 432      *
 433      */
 434     public boolean isChildFragmentResolved() {
 435         // if only a shorthand pointer was present
 436         if (fIsShortHand && fShortHandPointer != null && fChildSequence.length <= 0) {
 437                 return fShortHandPointer.isChildFragmentResolved();
 438         } else {
 439                 return fWasOnlyEmptyElementFound ? !fWasOnlyEmptyElementFound
 440                                 : (fIsFragmentResolved && (fCurrentChildDepth >= fFoundDepth));
 441         }
 442     }
 443 
 444     /**
 445          * Reports an XPointer error
 446          */
 447     protected void reportError(String key, Object[] arguments)
 448             throws XNIException {
 449         /*fErrorReporter.reportError(XPointerMessageFormatter.XPOINTER_DOMAIN,
 450          key, arguments, XMLErrorReporter.SEVERITY_ERROR);
 451          */
 452         throw new XNIException((fErrorReporter
 453                         .getMessageFormatter(XPointerMessageFormatter.XPOINTER_DOMAIN))
 454                                 .formatMessage(fErrorReporter.getLocale(), key, arguments));
 455     }
 456 
 457     /**
 458      * Initializes error handling objects
 459      */
 460     protected void initErrorReporter() {
 461         if (fErrorReporter == null) {
 462             fErrorReporter = new XMLErrorReporter();
 463         }
 464         if (fErrorHandler == null) {
 465             fErrorHandler = new XPointerErrorHandler();
 466         }
 467         fErrorReporter.putMessageFormatter(
 468                 XPointerMessageFormatter.XPOINTER_DOMAIN,
 469                 new XPointerMessageFormatter());
 470     }
 471 
 472     /**
 473      * Initializes the element scheme processor
 474      */
 475     protected void init() {
 476         fSchemeName = null;
 477         fSchemeData = null;
 478         fShortHandPointerName = null;
 479         fIsResolveElement = false;
 480         fIsElementFound = false;
 481         fWasOnlyEmptyElementFound = false;
 482         fFoundDepth = 0;
 483         fCurrentChildPosition = 1;
 484         fCurrentChildDepth = 0;
 485         fIsFragmentResolved = false;
 486         fShortHandPointer = null;
 487 
 488         initErrorReporter();
 489     }
 490 
 491     // ************************************************************************
 492     // element() Scheme expression scanner
 493     // ************************************************************************
 494 
 495     /**
 496      * List of XPointer Framework tokens.
 497      *
 498      * @xerces.internal
 499      *
 500      * @author Neil Delima, IBM
 501      * @version $Id: ElementSchemePointer.java,v 1.4 2009/06/11 23:51:50 joehw Exp $
 502      *
 503      */
 504     private final class Tokens {
 505 
 506         /**
 507          * XPointer element() scheme
 508          * [1]    ElementSchemeData    ::=    (NCName ChildSequence?) | ChildSequence
 509          * [2]    ChildSequence    ::=    ('/' [1-9] [0-9]*)+
 510          */
 511         private static final int XPTRTOKEN_ELEM_NCNAME = 0;
 512 
 513         private static final int XPTRTOKEN_ELEM_CHILD = 1;
 514 
 515         // Token names
 516         private final String[] fgTokenNames = { "XPTRTOKEN_ELEM_NCNAME",
 517                 "XPTRTOKEN_ELEM_CHILD" };
 518 
 519         // Token count
 520         private static final int INITIAL_TOKEN_COUNT = 1 << 8;
 521 
 522         private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
 523 
 524         private int fTokenCount = 0;
 525 
 526         // Current token position
 527         private int fCurrentTokenIndex;
 528 
 529         private SymbolTable fSymbolTable;
 530 
 531         private HashMap<Integer, String> fTokenNames = new HashMap<>();
 532 
 533         /**
 534          * Constructor
 535          *
 536          * @param symbolTable SymbolTable
 537          */
 538         private Tokens(SymbolTable symbolTable) {
 539             fSymbolTable = symbolTable;
 540 
 541             fTokenNames.put(new Integer(XPTRTOKEN_ELEM_NCNAME),
 542                     "XPTRTOKEN_ELEM_NCNAME");
 543             fTokenNames.put(new Integer(XPTRTOKEN_ELEM_CHILD),
 544                     "XPTRTOKEN_ELEM_CHILD");
 545         }
 546 
 547         /*
 548          * Returns the token String
 549          * @param token The index of the token
 550          * @return String The token string
 551          */
 552         private String getTokenString(int token) {
 553             return fTokenNames.get(new Integer(token));
 554         }
 555 
 556         /**
 557          * Add the specified string as a token
 558          *
 559          * @param token The token string
 560          */
 561         private void addToken(String tokenStr) {
 562             if (!fTokenNames.containsValue(tokenStr)) {
 563                 Integer tokenInt = new Integer(fTokenNames.size());
 564                 fTokenNames.put(tokenInt, tokenStr);
 565                 addToken(tokenInt.intValue());
 566             }
 567         }
 568 
 569         /**
 570          * Add the specified int token
 571          *
 572          * @param token The int specifying the token
 573          */
 574         private void addToken(int token) {
 575             try {
 576                 fTokens[fTokenCount] = token;
 577             } catch (ArrayIndexOutOfBoundsException ex) {
 578                 int[] oldList = fTokens;
 579                 fTokens = new int[fTokenCount << 1];
 580                 System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
 581                 fTokens[fTokenCount] = token;
 582             }
 583             fTokenCount++;
 584         }
 585 
 586         /**
 587          * Resets the current position to the head of the token list.
 588          */
 589         private void rewind() {
 590             fCurrentTokenIndex = 0;
 591         }
 592 
 593         /**
 594          * Returns true if the {@link #getNextToken()} method
 595          * returns a valid token.
 596          */
 597         private boolean hasMore() {
 598             return fCurrentTokenIndex < fTokenCount;
 599         }
 600 
 601         /**
 602          * Obtains the token at the current position, then advance
 603          * the current position by one.
 604          *
 605          * If there's no such next token, this method throws
 606          * <tt>new XNIException("InvalidXPointerExpression");</tt>.
 607          */
 608         private int nextToken() throws XNIException {
 609             if (fCurrentTokenIndex == fTokenCount)
 610                 reportError("XPointerElementSchemeProcessingError", null);
 611             return fTokens[fCurrentTokenIndex++];
 612         }
 613 
 614         /**
 615          * Obtains the token at the current position, without advancing
 616          * the current position.
 617          *
 618          * If there's no such next token, this method throws
 619          * <tt>new XNIException("InvalidXPointerExpression");</tt>.
 620          */
 621         private int peekToken() throws XNIException {
 622             if (fCurrentTokenIndex == fTokenCount)
 623                 reportError("XPointerElementSchemeProcessingError", null);
 624             return fTokens[fCurrentTokenIndex];
 625         }
 626 
 627         /**
 628          * Obtains the token at the current position as a String.
 629          *
 630          * If there's no current token or if the current token
 631          * is not a string token, this method throws
 632          * If there's no such next token, this method throws
 633          * <tt>new XNIException("InvalidXPointerExpression");</tt>.
 634          */
 635         private String nextTokenAsString() throws XNIException {
 636             String s = getTokenString(nextToken());
 637             if (s == null)
 638                 reportError("XPointerElementSchemeProcessingError", null);
 639             return s;
 640         }
 641 
 642         /**
 643          * Returns the number of tokens.
 644          *
 645          */
 646         private int getTokenCount() {
 647             return fTokenCount;
 648         }
 649     }
 650 
 651     /**
 652      *
 653      * The XPointer expression scanner.  Scans the XPointer framework expression.
 654      *
 655      * @xerces.internal
 656      *
 657      * @version $Id: ElementSchemePointer.java,v 1.4 2009/06/11 23:51:50 joehw Exp $
 658      */
 659     private class Scanner {
 660 
 661         /**
 662          * 7-bit ASCII subset
 663          *
 664          *  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
 665          *  0,  0,  0,  0,  0,  0,  0,  0,  0, HT, LF,  0,  0, CR,  0,  0,  // 0
 666          *  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 1
 667          * SP,  !,  ",  #,  $,  %,  &,  ',  (,  ),  *,  +,  ,,  -,  .,  /,  // 2
 668          *  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  :,  ;,  <,  =,  >,  ?,  // 3
 669          *  @,  A,  B,  C,  D,  E,  F,  G,  H,  I,  J,  K,  L,  M,  N,  O,  // 4
 670          *  P,  Q,  R,  S,  T,  U,  V,  W,  X,  Y,  Z,  [,  \,  ],  ^,  _,  // 5
 671          *  `,  a,  b,  c,  d,  e,  f,  g,  h,  i,  j,  k,  l,  m,  n,  o,  // 6
 672          *  p,  q,  r,  s,  t,  u,  v,  w,  x,  y,  z,  {,  |,  },  ~, DEL  // 7
 673          */
 674         private static final byte CHARTYPE_INVALID = 0, // invalid XML characters, control characters and 7F
 675                 CHARTYPE_OTHER = 1, // A valid XML character (possibly invalid NCNameChar) that does not fall in one of the other categories
 676                 CHARTYPE_MINUS = 2, // '-' (0x2D)
 677                 CHARTYPE_PERIOD = 3, // '.' (0x2E)
 678                 CHARTYPE_SLASH = 4, // '/' (0x2F)
 679                 CHARTYPE_DIGIT = 5, // '0'-'9' (0x30 to 0x39)
 680                 CHARTYPE_LETTER = 6, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
 681                 CHARTYPE_UNDERSCORE = 7, // '_' (0x5F)
 682                 CHARTYPE_NONASCII = 8; // Non-ASCII Unicode codepoint (>= 0x80)
 683 
 684         private final byte[] fASCIICharMap = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
 685                 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 686                 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 5, 5, 5, 5, 5,
 687                 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6,
 688                 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1,
 689                 7, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
 690                 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1 };
 691 
 692         /**
 693          * Symbol literals
 694          */
 695 
 696         //
 697         // Data
 698         //
 699         /** Symbol table. */
 700         private SymbolTable fSymbolTable;
 701 
 702         //
 703         // Constructors
 704         //
 705 
 706         /**
 707          * Constructs an XPath expression scanner.
 708          *
 709          * @param symbolTable SymbolTable
 710          */
 711         private Scanner(SymbolTable symbolTable) {
 712             // save pool and tokens
 713             fSymbolTable = symbolTable;
 714 
 715         } // <init>(SymbolTable)
 716 
 717         /**
 718          * Scans the XPointer Expression
 719          *
 720          */
 721         private boolean scanExpr(SymbolTable symbolTable, Tokens tokens,
 722                 String data, int currentOffset, int endOffset)
 723                 throws XNIException {
 724 
 725             int ch;
 726             int nameOffset;
 727             String nameHandle = null;
 728 
 729             while (true) {
 730                 if (currentOffset == endOffset) {
 731                     break;
 732                 }
 733 
 734                 ch = data.charAt(currentOffset);
 735                 byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
 736                         : fASCIICharMap[ch];
 737 
 738                 //
 739                 // [1]    ElementSchemeData    ::=    (NCName ChildSequence?) | ChildSequence
 740                 // [2]    ChildSequence    ::=    ('/' [1-9] [0-9]*)+
 741                 //
 742 
 743                 switch (chartype) {
 744 
 745                 case CHARTYPE_SLASH:
 746                     // if last character is '/', break and report an error
 747                     if (++currentOffset == endOffset) {
 748                         return false;
 749                     }
 750 
 751                     addToken(tokens, Tokens.XPTRTOKEN_ELEM_CHILD);
 752                     ch = data.charAt(currentOffset);
 753 
 754                     // ChildSequence    ::=    ('/' [1-9] [0-9]*)+
 755                     int child = 0;
 756                     while (ch >= '0' && ch <= '9') {
 757                         child = (child * 10) + (ch - '0');
 758                         if (++currentOffset == endOffset) {
 759                             break;
 760                         }
 761                         ch = data.charAt(currentOffset);
 762                     }
 763 
 764                     // An invalid child sequence character
 765                     if (child == 0) {
 766                         reportError("InvalidChildSequenceCharacter",
 767                                 new Object[] { new Character((char) ch) });
 768                         return false;
 769                     }
 770 
 771                     tokens.addToken(child);
 772 
 773                     break;
 774 
 775                 case CHARTYPE_DIGIT:
 776                 case CHARTYPE_LETTER:
 777                 case CHARTYPE_MINUS:
 778                 case CHARTYPE_NONASCII:
 779                 case CHARTYPE_OTHER:
 780                 case CHARTYPE_PERIOD:
 781                 case CHARTYPE_UNDERSCORE:
 782                     // Scan the ShortHand Pointer NCName
 783                     nameOffset = currentOffset;
 784                     currentOffset = scanNCName(data, endOffset, currentOffset);
 785 
 786                     if (currentOffset == nameOffset) {
 787                         //return false;
 788                         reportError("InvalidNCNameInElementSchemeData",
 789                                 new Object[] { data });
 790                         return false;
 791                     }
 792 
 793                     if (currentOffset < endOffset) {
 794                         ch = data.charAt(currentOffset);
 795                     } else {
 796                         ch = -1;
 797                     }
 798 
 799                     nameHandle = symbolTable.addSymbol(data.substring(
 800                             nameOffset, currentOffset));
 801                     addToken(tokens, Tokens.XPTRTOKEN_ELEM_NCNAME);
 802                     tokens.addToken(nameHandle);
 803 
 804                     break;
 805                 }
 806             }
 807             return true;
 808         }
 809 
 810         /**
 811          * Scans a NCName.
 812          * From Namespaces in XML
 813          * [5] NCName ::= (Letter | '_') (NCNameChar)*
 814          * [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
 815          *
 816          * @param data A String containing the XPointer expression
 817          * @param endOffset The int XPointer expression length
 818          * @param currentOffset An int representing the current position of the XPointer expression pointer
 819          */
 820         private int scanNCName(String data, int endOffset, int currentOffset) {
 821             int ch = data.charAt(currentOffset);
 822             if (ch >= 0x80) {
 823                 if (!XMLChar.isNameStart(ch)) {
 824                     return currentOffset;
 825                 }
 826             } else {
 827                 byte chartype = fASCIICharMap[ch];
 828                 if (chartype != CHARTYPE_LETTER
 829                         && chartype != CHARTYPE_UNDERSCORE) {
 830                     return currentOffset;
 831                 }
 832             }
 833             while (++currentOffset < endOffset) {
 834                 ch = data.charAt(currentOffset);
 835                 if (ch >= 0x80) {
 836                     if (!XMLChar.isName(ch)) {
 837                         break;
 838                     }
 839                 } else {
 840                     byte chartype = fASCIICharMap[ch];
 841                     if (chartype != CHARTYPE_LETTER
 842                             && chartype != CHARTYPE_DIGIT
 843                             && chartype != CHARTYPE_PERIOD
 844                             && chartype != CHARTYPE_MINUS
 845                             && chartype != CHARTYPE_UNDERSCORE) {
 846                         break;
 847                     }
 848                 }
 849             }
 850             return currentOffset;
 851         }
 852 
 853         //
 854         // Protected methods
 855         //
 856 
 857         /**
 858          * This method adds the specified token to the token list. By
 859          * default, this method allows all tokens. However, subclasses
 860          * of the XPathExprScanner can override this method in order
 861          * to disallow certain tokens from being used in the scanned
 862          * XPath expression. This is a convenient way of allowing only
 863          * a subset of XPath.
 864          */
 865         protected void addToken(Tokens tokens, int token) throws XNIException {
 866             tokens.addToken(token);
 867         } // addToken(int)
 868 
 869     } // class Scanner
 870 
 871 }