XPointerHandler.java revision 798:00fa5efc9ace
1259701Sdim/* 2259701Sdim * reserved comment block 3259701Sdim * DO NOT REMOVE OR ALTER! 4259701Sdim */ 5259701Sdim/* 6259701Sdim * Licensed to the Apache Software Foundation (ASF) under one or more 7259701Sdim * contributor license agreements. See the NOTICE file distributed with 8259701Sdim * this work for additional information regarding copyright ownership. 9259701Sdim * The ASF licenses this file to You under the Apache License, Version 2.0 10259701Sdim * (the "License"); you may not use this file except in compliance with 11259701Sdim * the License. You may obtain a copy of the License at 12259701Sdim * 13259701Sdim * http://www.apache.org/licenses/LICENSE-2.0 14259701Sdim * 15259701Sdim * Unless required by applicable law or agreed to in writing, software 16259701Sdim * distributed under the License is distributed on an "AS IS" BASIS, 17259701Sdim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18259701Sdim * See the License for the specific language governing permissions and 19259701Sdim * limitations under the License. 20259701Sdim */ 21259701Sdim 22259701Sdimpackage com.sun.org.apache.xerces.internal.xpointer; 23259701Sdim 24259701Sdimimport java.util.ArrayList; 25259701Sdimimport java.util.HashMap; 26259701Sdim 27259701Sdimimport com.sun.org.apache.xerces.internal.impl.Constants; 28259701Sdimimport com.sun.org.apache.xerces.internal.impl.XMLErrorReporter; 29259701Sdimimport com.sun.org.apache.xerces.internal.util.SymbolTable; 30259701Sdimimport com.sun.org.apache.xerces.internal.util.XMLChar; 31259701Sdimimport com.sun.org.apache.xerces.internal.util.XMLSymbols; 32259701Sdimimport com.sun.org.apache.xerces.internal.xinclude.XIncludeHandler; 33259701Sdimimport com.sun.org.apache.xerces.internal.xinclude.XIncludeNamespaceSupport; 34259701Sdimimport com.sun.org.apache.xerces.internal.xni.Augmentations; 35259701Sdimimport com.sun.org.apache.xerces.internal.xni.QName; 36259701Sdimimport com.sun.org.apache.xerces.internal.xni.XMLAttributes; 37259701Sdimimport com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler; 38259701Sdimimport com.sun.org.apache.xerces.internal.xni.XMLString; 39259701Sdimimport com.sun.org.apache.xerces.internal.xni.XNIException; 40259701Sdimimport com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; 41259701Sdimimport com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler; 42259701Sdim 43259701Sdim/** 44259701Sdim * <p> 45259701Sdim * This is a pipeline component which extends the XIncludeHandler to perform 46259701Sdim * XPointer specific processing specified in the W3C XPointerFramework and 47259701Sdim * element() Scheme Recommendations. 48259701Sdim * </p> 49259701Sdim * 50259701Sdim * <p> 51259701Sdim * This component analyzes each event in the pipeline, looking for an element 52259701Sdim * that matches a PointerPart in the parent XInclude element's xpointer attribute 53259701Sdim * value. If the match succeeds, all children are passed by this component. 54259701Sdim * </p> 55259701Sdim * 56259701Sdim * <p> 57259701Sdim * See the <a href="http://www.w3.org/TR/xptr-framework//">XPointer Framework Recommendation</a> for 58259701Sdim * more information on the XPointer Framework and ShortHand Pointers. 59259701Sdim * See the <a href="http://www.w3.org/TR/xptr-element/">XPointer element() Scheme Recommendation</a> for 60259701Sdim * more information on the XPointer element() Scheme. 61259701Sdim * </p> 62259701Sdim * 63259701Sdim * @xerces.internal 64259701Sdim * 65259701Sdim */ 66259701Sdimpublic final class XPointerHandler extends XIncludeHandler implements 67259701Sdim XPointerProcessor { 68259701Sdim 69259701Sdim // Fields 70259701Sdim // An ArrayList of XPointerParts 71259701Sdim protected ArrayList<XPointerPart> fXPointerParts = null; 72259701Sdim 73259701Sdim // The current XPointerPart 74259701Sdim protected XPointerPart fXPointerPart = null; 75259701Sdim 76259701Sdim // Has the fXPointerPart resolved successfully 77259701Sdim protected boolean fFoundMatchingPtrPart = false; 78259701Sdim 79259701Sdim // The XPointer Error reporter 80259701Sdim protected XMLErrorReporter fXPointerErrorReporter; 81259701Sdim 82259701Sdim // The XPointer Error Handler 83259701Sdim protected XMLErrorHandler fErrorHandler; 84259701Sdim 85259701Sdim // XPointerFramework symbol table 86259701Sdim protected SymbolTable fSymbolTable = null; 87259701Sdim 88259701Sdim // Supported schemes 89259701Sdim private final String ELEMENT_SCHEME_NAME = "element"; 90259701Sdim 91259701Sdim // Has the XPointer resolved the subresource 92259701Sdim protected boolean fIsXPointerResolved = false; 93259701Sdim 94259701Sdim // Fixup xml:base and xml:lang attributes 95259701Sdim protected boolean fFixupBase = false; 96259701Sdim protected boolean fFixupLang = false; 97259701Sdim 98259701Sdim // ************************************************************************ 99259701Sdim // Constructors 100259701Sdim // ************************************************************************ 101259701Sdim 102259701Sdim /** 103259701Sdim * 104259701Sdim */ 105259701Sdim public XPointerHandler() { 106259701Sdim super(); 107259701Sdim 108259701Sdim fXPointerParts = new ArrayList<>(); 109259701Sdim fSymbolTable = new SymbolTable(); 110259701Sdim } 111259701Sdim 112259701Sdim public XPointerHandler(SymbolTable symbolTable, 113259701Sdim XMLErrorHandler errorHandler, XMLErrorReporter errorReporter) { 114259701Sdim super(); 115259701Sdim 116259701Sdim fXPointerParts = new ArrayList<>(); 117259701Sdim fSymbolTable = symbolTable; 118259701Sdim fErrorHandler = errorHandler; 119259701Sdim fXPointerErrorReporter = errorReporter; 120259701Sdim //fErrorReporter = errorReporter; // The XInclude ErrorReporter 121259701Sdim } 122259701Sdim 123259701Sdim public void setDocumentHandler(XMLDocumentHandler handler) { 124259701Sdim fDocumentHandler = handler; 125259701Sdim } 126259701Sdim 127259701Sdim // ************************************************************************ 128259701Sdim // Implementation of the XPointerProcessor interface. 129259701Sdim // ************************************************************************ 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} 1257