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 import java.util.Vector;
  24 
  25 import com.sun.org.apache.xerces.internal.impl.Constants;
  26 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  27 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  28 import com.sun.org.apache.xerces.internal.util.XMLChar;
  29 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  30 import com.sun.org.apache.xerces.internal.xinclude.XIncludeHandler;
  31 import com.sun.org.apache.xerces.internal.xinclude.XIncludeNamespaceSupport;
  32 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  33 import com.sun.org.apache.xerces.internal.xni.QName;
  34 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  35 import com.sun.org.apache.xerces.internal.xni.XMLString;
  36 import com.sun.org.apache.xerces.internal.xni.XNIException;
  37 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  38 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
  39 
  40 /**
  41  * <p>
  42  * This is a pipeline component which extends the XIncludeHandler to perform
  43  * XPointer specific processing specified in the W3C XPointerFramework and
  44  * element() Scheme Recommendations.
  45  * </p>
  46  *
  47  * <p>
  48  * This component analyzes each event in the pipeline, looking for an element
  49  * that matches a PointerPart in the parent XInclude element's xpointer attribute
  50  * value.  If the match succeeds, all children are passed by this component.
  51  * </p>
  52  *
  53  * <p>
  54  * See the <a href="http://www.w3.org/TR/xptr-framework//">XPointer Framework Recommendation</a> for
  55  * more information on the XPointer Framework and ShortHand Pointers.
  56  * See the <a href="http://www.w3.org/TR/xptr-element/">XPointer element() Scheme Recommendation</a> for
  57  * more information on the XPointer element() Scheme.
  58  * </p>
  59  *
  60  * @xerces.internal
  61  *
  62  */
  63 public final class XPointerHandler extends XIncludeHandler implements
  64         XPointerProcessor {
  65 
  66     // Fields
  67     // A Vector of XPointerParts
  68     protected Vector fXPointerParts = null;
  69 
  70     // The current XPointerPart
  71     protected XPointerPart fXPointerPart = null;
  72 
  73     // Has the fXPointerPart resolved successfully
  74     protected boolean fFoundMatchingPtrPart = false;
  75 
  76     // The XPointer Error reporter
  77     protected XMLErrorReporter fXPointerErrorReporter;
  78 
  79     // The XPointer Error Handler
  80     protected XMLErrorHandler fErrorHandler;
  81 
  82     // XPointerFramework symbol table
  83     protected SymbolTable fSymbolTable = null;
  84 
  85     // Supported schemes
  86     private final String ELEMENT_SCHEME_NAME = "element";
  87 
  88    // Has the XPointer resolved the subresource
  89     protected boolean fIsXPointerResolved = false;
  90 
  91     // Fixup xml:base and xml:lang attributes
  92     protected boolean fFixupBase = false;
  93     protected boolean fFixupLang = false;
  94 
  95     // ************************************************************************
  96     // Constructors
  97     // ************************************************************************
  98 
  99     /**
 100      *
 101      */
 102     public XPointerHandler() {
 103         super();
 104 
 105         fXPointerParts = new Vector();
 106         fSymbolTable = new SymbolTable();
 107     }
 108 
 109     public XPointerHandler(SymbolTable symbolTable,
 110             XMLErrorHandler errorHandler, XMLErrorReporter errorReporter) {
 111         super();
 112 
 113         fXPointerParts = new Vector();
 114         fSymbolTable = symbolTable;
 115         fErrorHandler = errorHandler;
 116         fXPointerErrorReporter = errorReporter;
 117         //fErrorReporter = errorReporter; // The XInclude ErrorReporter
 118     }
 119 
 120     // ************************************************************************
 121     //  Implementation of the XPointerProcessor interface.
 122     // ************************************************************************
 123 
 124     /**
 125      * Parses the XPointer framework expression and delegates scheme specific parsing.
 126      *
 127      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#parseXPointer(java.lang.String)
 128      */
 129     public void parseXPointer(String xpointer) throws XNIException {
 130 
 131         // Initialize
 132         init();
 133 
 134         // tokens
 135         final Tokens tokens = new Tokens(fSymbolTable);
 136 
 137         // scanner
 138         Scanner scanner = new Scanner(fSymbolTable) {
 139             protected void addToken(Tokens tokens, int token)
 140                     throws XNIException {
 141                 if (token == Tokens.XPTRTOKEN_OPEN_PAREN
 142                         || token == Tokens.XPTRTOKEN_CLOSE_PAREN
 143                         || token == Tokens.XPTRTOKEN_SCHEMENAME
 144                         || token == Tokens.XPTRTOKEN_SCHEMEDATA
 145                         || token == Tokens.XPTRTOKEN_SHORTHAND) {
 146                     super.addToken(tokens, token);
 147                     return;
 148                 }
 149                 reportError("InvalidXPointerToken", new Object[] { tokens
 150                         .getTokenString(token) });
 151             }
 152         };
 153 
 154         // scan the XPointer expression
 155         int length = xpointer.length();
 156         boolean success = scanner.scanExpr(fSymbolTable, tokens, xpointer, 0,
 157                 length);
 158 
 159         if (!success)
 160             reportError("InvalidXPointerExpression", new Object[] { xpointer });
 161 
 162         while (tokens.hasMore()) {
 163             int token = tokens.nextToken();
 164 
 165             switch (token) {
 166             case Tokens.XPTRTOKEN_SHORTHAND: {
 167 
 168                 // The shortHand name
 169                 token = tokens.nextToken();
 170                 String shortHandPointerName = tokens.getTokenString(token);
 171 
 172                 if (shortHandPointerName == null) {
 173                     reportError("InvalidXPointerExpression",
 174                             new Object[] { xpointer });
 175                 }
 176 
 177                 XPointerPart shortHandPointer = new ShortHandPointer(
 178                         fSymbolTable);
 179                 shortHandPointer.setSchemeName(shortHandPointerName);
 180                 fXPointerParts.add(shortHandPointer);
 181                 break;
 182             }
 183             case Tokens.XPTRTOKEN_SCHEMENAME: {
 184 
 185                 // Retreive the local name and prefix to form the scheme name
 186                 token = tokens.nextToken();
 187                 String prefix = tokens.getTokenString(token);
 188                 token = tokens.nextToken();
 189                 String localName = tokens.getTokenString(token);
 190 
 191                 String schemeName = prefix + localName;
 192 
 193                 // The next character should be an open parenthesis
 194                 int openParenCount = 0;
 195                 int closeParenCount = 0;
 196 
 197                 token = tokens.nextToken();
 198                 String openParen = tokens.getTokenString(token);
 199                 if (openParen != "XPTRTOKEN_OPEN_PAREN") {
 200 
 201                     // can not have more than one ShortHand Pointer
 202                     if (token == Tokens.XPTRTOKEN_SHORTHAND) {
 203                         reportError("MultipleShortHandPointers",
 204                                 new Object[] { xpointer });
 205                     } else {
 206                         reportError("InvalidXPointerExpression",
 207                                 new Object[] { xpointer });
 208                     }
 209                 }
 210                 openParenCount++;
 211 
 212                 // followed by zero or more ( and  the schemeData
 213                 String schemeData = null;
 214                 while (tokens.hasMore()) {
 215                     token = tokens.nextToken();
 216                     schemeData = tokens.getTokenString(token);
 217                     if (schemeData != "XPTRTOKEN_OPEN_PAREN") {
 218                         break;
 219                     }
 220                     openParenCount++;
 221                 }
 222                 token = tokens.nextToken();
 223                 schemeData = tokens.getTokenString(token);
 224 
 225                 // followed by the same number of )
 226                 token = tokens.nextToken();
 227                 String closeParen = tokens.getTokenString(token);
 228                 if (closeParen != "XPTRTOKEN_CLOSE_PAREN") {
 229                     reportError("SchemeDataNotFollowedByCloseParenthesis",
 230                             new Object[] { xpointer });
 231                 }
 232                 closeParenCount++;
 233 
 234                 while (tokens.hasMore()) {
 235                     if (tokens.getTokenString(tokens.peekToken()) != "XPTRTOKEN_OPEN_PAREN") {
 236                         break;
 237                     }
 238                     closeParenCount++;
 239                 }
 240 
 241                 // check if the number of open parenthesis are equal to the number of close parenthesis
 242                 if (openParenCount != closeParenCount) {
 243                     reportError("UnbalancedParenthesisInXPointerExpression",
 244                             new Object[] { xpointer,
 245                                     new Integer(openParenCount),
 246                                     new Integer(closeParenCount) });
 247                 }
 248 
 249                 // Perform scheme specific parsing of the pointer part
 250                 if (schemeName.equals(ELEMENT_SCHEME_NAME)) {
 251                     XPointerPart elementSchemePointer = new ElementSchemePointer(
 252                             fSymbolTable, fErrorReporter);
 253                     elementSchemePointer.setSchemeName(schemeName);
 254                     elementSchemePointer.setSchemeData(schemeData);
 255 
 256                     // If an exception occurs while parsing the element() scheme expression
 257                     // ignore it and move on to the next pointer part
 258                     try {
 259                         elementSchemePointer.parseXPointer(schemeData);
 260                         fXPointerParts.add(elementSchemePointer);
 261                     } catch (XNIException e) {
 262                         // Re-throw the XPointer element() scheme syntax error.
 263                         throw new XNIException (e);
 264                     }
 265 
 266                 } else {
 267                     // ????
 268                     reportWarning("SchemeUnsupported",
 269                             new Object[] { schemeName });
 270                 }
 271 
 272                 break;
 273             }
 274             default:
 275                 reportError("InvalidXPointerExpression",
 276                         new Object[] { xpointer });
 277             }
 278         }
 279 
 280     }
 281 
 282     /**
 283      *
 284      * @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)
 285      */
 286     public boolean resolveXPointer(QName element, XMLAttributes attributes,
 287             Augmentations augs, int event) throws XNIException {
 288         boolean resolved = false;
 289 
 290         // The result of the first pointer part whose evaluation identifies
 291         // one or more subresources is reported by the XPointer processor as the
 292         // result of the pointer as a whole, and evaluation stops.
 293         // In our implementation, typically the first xpointer scheme that
 294         // matches an element is the document is considered.
 295         // If the pointer part resolved then use it, else search for the fragment
 296         // using next pointer part from lef-right.
 297         if (!fFoundMatchingPtrPart) {
 298 
 299             // for each element, attempt to resolve it against each pointer part
 300             // in the XPointer expression until a matching element is found.
 301             for (int i = 0; i < fXPointerParts.size(); i++) {
 302 
 303                 fXPointerPart = (XPointerPart) fXPointerParts.get(i);
 304 
 305                 if (fXPointerPart.resolveXPointer(element, attributes, augs,
 306                         event)) {
 307                     fFoundMatchingPtrPart = true;
 308                     resolved = true;
 309                 }
 310             }
 311         } else {
 312             if (fXPointerPart.resolveXPointer(element, attributes, augs, event)) {
 313                 resolved = true;
 314             }
 315         }
 316 
 317         if (!fIsXPointerResolved) {
 318             fIsXPointerResolved = resolved;
 319         }
 320 
 321         return resolved;
 322     }
 323 
 324     /**
 325      * Returns true if the Node fragment is resolved.
 326      *
 327      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#isFragmentResolved()
 328      */
 329     public boolean isFragmentResolved() throws XNIException {
 330         boolean resolved = (fXPointerPart != null) ? fXPointerPart.isFragmentResolved()
 331                 : false;
 332 
 333         if (!fIsXPointerResolved) {
 334             fIsXPointerResolved = resolved;
 335         }
 336 
 337         return resolved;
 338     }
 339 
 340     /**
 341      * Returns true if the XPointer expression resolves to a non-element child
 342      * of the current resource fragment.
 343      *
 344      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#isChildFragmentResolved()
 345      *
 346      */
 347     public boolean isChildFragmentResolved() throws XNIException {
 348         boolean resolved = (fXPointerPart != null) ? fXPointerPart
 349                                 .isChildFragmentResolved() : false;
 350                 return resolved;
 351     }
 352 
 353     /**
 354      * Returns true if the XPointer successfully found a sub-resource .
 355      *
 356      * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#isFragmentResolved()
 357      */
 358     public boolean isXPointerResolved() throws XNIException {
 359         return fIsXPointerResolved;
 360     }
 361 
 362     /**
 363      * Returns the pointer part used to resolve the document fragment.
 364      *
 365      * @return String - The pointer part used to resolve the document fragment.
 366      */
 367     public XPointerPart getXPointerPart() {
 368         return fXPointerPart;
 369     }
 370 
 371     /**
 372      * Reports XPointer Errors
 373      *
 374      */
 375     private void reportError(String key, Object[] arguments)
 376             throws XNIException {
 377         /*
 378         fXPointerErrorReporter.reportError(
 379                 XPointerMessageFormatter.XPOINTER_DOMAIN, key, arguments,
 380                 XMLErrorReporter.SEVERITY_ERROR);
 381         */
 382         throw new XNIException((fErrorReporter
 383                                 .getMessageFormatter(XPointerMessageFormatter.XPOINTER_DOMAIN))
 384                                 .formatMessage(fErrorReporter.getLocale(), key, arguments));
 385     }
 386 
 387     /**
 388      * Reports XPointer Warnings
 389      *
 390      */
 391     private void reportWarning(String key, Object[] arguments)
 392             throws XNIException {
 393         fXPointerErrorReporter.reportError(
 394                 XPointerMessageFormatter.XPOINTER_DOMAIN, key, arguments,
 395                 XMLErrorReporter.SEVERITY_WARNING);
 396     }
 397 
 398     /**
 399      * Initializes error handling objects
 400      *
 401      */
 402     protected void initErrorReporter() {
 403         if (fXPointerErrorReporter == null) {
 404             fXPointerErrorReporter = new XMLErrorReporter();
 405         }
 406         if (fErrorHandler == null) {
 407             fErrorHandler = new XPointerErrorHandler();
 408         }
 409         /*
 410          fXPointerErrorReporter.setProperty(Constants.XERCES_PROPERTY_PREFIX
 411          + Constants.ERROR_HANDLER_PROPERTY, fErrorHandler);
 412          */
 413         fXPointerErrorReporter.putMessageFormatter(
 414                 XPointerMessageFormatter.XPOINTER_DOMAIN,
 415                 new XPointerMessageFormatter());
 416     }
 417 
 418     /**
 419      * Initializes the XPointer Processor;
 420      */
 421     protected void init() {
 422         fXPointerParts.clear();
 423         fXPointerPart = null;
 424         fFoundMatchingPtrPart = false;
 425         fIsXPointerResolved = false;
 426         //fFixupBase = false;
 427         //fFixupLang = false;
 428 
 429         initErrorReporter();
 430     }
 431 
 432     /**
 433      * Returns a Vector of XPointerPart objects
 434      *
 435      * @return A Vector of XPointerPart objects.
 436      */
 437     public Vector getPointerParts() {
 438         return fXPointerParts;
 439     }
 440 
 441     /**
 442      * List of XPointer Framework tokens.
 443      *
 444      * @xerces.internal
 445      *
 446      */
 447     private final class Tokens {
 448 
 449         /**
 450          * XPointer Framework tokens
 451          * [1] Pointer     ::= Shorthand | SchemeBased
 452          * [2] Shorthand   ::= NCName
 453          * [3] SchemeBased ::= PointerPart (S? PointerPart)*
 454          * [4] PointerPart ::= SchemeName '(' SchemeData ')'
 455          * [5] SchemeName  ::= QName
 456          * [6] SchemeData  ::= EscapedData*
 457          * [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
 458          * [8] NormalChar  ::= UnicodeChar - [()^]
 459          * [9] UnicodeChar ::= [#x0-#x10FFFF]
 460          *
 461          */
 462         private static final int XPTRTOKEN_OPEN_PAREN = 0,
 463                 XPTRTOKEN_CLOSE_PAREN = 1, XPTRTOKEN_SHORTHAND = 2,
 464                 XPTRTOKEN_SCHEMENAME = 3, XPTRTOKEN_SCHEMEDATA = 4;
 465 
 466         // Token names
 467         private final String[] fgTokenNames = { "XPTRTOKEN_OPEN_PAREN",
 468                 "XPTRTOKEN_CLOSE_PAREN", "XPTRTOKEN_SHORTHAND",
 469                 "XPTRTOKEN_SCHEMENAME", "XPTRTOKEN_SCHEMEDATA" };
 470 
 471         // Token count
 472         private static final int INITIAL_TOKEN_COUNT = 1 << 8;
 473 
 474         private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
 475 
 476         private int fTokenCount = 0;
 477 
 478         // Current token position
 479         private int fCurrentTokenIndex;
 480 
 481         private SymbolTable fSymbolTable;
 482 
 483         private Hashtable fTokenNames = new Hashtable();
 484 
 485         /**
 486          * Constructor
 487          *
 488          * @param symbolTable SymbolTable
 489          */
 490         private Tokens(SymbolTable symbolTable) {
 491             fSymbolTable = symbolTable;
 492 
 493             fTokenNames.put(new Integer(XPTRTOKEN_OPEN_PAREN),
 494                     "XPTRTOKEN_OPEN_PAREN");
 495             fTokenNames.put(new Integer(XPTRTOKEN_CLOSE_PAREN),
 496                     "XPTRTOKEN_CLOSE_PAREN");
 497             fTokenNames.put(new Integer(XPTRTOKEN_SHORTHAND),
 498                     "XPTRTOKEN_SHORTHAND");
 499             fTokenNames.put(new Integer(XPTRTOKEN_SCHEMENAME),
 500                     "XPTRTOKEN_SCHEMENAME");
 501             fTokenNames.put(new Integer(XPTRTOKEN_SCHEMEDATA),
 502                     "XPTRTOKEN_SCHEMEDATA");
 503         }
 504 
 505         /**
 506          * Returns the token String
 507          * @param token The index of the token
 508          * @return String The token string
 509          */
 510         private String getTokenString(int token) {
 511             return (String) fTokenNames.get(new Integer(token));
 512         }
 513 
 514         /**
 515          * Add the specified string as a token
 516          *
 517          * @param token The token string
 518          */
 519         private void addToken(String tokenStr) {
 520             Integer tokenInt = (Integer) fTokenNames.get(tokenStr);
 521             if (tokenInt == null) {
 522                 tokenInt = new Integer(fTokenNames.size());
 523                 fTokenNames.put(tokenInt, tokenStr);
 524             }
 525             addToken(tokenInt.intValue());
 526         }
 527 
 528         /**
 529          * Add the specified int token
 530          *
 531          * @param token The int specifying the token
 532          */
 533         private void addToken(int token) {
 534             try {
 535                 fTokens[fTokenCount] = token;
 536             } catch (ArrayIndexOutOfBoundsException ex) {
 537                 int[] oldList = fTokens;
 538                 fTokens = new int[fTokenCount << 1];
 539                 System.arraycopy(oldList, 0, fTokens, 0, fTokenCount);
 540                 fTokens[fTokenCount] = token;
 541             }
 542             fTokenCount++;
 543         }
 544 
 545         /**
 546          * Resets the current position to the head of the token list.
 547          */
 548         private void rewind() {
 549             fCurrentTokenIndex = 0;
 550         }
 551 
 552         /**
 553          * Returns true if the {@link #getNextToken()} method
 554          * returns a valid token.
 555          */
 556         private boolean hasMore() {
 557             return fCurrentTokenIndex < fTokenCount;
 558         }
 559 
 560         /**
 561          * Obtains the token at the current position, then advance
 562          * the current position by one.
 563          *
 564          * throws If there's no such next token, this method throws
 565          * <tt>new XNIException("XPointerProcessingError");</tt>.
 566          */
 567         private int nextToken() throws XNIException {
 568             if (fCurrentTokenIndex == fTokenCount) {
 569                 reportError("XPointerProcessingError", null);
 570             }
 571             return fTokens[fCurrentTokenIndex++];
 572         }
 573 
 574         /**
 575          * Obtains the token at the current position, without advancing
 576          * the current position.
 577          *
 578          * If there's no such next token, this method throws
 579          * <tt>new XNIException("XPointerProcessingError");</tt>.
 580          */
 581         private int peekToken() throws XNIException {
 582             if (fCurrentTokenIndex == fTokenCount) {
 583                 reportError("XPointerProcessingError", null);
 584             }
 585             return fTokens[fCurrentTokenIndex];
 586         }
 587 
 588         /**
 589          * Obtains the token at the current position as a String.
 590          *
 591          * If there's no current token or if the current token
 592          * is not a string token, this method throws
 593          * If there's no such next token, this method throws
 594          * <tt>new XNIException("XPointerProcessingError");</tt>.
 595          */
 596         private String nextTokenAsString() throws XNIException {
 597             String tokenStrint = getTokenString(nextToken());
 598             if (tokenStrint == null) {
 599                 reportError("XPointerProcessingError", null);
 600             }
 601             return tokenStrint;
 602         }
 603     }
 604 
 605     /**
 606      *
 607      * The XPointer expression scanner.  Scans the XPointer framework expression.
 608      *
 609      * @xerces.internal
 610      *
 611      */
 612     private class Scanner {
 613 
 614         /**
 615          * 7-bit ASCII subset
 616          *
 617          *  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
 618          *  0,  0,  0,  0,  0,  0,  0,  0,  0, HT, LF,  0,  0, CR,  0,  0,  // 0
 619          *  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 1
 620          * SP,  !,  ",  #,  $,  %,  &,  ',  (,  ),  *,  +,  ,,  -,  .,  /,  // 2
 621          *  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  :,  ;,  <,  =,  >,  ?,  // 3
 622          *  @,  A,  B,  C,  D,  E,  F,  G,  H,  I,  J,  K,  L,  M,  N,  O,  // 4
 623          *  P,  Q,  R,  S,  T,  U,  V,  W,  X,  Y,  Z,  [,  \,  ],  ^,  _,  // 5
 624          *  `,  a,  b,  c,  d,  e,  f,  g,  h,  i,  j,  k,  l,  m,  n,  o,  // 6
 625          *  p,  q,  r,  s,  t,  u,  v,  w,  x,  y,  z,  {,  |,  },  ~, DEL  // 7
 626          */
 627         private static final byte CHARTYPE_INVALID = 0, // invalid XML character
 628                 CHARTYPE_OTHER = 1, // not special - one of "#%&;?\`{}~" or DEL
 629                 CHARTYPE_WHITESPACE = 2, // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20)
 630                 CHARTYPE_CARRET = 3, // ^
 631                 CHARTYPE_OPEN_PAREN = 4, // '(' (0x28)
 632                 CHARTYPE_CLOSE_PAREN = 5, // ')' (0x29)
 633                 CHARTYPE_MINUS = 6, // '-' (0x2D)
 634                 CHARTYPE_PERIOD = 7, // '.' (0x2E)
 635                 CHARTYPE_SLASH = 8, // '/' (0x2F)
 636                 CHARTYPE_DIGIT = 9, // '0'-'9' (0x30 to 0x39)
 637                 CHARTYPE_COLON = 10, // ':' (0x3A)
 638                 CHARTYPE_EQUAL = 11, // '=' (0x3D)
 639                 CHARTYPE_LETTER = 12, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
 640                 CHARTYPE_UNDERSCORE = 13, // '_' (0x5F)
 641                 CHARTYPE_NONASCII = 14; // Non-ASCII Unicode codepoint (>= 0x80)
 642 
 643         private final byte[] fASCIICharMap = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2,
 644                 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 645                 2, 1, 1, 1, 1, 1, 1, 1, 4, 5, 1, 1, 1, 6, 7, 8, 9, 9, 9, 9, 9,
 646                 9, 9, 9, 9, 9, 10, 1, 1, 11, 1, 1, 1, 12, 12, 12, 12, 12, 12,
 647                 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
 648                 12, 12, 12, 12, 1, 1, 1, 3, 13, 1, 12, 12, 12, 12, 12, 12, 12,
 649                 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
 650                 12, 12, 12, 1, 1, 1, 1, 1 };
 651 
 652         //
 653         // Data
 654         //
 655         /** Symbol table. */
 656         private SymbolTable fSymbolTable;
 657 
 658         /**
 659          * Constructs an XPointer Framework expression scanner.
 660          *
 661          * @param symbolTable SymbolTable
 662          */
 663         private Scanner(SymbolTable symbolTable) {
 664             // save pool and tokens
 665             fSymbolTable = symbolTable;
 666 
 667         } // <init>(SymbolTable)
 668 
 669         /**
 670          * Scans the XPointer Expression
 671          *
 672          */
 673         private boolean scanExpr(SymbolTable symbolTable, Tokens tokens,
 674                 String data, int currentOffset, int endOffset)
 675                 throws XNIException {
 676 
 677             int ch;
 678             int openParen = 0;
 679             int closeParen = 0;
 680             int nameOffset, dataOffset;
 681             boolean isQName = false;
 682             String name = null;
 683             String prefix = null;
 684             String schemeData = null;
 685             StringBuffer schemeDataBuff = new StringBuffer();
 686 
 687             while (true) {
 688 
 689                 if (currentOffset == endOffset) {
 690                     break;
 691                 }
 692                 ch = data.charAt(currentOffset);
 693 
 694                 //
 695                 while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
 696                     if (++currentOffset == endOffset) {
 697                         break;
 698                     }
 699                     ch = data.charAt(currentOffset);
 700                 }
 701                 if (currentOffset == endOffset) {
 702                     break;
 703                 }
 704 
 705                 //
 706                 // [1]    Pointer      ::=    Shorthand | SchemeBased
 707                 // [2]    Shorthand    ::=    NCName
 708                 // [3]    SchemeBased  ::=    PointerPart (S? PointerPart)*
 709                 // [4]    PointerPart  ::=    SchemeName '(' SchemeData ')'
 710                 // [5]    SchemeName   ::=    QName
 711                 // [6]    SchemeData   ::=    EscapedData*
 712                 // [7]    EscapedData  ::=    NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
 713                 // [8]    NormalChar   ::=    UnicodeChar - [()^]
 714                 // [9]    UnicodeChar  ::=    [#x0-#x10FFFF]
 715                 // [?]    QName        ::=    (NCName ':')? NCName
 716                 // [?]    NCName       ::=    (Letter | '_') (NCNameChar)*
 717                 // [?]    NCNameChar   ::=    Letter | Digit | '.' | '-' | '_'  (ascii subset of 'NCNameChar')
 718                 // [?]    Letter       ::=    [A-Za-z]                              (ascii subset of 'Letter')
 719                 // [?]    Digit        ::=    [0-9]                                  (ascii subset of 'Digit')
 720                 //
 721                 byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
 722                         : fASCIICharMap[ch];
 723 
 724                 switch (chartype) {
 725 
 726                 case CHARTYPE_OPEN_PAREN: // '('
 727                     addToken(tokens, Tokens.XPTRTOKEN_OPEN_PAREN);
 728                     openParen++;
 729                     ++currentOffset;
 730                     break;
 731 
 732                 case CHARTYPE_CLOSE_PAREN: // ')'
 733                     addToken(tokens, Tokens.XPTRTOKEN_CLOSE_PAREN);
 734                     closeParen++;
 735                     ++currentOffset;
 736                     break;
 737 
 738                 case CHARTYPE_CARRET:
 739                 case CHARTYPE_COLON:
 740                 case CHARTYPE_DIGIT:
 741                 case CHARTYPE_EQUAL:
 742                 case CHARTYPE_LETTER:
 743                 case CHARTYPE_MINUS:
 744                 case CHARTYPE_NONASCII:
 745                 case CHARTYPE_OTHER:
 746                 case CHARTYPE_PERIOD:
 747                 case CHARTYPE_SLASH:
 748                 case CHARTYPE_UNDERSCORE:
 749                 case CHARTYPE_WHITESPACE:
 750                     // Scanning SchemeName | Shorthand
 751                     if (openParen == 0) {
 752                         nameOffset = currentOffset;
 753                         currentOffset = scanNCName(data, endOffset,
 754                                 currentOffset);
 755 
 756                         if (currentOffset == nameOffset) {
 757                             reportError("InvalidShortHandPointer",
 758                                     new Object[] { data });
 759                             return false;
 760                         }
 761 
 762                         if (currentOffset < endOffset) {
 763                             ch = data.charAt(currentOffset);
 764                         } else {
 765                             ch = -1;
 766                         }
 767 
 768                         name = symbolTable.addSymbol(data.substring(nameOffset,
 769                                 currentOffset));
 770                         prefix = XMLSymbols.EMPTY_STRING;
 771 
 772                         // The name is a QName => a SchemeName
 773                         if (ch == ':') {
 774                             if (++currentOffset == endOffset) {
 775                                 return false;
 776                             }
 777 
 778                             ch = data.charAt(currentOffset);
 779                             prefix = name;
 780                             nameOffset = currentOffset;
 781                             currentOffset = scanNCName(data, endOffset,
 782                                     currentOffset);
 783 
 784                             if (currentOffset == nameOffset) {
 785                                 return false;
 786                             }
 787 
 788                             if (currentOffset < endOffset) {
 789                                 ch = data.charAt(currentOffset);
 790                             } else {
 791                                 ch = -1;
 792                             }
 793 
 794                             isQName = true;
 795                             name = symbolTable.addSymbol(data.substring(
 796                                     nameOffset, currentOffset));
 797                         }
 798 
 799                         // REVISIT:
 800                         if (currentOffset != endOffset) {
 801                             addToken(tokens, Tokens.XPTRTOKEN_SCHEMENAME);
 802                             tokens.addToken(prefix);
 803                             tokens.addToken(name);
 804                             isQName = false;
 805                         } else if (currentOffset == endOffset) {
 806                             // NCName => Shorthand
 807                             addToken(tokens, Tokens.XPTRTOKEN_SHORTHAND);
 808                             tokens.addToken(name);
 809                             isQName = false;
 810                         }
 811 
 812                         // reset open/close paren for the next pointer part
 813                         closeParen = 0;
 814 
 815                         break;
 816 
 817                     } else if (openParen > 0 && closeParen == 0 && name != null) {
 818                         // Scanning SchemeData
 819                         dataOffset = currentOffset;
 820                         currentOffset = scanData(data, schemeDataBuff,
 821                                 endOffset, currentOffset);
 822 
 823                         if (currentOffset == dataOffset) {
 824                             reportError("InvalidSchemeDataInXPointer",
 825                                     new Object[] { data });
 826                             return false;
 827                         }
 828 
 829                         if (currentOffset < endOffset) {
 830                             ch = data.charAt(currentOffset);
 831                         } else {
 832                             ch = -1;
 833                         }
 834 
 835                         schemeData = symbolTable.addSymbol(schemeDataBuff
 836                                 .toString());
 837                         addToken(tokens, Tokens.XPTRTOKEN_SCHEMEDATA);
 838                         tokens.addToken(schemeData);
 839 
 840                         // reset open/close paren for the next pointer part
 841                         openParen = 0;
 842                         schemeDataBuff.delete(0, schemeDataBuff.length());
 843 
 844                     } else {
 845                         // ex. schemeName()
 846                         // Should we throw an exception with a more suitable message instead??
 847                         return false;
 848                     }
 849                 }
 850             } // end while
 851             return true;
 852         }
 853 
 854         /**
 855          * Scans a NCName.
 856          * From Namespaces in XML
 857          * [5] NCName ::= (Letter | '_') (NCNameChar)*
 858          * [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
 859          *
 860          * @param data A String containing the XPointer expression
 861          * @param endOffset The int XPointer expression length
 862          * @param currentOffset An int representing the current position of the XPointer expression pointer
 863          */
 864         private int scanNCName(String data, int endOffset, int currentOffset) {
 865             int ch = data.charAt(currentOffset);
 866             if (ch >= 0x80) {
 867                 if (!XMLChar.isNameStart(ch)) {
 868                     return currentOffset;
 869                 }
 870             } else {
 871                 byte chartype = fASCIICharMap[ch];
 872                 if (chartype != CHARTYPE_LETTER
 873                         && chartype != CHARTYPE_UNDERSCORE) {
 874                     return currentOffset;
 875                 }
 876             }
 877 
 878             //while (currentOffset++ < endOffset) {
 879             while (++currentOffset < endOffset) {
 880                 ch = data.charAt(currentOffset);
 881                 if (ch >= 0x80) {
 882                     if (!XMLChar.isName(ch)) {
 883                         break;
 884                     }
 885                 } else {
 886                     byte chartype = fASCIICharMap[ch];
 887                     if (chartype != CHARTYPE_LETTER
 888                             && chartype != CHARTYPE_DIGIT
 889                             && chartype != CHARTYPE_PERIOD
 890                             && chartype != CHARTYPE_MINUS
 891                             && chartype != CHARTYPE_UNDERSCORE) {
 892                         break;
 893                     }
 894                 }
 895             }
 896             return currentOffset;
 897         }
 898 
 899         /**
 900          * Scans the SchemeData.
 901          * [6]    SchemeData   ::=    EscapedData*
 902          * [7]    EscapedData  ::=    NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
 903          * [8]    NormalChar   ::=    UnicodeChar - [()^]
 904          * [9]    UnicodeChar  ::=    [#x0-#x10FFFF]
 905          *
 906          */
 907         private int scanData(String data, StringBuffer schemeData,
 908                 int endOffset, int currentOffset) {
 909             while (true) {
 910 
 911                 if (currentOffset == endOffset) {
 912                     break;
 913                 }
 914 
 915                 int ch = data.charAt(currentOffset);
 916                 byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
 917                         : fASCIICharMap[ch];
 918 
 919                 if (chartype == CHARTYPE_OPEN_PAREN) {
 920                     schemeData.append(ch);
 921                     //schemeData.append(Tokens.XPTRTOKEN_OPEN_PAREN);
 922                     currentOffset = scanData(data, schemeData, endOffset,
 923                             ++currentOffset);
 924                     if (currentOffset == endOffset) {
 925                         return currentOffset;
 926                     }
 927 
 928                     ch = data.charAt(currentOffset);
 929                     chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
 930                             : fASCIICharMap[ch];
 931 
 932                     if (chartype != CHARTYPE_CLOSE_PAREN) {
 933                         return endOffset;
 934                     }
 935                     schemeData.append((char) ch);
 936                     ++currentOffset;//
 937 
 938                 } else if (chartype == CHARTYPE_CLOSE_PAREN) {
 939                     return currentOffset;
 940 
 941                 } else  if (chartype == CHARTYPE_CARRET) {
 942                     ch = data.charAt(++currentOffset);
 943                     chartype = (ch >= 0x80) ? CHARTYPE_NONASCII
 944                             : fASCIICharMap[ch];
 945 
 946                     if (chartype != CHARTYPE_CARRET
 947                             && chartype != CHARTYPE_OPEN_PAREN
 948                             && chartype != CHARTYPE_CLOSE_PAREN) {
 949                         break;
 950                     }
 951                     schemeData.append((char) ch);
 952                     ++currentOffset;
 953 
 954                 } else {
 955                     schemeData.append((char) ch);
 956                     ++currentOffset;//
 957                 }
 958             }
 959 
 960             return currentOffset;
 961         }
 962 
 963         //
 964         // Protected methods
 965         //
 966 
 967         /**
 968          * This method adds the specified token to the token list. By
 969          * default, this method allows all tokens. However, subclasses
 970          * of the XPathExprScanner can override this method in order
 971          * to disallow certain tokens from being used in the scanned
 972          * XPath expression. This is a convenient way of allowing only
 973          * a subset of XPath.
 974          */
 975         protected void addToken(Tokens tokens, int token) throws XNIException {
 976             tokens.addToken(token);
 977         } // addToken(int)
 978 
 979     } // class Scanner
 980 
 981     // ************************************************************************
 982     //  Overridden XMLDocumentHandler methods
 983     // ************************************************************************
 984     /**
 985      * If the comment is a child of a matched element, then pass else return.
 986      *
 987      * @param text   The text in the comment.
 988      * @param augs   Additional information that may include infoset augmentations
 989      *
 990      * @exception XNIException
 991      *                   Thrown by application to signal an error.
 992      */
 993     public void comment(XMLString text, Augmentations augs) throws XNIException {
 994         if (!isChildFragmentResolved()) {
 995             return;
 996         }
 997         super.comment(text, augs);
 998     }
 999 
1000     /**
1001      * A processing instruction. Processing instructions consist of a
1002      * target name and, optionally, text data. The data is only meaningful
1003      * to the application.
1004      * <p>
1005      * Typically, a processing instruction's data will contain a series
1006      * of pseudo-attributes. These pseudo-attributes follow the form of
1007      * element attributes but are <strong>not</strong> parsed or presented
1008      * to the application as anything other than text. The application is
1009      * responsible for parsing the data.
1010      *
1011      * @param target The target.
1012      * @param data   The data or null if none specified.
1013      * @param augs   Additional information that may include infoset augmentations
1014      *
1015      * @exception XNIException
1016      *                   Thrown by handler to signal an error.
1017      */
1018     public void processingInstruction(String target, XMLString data,
1019             Augmentations augs) throws XNIException {
1020         if (!isChildFragmentResolved()) {
1021             return;
1022         }
1023         super.processingInstruction(target, data, augs);
1024     }
1025 
1026     /**
1027      * The start of an element.
1028      *
1029      * @param element    The name of the element.
1030      * @param attributes The element attributes.
1031      * @param augs       Additional information that may include infoset augmentations
1032      *
1033      * @exception XNIException
1034      *                   Thrown by handler to signal an error.
1035      */
1036     public void startElement(QName element, XMLAttributes attributes,
1037             Augmentations augs) throws XNIException {
1038         if (!resolveXPointer(element, attributes, augs,
1039                 XPointerPart.EVENT_ELEMENT_START)) {
1040 
1041             // xml:base and xml:lang processing
1042                 if (fFixupBase) {
1043                 processXMLBaseAttributes(attributes);
1044                 }
1045             if (fFixupLang) {
1046                 processXMLLangAttributes(attributes);
1047             }
1048 
1049             // set the context invalid if the element till an element from the result infoset is included
1050             fNamespaceContext.setContextInvalid();
1051 
1052             return;
1053         }
1054         super.startElement(element, attributes, augs);
1055     }
1056 
1057     /**
1058      * An empty element.
1059      *
1060      * @param element    The name of the element.
1061      * @param attributes The element attributes.
1062      * @param augs       Additional information that may include infoset augmentations
1063      *
1064      * @exception XNIException
1065      *                   Thrown by handler to signal an error.
1066      */
1067     public void emptyElement(QName element, XMLAttributes attributes,
1068             Augmentations augs) throws XNIException {
1069         if (!resolveXPointer(element, attributes, augs,
1070                 XPointerPart.EVENT_ELEMENT_EMPTY)) {
1071             // xml:base and xml:lang processing
1072                 if (fFixupBase) {
1073                 processXMLBaseAttributes(attributes);
1074                 }
1075             if (fFixupLang) {
1076                 processXMLLangAttributes(attributes);
1077             }
1078             // no need to restore restoreBaseURI() for xml:base and xml:lang processing
1079 
1080             // set the context invalid if the element till an element from the result infoset is included
1081             fNamespaceContext.setContextInvalid();
1082             return;
1083         }
1084         super.emptyElement(element, attributes, augs);
1085     }
1086 
1087     /**
1088      * Character content.
1089      *
1090      * @param text   The content.
1091      * @param augs   Additional information that may include infoset augmentations
1092      *
1093      * @exception XNIException
1094      *                   Thrown by handler to signal an error.
1095      */
1096     public void characters(XMLString text, Augmentations augs)
1097             throws XNIException {
1098         if (!isChildFragmentResolved()) {
1099             return;
1100         }
1101         super.characters(text, augs);
1102     }
1103 
1104     /**
1105      * Ignorable whitespace. For this method to be called, the document
1106      * source must have some way of determining that the text containing
1107      * only whitespace characters should be considered ignorable. For
1108      * example, the validator can determine if a length of whitespace
1109      * characters in the document are ignorable based on the element
1110      * content model.
1111      *
1112      * @param text   The ignorable whitespace.
1113      * @param augs   Additional information that may include infoset augmentations
1114      *
1115      * @exception XNIException
1116      *                   Thrown by handler to signal an error.
1117      */
1118     public void ignorableWhitespace(XMLString text, Augmentations augs)
1119             throws XNIException {
1120         if (!isChildFragmentResolved()) {
1121             return;
1122         }
1123         super.ignorableWhitespace(text, augs);
1124     }
1125 
1126     /**
1127      * The end of an element.
1128      *
1129      * @param element The name of the element.
1130      * @param augs    Additional information that may include infoset augmentations
1131      *
1132      * @exception XNIException
1133      *                   Thrown by handler to signal an error.
1134      */
1135     public void endElement(QName element, Augmentations augs)
1136             throws XNIException {
1137         if (!resolveXPointer(element, null, augs,
1138                 XPointerPart.EVENT_ELEMENT_END)) {
1139 
1140             // no need to restore restoreBaseURI() for xml:base and xml:lang processing
1141             return;
1142         }
1143         super.endElement(element, augs);
1144     }
1145 
1146     /**
1147      * The start of a CDATA section.
1148      *
1149      * @param augs   Additional information that may include infoset augmentations
1150      *
1151      * @exception XNIException
1152      *                   Thrown by handler to signal an error.
1153      */
1154     public void startCDATA(Augmentations augs) throws XNIException {
1155         if (!isChildFragmentResolved()) {
1156             return;
1157         }
1158         super.startCDATA(augs);
1159     }
1160 
1161     /**
1162      * The end of a CDATA section.
1163      *
1164      * @param augs   Additional information that may include infoset augmentations
1165      *
1166      * @exception XNIException
1167      *                   Thrown by handler to signal an error.
1168      */
1169     public void endCDATA(Augmentations augs) throws XNIException {
1170         if (!isChildFragmentResolved()) {
1171             return;
1172         }
1173         super.endCDATA(augs);
1174     }
1175 
1176     // ************************************************************************
1177     // Overridden XMLComponent methods
1178     // ************************************************************************
1179     /**
1180      * <p>
1181      * Sets the value of a property. This method is called by the component
1182      * manager any time after reset when a property changes value.
1183      * </p>
1184      * <strong>Note:</strong> Components should silently ignore properties
1185      * that do not affect the operation of the component.
1186      *
1187      * @param propertyId The property identifier.
1188      * @param value      The value of the property.
1189      *
1190      * @throws XMLConfigurationException Thrown for configuration error.
1191      *                                  In general, components should
1192      *                                  only throw this exception if
1193      *                                  it is <strong>really</strong>
1194      *                                  a critical error.
1195      */
1196     public void setProperty(String propertyId, Object value)
1197             throws XMLConfigurationException {
1198 
1199         // Error reporter
1200         if (propertyId == Constants.XERCES_PROPERTY_PREFIX
1201                 + Constants.ERROR_REPORTER_PROPERTY) {
1202             if (value != null) {
1203                 fXPointerErrorReporter = (XMLErrorReporter) value;
1204             } else {
1205                 fXPointerErrorReporter = null;
1206             }
1207         }
1208 
1209         // Error handler
1210         if (propertyId == Constants.XERCES_PROPERTY_PREFIX
1211                 + Constants.ERROR_HANDLER_PROPERTY) {
1212             if (value != null) {
1213                 fErrorHandler = (XMLErrorHandler) value;
1214             } else {
1215                 fErrorHandler = null;
1216             }
1217         }
1218 
1219         // xml:lang
1220         if (propertyId == Constants.XERCES_FEATURE_PREFIX
1221                 + Constants.XINCLUDE_FIXUP_LANGUAGE_FEATURE) {
1222             if (value != null) {
1223                 fFixupLang = ((Boolean)value).booleanValue();
1224             } else {
1225                 fFixupLang = false;
1226             }
1227         }
1228 
1229         // xml:base
1230         if (propertyId == Constants.XERCES_FEATURE_PREFIX
1231                 + Constants.XINCLUDE_FIXUP_BASE_URIS_FEATURE) {
1232             if (value != null) {
1233                 fFixupBase = ((Boolean)value).booleanValue();
1234             } else {
1235                 fFixupBase = false;
1236             }
1237         }
1238 
1239         //
1240         if (propertyId == Constants.XERCES_PROPERTY_PREFIX
1241                 + Constants.NAMESPACE_CONTEXT_PROPERTY) {
1242             fNamespaceContext = (XIncludeNamespaceSupport) value;
1243         }
1244 
1245         super.setProperty(propertyId, value);
1246     }
1247 
1248 }