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 24 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter; 25 import com.sun.org.apache.xerces.internal.util.SymbolTable; 26 import com.sun.org.apache.xerces.internal.util.XMLChar; 27 import com.sun.org.apache.xerces.internal.xni.Augmentations; 28 import com.sun.org.apache.xerces.internal.xni.QName; 29 import com.sun.org.apache.xerces.internal.xni.XMLAttributes; 30 import com.sun.org.apache.xerces.internal.xni.XNIException; 31 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler; 32 33 /** 34 * <p> 35 * Implements the XPointerPart interface for element() scheme specific processing. 36 * </p> 37 * 38 * @xerces.internal 39 * 40 * @version $Id: ElementSchemePointer.java,v 1.4 2009/06/11 23:51:50 joehw Exp $ 41 * 42 */ 43 class ElementSchemePointer implements XPointerPart { 44 45 // Fields 46 47 // The Scheme Name i.e element 48 private String fSchemeName; 49 50 // The scheme Data 51 private String fSchemeData; 52 53 // The scheme Data & child sequence 54 private String fShortHandPointerName; 55 56 // Should we attempt to resolve the ChildSequence from the 57 // current element position. If a ShortHand Pointer is present 58 // attempt to resolve relative to the short hand pointer. 59 private boolean fIsResolveElement = false; 60 61 // Has the element been found 62 private boolean fIsElementFound = false; 63 64 // Was only an empty element found 65 private boolean fWasOnlyEmptyElementFound = false; 66 67 // If a shorthand pointer is present and resolved 68 boolean fIsShortHand = false; 69 70 // The depth at which the element was found 71 int fFoundDepth = 0; 72 73 // The XPointer element child sequence 74 private int fChildSequence[]; 75 76 // The current child position 77 private int fCurrentChildPosition = 1; 78 79 // The current child depth 80 private int fCurrentChildDepth = 0; 81 82 // The current element's child sequence 83 private int fCurrentChildSequence[];; 84 85 // Stores if the Fragment was resolved by the pointer 86 private boolean fIsFragmentResolved = false; 87 88 // Stores if the Fragment was resolved by the pointer 89 private ShortHandPointer fShortHandPointer; 90 91 // The XPointer Error reporter 92 protected XMLErrorReporter fErrorReporter; 93 94 // The XPointer Error Handler 95 protected XMLErrorHandler fErrorHandler; 96 97 // 98 private SymbolTable fSymbolTable; 99 100 // ************************************************************************ 101 // Constructors 102 // ************************************************************************ 103 public ElementSchemePointer() { 104 } 105 106 public ElementSchemePointer(SymbolTable symbolTable) { 107 fSymbolTable = symbolTable; 108 } 109 110 public ElementSchemePointer(SymbolTable symbolTable, 111 XMLErrorReporter errorReporter) { 112 fSymbolTable = symbolTable; 113 fErrorReporter = errorReporter; 114 } 115 116 // ************************************************************************ 117 // XPointerPart implementation 118 // ************************************************************************ 119 120 /** 121 * Parses the XPointer expression and tokenizes it into Strings 122 * delimited by whitespace. 123 * 124 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#parseXPointer(java.lang.String) 125 */ 126 public void parseXPointer(String xpointer) throws XNIException { 127 128 // 129 init(); 130 131 // tokens 132 final Tokens tokens = new Tokens(fSymbolTable); 133 134 // scanner 135 Scanner scanner = new Scanner(fSymbolTable) { 136 protected void addToken(Tokens tokens, int token) 137 throws XNIException { 138 if (token == Tokens.XPTRTOKEN_ELEM_CHILD 139 || token == Tokens.XPTRTOKEN_ELEM_NCNAME) { 140 super.addToken(tokens, token); 141 return; 142 } 143 reportError("InvalidElementSchemeToken", new Object[] { tokens 144 .getTokenString(token) }); 145 } 146 }; 147 148 // scan the element() XPointer expression 149 int length = xpointer.length(); 150 boolean success = scanner.scanExpr(fSymbolTable, tokens, xpointer, 0, 151 length); 152 153 if (!success) { 154 reportError("InvalidElementSchemeXPointer", 155 new Object[] { xpointer }); 156 } 157 158 // Initialize a temp arrays to the size of token count which should 159 // be atleast twice the size of child sequence, to hold the ChildSequence. 160 int tmpChildSequence[] = new int[tokens.getTokenCount() / 2 + 1]; 161 162 // the element depth 163 int i = 0; 164 165 // Traverse the scanned tokens 166 while (tokens.hasMore()) { 167 int token = tokens.nextToken(); 168 169 switch (token) { 170 case Tokens.XPTRTOKEN_ELEM_NCNAME: { 171 // Note: Only a single ShortHand pointer can be present 172 173 // The shortHand name 174 token = tokens.nextToken(); 175 fShortHandPointerName = tokens.getTokenString(token); 176 177 // Create a new ShortHandPointer 178 fShortHandPointer = new ShortHandPointer(fSymbolTable); 179 fShortHandPointer.setSchemeName(fShortHandPointerName); 180 181 break; 182 } 183 case Tokens.XPTRTOKEN_ELEM_CHILD: { 184 tmpChildSequence[i] = tokens.nextToken(); 185 i++; 186 187 break; 188 } 189 default: 190 reportError("InvalidElementSchemeXPointer", 191 new Object[] { xpointer }); 192 } 193 } 194 195 // Initialize the arrays to the number of elements in the ChildSequence. 196 fChildSequence = new int[i]; 197 fCurrentChildSequence = new int[i]; 198 System.arraycopy(tmpChildSequence, 0, fChildSequence, 0, i); 199 200 } 201 202 /** 203 * Returns the scheme name i.e element 204 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#getSchemeName() 205 */ 206 public String getSchemeName() { 207 return fSchemeName; 208 } 209 210 /** 211 * Returns the scheme data 212 * 213 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#getSchemeData() 214 */ 215 public String getSchemeData() { 216 return fSchemeData; 217 } 218 219 /** 220 * Sets the scheme name 221 * 222 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#setSchemeName(java.lang.String) 223 */ 224 public void setSchemeName(String schemeName) { 225 fSchemeName = schemeName; 226 227 } 228 229 /** 230 * Sets the scheme data 231 * 232 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#setSchemeData(java.lang.String) 233 */ 234 public void setSchemeData(String schemeData) { 235 fSchemeData = schemeData; 236 } 237 238 /** 239 * Responsible for resolving the element() scheme XPointer. If a ShortHand 240 * Pointer is present and it is successfully resolved and if a child 241 * sequence is present, the child sequence is resolved relative to it. 242 * 243 * @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) 244 */ 245 public boolean resolveXPointer(QName element, XMLAttributes attributes, 246 Augmentations augs, int event) throws XNIException { 247 248 boolean isShortHandPointerResolved = false; 249 250 // if a ChildSequence exisits, resolve child elements 251 252 // if an element name exists 253 if (fShortHandPointerName != null) { 254 // resolve ShortHand Pointer 255 isShortHandPointerResolved = fShortHandPointer.resolveXPointer( 256 element, attributes, augs, event); 257 if (isShortHandPointerResolved) { 258 fIsResolveElement = true; 259 fIsShortHand = true; 260 } else { 261 fIsResolveElement = false; 262 } 263 } else { 264 fIsResolveElement = true; 265 } 266 267 // Added here to skip the ShortHand pointer corresponding to 268 // an element if one exisits and start searching from its child 269 if (fChildSequence.length > 0) { 270 fIsFragmentResolved = matchChildSequence(element, event); 271 } else if (isShortHandPointerResolved && fChildSequence.length <= 0) { 272 // if only a resolved shorthand pointer exists 273 fIsFragmentResolved = isShortHandPointerResolved; 274 } else { 275 fIsFragmentResolved = false; 276 } 277 278 return fIsFragmentResolved; 279 } 280 281 /** 282 * Matches the current element position in the document tree with the 283 * element position specified in the element XPointer scheme. 284 * 285 * @param event 286 * @return boolean - true if the current element position in the document 287 * tree matches theelement position specified in the element XPointer 288 * scheme. 289 */ 290 protected boolean matchChildSequence(QName element, int event) 291 throws XNIException { 292 293 // need to resize fCurrentChildSequence 294 if (fCurrentChildDepth >= fCurrentChildSequence.length) { 295 int tmpCurrentChildSequence[] = new int[fCurrentChildSequence.length]; 296 System.arraycopy(fCurrentChildSequence, 0, tmpCurrentChildSequence, 297 0, fCurrentChildSequence.length); 298 299 // Increase the size by a factor of 2 (?) 300 fCurrentChildSequence = new int[fCurrentChildDepth * 2]; 301 System.arraycopy(tmpCurrentChildSequence, 0, fCurrentChildSequence, 302 0, tmpCurrentChildSequence.length); 303 } 304 305 // 306 if (fIsResolveElement) { 307 // start 308 fWasOnlyEmptyElementFound = false; 309 if (event == XPointerPart.EVENT_ELEMENT_START) { 310 fCurrentChildSequence[fCurrentChildDepth] = fCurrentChildPosition; 311 fCurrentChildDepth++; 312 313 // reset the current child position 314 fCurrentChildPosition = 1; 315 316 //if (!fSchemeNameFound) { 317 if ((fCurrentChildDepth <= fFoundDepth) || (fFoundDepth == 0)) { 318 if (checkMatch()) { 319 fIsElementFound = true; 320 fFoundDepth = fCurrentChildDepth; 321 } else { 322 fIsElementFound = false; 323 fFoundDepth = 0; 324 } 325 } 326 327 } else if (event == XPointerPart.EVENT_ELEMENT_END) { 328 if (fCurrentChildDepth == fFoundDepth) { 329 fIsElementFound = true; 330 } else if (((fCurrentChildDepth < fFoundDepth) && (fFoundDepth != 0)) 331 || ((fCurrentChildDepth > fFoundDepth) // or empty element found 332 && (fFoundDepth == 0))) { 333 fIsElementFound = false; 334 } 335 336 // reset array position of last child 337 fCurrentChildSequence[fCurrentChildDepth] = 0; 338 339 fCurrentChildDepth--; 340 fCurrentChildPosition = fCurrentChildSequence[fCurrentChildDepth] + 1; 341 342 } else if (event == XPointerPart.EVENT_ELEMENT_EMPTY) { 343 344 fCurrentChildSequence[fCurrentChildDepth] = fCurrentChildPosition; 345 fCurrentChildPosition++; 346 347 // Donot check for empty elements if the empty element is 348 // a child of a found parent element 349 //if (!fIsElementFound) { 350 if (checkMatch()) { 351 fIsElementFound = true; 352 fWasOnlyEmptyElementFound = true; 353 } else { 354 fIsElementFound = false; 355 } 356 //} 357 358 } 359 } 360 361 return fIsElementFound; 362 } 363 364 /** 365 * Matches the current position of the element being visited by checking 366 * its position and previous elements against the element XPointer expression. 367 * If a match is found it return true else false. 368 * 369 * @return boolean 370 */ 371 protected boolean checkMatch() { 372 // If the number of elements in the ChildSequence is greater than the 373 // current child depth, there is not point in checking further 374 if (!fIsShortHand) { 375 // If a shorthand pointer is not present traverse the children 376 // and compare 377 if (fChildSequence.length <= fCurrentChildDepth + 1) { 378 379 for (int i = 0; i < fChildSequence.length; i++) { 380 if (fChildSequence[i] != fCurrentChildSequence[i]) { 381 return false; 382 } 383 } 384 } else { 385 return false; 386 } 387 } else { 388 // If a shorthand pointer is present traverse the children 389 // ignoring the first element of the CurrenChildSequence which 390 // contains the ShortHand pointer element and compare 391 if (fChildSequence.length <= fCurrentChildDepth + 1) { 392 393 for (int i = 0; i < fChildSequence.length; i++) { 394 // ensure fCurrentChildSequence is large enough 395 if (fCurrentChildSequence.length < i + 2) { 396 return false; 397 } 398 399 // ignore the first element of fCurrentChildSequence 400 if (fChildSequence[i] != fCurrentChildSequence[i + 1]) { 401 return false; 402 } 403 } 404 } else { 405 return false; 406 } 407 408 } 409 410 return true; 411 } 412 413 /** 414 * Returns true if the node matches or is a child of a matching element() 415 * scheme XPointer. 416 * 417 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor#isFragmentResolved() 418 */ 419 public boolean isFragmentResolved() throws XNIException { 420 // Return true if the Fragment was resolved and the current Node depth 421 // is greater than or equal to the depth at which the element was found 422 return fIsFragmentResolved ; 423 } 424 425 /** 426 * Returns true if the XPointer expression resolves to a non-element child 427 * of the current resource fragment. 428 * 429 * @see com.sun.org.apache.xerces.internal.xpointer.XPointerPart#isChildFragmentResolved() 430 * 431 */ 432 public boolean isChildFragmentResolved() { 433 // if only a shorthand pointer was present 434 if (fIsShortHand && fShortHandPointer != null && fChildSequence.length <= 0) { 435 return fShortHandPointer.isChildFragmentResolved(); 436 } else { 437 return fWasOnlyEmptyElementFound ? !fWasOnlyEmptyElementFound 438 : (fIsFragmentResolved && (fCurrentChildDepth >= fFoundDepth)); 439 } 440 } 441 442 /** 443 * Reports an XPointer error 444 */ 445 protected void reportError(String key, Object[] arguments) 446 throws XNIException { 447 /*fErrorReporter.reportError(XPointerMessageFormatter.XPOINTER_DOMAIN, 448 key, arguments, XMLErrorReporter.SEVERITY_ERROR); 449 */ 450 throw new XNIException((fErrorReporter 451 .getMessageFormatter(XPointerMessageFormatter.XPOINTER_DOMAIN)) 452 .formatMessage(fErrorReporter.getLocale(), key, arguments)); 453 } 454 455 /** 456 * Initializes error handling objects 457 */ 458 protected void initErrorReporter() { 459 if (fErrorReporter == null) { 460 fErrorReporter = new XMLErrorReporter(); 461 } 462 if (fErrorHandler == null) { 463 fErrorHandler = new XPointerErrorHandler(); 464 } 465 fErrorReporter.putMessageFormatter( 466 XPointerMessageFormatter.XPOINTER_DOMAIN, 467 new XPointerMessageFormatter()); 468 } 469 470 /** 471 * Initializes the element scheme processor 472 */ 473 protected void init() { 474 fSchemeName = null; 475 fSchemeData = null; 476 fShortHandPointerName = null; 477 fIsResolveElement = false; 478 fIsElementFound = false; 479 fWasOnlyEmptyElementFound = false; 480 fFoundDepth = 0; 481 fCurrentChildPosition = 1; 482 fCurrentChildDepth = 0; 483 fIsFragmentResolved = false; 484 fShortHandPointer = null; 485 486 initErrorReporter(); 487 } 488 489 // ************************************************************************ 490 // element() Scheme expression scanner 491 // ************************************************************************ 492 493 /** 494 * List of XPointer Framework tokens. 495 * 496 * @xerces.internal 497 * 498 * @author Neil Delima, IBM 499 * @version $Id: ElementSchemePointer.java,v 1.4 2009/06/11 23:51:50 joehw Exp $ 500 * 501 */ 502 private final class Tokens { 503 504 /** 505 * XPointer element() scheme 506 * [1] ElementSchemeData ::= (NCName ChildSequence?) | ChildSequence 507 * [2] ChildSequence ::= ('/' [1-9] [0-9]*)+ 508 */ 509 private static final int XPTRTOKEN_ELEM_NCNAME = 0; 510 511 private static final int XPTRTOKEN_ELEM_CHILD = 1; 512 513 // Token names 514 private final String[] fgTokenNames = { "XPTRTOKEN_ELEM_NCNAME", 515 "XPTRTOKEN_ELEM_CHILD" }; 516 517 // Token count 518 private static final int INITIAL_TOKEN_COUNT = 1 << 8; 519 520 private int[] fTokens = new int[INITIAL_TOKEN_COUNT]; 521 522 private int fTokenCount = 0; 523 524 // Current token position 525 private int fCurrentTokenIndex; 526 527 private SymbolTable fSymbolTable; 528 529 private Hashtable fTokenNames = new Hashtable(); 530 531 /** 532 * Constructor 533 * 534 * @param symbolTable SymbolTable 535 */ 536 private Tokens(SymbolTable symbolTable) { 537 fSymbolTable = symbolTable; 538 539 fTokenNames.put(new Integer(XPTRTOKEN_ELEM_NCNAME), 540 "XPTRTOKEN_ELEM_NCNAME"); 541 fTokenNames.put(new Integer(XPTRTOKEN_ELEM_CHILD), 542 "XPTRTOKEN_ELEM_CHILD"); 543 } 544 545 /* 546 * Returns the token String 547 * @param token The index of the token 548 * @return String The token string 549 */ 550 private String getTokenString(int token) { 551 return (String) fTokenNames.get(new Integer(token)); 552 } 553 554 /** 555 * Returns the token String 556 * @param token The index of the token 557 * @return String The token string 558 */ 559 private Integer getToken(int token) { 560 return (Integer) fTokenNames.get(new Integer(token)); 561 } 562 563 /** 564 * Add the specified string as a token 565 * 566 * @param token The token string 567 */ 568 private void addToken(String tokenStr) { 569 Integer tokenInt = (Integer) fTokenNames.get(tokenStr); 570 if (tokenInt == null) { 571 tokenInt = new Integer(fTokenNames.size()); 572 fTokenNames.put(tokenInt, tokenStr); 573 } 574 addToken(tokenInt.intValue()); 575 } 576 577 /** 578 * Add the specified int token 579 * 580 * @param token The int specifying the token 581 */ 582 private void addToken(int token) { 583 try { 584 fTokens[fTokenCount] = token; 585 } catch (ArrayIndexOutOfBoundsException ex) { 586 int[] oldList = fTokens; 587 fTokens = new int[fTokenCount << 1]; 588 System.arraycopy(oldList, 0, fTokens, 0, fTokenCount); 589 fTokens[fTokenCount] = token; 590 } 591 fTokenCount++; 592 } 593 594 /** 595 * Resets the current position to the head of the token list. 596 */ 597 private void rewind() { 598 fCurrentTokenIndex = 0; 599 } 600 601 /** 602 * Returns true if the {@link #getNextToken()} method 603 * returns a valid token. 604 */ 605 private boolean hasMore() { 606 return fCurrentTokenIndex < fTokenCount; 607 } 608 609 /** 610 * Obtains the token at the current position, then advance 611 * the current position by one. 612 * 613 * If there's no such next token, this method throws 614 * <tt>new XNIException("InvalidXPointerExpression");</tt>. 615 */ 616 private int nextToken() throws XNIException { 617 if (fCurrentTokenIndex == fTokenCount) 618 reportError("XPointerElementSchemeProcessingError", null); 619 return fTokens[fCurrentTokenIndex++]; 620 } 621 622 /** 623 * Obtains the token at the current position, without advancing 624 * the current position. 625 * 626 * If there's no such next token, this method throws 627 * <tt>new XNIException("InvalidXPointerExpression");</tt>. 628 */ 629 private int peekToken() throws XNIException { 630 if (fCurrentTokenIndex == fTokenCount) 631 reportError("XPointerElementSchemeProcessingError", null); 632 return fTokens[fCurrentTokenIndex]; 633 } 634 635 /** 636 * Obtains the token at the current position as a String. 637 * 638 * If there's no current token or if the current token 639 * is not a string token, this method throws 640 * If there's no such next token, this method throws 641 * <tt>new XNIException("InvalidXPointerExpression");</tt>. 642 */ 643 private String nextTokenAsString() throws XNIException { 644 String s = getTokenString(nextToken()); 645 if (s == null) 646 reportError("XPointerElementSchemeProcessingError", null); 647 return s; 648 } 649 650 /** 651 * Returns the number of tokens. 652 * 653 */ 654 private int getTokenCount() { 655 return fTokenCount; 656 } 657 } 658 659 /** 660 * 661 * The XPointer expression scanner. Scans the XPointer framework expression. 662 * 663 * @xerces.internal 664 * 665 * @version $Id: ElementSchemePointer.java,v 1.4 2009/06/11 23:51:50 joehw Exp $ 666 */ 667 private class Scanner { 668 669 /** 670 * 7-bit ASCII subset 671 * 672 * 0 1 2 3 4 5 6 7 8 9 A B C D E F 673 * 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0 674 * 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 675 * SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2 676 * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3 677 * @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4 678 * P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5 679 * `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6 680 * p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7 681 */ 682 private static final byte CHARTYPE_INVALID = 0, // invalid XML characters, control characters and 7F 683 CHARTYPE_OTHER = 1, // A valid XML character (possibly invalid NCNameChar) that does not fall in one of the other categories 684 CHARTYPE_MINUS = 2, // '-' (0x2D) 685 CHARTYPE_PERIOD = 3, // '.' (0x2E) 686 CHARTYPE_SLASH = 4, // '/' (0x2F) 687 CHARTYPE_DIGIT = 5, // '0'-'9' (0x30 to 0x39) 688 CHARTYPE_LETTER = 6, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A) 689 CHARTYPE_UNDERSCORE = 7, // '_' (0x5F) 690 CHARTYPE_NONASCII = 8; // Non-ASCII Unicode codepoint (>= 0x80) 691 692 private final byte[] fASCIICharMap = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 693 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 694 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 5, 5, 5, 5, 5, 695 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 696 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 697 7, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 698 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1 }; 699 700 /** 701 * Symbol literals 702 */ 703 704 // 705 // Data 706 // 707 /** Symbol table. */ 708 private SymbolTable fSymbolTable; 709 710 // 711 // Constructors 712 // 713 714 /** 715 * Constructs an XPath expression scanner. 716 * 717 * @param symbolTable SymbolTable 718 */ 719 private Scanner(SymbolTable symbolTable) { 720 // save pool and tokens 721 fSymbolTable = symbolTable; 722 723 } // <init>(SymbolTable) 724 725 /** 726 * Scans the XPointer Expression 727 * 728 */ 729 private boolean scanExpr(SymbolTable symbolTable, Tokens tokens, 730 String data, int currentOffset, int endOffset) 731 throws XNIException { 732 733 int ch; 734 int nameOffset; 735 String nameHandle = null; 736 737 while (true) { 738 if (currentOffset == endOffset) { 739 break; 740 } 741 742 ch = data.charAt(currentOffset); 743 byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII 744 : fASCIICharMap[ch]; 745 746 // 747 // [1] ElementSchemeData ::= (NCName ChildSequence?) | ChildSequence 748 // [2] ChildSequence ::= ('/' [1-9] [0-9]*)+ 749 // 750 751 switch (chartype) { 752 753 case CHARTYPE_SLASH: 754 // if last character is '/', break and report an error 755 if (++currentOffset == endOffset) { 756 return false; 757 } 758 759 addToken(tokens, Tokens.XPTRTOKEN_ELEM_CHILD); 760 ch = data.charAt(currentOffset); 761 762 // ChildSequence ::= ('/' [1-9] [0-9]*)+ 763 int child = 0; 764 while (ch >= '0' && ch <= '9') { 765 child = (child * 10) + (ch - '0'); 766 if (++currentOffset == endOffset) { 767 break; 768 } 769 ch = data.charAt(currentOffset); 770 } 771 772 // An invalid child sequence character 773 if (child == 0) { 774 reportError("InvalidChildSequenceCharacter", 775 new Object[] { new Character((char) ch) }); 776 return false; 777 } 778 779 tokens.addToken(child); 780 781 break; 782 783 case CHARTYPE_DIGIT: 784 case CHARTYPE_LETTER: 785 case CHARTYPE_MINUS: 786 case CHARTYPE_NONASCII: 787 case CHARTYPE_OTHER: 788 case CHARTYPE_PERIOD: 789 case CHARTYPE_UNDERSCORE: 790 // Scan the ShortHand Pointer NCName 791 nameOffset = currentOffset; 792 currentOffset = scanNCName(data, endOffset, currentOffset); 793 794 if (currentOffset == nameOffset) { 795 //return false; 796 reportError("InvalidNCNameInElementSchemeData", 797 new Object[] { data }); 798 return false; 799 } 800 801 if (currentOffset < endOffset) { 802 ch = data.charAt(currentOffset); 803 } else { 804 ch = -1; 805 } 806 807 nameHandle = symbolTable.addSymbol(data.substring( 808 nameOffset, currentOffset)); 809 addToken(tokens, Tokens.XPTRTOKEN_ELEM_NCNAME); 810 tokens.addToken(nameHandle); 811 812 break; 813 } 814 } 815 return true; 816 } 817 818 /** 819 * Scans a NCName. 820 * From Namespaces in XML 821 * [5] NCName ::= (Letter | '_') (NCNameChar)* 822 * [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender 823 * 824 * @param data A String containing the XPointer expression 825 * @param endOffset The int XPointer expression length 826 * @param currentOffset An int representing the current position of the XPointer expression pointer 827 */ 828 private int scanNCName(String data, int endOffset, int currentOffset) { 829 int ch = data.charAt(currentOffset); 830 if (ch >= 0x80) { 831 if (!XMLChar.isNameStart(ch)) { 832 return currentOffset; 833 } 834 } else { 835 byte chartype = fASCIICharMap[ch]; 836 if (chartype != CHARTYPE_LETTER 837 && chartype != CHARTYPE_UNDERSCORE) { 838 return currentOffset; 839 } 840 } 841 while (++currentOffset < endOffset) { 842 ch = data.charAt(currentOffset); 843 if (ch >= 0x80) { 844 if (!XMLChar.isName(ch)) { 845 break; 846 } 847 } else { 848 byte chartype = fASCIICharMap[ch]; 849 if (chartype != CHARTYPE_LETTER 850 && chartype != CHARTYPE_DIGIT 851 && chartype != CHARTYPE_PERIOD 852 && chartype != CHARTYPE_MINUS 853 && chartype != CHARTYPE_UNDERSCORE) { 854 break; 855 } 856 } 857 } 858 return currentOffset; 859 } 860 861 // 862 // Protected methods 863 // 864 865 /** 866 * This method adds the specified token to the token list. By 867 * default, this method allows all tokens. However, subclasses 868 * of the XPathExprScanner can override this method in order 869 * to disallow certain tokens from being used in the scanned 870 * XPath expression. This is a convenient way of allowing only 871 * a subset of XPath. 872 */ 873 protected void addToken(Tokens tokens, int token) throws XNIException { 874 tokens.addToken(token); 875 } // addToken(int) 876 877 } // class Scanner 878 879 }