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