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