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 if (!fTokenNames.containsValue(tokenStr)) { 528 Integer tokenInt = new Integer(fTokenNames.size()); 529 fTokenNames.put(tokenInt, tokenStr); 530 addToken(tokenInt.intValue()); 531 } 532 } 533 534 /** 535 * Add the specified int token 536 * 537 * @param token The int specifying the token 538 */ 539 private void addToken(int token) { 540 try { 541 fTokens[fTokenCount] = token; 542 } catch (ArrayIndexOutOfBoundsException ex) { 543 int[] oldList = fTokens; 544 fTokens = new int[fTokenCount << 1]; 545 System.arraycopy(oldList, 0, fTokens, 0, fTokenCount); 546 fTokens[fTokenCount] = token; 547 } 548 fTokenCount++; 549 } 550 551 /** 552 * Resets the current position to the head of the token list. 553 */ 554 private void rewind() { 555 fCurrentTokenIndex = 0; 556 } 557 558 /** 559 * Returns true if the {@link #getNextToken()} method 560 * returns a valid token. 561 */ 562 private boolean hasMore() { 563 return fCurrentTokenIndex < fTokenCount; 564 } 565 566 /** 567 * Obtains the token at the current position, then advance 568 * the current position by one. 569 * 570 * throws If there's no such next token, this method throws 571 * <tt>new XNIException("XPointerProcessingError");</tt>. 572 */ 573 private int nextToken() throws XNIException { 574 if (fCurrentTokenIndex == fTokenCount) { 575 reportError("XPointerProcessingError", null); 576 } 577 return fTokens[fCurrentTokenIndex++]; 578 } 579 580 /** 581 * Obtains the token at the current position, without advancing 582 * the current position. 583 * 584 * If there's no such next token, this method throws 585 * <tt>new XNIException("XPointerProcessingError");</tt>. 586 */ 587 private int peekToken() throws XNIException { 588 if (fCurrentTokenIndex == fTokenCount) { 589 reportError("XPointerProcessingError", null); 590 } 591 return fTokens[fCurrentTokenIndex]; 592 } 593 594 /** 595 * Obtains the token at the current position as a String. 596 * 597 * If there's no current token or if the current token 598 * is not a string token, this method throws 599 * If there's no such next token, this method throws 600 * <tt>new XNIException("XPointerProcessingError");</tt>. 601 */ 602 private String nextTokenAsString() throws XNIException { 603 String tokenStrint = getTokenString(nextToken()); 604 if (tokenStrint == null) { 605 reportError("XPointerProcessingError", null); 606 } 607 return tokenStrint; 608 } 609 } 610 611 /** 612 * 613 * The XPointer expression scanner. Scans the XPointer framework expression. 614 * 615 * @xerces.internal 616 * 617 */ 618 private class Scanner { 619 620 /** 621 * 7-bit ASCII subset 622 * 623 * 0 1 2 3 4 5 6 7 8 9 A B C D E F 624 * 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0 625 * 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 626 * SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2 627 * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3 628 * @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4 629 * P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5 630 * `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6 631 * p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7 632 */ 633 private static final byte CHARTYPE_INVALID = 0, // invalid XML character 634 CHARTYPE_OTHER = 1, // not special - one of "#%&;?\`{}~" or DEL 635 CHARTYPE_WHITESPACE = 2, // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20) 636 CHARTYPE_CARRET = 3, // ^ 637 CHARTYPE_OPEN_PAREN = 4, // '(' (0x28) 638 CHARTYPE_CLOSE_PAREN = 5, // ')' (0x29) 639 CHARTYPE_MINUS = 6, // '-' (0x2D) 640 CHARTYPE_PERIOD = 7, // '.' (0x2E) 641 CHARTYPE_SLASH = 8, // '/' (0x2F) 642 CHARTYPE_DIGIT = 9, // '0'-'9' (0x30 to 0x39) 643 CHARTYPE_COLON = 10, // ':' (0x3A) 644 CHARTYPE_EQUAL = 11, // '=' (0x3D) 645 CHARTYPE_LETTER = 12, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A) 646 CHARTYPE_UNDERSCORE = 13, // '_' (0x5F) 647 CHARTYPE_NONASCII = 14; // Non-ASCII Unicode codepoint (>= 0x80) 648 649 private final byte[] fASCIICharMap = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 650 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 651 2, 1, 1, 1, 1, 1, 1, 1, 4, 5, 1, 1, 1, 6, 7, 8, 9, 9, 9, 9, 9, 652 9, 9, 9, 9, 9, 10, 1, 1, 11, 1, 1, 1, 12, 12, 12, 12, 12, 12, 653 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 654 12, 12, 12, 12, 1, 1, 1, 3, 13, 1, 12, 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, 1, 1, 1, 1, 1 }; 657 658 // 659 // Data 660 // 661 /** Symbol table. */ 662 private SymbolTable fSymbolTable; 663 664 /** 665 * Constructs an XPointer Framework expression scanner. 666 * 667 * @param symbolTable SymbolTable 668 */ 669 private Scanner(SymbolTable symbolTable) { 670 // save pool and tokens 671 fSymbolTable = symbolTable; 672 673 } // <init>(SymbolTable) 674 675 /** 676 * Scans the XPointer Expression 677 * 678 */ 679 private boolean scanExpr(SymbolTable symbolTable, Tokens tokens, 680 String data, int currentOffset, int endOffset) 681 throws XNIException { 682 683 int ch; 684 int openParen = 0; 685 int closeParen = 0; 686 int nameOffset, dataOffset; 687 boolean isQName = false; 688 String name = null; 689 String prefix = null; 690 String schemeData = null; 691 StringBuffer schemeDataBuff = new StringBuffer(); 692 693 while (true) { 694 695 if (currentOffset == endOffset) { 696 break; 697 } 698 ch = data.charAt(currentOffset); 699 700 // 701 while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) { 702 if (++currentOffset == endOffset) { 703 break; 704 } 705 ch = data.charAt(currentOffset); 706 } 707 if (currentOffset == endOffset) { 708 break; 709 } 710 711 // 712 // [1] Pointer ::= Shorthand | SchemeBased 713 // [2] Shorthand ::= NCName 714 // [3] SchemeBased ::= PointerPart (S? PointerPart)* 715 // [4] PointerPart ::= SchemeName '(' SchemeData ')' 716 // [5] SchemeName ::= QName 717 // [6] SchemeData ::= EscapedData* 718 // [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')' 719 // [8] NormalChar ::= UnicodeChar - [()^] 720 // [9] UnicodeChar ::= [#x0-#x10FFFF] 721 // [?] QName ::= (NCName ':')? NCName 722 // [?] NCName ::= (Letter | '_') (NCNameChar)* 723 // [?] NCNameChar ::= Letter | Digit | '.' | '-' | '_' (ascii subset of 'NCNameChar') 724 // [?] Letter ::= [A-Za-z] (ascii subset of 'Letter') 725 // [?] Digit ::= [0-9] (ascii subset of 'Digit') 726 // 727 byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII 728 : fASCIICharMap[ch]; 729 730 switch (chartype) { 731 732 case CHARTYPE_OPEN_PAREN: // '(' 733 addToken(tokens, Tokens.XPTRTOKEN_OPEN_PAREN); 734 openParen++; 735 ++currentOffset; 736 break; 737 738 case CHARTYPE_CLOSE_PAREN: // ')' 739 addToken(tokens, Tokens.XPTRTOKEN_CLOSE_PAREN); 740 closeParen++; 741 ++currentOffset; 742 break; 743 744 case CHARTYPE_CARRET: 745 case CHARTYPE_COLON: 746 case CHARTYPE_DIGIT: 747 case CHARTYPE_EQUAL: 748 case CHARTYPE_LETTER: 749 case CHARTYPE_MINUS: 750 case CHARTYPE_NONASCII: 751 case CHARTYPE_OTHER: 752 case CHARTYPE_PERIOD: 753 case CHARTYPE_SLASH: 754 case CHARTYPE_UNDERSCORE: 755 case CHARTYPE_WHITESPACE: 756 // Scanning SchemeName | Shorthand 757 if (openParen == 0) { 758 nameOffset = currentOffset; 759 currentOffset = scanNCName(data, endOffset, 760 currentOffset); 761 762 if (currentOffset == nameOffset) { 763 reportError("InvalidShortHandPointer", 764 new Object[] { data }); 765 return false; 766 } 767 768 if (currentOffset < endOffset) { 769 ch = data.charAt(currentOffset); 770 } else { 771 ch = -1; 772 } 773 774 name = symbolTable.addSymbol(data.substring(nameOffset, 775 currentOffset)); 776 prefix = XMLSymbols.EMPTY_STRING; 777 778 // The name is a QName => a SchemeName 779 if (ch == ':') { 780 if (++currentOffset == endOffset) { 781 return false; 782 } 783 784 ch = data.charAt(currentOffset); 785 prefix = name; 786 nameOffset = currentOffset; 787 currentOffset = scanNCName(data, endOffset, 788 currentOffset); 789 790 if (currentOffset == nameOffset) { 791 return false; 792 } 793 794 if (currentOffset < endOffset) { 795 ch = data.charAt(currentOffset); 796 } else { 797 ch = -1; 798 } 799 800 isQName = true; 801 name = symbolTable.addSymbol(data.substring( 802 nameOffset, currentOffset)); 803 } 804 805 // REVISIT: 806 if (currentOffset != endOffset) { 807 addToken(tokens, Tokens.XPTRTOKEN_SCHEMENAME); 808 tokens.addToken(prefix); 809 tokens.addToken(name); 810 isQName = false; 811 } else if (currentOffset == endOffset) { 812 // NCName => Shorthand 813 addToken(tokens, Tokens.XPTRTOKEN_SHORTHAND); 814 tokens.addToken(name); 815 isQName = false; 816 } 817 818 // reset open/close paren for the next pointer part 819 closeParen = 0; 820 821 break; 822 823 } else if (openParen > 0 && closeParen == 0 && name != null) { 824 // Scanning SchemeData 825 dataOffset = currentOffset; 826 currentOffset = scanData(data, schemeDataBuff, 827 endOffset, currentOffset); 828 829 if (currentOffset == dataOffset) { 830 reportError("InvalidSchemeDataInXPointer", 831 new Object[] { data }); 832 return false; 833 } 834 835 if (currentOffset < endOffset) { 836 ch = data.charAt(currentOffset); 837 } else { 838 ch = -1; 839 } 840 841 schemeData = symbolTable.addSymbol(schemeDataBuff 842 .toString()); 843 addToken(tokens, Tokens.XPTRTOKEN_SCHEMEDATA); 844 tokens.addToken(schemeData); 845 846 // reset open/close paren for the next pointer part 847 openParen = 0; 848 schemeDataBuff.delete(0, schemeDataBuff.length()); 849 850 } else { 851 // ex. schemeName() 852 // Should we throw an exception with a more suitable message instead?? 853 return false; 854 } 855 } 856 } // end while 857 return true; 858 } 859 860 /** 861 * Scans a NCName. 862 * From Namespaces in XML 863 * [5] NCName ::= (Letter | '_') (NCNameChar)* 864 * [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender 865 * 866 * @param data A String containing the XPointer expression 867 * @param endOffset The int XPointer expression length 868 * @param currentOffset An int representing the current position of the XPointer expression pointer 869 */ 870 private int scanNCName(String data, int endOffset, int currentOffset) { 871 int ch = data.charAt(currentOffset); 872 if (ch >= 0x80) { 873 if (!XMLChar.isNameStart(ch)) { 874 return currentOffset; 875 } 876 } else { 877 byte chartype = fASCIICharMap[ch]; 878 if (chartype != CHARTYPE_LETTER 879 && chartype != CHARTYPE_UNDERSCORE) { 880 return currentOffset; 881 } 882 } 883 884 //while (currentOffset++ < endOffset) { 885 while (++currentOffset < endOffset) { 886 ch = data.charAt(currentOffset); 887 if (ch >= 0x80) { 888 if (!XMLChar.isName(ch)) { 889 break; 890 } 891 } else { 892 byte chartype = fASCIICharMap[ch]; 893 if (chartype != CHARTYPE_LETTER 894 && chartype != CHARTYPE_DIGIT 895 && chartype != CHARTYPE_PERIOD 896 && chartype != CHARTYPE_MINUS 897 && chartype != CHARTYPE_UNDERSCORE) { 898 break; 899 } 900 } 901 } 902 return currentOffset; 903 } 904 905 /** 906 * Scans the SchemeData. 907 * [6] SchemeData ::= EscapedData* 908 * [7] EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')' 909 * [8] NormalChar ::= UnicodeChar - [()^] 910 * [9] UnicodeChar ::= [#x0-#x10FFFF] 911 * 912 */ 913 private int scanData(String data, StringBuffer schemeData, 914 int endOffset, int currentOffset) { 915 while (true) { 916 917 if (currentOffset == endOffset) { 918 break; 919 } 920 921 int ch = data.charAt(currentOffset); 922 byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII 923 : fASCIICharMap[ch]; 924 925 if (chartype == CHARTYPE_OPEN_PAREN) { 926 schemeData.append(ch); 927 //schemeData.append(Tokens.XPTRTOKEN_OPEN_PAREN); 928 currentOffset = scanData(data, schemeData, endOffset, 929 ++currentOffset); 930 if (currentOffset == endOffset) { 931 return currentOffset; 932 } 933 934 ch = data.charAt(currentOffset); 935 chartype = (ch >= 0x80) ? CHARTYPE_NONASCII 936 : fASCIICharMap[ch]; 937 938 if (chartype != CHARTYPE_CLOSE_PAREN) { 939 return endOffset; 940 } 941 schemeData.append((char) ch); 942 ++currentOffset;// 943 944 } else if (chartype == CHARTYPE_CLOSE_PAREN) { 945 return currentOffset; 946 947 } else if (chartype == CHARTYPE_CARRET) { 948 ch = data.charAt(++currentOffset); 949 chartype = (ch >= 0x80) ? CHARTYPE_NONASCII 950 : fASCIICharMap[ch]; 951 952 if (chartype != CHARTYPE_CARRET 953 && chartype != CHARTYPE_OPEN_PAREN 954 && chartype != CHARTYPE_CLOSE_PAREN) { 955 break; 956 } 957 schemeData.append((char) ch); 958 ++currentOffset; 959 960 } else { 961 schemeData.append((char) ch); 962 ++currentOffset;// 963 } 964 } 965 966 return currentOffset; 967 } 968 969 // 970 // Protected methods 971 // 972 973 /** 974 * This method adds the specified token to the token list. By 975 * default, this method allows all tokens. However, subclasses 976 * of the XPathExprScanner can override this method in order 977 * to disallow certain tokens from being used in the scanned 978 * XPath expression. This is a convenient way of allowing only 979 * a subset of XPath. 980 */ 981 protected void addToken(Tokens tokens, int token) throws XNIException { 982 tokens.addToken(token); 983 } // addToken(int) 984 985 } // class Scanner 986 987 // ************************************************************************ 988 // Overridden XMLDocumentHandler methods 989 // ************************************************************************ 990 /** 991 * If the comment is a child of a matched element, then pass else return. 992 * 993 * @param text The text in the comment. 994 * @param augs Additional information that may include infoset augmentations 995 * 996 * @exception XNIException 997 * Thrown by application to signal an error. 998 */ 999 public void comment(XMLString text, Augmentations augs) throws XNIException { 1000 if (!isChildFragmentResolved()) { 1001 return; 1002 } 1003 super.comment(text, augs); 1004 } 1005 1006 /** 1007 * A processing instruction. Processing instructions consist of a 1008 * target name and, optionally, text data. The data is only meaningful 1009 * to the application. 1010 * <p> 1011 * Typically, a processing instruction's data will contain a series 1012 * of pseudo-attributes. These pseudo-attributes follow the form of 1013 * element attributes but are <strong>not</strong> parsed or presented 1014 * to the application as anything other than text. The application is 1015 * responsible for parsing the data. 1016 * 1017 * @param target The target. 1018 * @param data The data or null if none specified. 1019 * @param augs Additional information that may include infoset augmentations 1020 * 1021 * @exception XNIException 1022 * Thrown by handler to signal an error. 1023 */ 1024 public void processingInstruction(String target, XMLString data, 1025 Augmentations augs) throws XNIException { 1026 if (!isChildFragmentResolved()) { 1027 return; 1028 } 1029 super.processingInstruction(target, data, augs); 1030 } 1031 1032 /** 1033 * The start of an element. 1034 * 1035 * @param element The name of the element. 1036 * @param attributes The element attributes. 1037 * @param augs Additional information that may include infoset augmentations 1038 * 1039 * @exception XNIException 1040 * Thrown by handler to signal an error. 1041 */ 1042 public void startElement(QName element, XMLAttributes attributes, 1043 Augmentations augs) throws XNIException { 1044 if (!resolveXPointer(element, attributes, augs, 1045 XPointerPart.EVENT_ELEMENT_START)) { 1046 1047 // xml:base and xml:lang processing 1048 if (fFixupBase) { 1049 processXMLBaseAttributes(attributes); 1050 } 1051 if (fFixupLang) { 1052 processXMLLangAttributes(attributes); 1053 } 1054 1055 // set the context invalid if the element till an element from the result infoset is included 1056 fNamespaceContext.setContextInvalid(); 1057 1058 return; 1059 } 1060 super.startElement(element, attributes, augs); 1061 } 1062 1063 /** 1064 * An empty element. 1065 * 1066 * @param element The name of the element. 1067 * @param attributes The element attributes. 1068 * @param augs Additional information that may include infoset augmentations 1069 * 1070 * @exception XNIException 1071 * Thrown by handler to signal an error. 1072 */ 1073 public void emptyElement(QName element, XMLAttributes attributes, 1074 Augmentations augs) throws XNIException { 1075 if (!resolveXPointer(element, attributes, augs, 1076 XPointerPart.EVENT_ELEMENT_EMPTY)) { 1077 // xml:base and xml:lang processing 1078 if (fFixupBase) { 1079 processXMLBaseAttributes(attributes); 1080 } 1081 if (fFixupLang) { 1082 processXMLLangAttributes(attributes); 1083 } 1084 // no need to restore restoreBaseURI() for xml:base and xml:lang processing 1085 1086 // set the context invalid if the element till an element from the result infoset is included 1087 fNamespaceContext.setContextInvalid(); 1088 return; 1089 } 1090 super.emptyElement(element, attributes, augs); 1091 } 1092 1093 /** 1094 * Character content. 1095 * 1096 * @param text The content. 1097 * @param augs Additional information that may include infoset augmentations 1098 * 1099 * @exception XNIException 1100 * Thrown by handler to signal an error. 1101 */ 1102 public void characters(XMLString text, Augmentations augs) 1103 throws XNIException { 1104 if (!isChildFragmentResolved()) { 1105 return; 1106 } 1107 super.characters(text, augs); 1108 } 1109 1110 /** 1111 * Ignorable whitespace. For this method to be called, the document 1112 * source must have some way of determining that the text containing 1113 * only whitespace characters should be considered ignorable. For 1114 * example, the validator can determine if a length of whitespace 1115 * characters in the document are ignorable based on the element 1116 * content model. 1117 * 1118 * @param text The ignorable whitespace. 1119 * @param augs Additional information that may include infoset augmentations 1120 * 1121 * @exception XNIException 1122 * Thrown by handler to signal an error. 1123 */ 1124 public void ignorableWhitespace(XMLString text, Augmentations augs) 1125 throws XNIException { 1126 if (!isChildFragmentResolved()) { 1127 return; 1128 } 1129 super.ignorableWhitespace(text, augs); 1130 } 1131 1132 /** 1133 * The end of an element. 1134 * 1135 * @param element The name of the element. 1136 * @param augs Additional information that may include infoset augmentations 1137 * 1138 * @exception XNIException 1139 * Thrown by handler to signal an error. 1140 */ 1141 public void endElement(QName element, Augmentations augs) 1142 throws XNIException { 1143 if (!resolveXPointer(element, null, augs, 1144 XPointerPart.EVENT_ELEMENT_END)) { 1145 1146 // no need to restore restoreBaseURI() for xml:base and xml:lang processing 1147 return; 1148 } 1149 super.endElement(element, augs); 1150 } 1151 1152 /** 1153 * The start of a CDATA section. 1154 * 1155 * @param augs Additional information that may include infoset augmentations 1156 * 1157 * @exception XNIException 1158 * Thrown by handler to signal an error. 1159 */ 1160 public void startCDATA(Augmentations augs) throws XNIException { 1161 if (!isChildFragmentResolved()) { 1162 return; 1163 } 1164 super.startCDATA(augs); 1165 } 1166 1167 /** 1168 * The end of a CDATA section. 1169 * 1170 * @param augs Additional information that may include infoset augmentations 1171 * 1172 * @exception XNIException 1173 * Thrown by handler to signal an error. 1174 */ 1175 public void endCDATA(Augmentations augs) throws XNIException { 1176 if (!isChildFragmentResolved()) { 1177 return; 1178 } 1179 super.endCDATA(augs); 1180 } 1181 1182 // ************************************************************************ 1183 // Overridden XMLComponent methods 1184 // ************************************************************************ 1185 /** 1186 * <p> 1187 * Sets the value of a property. This method is called by the component 1188 * manager any time after reset when a property changes value. 1189 * </p> 1190 * <strong>Note:</strong> Components should silently ignore properties 1191 * that do not affect the operation of the component. 1192 * 1193 * @param propertyId The property identifier. 1194 * @param value The value of the property. 1195 * 1196 * @throws XMLConfigurationException Thrown for configuration error. 1197 * In general, components should 1198 * only throw this exception if 1199 * it is <strong>really</strong> 1200 * a critical error. 1201 */ 1202 public void setProperty(String propertyId, Object value) 1203 throws XMLConfigurationException { 1204 1205 // Error reporter 1206 if (propertyId == Constants.XERCES_PROPERTY_PREFIX 1207 + Constants.ERROR_REPORTER_PROPERTY) { 1208 if (value != null) { 1209 fXPointerErrorReporter = (XMLErrorReporter) value; 1210 } else { 1211 fXPointerErrorReporter = null; 1212 } 1213 } 1214 1215 // Error handler 1216 if (propertyId == Constants.XERCES_PROPERTY_PREFIX 1217 + Constants.ERROR_HANDLER_PROPERTY) { 1218 if (value != null) { 1219 fErrorHandler = (XMLErrorHandler) value; 1220 } else { 1221 fErrorHandler = null; 1222 } 1223 } 1224 1225 // xml:lang 1226 if (propertyId == Constants.XERCES_FEATURE_PREFIX 1227 + Constants.XINCLUDE_FIXUP_LANGUAGE_FEATURE) { 1228 if (value != null) { 1229 fFixupLang = ((Boolean)value).booleanValue(); 1230 } else { 1231 fFixupLang = false; 1232 } 1233 } 1234 1235 // xml:base 1236 if (propertyId == Constants.XERCES_FEATURE_PREFIX 1237 + Constants.XINCLUDE_FIXUP_BASE_URIS_FEATURE) { 1238 if (value != null) { 1239 fFixupBase = ((Boolean)value).booleanValue(); 1240 } else { 1241 fFixupBase = false; 1242 } 1243 } 1244 1245 // 1246 if (propertyId == Constants.XERCES_PROPERTY_PREFIX 1247 + Constants.NAMESPACE_CONTEXT_PROPERTY) { 1248 fNamespaceContext = (XIncludeNamespaceSupport) value; 1249 } 1250 1251 super.setProperty(propertyId, value); 1252 } 1253 1254 }