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 }