1/* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 */ 4/* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. 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 21package com.sun.org.apache.xerces.internal.impl.xpath; 22 23import com.sun.org.apache.xerces.internal.util.SymbolTable; 24import com.sun.org.apache.xerces.internal.util.XMLChar; 25import com.sun.org.apache.xerces.internal.util.XMLSymbols; 26import com.sun.org.apache.xerces.internal.xni.NamespaceContext; 27import com.sun.org.apache.xerces.internal.xni.QName; 28import java.util.ArrayList; 29import java.util.Arrays; 30import java.util.HashMap; 31import java.util.List; 32import java.util.Map; 33import java.util.stream.Collectors; 34 35/** 36 * Bare minimum XPath parser. 37 * 38 * @xerces.internal 39 * 40 * @author Andy Clark, IBM 41 * @author Sunitha Reddy, Sun Microsystems 42 */ 43public class XPath { 44 45 // 46 // Constants 47 // 48 49 private static final boolean DEBUG_ALL = false; 50 51 private static final boolean DEBUG_XPATH_PARSE = DEBUG_ALL || false; 52 53 // 54 // Data 55 // 56 57 /** Expression. */ 58 protected final String fExpression; 59 60 /** Symbol table. */ 61 protected final SymbolTable fSymbolTable; 62 63 /** Location paths. */ 64 protected final LocationPath[] fLocationPaths; 65 66 // 67 // Constructors 68 // 69 70 /** Constructs an XPath object from the specified expression. */ 71 public XPath(String xpath, SymbolTable symbolTable, 72 NamespaceContext context) 73 throws XPathException { 74 fExpression = xpath; 75 fSymbolTable = symbolTable; 76 fLocationPaths = parseExpression(context); 77 } // <init>(String,SymbolTable,NamespaceContext) 78 79 // 80 // Public methods 81 // 82 83 /** 84 * Returns a representation of all location paths for this XPath. 85 * XPath = locationPath ( '|' locationPath) 86 */ 87 public LocationPath[] getLocationPaths() { 88 LocationPath[] ret=new LocationPath[fLocationPaths.length]; 89 for (int i=0;i<fLocationPaths.length;i++){ 90 ret[i]=(LocationPath)fLocationPaths[i].clone(); 91 } 92 return ret; 93 } // getLocationPath(LocationPath) 94 95 /** Returns a representation of the first location path for this XPath. */ 96 public LocationPath getLocationPath() { 97 return (LocationPath)fLocationPaths[0].clone(); 98 } // getLocationPath(LocationPath) 99 100 // 101 // Object methods 102 // 103 104 /** Returns a string representation of this object. */ 105 @Override 106 public String toString() { 107 final List<LocationPath> l = Arrays.asList(fLocationPaths); 108 final String s = l.stream() 109 .map(aPath -> aPath.toString()) 110 .collect(Collectors.joining("|")); 111 112 return s; 113 } // toString():String 114 115 // 116 // Private methods 117 // 118 119 /** 120 * Used by the {@link #parseExpression(NamespaceContext)} method 121 * to verify the assumption. 122 * 123 * If <tt>b</tt> is false, this method throws XPathException 124 * to report the error. 125 */ 126 private static void check( boolean b ) throws XPathException { 127 if(!b) throw new XPathException("c-general-xpath"); 128 } 129 130 /** 131 * Used by the {@link #parseExpression(NamespaceContext)} method 132 * to build a {@link LocationPath} object from the accumulated 133 * {@link Step}s. 134 */ 135 private LocationPath buildLocationPath( ArrayList<Step> stepsVector ) throws XPathException { 136 int size = stepsVector.size(); 137 check(size!=0); 138 Step[] steps = new Step[size]; 139 steps = stepsVector.toArray(steps); 140 stepsVector.clear(); 141 142 return new LocationPath(steps); 143 } 144 145 /** 146 * This method is implemented by using the XPathExprScanner and 147 * examining the list of tokens that it returns. 148 */ 149 private LocationPath[] parseExpression(final NamespaceContext context) 150 throws XPathException { 151 152 // tokens 153 final XPath.Tokens xtokens = new XPath.Tokens(fSymbolTable); 154 155 // scanner 156 XPath.Scanner scanner = new XPath.Scanner(fSymbolTable) { 157 protected void addToken(XPath.Tokens tokens, int token) 158 throws XPathException { 159 if ( 160 token == XPath.Tokens.EXPRTOKEN_ATSIGN || 161 token == XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE || 162 token == XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME || 163 token == XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH || 164 token == XPath.Tokens.EXPRTOKEN_PERIOD || 165 token == XPath.Tokens.EXPRTOKEN_NAMETEST_ANY || 166 token == XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE || 167 token == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH || 168 token == XPath.Tokens.EXPRTOKEN_OPERATOR_UNION || 169 token == XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD || 170 token == XPath.Tokens.EXPRTOKEN_DOUBLE_COLON 171 ) { 172 super.addToken(tokens, token); 173 return; 174 } 175 throw new XPathException("c-general-xpath"); 176 } 177 }; 178 179 int length = fExpression.length(); 180 181 boolean success = scanner.scanExpr(fSymbolTable, 182 xtokens, fExpression, 0, length); 183 if(!success) 184 throw new XPathException("c-general-xpath"); 185 186 //fTokens.dumpTokens(); 187 ArrayList<Step> stepsVector = new ArrayList<>(); 188 ArrayList<LocationPath> locationPathsVector= new ArrayList<>(); 189 190 // true when the next token should be 'Step' (as defined in 191 // the production rule [3] of XML Schema P1 section 3.11.6 192 // if false, we are expecting either '|' or '/'. 193 // 194 // this is to make sure we can detect a token list like 195 // 'abc' '/' '/' 'def' 'ghi' 196 boolean expectingStep = true; 197 198 while (xtokens.hasMore()) { 199 final int token = xtokens.nextToken(); 200 201 switch (token) { 202 case XPath.Tokens.EXPRTOKEN_OPERATOR_UNION :{ 203 check(!expectingStep); 204 locationPathsVector.add(buildLocationPath(stepsVector)); 205 expectingStep=true; 206 break; 207 } 208 case XPath.Tokens.EXPRTOKEN_ATSIGN: { 209 check(expectingStep); 210 Step step = new Step( 211 new Axis(Axis.ATTRIBUTE), 212 parseNodeTest(xtokens.nextToken(),xtokens,context)); 213 stepsVector.add(step); 214 expectingStep=false; 215 break; 216 } 217 case XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE: { 218 check(expectingStep); 219 // If we got here we're expecting attribute:: 220 if (xtokens.nextToken() != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) { 221 throw new XPathException("c-general-xpath"); 222 } 223 Step step = new Step( 224 new Axis(Axis.ATTRIBUTE), 225 parseNodeTest(xtokens.nextToken(),xtokens,context)); 226 stepsVector.add(step); 227 expectingStep = false; 228 break; 229 } 230 case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY: 231 case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE: 232 case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME: { 233 check(expectingStep); 234 Step step = new Step( 235 new Axis(Axis.CHILD), 236 parseNodeTest(token,xtokens,context)); 237 stepsVector.add(step); 238 expectingStep=false; 239 break; 240 } 241 case XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD: { 242 check(expectingStep); 243 // If we got here we're expecting child:: 244 if (xtokens.nextToken() != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) { 245 throw new XPathException("c-general-xpath"); 246 } 247 Step step = new Step( 248 new Axis(Axis.CHILD), 249 parseNodeTest(xtokens.nextToken(),xtokens,context)); 250 stepsVector.add(step); 251 expectingStep = false; 252 break; 253 } 254 case XPath.Tokens.EXPRTOKEN_PERIOD: { 255 check(expectingStep); 256 expectingStep=false; 257 258 // unless this is the first step in this location path, 259 // there's really no reason to keep them in LocationPath. 260 // This amounts to shorten "a/././b/./c" to "a/b/c". 261 // Also, the matcher fails to work correctly if XPath 262 // has those redundant dots. 263 if (stepsVector.isEmpty()) { 264 // build step 265 Axis axis = new Axis(Axis.SELF); 266 NodeTest nodeTest = new NodeTest(NodeTest.NODE); 267 Step step = new Step(axis, nodeTest); 268 stepsVector.add(step); 269 270 if( xtokens.hasMore() 271 && xtokens.peekToken() == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH){ 272 // consume '//' 273 xtokens.nextToken(); 274 275 // build step 276 axis = new Axis(Axis.DESCENDANT); 277 nodeTest = new NodeTest(NodeTest.NODE); 278 step = new Step(axis, nodeTest); 279 stepsVector.add(step); 280 expectingStep=true; 281 } 282 } 283 break; 284 } 285 case XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH:{ 286 // this cannot appear in an arbitrary position. 287 // it is only allowed right after '.' when 288 // '.' is the first token of a location path. 289 throw new XPathException("c-general-xpath"); 290 } 291 case XPath.Tokens.EXPRTOKEN_DOUBLE_COLON: { 292 // :: cannot appear in an arbitrary position. 293 // We only expect this token if the xpath 294 // contains child:: or attribute:: 295 throw new XPathException("c-general-xpath"); 296 } 297 case XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH: { 298 check(!expectingStep); 299 expectingStep=true; 300 break; 301 } 302 default: 303 // we should have covered all the tokens that we can possibly see. 304 throw new InternalError(); 305 } 306 } 307 308 check(!expectingStep); 309 310 locationPathsVector.add(buildLocationPath(stepsVector)); 311 312 // return location path 313 return locationPathsVector.toArray(new LocationPath[locationPathsVector.size()]); 314 315 } // parseExpression(SymbolTable,NamespaceContext) 316 317 /** 318 * Used by {@link #parseExpression} to parse a node test 319 * from the token list. 320 */ 321 private NodeTest parseNodeTest( int typeToken, Tokens xtokens, NamespaceContext context ) 322 throws XPathException { 323 switch(typeToken) { 324 case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY: 325 return new NodeTest(NodeTest.WILDCARD); 326 327 case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE: 328 case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME: 329 // consume QName token 330 String prefix = xtokens.nextTokenAsString(); 331 String uri = null; 332 if (context != null && prefix != XMLSymbols.EMPTY_STRING) { 333 uri = context.getURI(prefix); 334 } 335 if (prefix != XMLSymbols.EMPTY_STRING && context != null && uri == null) { 336 throw new XPathException("c-general-xpath-ns"); 337 } 338 339 if (typeToken==XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE) 340 return new NodeTest(prefix,uri); 341 342 String localpart = xtokens.nextTokenAsString(); 343 String rawname = prefix != XMLSymbols.EMPTY_STRING 344 ? fSymbolTable.addSymbol(prefix+':'+localpart) 345 : localpart; 346 347 return new NodeTest(new QName(prefix, localpart, rawname, uri)); 348 349 default: 350 // assertion error 351 throw new XPathException("c-general-xpath"); 352 353 } 354 } 355 356 357 // 358 // Classes 359 // 360 361 // location path information 362 363 /** 364 * A location path representation for an XPath expression. 365 * 366 * @xerces.internal 367 * 368 * @author Andy Clark, IBM 369 */ 370 public static class LocationPath 371 implements Cloneable { 372 373 // 374 // Data 375 // 376 377 /** List of steps. */ 378 public final Step[] steps; 379 380 // 381 // Constructors 382 // 383 384 /** Creates a location path from a series of steps. */ 385 public LocationPath(Step[] steps) { 386 this.steps = steps; 387 } // <init>(Step[]) 388 389 /** Copy constructor. */ 390 protected LocationPath(LocationPath path) { 391 steps = new Step[path.steps.length]; 392 for (int i = 0; i < steps.length; i++) { 393 steps[i] = (Step)path.steps[i].clone(); 394 } 395 } // <init>(LocationPath) 396 397 // 398 // Object methods 399 // 400 401 /** Returns a string representation of this object. */ 402 public String toString() { 403 StringBuffer str = new StringBuffer(); 404 for (int i = 0; i < steps.length; i++) { 405 if (i > 0 && (steps[i-1].axis.type!=Axis.DESCENDANT 406 && steps[i].axis.type!=Axis.DESCENDANT) ){ 407 str.append('/'); 408 } 409 str.append(steps[i].toString()); 410 } 411 // DEBUG: This code is just for debugging and should *not* 412 // be left in because it will mess up hashcodes of 413 // serialized versions of this object. -Ac 414 if (false) { 415 str.append('['); 416 String s = super.toString(); 417 str.append(s.substring(s.indexOf('@'))); 418 str.append(']'); 419 } 420 return str.toString(); 421 } // toString():String 422 423 /** Returns a clone of this object. */ 424 public Object clone() { 425 return new LocationPath(this); 426 } // clone():Object 427 428 } // class locationPath 429 430 /** 431 * A location path step comprised of an axis and node test. 432 * 433 * @xerces.internal 434 * 435 * @author Andy Clark, IBM 436 */ 437 public static class Step 438 implements Cloneable { 439 440 // 441 // Data 442 // 443 444 /** Axis. */ 445 public final Axis axis; 446 447 /** Node test. */ 448 public final NodeTest nodeTest; 449 450 // 451 // Constructors 452 // 453 454 /** Constructs a step from an axis and node test. */ 455 public Step(Axis axis, NodeTest nodeTest) { 456 this.axis = axis; 457 this.nodeTest = nodeTest; 458 } // <init>(Axis,NodeTest) 459 460 /** Copy constructor. */ 461 protected Step(Step step) { 462 axis = (Axis)step.axis.clone(); 463 nodeTest = (NodeTest)step.nodeTest.clone(); 464 } // <init>(Step) 465 466 // 467 // Object methods 468 // 469 470 /** Returns a string representation of this object. */ 471 public String toString() { 472 if (axis.type == Axis.SELF) { 473 return "."; 474 } 475 if (axis.type == Axis.ATTRIBUTE) { 476 return "@" + nodeTest.toString(); 477 } 478 if (axis.type == Axis.CHILD) { 479 return nodeTest.toString(); 480 } 481 if (axis.type == Axis.DESCENDANT) { 482 return "//"; 483 } 484 return "??? ("+axis.type+')'; 485 } // toString():String 486 487 /** Returns a clone of this object. */ 488 public Object clone() { 489 return new Step(this); 490 } // clone():Object 491 492 } // class Step 493 494 /** 495 * Axis. 496 * 497 * @xerces.internal 498 * 499 * @author Andy Clark, IBM 500 */ 501 public static class Axis 502 implements Cloneable { 503 504 // 505 // Constants 506 // 507 508 /** Type: child. */ 509 public static final short CHILD = 1; 510 511 /** Type: attribute. */ 512 public static final short ATTRIBUTE = 2; 513 514 /** Type: self. */ 515 public static final short SELF = 3; 516 517 518 /** Type: descendant. */ 519 public static final short DESCENDANT = 4; 520 // 521 // Data 522 // 523 524 /** Axis type. */ 525 public final short type; 526 527 // 528 // Constructors 529 // 530 531 /** Constructs an axis with the specified type. */ 532 public Axis(short type) { 533 this.type = type; 534 } // <init>(short) 535 536 /** Copy constructor. */ 537 protected Axis(Axis axis) { 538 type = axis.type; 539 } // <init>(Axis) 540 541 // 542 // Object methods 543 // 544 545 /** Returns a string representation of this object. */ 546 public String toString() { 547 switch (type) { 548 case CHILD: return "child"; 549 case ATTRIBUTE: return "attribute"; 550 case SELF: return "self"; 551 case DESCENDANT: return "descendant"; 552 } 553 return "???"; 554 } // toString():String 555 556 /** Returns a clone of this object. */ 557 public Object clone() { 558 return new Axis(this); 559 } // clone():Object 560 561 } // class Axis 562 563 /** 564 * Node test. 565 * 566 * @xerces.internal 567 * 568 * @author Andy Clark, IBM 569 */ 570 public static class NodeTest 571 implements Cloneable { 572 573 // 574 // Constants 575 // 576 577 /** Type: qualified name. */ 578 public static final short QNAME = 1; 579 580 /** Type: wildcard. */ 581 public static final short WILDCARD = 2; 582 583 /** Type: node. */ 584 public static final short NODE = 3; 585 586 /** Type: namespace */ 587 public static final short NAMESPACE= 4; 588 589 // 590 // Data 591 // 592 593 /** Node test type. */ 594 public final short type; 595 596 /** Node qualified name. */ 597 public final QName name = new QName(); 598 599 // 600 // Constructors 601 // 602 603 /** Constructs a node test of type WILDCARD or NODE. */ 604 public NodeTest(short type) { 605 this.type = type; 606 } // <init>(int) 607 608 /** Constructs a node test of type QName. */ 609 public NodeTest(QName name) { 610 this.type = QNAME; 611 this.name.setValues(name); 612 } // <init>(QName) 613 /** Constructs a node test of type Namespace. */ 614 public NodeTest(String prefix, String uri) { 615 this.type = NAMESPACE; 616 this.name.setValues(prefix, null, null, uri); 617 } // <init>(String,String) 618 619 /** Copy constructor. */ 620 public NodeTest(NodeTest nodeTest) { 621 type = nodeTest.type; 622 name.setValues(nodeTest.name); 623 } // <init>(NodeTest) 624 625 // 626 // Object methods 627 // 628 629 /** Returns a string representation of this object. */ 630 public String toString() { 631 632 switch (type) { 633 case QNAME: { 634 if (name.prefix.length() !=0) { 635 if (name.uri != null) { 636 return name.prefix+':'+name.localpart; 637 } 638 return "{"+name.uri+'}'+name.prefix+':'+name.localpart; 639 } 640 return name.localpart; 641 } 642 case NAMESPACE: { 643 if (name.prefix.length() !=0) { 644 if (name.uri != null) { 645 return name.prefix+":*"; 646 } 647 return "{"+name.uri+'}'+name.prefix+":*"; 648 } 649 return "???:*"; 650 } 651 case WILDCARD: { 652 return "*"; 653 } 654 case NODE: { 655 return "node()"; 656 } 657 } 658 return "???"; 659 660 } // toString():String 661 662 /** Returns a clone of this object. */ 663 public Object clone() { 664 return new NodeTest(this); 665 } // clone():Object 666 667 } // class NodeTest 668 669 // xpath implementation 670 671 // NOTE: The XPath implementation classes are kept internal because 672 // this implementation is just a temporary hack until a better 673 // and/or more appropriate implementation can be written. 674 // keeping the code in separate source files would "muddy" the 675 // CVS directory when it's not needed. -Ac 676 677 /** 678 * List of tokens. 679 * 680 * @xerces.internal 681 * 682 * @author Glenn Marcy, IBM 683 * @author Andy Clark, IBM 684 * 685 */ 686 private static final class Tokens { 687 688 static final boolean DUMP_TOKENS = false; 689 690 /** 691 * [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::' 692 * | NameTest | NodeType | Operator | FunctionName 693 * | AxisName | Literal | Number | VariableReference 694 */ 695 public static final int 696 EXPRTOKEN_OPEN_PAREN = 0, 697 EXPRTOKEN_CLOSE_PAREN = 1, 698 EXPRTOKEN_OPEN_BRACKET = 2, 699 EXPRTOKEN_CLOSE_BRACKET = 3, 700 EXPRTOKEN_PERIOD = 4, 701 EXPRTOKEN_DOUBLE_PERIOD = 5, 702 EXPRTOKEN_ATSIGN = 6, 703 EXPRTOKEN_COMMA = 7, 704 EXPRTOKEN_DOUBLE_COLON = 8, 705 // 706 // [37] NameTest ::= '*' | NCName ':' '*' | QName 707 // 708 // followed by symbol handle of NCName or QName 709 // 710 EXPRTOKEN_NAMETEST_ANY = 9, 711 EXPRTOKEN_NAMETEST_NAMESPACE = 10, 712 EXPRTOKEN_NAMETEST_QNAME = 11, 713 // 714 // [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node' 715 // 716 EXPRTOKEN_NODETYPE_COMMENT = 12, 717 EXPRTOKEN_NODETYPE_TEXT = 13, 718 EXPRTOKEN_NODETYPE_PI = 14, 719 EXPRTOKEN_NODETYPE_NODE = 15, 720 // 721 // [32] Operator ::= OperatorName 722 // | MultiplyOperator 723 // | '/' | '//' | '|' | '+' | '-' | '=' | '!=' | '<' | '<=' | '>' | '>=' 724 // [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div' 725 // [34] MultiplyOperator ::= '*' 726 // 727 EXPRTOKEN_OPERATOR_AND = 16, 728 EXPRTOKEN_OPERATOR_OR = 17, 729 EXPRTOKEN_OPERATOR_MOD = 18, 730 EXPRTOKEN_OPERATOR_DIV = 19, 731 EXPRTOKEN_OPERATOR_MULT = 20, 732 EXPRTOKEN_OPERATOR_SLASH = 21, 733 EXPRTOKEN_OPERATOR_DOUBLE_SLASH = 22, 734 EXPRTOKEN_OPERATOR_UNION = 23, 735 EXPRTOKEN_OPERATOR_PLUS = 24, 736 EXPRTOKEN_OPERATOR_MINUS = 25, 737 EXPRTOKEN_OPERATOR_EQUAL = 26, 738 EXPRTOKEN_OPERATOR_NOT_EQUAL = 27, 739 EXPRTOKEN_OPERATOR_LESS = 28, 740 EXPRTOKEN_OPERATOR_LESS_EQUAL = 29, 741 EXPRTOKEN_OPERATOR_GREATER = 30, 742 EXPRTOKEN_OPERATOR_GREATER_EQUAL = 31, 743 744 //EXPRTOKEN_FIRST_OPERATOR = EXPRTOKEN_OPERATOR_AND, 745 //EXPRTOKEN_LAST_OPERATOR = EXPRTOKEN_OPERATOR_GREATER_EQUAL, 746 747 // 748 // [35] FunctionName ::= QName - NodeType 749 // 750 // followed by symbol handle 751 // 752 EXPRTOKEN_FUNCTION_NAME = 32, 753 // 754 // [6] AxisName ::= 'ancestor' | 'ancestor-or-self' 755 // | 'attribute' 756 // | 'child' 757 // | 'descendant' | 'descendant-or-self' 758 // | 'following' | 'following-sibling' 759 // | 'namespace' 760 // | 'parent' 761 // | 'preceding' | 'preceding-sibling' 762 // | 'self' 763 // 764 EXPRTOKEN_AXISNAME_ANCESTOR = 33, 765 EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF = 34, 766 EXPRTOKEN_AXISNAME_ATTRIBUTE = 35, 767 EXPRTOKEN_AXISNAME_CHILD = 36, 768 EXPRTOKEN_AXISNAME_DESCENDANT = 37, 769 EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF = 38, 770 EXPRTOKEN_AXISNAME_FOLLOWING = 39, 771 EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING = 40, 772 EXPRTOKEN_AXISNAME_NAMESPACE = 41, 773 EXPRTOKEN_AXISNAME_PARENT = 42, 774 EXPRTOKEN_AXISNAME_PRECEDING = 43, 775 EXPRTOKEN_AXISNAME_PRECEDING_SIBLING = 44, 776 EXPRTOKEN_AXISNAME_SELF = 45, 777 // 778 // [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'" 779 // 780 // followed by symbol handle for literal 781 // 782 EXPRTOKEN_LITERAL = 46, 783 // 784 // [30] Number ::= Digits ('.' Digits?)? | '.' Digits 785 // [31] Digits ::= [0-9]+ 786 // 787 // followed by number handle 788 // 789 EXPRTOKEN_NUMBER = 47, 790 // 791 // [36] VariableReference ::= '$' QName 792 // 793 // followed by symbol handle for QName 794 // 795 EXPRTOKEN_VARIABLE_REFERENCE = 48; 796 797 private static final String[] fgTokenNames = { 798 "EXPRTOKEN_OPEN_PAREN", 799 "EXPRTOKEN_CLOSE_PAREN", 800 "EXPRTOKEN_OPEN_BRACKET", 801 "EXPRTOKEN_CLOSE_BRACKET", 802 "EXPRTOKEN_PERIOD", 803 "EXPRTOKEN_DOUBLE_PERIOD", 804 "EXPRTOKEN_ATSIGN", 805 "EXPRTOKEN_COMMA", 806 "EXPRTOKEN_DOUBLE_COLON", 807 "EXPRTOKEN_NAMETEST_ANY", 808 "EXPRTOKEN_NAMETEST_NAMESPACE", 809 "EXPRTOKEN_NAMETEST_QNAME", 810 "EXPRTOKEN_NODETYPE_COMMENT", 811 "EXPRTOKEN_NODETYPE_TEXT", 812 "EXPRTOKEN_NODETYPE_PI", 813 "EXPRTOKEN_NODETYPE_NODE", 814 "EXPRTOKEN_OPERATOR_AND", 815 "EXPRTOKEN_OPERATOR_OR", 816 "EXPRTOKEN_OPERATOR_MOD", 817 "EXPRTOKEN_OPERATOR_DIV", 818 "EXPRTOKEN_OPERATOR_MULT", 819 "EXPRTOKEN_OPERATOR_SLASH", 820 "EXPRTOKEN_OPERATOR_DOUBLE_SLASH", 821 "EXPRTOKEN_OPERATOR_UNION", 822 "EXPRTOKEN_OPERATOR_PLUS", 823 "EXPRTOKEN_OPERATOR_MINUS", 824 "EXPRTOKEN_OPERATOR_EQUAL", 825 "EXPRTOKEN_OPERATOR_NOT_EQUAL", 826 "EXPRTOKEN_OPERATOR_LESS", 827 "EXPRTOKEN_OPERATOR_LESS_EQUAL", 828 "EXPRTOKEN_OPERATOR_GREATER", 829 "EXPRTOKEN_OPERATOR_GREATER_EQUAL", 830 "EXPRTOKEN_FUNCTION_NAME", 831 "EXPRTOKEN_AXISNAME_ANCESTOR", 832 "EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF", 833 "EXPRTOKEN_AXISNAME_ATTRIBUTE", 834 "EXPRTOKEN_AXISNAME_CHILD", 835 "EXPRTOKEN_AXISNAME_DESCENDANT", 836 "EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF", 837 "EXPRTOKEN_AXISNAME_FOLLOWING", 838 "EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING", 839 "EXPRTOKEN_AXISNAME_NAMESPACE", 840 "EXPRTOKEN_AXISNAME_PARENT", 841 "EXPRTOKEN_AXISNAME_PRECEDING", 842 "EXPRTOKEN_AXISNAME_PRECEDING_SIBLING", 843 "EXPRTOKEN_AXISNAME_SELF", 844 "EXPRTOKEN_LITERAL", 845 "EXPRTOKEN_NUMBER", 846 "EXPRTOKEN_VARIABLE_REFERENCE" 847 }; 848 849 /** 850 * 851 */ 852 private static final int INITIAL_TOKEN_COUNT = 1 << 8; 853 private int[] fTokens = new int[INITIAL_TOKEN_COUNT]; 854 private int fTokenCount = 0; // for writing 855 856 private final SymbolTable fSymbolTable; 857 858 // REVISIT: Code something better here. -Ac 859 private final Map<String, Integer> fSymbolMapping = new HashMap<>(); 860 861 // REVISIT: Code something better here. -Ac 862 private final Map<Integer, String> fTokenNames = new HashMap<>(); 863 864 /** 865 * Current position in the token list. 866 */ 867 private int fCurrentTokenIndex; 868 869 // 870 // Constructors 871 // 872 873 public Tokens(SymbolTable symbolTable) { 874 fSymbolTable = symbolTable; 875 final String[] symbols = { 876 "ancestor", "ancestor-or-self", "attribute", 877 "child", "descendant", "descendant-or-self", 878 "following", "following-sibling", "namespace", 879 "parent", "preceding", "preceding-sibling", 880 "self", 881 }; 882 for (int i = 0; i < symbols.length; i++) { 883 fSymbolMapping.put(fSymbolTable.addSymbol(symbols[i]), i); 884 } 885 fTokenNames.put(EXPRTOKEN_OPEN_PAREN, "EXPRTOKEN_OPEN_PAREN"); 886 fTokenNames.put(EXPRTOKEN_CLOSE_PAREN, "EXPRTOKEN_CLOSE_PAREN"); 887 fTokenNames.put(EXPRTOKEN_OPEN_BRACKET, "EXPRTOKEN_OPEN_BRACKET"); 888 fTokenNames.put(EXPRTOKEN_CLOSE_BRACKET, "EXPRTOKEN_CLOSE_BRACKET"); 889 fTokenNames.put(EXPRTOKEN_PERIOD, "EXPRTOKEN_PERIOD"); 890 fTokenNames.put(EXPRTOKEN_DOUBLE_PERIOD, "EXPRTOKEN_DOUBLE_PERIOD"); 891 fTokenNames.put(EXPRTOKEN_ATSIGN, "EXPRTOKEN_ATSIGN"); 892 fTokenNames.put(EXPRTOKEN_COMMA, "EXPRTOKEN_COMMA"); 893 fTokenNames.put(EXPRTOKEN_DOUBLE_COLON, "EXPRTOKEN_DOUBLE_COLON"); 894 fTokenNames.put(EXPRTOKEN_NAMETEST_ANY, "EXPRTOKEN_NAMETEST_ANY"); 895 fTokenNames.put(EXPRTOKEN_NAMETEST_NAMESPACE, "EXPRTOKEN_NAMETEST_NAMESPACE"); 896 fTokenNames.put(EXPRTOKEN_NAMETEST_QNAME, "EXPRTOKEN_NAMETEST_QNAME"); 897 fTokenNames.put(EXPRTOKEN_NODETYPE_COMMENT, "EXPRTOKEN_NODETYPE_COMMENT"); 898 fTokenNames.put(EXPRTOKEN_NODETYPE_TEXT, "EXPRTOKEN_NODETYPE_TEXT"); 899 fTokenNames.put(EXPRTOKEN_NODETYPE_PI, "EXPRTOKEN_NODETYPE_PI"); 900 fTokenNames.put(EXPRTOKEN_NODETYPE_NODE, "EXPRTOKEN_NODETYPE_NODE"); 901 fTokenNames.put(EXPRTOKEN_OPERATOR_AND, "EXPRTOKEN_OPERATOR_AND"); 902 fTokenNames.put(EXPRTOKEN_OPERATOR_OR, "EXPRTOKEN_OPERATOR_OR"); 903 fTokenNames.put(EXPRTOKEN_OPERATOR_MOD, "EXPRTOKEN_OPERATOR_MOD"); 904 fTokenNames.put(EXPRTOKEN_OPERATOR_DIV, "EXPRTOKEN_OPERATOR_DIV"); 905 fTokenNames.put(EXPRTOKEN_OPERATOR_MULT, "EXPRTOKEN_OPERATOR_MULT"); 906 fTokenNames.put(EXPRTOKEN_OPERATOR_SLASH, "EXPRTOKEN_OPERATOR_SLASH"); 907 fTokenNames.put(EXPRTOKEN_OPERATOR_DOUBLE_SLASH, "EXPRTOKEN_OPERATOR_DOUBLE_SLASH"); 908 fTokenNames.put(EXPRTOKEN_OPERATOR_UNION, "EXPRTOKEN_OPERATOR_UNION"); 909 fTokenNames.put(EXPRTOKEN_OPERATOR_PLUS, "EXPRTOKEN_OPERATOR_PLUS"); 910 fTokenNames.put(EXPRTOKEN_OPERATOR_MINUS, "EXPRTOKEN_OPERATOR_MINUS"); 911 fTokenNames.put(EXPRTOKEN_OPERATOR_EQUAL, "EXPRTOKEN_OPERATOR_EQUAL"); 912 fTokenNames.put(EXPRTOKEN_OPERATOR_NOT_EQUAL, "EXPRTOKEN_OPERATOR_NOT_EQUAL"); 913 fTokenNames.put(EXPRTOKEN_OPERATOR_LESS, "EXPRTOKEN_OPERATOR_LESS"); 914 fTokenNames.put(EXPRTOKEN_OPERATOR_LESS_EQUAL, "EXPRTOKEN_OPERATOR_LESS_EQUAL"); 915 fTokenNames.put(EXPRTOKEN_OPERATOR_GREATER, "EXPRTOKEN_OPERATOR_GREATER"); 916 fTokenNames.put(EXPRTOKEN_OPERATOR_GREATER_EQUAL, "EXPRTOKEN_OPERATOR_GREATER_EQUAL"); 917 fTokenNames.put(EXPRTOKEN_FUNCTION_NAME, "EXPRTOKEN_FUNCTION_NAME"); 918 fTokenNames.put(EXPRTOKEN_AXISNAME_ANCESTOR, "EXPRTOKEN_AXISNAME_ANCESTOR"); 919 fTokenNames.put(EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF, "EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF"); 920 fTokenNames.put(EXPRTOKEN_AXISNAME_ATTRIBUTE, "EXPRTOKEN_AXISNAME_ATTRIBUTE"); 921 fTokenNames.put(EXPRTOKEN_AXISNAME_CHILD, "EXPRTOKEN_AXISNAME_CHILD"); 922 fTokenNames.put(EXPRTOKEN_AXISNAME_DESCENDANT, "EXPRTOKEN_AXISNAME_DESCENDANT"); 923 fTokenNames.put(EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF, "EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF"); 924 fTokenNames.put(EXPRTOKEN_AXISNAME_FOLLOWING, "EXPRTOKEN_AXISNAME_FOLLOWING"); 925 fTokenNames.put(EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING, "EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING"); 926 fTokenNames.put(EXPRTOKEN_AXISNAME_NAMESPACE, "EXPRTOKEN_AXISNAME_NAMESPACE"); 927 fTokenNames.put(EXPRTOKEN_AXISNAME_PARENT, "EXPRTOKEN_AXISNAME_PARENT"); 928 fTokenNames.put(EXPRTOKEN_AXISNAME_PRECEDING, "EXPRTOKEN_AXISNAME_PRECEDING"); 929 fTokenNames.put(EXPRTOKEN_AXISNAME_PRECEDING_SIBLING, "EXPRTOKEN_AXISNAME_PRECEDING_SIBLING"); 930 fTokenNames.put(EXPRTOKEN_AXISNAME_SELF, "EXPRTOKEN_AXISNAME_SELF"); 931 fTokenNames.put(EXPRTOKEN_LITERAL, "EXPRTOKEN_LITERAL"); 932 fTokenNames.put(EXPRTOKEN_NUMBER, "EXPRTOKEN_NUMBER"); 933 fTokenNames.put(EXPRTOKEN_VARIABLE_REFERENCE, "EXPRTOKEN_VARIABLE_REFERENCE"); 934 } 935 936 // 937 // Public methods 938 // 939 940// public String getTokenName(int token) { 941// if (token < 0 || token >= fgTokenNames.length) 942// return null; 943// return fgTokenNames[token]; 944// } 945// 946 public String getTokenString(int token) { 947 return fTokenNames.get(token); 948 } 949 950 public void addToken(String tokenStr) { 951 Integer tokenInt = null; 952 for (Map.Entry<Integer, String> entry : fTokenNames.entrySet()) { 953 if (entry.getValue().equals(tokenStr)) { 954 tokenInt = entry.getKey(); 955 } 956 } 957 if (tokenInt == null) { 958 tokenInt = fTokenNames.size(); 959 fTokenNames.put(tokenInt, tokenStr); 960 } 961 addToken(tokenInt); 962 } 963 964 public void addToken(int token) { 965 try { 966 fTokens[fTokenCount] = token; 967 } catch (ArrayIndexOutOfBoundsException ex) { 968 int[] oldList = fTokens; 969 fTokens = new int[fTokenCount << 1]; 970 System.arraycopy(oldList, 0, fTokens, 0, fTokenCount); 971 fTokens[fTokenCount] = token; 972 } 973 fTokenCount++; 974 } 975// public int getTokenCount() { 976// return fTokenCount; 977// } 978// public int getToken(int tokenIndex) { 979// return fTokens[tokenIndex]; 980// } 981 982 /** 983 * Resets the current position to the head of the token list. 984 */ 985 public void rewind() { 986 fCurrentTokenIndex=0; 987 } 988 /** 989 * Returns true if the {@link #getNextToken()} method 990 * returns a valid token. 991 */ 992 public boolean hasMore() { 993 return fCurrentTokenIndex<fTokenCount; 994 } 995 /** 996 * Obtains the token at the current position, then advance 997 * the current position by one. 998 * 999 * If there's no such next token, this method throws 1000 * <tt>new XPathException("c-general-xpath");</tt>. 1001 */ 1002 public int nextToken() throws XPathException { 1003 if( fCurrentTokenIndex==fTokenCount ) 1004 throw new XPathException("c-general-xpath"); 1005 return fTokens[fCurrentTokenIndex++]; 1006 } 1007 /** 1008 * Obtains the token at the current position, without advancing 1009 * the current position. 1010 * 1011 * If there's no such next token, this method throws 1012 * <tt>new XPathException("c-general-xpath");</tt>. 1013 */ 1014 public int peekToken() throws XPathException { 1015 if( fCurrentTokenIndex==fTokenCount ) 1016 throw new XPathException("c-general-xpath"); 1017 return fTokens[fCurrentTokenIndex]; 1018 } 1019 /** 1020 * Obtains the token at the current position as a String. 1021 * 1022 * If there's no current token or if the current token 1023 * is not a string token, this method throws 1024 * <tt>new XPathException("c-general-xpath");</tt>. 1025 */ 1026 public String nextTokenAsString() throws XPathException { 1027 String s = getTokenString(nextToken()); 1028 if(s==null) throw new XPathException("c-general-xpath"); 1029 return s; 1030 } 1031 1032 public void dumpTokens() { 1033 //if (DUMP_TOKENS) { 1034 for (int i = 0; i < fTokenCount; i++) { 1035 switch (fTokens[i]) { 1036 case EXPRTOKEN_OPEN_PAREN: 1037 System.out.print("<OPEN_PAREN/>"); 1038 break; 1039 case EXPRTOKEN_CLOSE_PAREN: 1040 System.out.print("<CLOSE_PAREN/>"); 1041 break; 1042 case EXPRTOKEN_OPEN_BRACKET: 1043 System.out.print("<OPEN_BRACKET/>"); 1044 break; 1045 case EXPRTOKEN_CLOSE_BRACKET: 1046 System.out.print("<CLOSE_BRACKET/>"); 1047 break; 1048 case EXPRTOKEN_PERIOD: 1049 System.out.print("<PERIOD/>"); 1050 break; 1051 case EXPRTOKEN_DOUBLE_PERIOD: 1052 System.out.print("<DOUBLE_PERIOD/>"); 1053 break; 1054 case EXPRTOKEN_ATSIGN: 1055 System.out.print("<ATSIGN/>"); 1056 break; 1057 case EXPRTOKEN_COMMA: 1058 System.out.print("<COMMA/>"); 1059 break; 1060 case EXPRTOKEN_DOUBLE_COLON: 1061 System.out.print("<DOUBLE_COLON/>"); 1062 break; 1063 case EXPRTOKEN_NAMETEST_ANY: 1064 System.out.print("<NAMETEST_ANY/>"); 1065 break; 1066 case EXPRTOKEN_NAMETEST_NAMESPACE: 1067 System.out.print("<NAMETEST_NAMESPACE"); 1068 System.out.print(" prefix=\"" + getTokenString(fTokens[++i]) + "\""); 1069 System.out.print("/>"); 1070 break; 1071 case EXPRTOKEN_NAMETEST_QNAME: 1072 System.out.print("<NAMETEST_QNAME"); 1073 if (fTokens[++i] != -1) 1074 System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\""); 1075 System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\""); 1076 System.out.print("/>"); 1077 break; 1078 case EXPRTOKEN_NODETYPE_COMMENT: 1079 System.out.print("<NODETYPE_COMMENT/>"); 1080 break; 1081 case EXPRTOKEN_NODETYPE_TEXT: 1082 System.out.print("<NODETYPE_TEXT/>"); 1083 break; 1084 case EXPRTOKEN_NODETYPE_PI: 1085 System.out.print("<NODETYPE_PI/>"); 1086 break; 1087 case EXPRTOKEN_NODETYPE_NODE: 1088 System.out.print("<NODETYPE_NODE/>"); 1089 break; 1090 case EXPRTOKEN_OPERATOR_AND: 1091 System.out.print("<OPERATOR_AND/>"); 1092 break; 1093 case EXPRTOKEN_OPERATOR_OR: 1094 System.out.print("<OPERATOR_OR/>"); 1095 break; 1096 case EXPRTOKEN_OPERATOR_MOD: 1097 System.out.print("<OPERATOR_MOD/>"); 1098 break; 1099 case EXPRTOKEN_OPERATOR_DIV: 1100 System.out.print("<OPERATOR_DIV/>"); 1101 break; 1102 case EXPRTOKEN_OPERATOR_MULT: 1103 System.out.print("<OPERATOR_MULT/>"); 1104 break; 1105 case EXPRTOKEN_OPERATOR_SLASH: 1106 System.out.print("<OPERATOR_SLASH/>"); 1107 if (i + 1 < fTokenCount) { 1108 System.out.println(); 1109 System.out.print(" "); 1110 } 1111 break; 1112 case EXPRTOKEN_OPERATOR_DOUBLE_SLASH: 1113 System.out.print("<OPERATOR_DOUBLE_SLASH/>"); 1114 break; 1115 case EXPRTOKEN_OPERATOR_UNION: 1116 System.out.print("<OPERATOR_UNION/>"); 1117 break; 1118 case EXPRTOKEN_OPERATOR_PLUS: 1119 System.out.print("<OPERATOR_PLUS/>"); 1120 break; 1121 case EXPRTOKEN_OPERATOR_MINUS: 1122 System.out.print("<OPERATOR_MINUS/>"); 1123 break; 1124 case EXPRTOKEN_OPERATOR_EQUAL: 1125 System.out.print("<OPERATOR_EQUAL/>"); 1126 break; 1127 case EXPRTOKEN_OPERATOR_NOT_EQUAL: 1128 System.out.print("<OPERATOR_NOT_EQUAL/>"); 1129 break; 1130 case EXPRTOKEN_OPERATOR_LESS: 1131 System.out.print("<OPERATOR_LESS/>"); 1132 break; 1133 case EXPRTOKEN_OPERATOR_LESS_EQUAL: 1134 System.out.print("<OPERATOR_LESS_EQUAL/>"); 1135 break; 1136 case EXPRTOKEN_OPERATOR_GREATER: 1137 System.out.print("<OPERATOR_GREATER/>"); 1138 break; 1139 case EXPRTOKEN_OPERATOR_GREATER_EQUAL: 1140 System.out.print("<OPERATOR_GREATER_EQUAL/>"); 1141 break; 1142 case EXPRTOKEN_FUNCTION_NAME: 1143 System.out.print("<FUNCTION_NAME"); 1144 if (fTokens[++i] != -1) 1145 System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\""); 1146 System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\""); 1147 System.out.print("/>"); 1148 break; 1149 case EXPRTOKEN_AXISNAME_ANCESTOR: 1150 System.out.print("<AXISNAME_ANCESTOR/>"); 1151 break; 1152 case EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF: 1153 System.out.print("<AXISNAME_ANCESTOR_OR_SELF/>"); 1154 break; 1155 case EXPRTOKEN_AXISNAME_ATTRIBUTE: 1156 System.out.print("<AXISNAME_ATTRIBUTE/>"); 1157 break; 1158 case EXPRTOKEN_AXISNAME_CHILD: 1159 System.out.print("<AXISNAME_CHILD/>"); 1160 break; 1161 case EXPRTOKEN_AXISNAME_DESCENDANT: 1162 System.out.print("<AXISNAME_DESCENDANT/>"); 1163 break; 1164 case EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF: 1165 System.out.print("<AXISNAME_DESCENDANT_OR_SELF/>"); 1166 break; 1167 case EXPRTOKEN_AXISNAME_FOLLOWING: 1168 System.out.print("<AXISNAME_FOLLOWING/>"); 1169 break; 1170 case EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING: 1171 System.out.print("<AXISNAME_FOLLOWING_SIBLING/>"); 1172 break; 1173 case EXPRTOKEN_AXISNAME_NAMESPACE: 1174 System.out.print("<AXISNAME_NAMESPACE/>"); 1175 break; 1176 case EXPRTOKEN_AXISNAME_PARENT: 1177 System.out.print("<AXISNAME_PARENT/>"); 1178 break; 1179 case EXPRTOKEN_AXISNAME_PRECEDING: 1180 System.out.print("<AXISNAME_PRECEDING/>"); 1181 break; 1182 case EXPRTOKEN_AXISNAME_PRECEDING_SIBLING: 1183 System.out.print("<AXISNAME_PRECEDING_SIBLING/>"); 1184 break; 1185 case EXPRTOKEN_AXISNAME_SELF: 1186 System.out.print("<AXISNAME_SELF/>"); 1187 break; 1188 case EXPRTOKEN_LITERAL: 1189 System.out.print("<LITERAL"); 1190 System.out.print(" value=\"" + getTokenString(fTokens[++i]) + "\""); 1191 System.out.print("/>"); 1192 break; 1193 case EXPRTOKEN_NUMBER: 1194 System.out.print("<NUMBER"); 1195 System.out.print(" whole=\"" + getTokenString(fTokens[++i]) + "\""); 1196 System.out.print(" part=\"" + getTokenString(fTokens[++i]) + "\""); 1197 System.out.print("/>"); 1198 break; 1199 case EXPRTOKEN_VARIABLE_REFERENCE: 1200 System.out.print("<VARIABLE_REFERENCE"); 1201 if (fTokens[++i] != -1) 1202 System.out.print(" prefix=\"" + getTokenString(fTokens[i]) + "\""); 1203 System.out.print(" localpart=\"" + getTokenString(fTokens[++i]) + "\""); 1204 System.out.print("/>"); 1205 break; 1206 default: 1207 System.out.println("<???/>"); 1208 } 1209 } 1210 System.out.println(); 1211 //} 1212 } 1213 1214 } // class Tokens 1215 1216 /** 1217 * @xerces.internal 1218 * 1219 * @author Glenn Marcy, IBM 1220 * @author Andy Clark, IBM 1221 * 1222 */ 1223 private static class Scanner { 1224 1225 /** 1226 * 7-bit ASCII subset 1227 * 1228 * 0 1 2 3 4 5 6 7 8 9 A B C D E F 1229 * 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0 1230 * 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1231 * SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2 1232 * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3 1233 * @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4 1234 * P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5 1235 * `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6 1236 * p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7 1237 */ 1238 private static final byte 1239 CHARTYPE_INVALID = 0, // invalid XML character 1240 CHARTYPE_OTHER = 1, // not special - one of "#%&;?\^`{}~" or DEL 1241 CHARTYPE_WHITESPACE = 2, // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20) 1242 CHARTYPE_EXCLAMATION = 3, // '!' (0x21) 1243 CHARTYPE_QUOTE = 4, // '\"' or '\'' (0x22 and 0x27) 1244 CHARTYPE_DOLLAR = 5, // '$' (0x24) 1245 CHARTYPE_OPEN_PAREN = 6, // '(' (0x28) 1246 CHARTYPE_CLOSE_PAREN = 7, // ')' (0x29) 1247 CHARTYPE_STAR = 8, // '*' (0x2A) 1248 CHARTYPE_PLUS = 9, // '+' (0x2B) 1249 CHARTYPE_COMMA = 10, // ',' (0x2C) 1250 CHARTYPE_MINUS = 11, // '-' (0x2D) 1251 CHARTYPE_PERIOD = 12, // '.' (0x2E) 1252 CHARTYPE_SLASH = 13, // '/' (0x2F) 1253 CHARTYPE_DIGIT = 14, // '0'-'9' (0x30 to 0x39) 1254 CHARTYPE_COLON = 15, // ':' (0x3A) 1255 CHARTYPE_LESS = 16, // '<' (0x3C) 1256 CHARTYPE_EQUAL = 17, // '=' (0x3D) 1257 CHARTYPE_GREATER = 18, // '>' (0x3E) 1258 CHARTYPE_ATSIGN = 19, // '@' (0x40) 1259 CHARTYPE_LETTER = 20, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A) 1260 CHARTYPE_OPEN_BRACKET = 21, // '[' (0x5B) 1261 CHARTYPE_CLOSE_BRACKET = 22, // ']' (0x5D) 1262 CHARTYPE_UNDERSCORE = 23, // '_' (0x5F) 1263 CHARTYPE_UNION = 24, // '|' (0x7C) 1264 CHARTYPE_NONASCII = 25; // Non-ASCII Unicode codepoint (>= 0x80) 1265 1266 private static final byte[] fASCIICharMap = { 1267 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 0, 0, 1268 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1269 2, 3, 4, 1, 5, 1, 1, 4, 6, 7, 8, 9, 10, 11, 12, 13, 1270 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 1, 16, 17, 18, 1, 1271 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 1272 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 1, 22, 1, 23, 1273 1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 1274 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 1, 24, 1, 1, 1 1275 }; 1276 1277 /** 1278 * Symbol literals 1279 */ 1280 1281 // 1282 // Data 1283 // 1284 1285 /** Symbol table. */ 1286 private SymbolTable fSymbolTable; 1287 1288 // symbols 1289 1290 private static final String fAndSymbol = "and".intern(); 1291 private static final String fOrSymbol = "or".intern(); 1292 private static final String fModSymbol = "mod".intern(); 1293 private static final String fDivSymbol = "div".intern(); 1294 1295 private static final String fCommentSymbol = "comment".intern(); 1296 private static final String fTextSymbol = "text".intern(); 1297 private static final String fPISymbol = "processing-instruction".intern(); 1298 private static final String fNodeSymbol = "node".intern(); 1299 1300 private static final String fAncestorSymbol = "ancestor".intern(); 1301 private static final String fAncestorOrSelfSymbol = "ancestor-or-self".intern(); 1302 private static final String fAttributeSymbol = "attribute".intern(); 1303 private static final String fChildSymbol = "child".intern(); 1304 private static final String fDescendantSymbol = "descendant".intern(); 1305 private static final String fDescendantOrSelfSymbol = "descendant-or-self".intern(); 1306 private static final String fFollowingSymbol = "following".intern(); 1307 private static final String fFollowingSiblingSymbol = "following-sibling".intern(); 1308 private static final String fNamespaceSymbol = "namespace".intern(); 1309 private static final String fParentSymbol = "parent".intern(); 1310 private static final String fPrecedingSymbol = "preceding".intern(); 1311 private static final String fPrecedingSiblingSymbol = "preceding-sibling".intern(); 1312 private static final String fSelfSymbol = "self".intern(); 1313 1314 // 1315 // Constructors 1316 // 1317 1318 /** Constructs an XPath expression scanner. */ 1319 public Scanner(SymbolTable symbolTable) { 1320 1321 // save pool and tokens 1322 fSymbolTable = symbolTable; 1323 1324 } // <init>(SymbolTable) 1325 1326 /** 1327 * 1328 */ 1329 public boolean scanExpr(SymbolTable symbolTable, 1330 XPath.Tokens tokens, String data, 1331 int currentOffset, int endOffset) 1332 throws XPathException { 1333 1334 int nameOffset; 1335 String nameHandle, prefixHandle; 1336 boolean starIsMultiplyOperator = false; 1337 int ch; 1338 1339 while (true) { 1340 if (currentOffset == endOffset) { 1341 break; 1342 } 1343 ch = data.charAt(currentOffset); 1344 // 1345 // [39] ExprWhitespace ::= S 1346 // 1347 while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) { 1348 if (++currentOffset == endOffset) { 1349 break; 1350 } 1351 ch = data.charAt(currentOffset); 1352 } 1353 if (currentOffset == endOffset) { 1354 break; 1355 } 1356 // 1357 // [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::' 1358 // | NameTest | NodeType | Operator | FunctionName 1359 // | AxisName | Literal | Number | VariableReference 1360 // 1361 byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII : fASCIICharMap[ch]; 1362 switch (chartype) { 1363 case CHARTYPE_OPEN_PAREN: // '(' 1364 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN); 1365 starIsMultiplyOperator = false; 1366 if (++currentOffset == endOffset) { 1367 break; 1368 } 1369 break; 1370 case CHARTYPE_CLOSE_PAREN: // ')' 1371 addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_PAREN); 1372 starIsMultiplyOperator = true; 1373 if (++currentOffset == endOffset) { 1374 break; 1375 } 1376 break; 1377 case CHARTYPE_OPEN_BRACKET: // '[' 1378 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_BRACKET); 1379 starIsMultiplyOperator = false; 1380 if (++currentOffset == endOffset) { 1381 break; 1382 } 1383 break; 1384 case CHARTYPE_CLOSE_BRACKET: // ']' 1385 addToken(tokens, XPath.Tokens.EXPRTOKEN_CLOSE_BRACKET); 1386 starIsMultiplyOperator = true; 1387 if (++currentOffset == endOffset) { 1388 break; 1389 } 1390 break; 1391 // 1392 // [30] Number ::= Digits ('.' Digits?)? | '.' Digits 1393 // ^^^^^^^^^^ 1394 // 1395 case CHARTYPE_PERIOD: // '.', '..' or '.' Digits 1396 if (currentOffset + 1 == endOffset) { 1397 addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD); 1398 starIsMultiplyOperator = true; 1399 currentOffset++; 1400 break; 1401 } 1402 ch = data.charAt(currentOffset + 1); 1403 if (ch == '.') { // '..' 1404 addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_PERIOD); 1405 starIsMultiplyOperator = true; 1406 currentOffset += 2; 1407 } else if (ch >= '0' && ch <= '9') { 1408 addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER); 1409 starIsMultiplyOperator = true; 1410 currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/); 1411 } else if (ch == '/') { 1412 addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD); 1413 starIsMultiplyOperator = true; 1414 currentOffset++; 1415 } else if (ch == '|') { 1416 addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD); 1417 starIsMultiplyOperator = true; 1418 currentOffset++; 1419 break; 1420 } else if (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) { 1421 // this is legal if the next token is non-existent or | 1422 do { 1423 if (++currentOffset == endOffset) { 1424 break; 1425 } 1426 ch = data.charAt(currentOffset); 1427 } while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D); 1428 if (currentOffset == endOffset || ch == '|' || ch == '/') { 1429 addToken(tokens, XPath.Tokens.EXPRTOKEN_PERIOD); 1430 starIsMultiplyOperator = true; 1431 break; 1432 } 1433 throw new XPathException ("c-general-xpath"); 1434 } else { // '.' 1435 throw new XPathException ("c-general-xpath"); 1436 } 1437 if (currentOffset == endOffset) { 1438 break; 1439 } 1440 break; 1441 case CHARTYPE_ATSIGN: // '@' 1442 addToken(tokens, XPath.Tokens.EXPRTOKEN_ATSIGN); 1443 starIsMultiplyOperator = false; 1444 if (++currentOffset == endOffset) { 1445 break; 1446 } 1447 break; 1448 case CHARTYPE_COMMA: // ',' 1449 addToken(tokens, XPath.Tokens.EXPRTOKEN_COMMA); 1450 starIsMultiplyOperator = false; 1451 if (++currentOffset == endOffset) { 1452 break; 1453 } 1454 break; 1455 case CHARTYPE_COLON: // '::' 1456 if (++currentOffset == endOffset) { 1457 // System.out.println("abort 1a"); 1458 return false; // REVISIT 1459 } 1460 ch = data.charAt(currentOffset); 1461 if (ch != ':') { 1462 // System.out.println("abort 1b"); 1463 return false; // REVISIT 1464 } 1465 addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON); 1466 starIsMultiplyOperator = false; 1467 if (++currentOffset == endOffset) { 1468 break; 1469 } 1470 break; 1471 case CHARTYPE_SLASH: // '/' and '//' 1472 if (++currentOffset == endOffset) { 1473 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH); 1474 starIsMultiplyOperator = false; 1475 break; 1476 } 1477 ch = data.charAt(currentOffset); 1478 if (ch == '/') { // '//' 1479 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH); 1480 starIsMultiplyOperator = false; 1481 if (++currentOffset == endOffset) { 1482 break; 1483 } 1484 } else { 1485 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH); 1486 starIsMultiplyOperator = false; 1487 } 1488 break; 1489 case CHARTYPE_UNION: // '|' 1490 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_UNION); 1491 starIsMultiplyOperator = false; 1492 if (++currentOffset == endOffset) { 1493 break; 1494 } 1495 break; 1496 case CHARTYPE_PLUS: // '+' 1497 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_PLUS); 1498 starIsMultiplyOperator = false; 1499 if (++currentOffset == endOffset) { 1500 break; 1501 } 1502 break; 1503 case CHARTYPE_MINUS: // '-' 1504 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MINUS); 1505 starIsMultiplyOperator = false; 1506 if (++currentOffset == endOffset) { 1507 break; 1508 } 1509 break; 1510 case CHARTYPE_EQUAL: // '=' 1511 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_EQUAL); 1512 starIsMultiplyOperator = false; 1513 if (++currentOffset == endOffset) { 1514 break; 1515 } 1516 break; 1517 case CHARTYPE_EXCLAMATION: // '!=' 1518 if (++currentOffset == endOffset) { 1519 // System.out.println("abort 2a"); 1520 return false; // REVISIT 1521 } 1522 ch = data.charAt(currentOffset); 1523 if (ch != '=') { 1524 // System.out.println("abort 2b"); 1525 return false; // REVISIT 1526 } 1527 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_NOT_EQUAL); 1528 starIsMultiplyOperator = false; 1529 if (++currentOffset == endOffset) { 1530 break; 1531 } 1532 break; 1533 case CHARTYPE_LESS: // '<' and '<=' 1534 if (++currentOffset == endOffset) { 1535 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS); 1536 starIsMultiplyOperator = false; 1537 break; 1538 } 1539 ch = data.charAt(currentOffset); 1540 if (ch == '=') { // '<=' 1541 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS_EQUAL); 1542 starIsMultiplyOperator = false; 1543 if (++currentOffset == endOffset) { 1544 break; 1545 } 1546 } else { 1547 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_LESS); 1548 starIsMultiplyOperator = false; 1549 } 1550 break; 1551 case CHARTYPE_GREATER: // '>' and '>=' 1552 if (++currentOffset == endOffset) { 1553 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER); 1554 starIsMultiplyOperator = false; 1555 break; 1556 } 1557 ch = data.charAt(currentOffset); 1558 if (ch == '=') { // '>=' 1559 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER_EQUAL); 1560 starIsMultiplyOperator = false; 1561 if (++currentOffset == endOffset) { 1562 break; 1563 } 1564 } else { 1565 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_GREATER); 1566 starIsMultiplyOperator = false; 1567 } 1568 break; 1569 // 1570 // [29] Literal ::= '"' [^"]* '"' | "'" [^']* "'" 1571 // 1572 case CHARTYPE_QUOTE: // '\"' or '\'' 1573 int qchar = ch; 1574 if (++currentOffset == endOffset) { 1575 // System.out.println("abort 2c"); 1576 return false; // REVISIT 1577 } 1578 ch = data.charAt(currentOffset); 1579 int litOffset = currentOffset; 1580 while (ch != qchar) { 1581 if (++currentOffset == endOffset) { 1582 // System.out.println("abort 2d"); 1583 return false; // REVISIT 1584 } 1585 ch = data.charAt(currentOffset); 1586 } 1587 int litLength = currentOffset - litOffset; 1588 addToken(tokens, XPath.Tokens.EXPRTOKEN_LITERAL); 1589 starIsMultiplyOperator = true; 1590 tokens.addToken(symbolTable.addSymbol(data.substring(litOffset, litOffset + litLength))); 1591 if (++currentOffset == endOffset) { 1592 break; 1593 } 1594 break; 1595 // 1596 // [30] Number ::= Digits ('.' Digits?)? | '.' Digits 1597 // [31] Digits ::= [0-9]+ 1598 // 1599 case CHARTYPE_DIGIT: 1600 addToken(tokens, XPath.Tokens.EXPRTOKEN_NUMBER); 1601 starIsMultiplyOperator = true; 1602 currentOffset = scanNumber(tokens, data, endOffset, currentOffset/*, encoding*/); 1603 break; 1604 // 1605 // [36] VariableReference ::= '$' QName 1606 // 1607 case CHARTYPE_DOLLAR: 1608 if (++currentOffset == endOffset) { 1609 // System.out.println("abort 3a"); 1610 return false; // REVISIT 1611 } 1612 nameOffset = currentOffset; 1613 currentOffset = scanNCName(data, endOffset, currentOffset); 1614 if (currentOffset == nameOffset) { 1615 // System.out.println("abort 3b"); 1616 return false; // REVISIT 1617 } 1618 if (currentOffset < endOffset) { 1619 ch = data.charAt(currentOffset); 1620 } 1621 else { 1622 ch = -1; 1623 } 1624 nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset)); 1625 if (ch != ':') { 1626 prefixHandle = XMLSymbols.EMPTY_STRING; 1627 } else { 1628 prefixHandle = nameHandle; 1629 if (++currentOffset == endOffset) { 1630 // System.out.println("abort 4a"); 1631 return false; // REVISIT 1632 } 1633 nameOffset = currentOffset; 1634 currentOffset = scanNCName(data, endOffset, currentOffset); 1635 if (currentOffset == nameOffset) { 1636 // System.out.println("abort 4b"); 1637 return false; // REVISIT 1638 } 1639 if (currentOffset < endOffset) { 1640 ch = data.charAt(currentOffset); 1641 } 1642 else { 1643 ch = -1; 1644 } 1645 nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset)); 1646 } 1647 addToken(tokens, XPath.Tokens.EXPRTOKEN_VARIABLE_REFERENCE); 1648 starIsMultiplyOperator = true; 1649 tokens.addToken(prefixHandle); 1650 tokens.addToken(nameHandle); 1651 break; 1652 // 1653 // [37] NameTest ::= '*' | NCName ':' '*' | QName 1654 // [34] MultiplyOperator ::= '*' 1655 // 1656 case CHARTYPE_STAR: // '*' 1657 // 1658 // 3.7 Lexical Structure 1659 // 1660 // If there is a preceding token and the preceding token is not one of @, ::, (, [, , or 1661 // an Operator, then a * must be recognized as a MultiplyOperator. 1662 // 1663 // Otherwise, the token must not be recognized as a MultiplyOperator. 1664 // 1665 if (starIsMultiplyOperator) { 1666 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MULT); 1667 starIsMultiplyOperator = false; 1668 } else { 1669 addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_ANY); 1670 starIsMultiplyOperator = true; 1671 } 1672 if (++currentOffset == endOffset) { 1673 break; 1674 } 1675 break; 1676 // 1677 // NCName, QName and non-terminals 1678 // 1679 case CHARTYPE_NONASCII: // possibly a valid non-ascii 'Letter' (BaseChar | Ideographic) 1680 case CHARTYPE_LETTER: 1681 case CHARTYPE_UNDERSCORE: 1682 // 1683 // 3.7 Lexical Structure 1684 // 1685 // If there is a preceding token and the preceding token is not one of @, ::, (, [, , or 1686 // an Operator, then an NCName must be recognized as an OperatorName. 1687 // 1688 // If the character following an NCName (possibly after intervening ExprWhitespace) is (, 1689 // then the token must be recognized as a NodeType or a FunctionName. 1690 // 1691 // If the two characters following an NCName (possibly after intervening ExprWhitespace) 1692 // are ::, then the token must be recognized as an AxisName. 1693 // 1694 // Otherwise, the token must not be recognized as an OperatorName, a NodeType, a 1695 // FunctionName, or an AxisName. 1696 // 1697 // [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div' 1698 // [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node' 1699 // [35] FunctionName ::= QName - NodeType 1700 // [6] AxisName ::= (see above) 1701 // 1702 // [37] NameTest ::= '*' | NCName ':' '*' | QName 1703 // [5] NCName ::= (Letter | '_') (NCNameChar)* 1704 // [?] NCNameChar ::= Letter | Digit | '.' | '-' | '_' (ascii subset of 'NCNameChar') 1705 // [?] QName ::= (NCName ':')? NCName 1706 // [?] Letter ::= [A-Za-z] (ascii subset of 'Letter') 1707 // [?] Digit ::= [0-9] (ascii subset of 'Digit') 1708 // 1709 nameOffset = currentOffset; 1710 currentOffset = scanNCName(data, endOffset, currentOffset); 1711 if (currentOffset == nameOffset) { 1712 // System.out.println("abort 4c"); 1713 return false; // REVISIT 1714 } 1715 if (currentOffset < endOffset) { 1716 ch = data.charAt(currentOffset); 1717 } 1718 else { 1719 ch = -1; 1720 } 1721 nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset)); 1722 boolean isNameTestNCName = false; 1723 boolean isAxisName = false; 1724 prefixHandle = XMLSymbols.EMPTY_STRING; 1725 if (ch == ':') { 1726 if (++currentOffset == endOffset) { 1727 // System.out.println("abort 5"); 1728 return false; // REVISIT 1729 } 1730 ch = data.charAt(currentOffset); 1731 if (ch == '*') { 1732 if (++currentOffset < endOffset) { 1733 ch = data.charAt(currentOffset); 1734 } 1735 isNameTestNCName = true; 1736 } else if (ch == ':') { 1737 if (++currentOffset < endOffset) { 1738 ch = data.charAt(currentOffset); 1739 } 1740 isAxisName = true; 1741 } else { 1742 prefixHandle = nameHandle; 1743 nameOffset = currentOffset; 1744 currentOffset = scanNCName(data, endOffset, currentOffset); 1745 if (currentOffset == nameOffset) { 1746 // System.out.println("abort 5b"); 1747 return false; // REVISIT 1748 } 1749 if (currentOffset < endOffset) { 1750 ch = data.charAt(currentOffset); 1751 } 1752 else { 1753 ch = -1; 1754 } 1755 nameHandle = symbolTable.addSymbol(data.substring(nameOffset, currentOffset)); 1756 } 1757 } 1758 // 1759 // [39] ExprWhitespace ::= S 1760 // 1761 while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) { 1762 if (++currentOffset == endOffset) { 1763 break; 1764 } 1765 ch = data.charAt(currentOffset); 1766 } 1767 // 1768 // If there is a preceding token and the preceding token is not one of @, ::, (, [, , or 1769 // an Operator, then an NCName must be recognized as an OperatorName. 1770 // 1771 if (starIsMultiplyOperator) { 1772 if (nameHandle == fAndSymbol) { 1773 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_AND); 1774 starIsMultiplyOperator = false; 1775 } else if (nameHandle == fOrSymbol) { 1776 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_OR); 1777 starIsMultiplyOperator = false; 1778 } else if (nameHandle == fModSymbol) { 1779 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_MOD); 1780 starIsMultiplyOperator = false; 1781 } else if (nameHandle == fDivSymbol) { 1782 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPERATOR_DIV); 1783 starIsMultiplyOperator = false; 1784 } else { 1785 // System.out.println("abort 6"); 1786 return false; // REVISIT 1787 } 1788 if (isNameTestNCName) { 1789 // System.out.println("abort 7"); 1790 return false; // REVISIT - NCName:* where an OperatorName is required 1791 } else if (isAxisName) { 1792 // System.out.println("abort 8"); 1793 return false; // REVISIT - AxisName:: where an OperatorName is required 1794 } 1795 break; 1796 } 1797 // 1798 // If the character following an NCName (possibly after intervening ExprWhitespace) is (, 1799 // then the token must be recognized as a NodeType or a FunctionName. 1800 // 1801 if (ch == '(' && !isNameTestNCName && !isAxisName) { 1802 if (nameHandle == fCommentSymbol) { 1803 addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_COMMENT); 1804 } else if (nameHandle == fTextSymbol) { 1805 addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_TEXT); 1806 } else if (nameHandle == fPISymbol) { 1807 addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_PI); 1808 } else if (nameHandle == fNodeSymbol) { 1809 addToken(tokens, XPath.Tokens.EXPRTOKEN_NODETYPE_NODE); 1810 } else { 1811 addToken(tokens, XPath.Tokens.EXPRTOKEN_FUNCTION_NAME); 1812 tokens.addToken(prefixHandle); 1813 tokens.addToken(nameHandle); 1814 } 1815 addToken(tokens, XPath.Tokens.EXPRTOKEN_OPEN_PAREN); 1816 starIsMultiplyOperator = false; 1817 if (++currentOffset == endOffset) { 1818 break; 1819 } 1820 break; 1821 } 1822 // 1823 // If the two characters following an NCName (possibly after intervening ExprWhitespace) 1824 // are ::, then the token must be recognized as an AxisName. 1825 // 1826 if (isAxisName || 1827 (ch == ':' && currentOffset + 1 < endOffset && 1828 data.charAt(currentOffset + 1) == ':')) { 1829 if (nameHandle == fAncestorSymbol) { 1830 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR); 1831 } else if (nameHandle == fAncestorOrSelfSymbol) { 1832 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ANCESTOR_OR_SELF); 1833 } else if (nameHandle == fAttributeSymbol) { 1834 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE); 1835 } else if (nameHandle == fChildSymbol) { 1836 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD); 1837 } else if (nameHandle == fDescendantSymbol) { 1838 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT); 1839 } else if (nameHandle == fDescendantOrSelfSymbol) { 1840 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_DESCENDANT_OR_SELF); 1841 } else if (nameHandle == fFollowingSymbol) { 1842 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING); 1843 } else if (nameHandle == fFollowingSiblingSymbol) { 1844 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_FOLLOWING_SIBLING); 1845 } else if (nameHandle == fNamespaceSymbol) { 1846 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_NAMESPACE); 1847 } else if (nameHandle == fParentSymbol) { 1848 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PARENT); 1849 } else if (nameHandle == fPrecedingSymbol) { 1850 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING); 1851 } else if (nameHandle == fPrecedingSiblingSymbol) { 1852 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_PRECEDING_SIBLING); 1853 } else if (nameHandle == fSelfSymbol) { 1854 addToken(tokens, XPath.Tokens.EXPRTOKEN_AXISNAME_SELF); 1855 } else { 1856 // System.out.println("abort 9"); 1857 return false; // REVISIT 1858 } 1859 if (isNameTestNCName) { 1860 // System.out.println("abort 10"); 1861 return false; // REVISIT - "NCName:* ::" where "AxisName ::" is required 1862 } 1863 addToken(tokens, XPath.Tokens.EXPRTOKEN_DOUBLE_COLON); 1864 starIsMultiplyOperator = false; 1865 if (!isAxisName) { 1866 currentOffset++; 1867 if (++currentOffset == endOffset) { 1868 break; 1869 } 1870 } 1871 break; 1872 } 1873 // 1874 // Otherwise, the token must not be recognized as an OperatorName, a NodeType, a 1875 // FunctionName, or an AxisName. 1876 // 1877 if (isNameTestNCName) { 1878 addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE); 1879 starIsMultiplyOperator = true; 1880 tokens.addToken(nameHandle); 1881 } else { 1882 addToken(tokens, XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME); 1883 starIsMultiplyOperator = true; 1884 tokens.addToken(prefixHandle); 1885 tokens.addToken(nameHandle); 1886 } 1887 break; 1888 default: 1889 // CHARTYPE_INVALID or CHARTYPE_OTHER 1890 // We're not expecting to find either of these in a valid expression. 1891 return false; 1892 } 1893 } 1894 if (XPath.Tokens.DUMP_TOKENS) { 1895 tokens.dumpTokens(); 1896 } 1897 return true; 1898 } 1899 // 1900 // [5] NCName ::= (Letter | '_') (NCNameChar)* 1901 // [6] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender 1902 // 1903 int scanNCName(String data, int endOffset, int currentOffset) { 1904 int ch = data.charAt(currentOffset); 1905 if (ch >= 0x80) { 1906 if (!XMLChar.isNameStart(ch)) 1907 /*** // REVISIT: Make sure this is a negation. *** 1908 if ((XMLCharacterProperties.fgCharFlags[ch] & 1909 XMLCharacterProperties.E_InitialNameCharFlag) == 0) 1910 /***/ 1911 { 1912 return currentOffset; 1913 } 1914 } 1915 else { 1916 byte chartype = fASCIICharMap[ch]; 1917 if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_UNDERSCORE) { 1918 return currentOffset; 1919 } 1920 } 1921 while (++currentOffset < endOffset) { 1922 ch = data.charAt(currentOffset); 1923 if (ch >= 0x80) { 1924 if (!XMLChar.isName(ch)) 1925 /*** // REVISIT: Make sure this is a negation. *** 1926 if ((XMLCharacterProperties.fgCharFlags[ch] & 1927 XMLCharacterProperties.E_NameCharFlag) == 0) 1928 /***/ 1929 { 1930 break; 1931 } 1932 } 1933 else { 1934 byte chartype = fASCIICharMap[ch]; 1935 if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_DIGIT && 1936 chartype != CHARTYPE_PERIOD && chartype != CHARTYPE_MINUS && 1937 chartype != CHARTYPE_UNDERSCORE) 1938 { 1939 break; 1940 } 1941 } 1942 } 1943 return currentOffset; 1944 } 1945 // 1946 // [30] Number ::= Digits ('.' Digits?)? | '.' Digits 1947 // [31] Digits ::= [0-9]+ 1948 // 1949 private int scanNumber(XPath.Tokens tokens, String/*byte[]*/ data, int endOffset, int currentOffset/*, EncodingSupport encoding*/) { 1950 int ch = data.charAt(currentOffset); 1951 int whole = 0; 1952 int part = 0; 1953 while (ch >= '0' && ch <= '9') { 1954 whole = (whole * 10) + (ch - '0'); 1955 if (++currentOffset == endOffset) { 1956 break; 1957 } 1958 ch = data.charAt(currentOffset); 1959 } 1960 if (ch == '.') { 1961 if (++currentOffset < endOffset) { 1962 /** int start = currentOffset; **/ 1963 ch = data.charAt(currentOffset); 1964 while (ch >= '0' && ch <= '9') { 1965 part = (part * 10) + (ch - '0'); 1966 if (++currentOffset == endOffset) { 1967 break; 1968 } 1969 ch = data.charAt(currentOffset); 1970 } 1971 if (part != 0) { 1972 /*** 1973 part = tokens.addSymbol(data, start, currentOffset - start, encoding); 1974 /***/ 1975 throw new RuntimeException("find a solution!"); 1976 //part = fStringPool.addSymbol(data.substring(start, currentOffset)); 1977 /***/ 1978 } 1979 } 1980 } 1981 tokens.addToken(whole); 1982 tokens.addToken(part); 1983 return currentOffset; 1984 } 1985 1986 // 1987 // Protected methods 1988 // 1989 1990 /** 1991 * This method adds the specified token to the token list. By 1992 * default, this method allows all tokens. However, subclasses 1993 * of the XPathExprScanner can override this method in order 1994 * to disallow certain tokens from being used in the scanned 1995 * XPath expression. This is a convenient way of allowing only 1996 * a subset of XPath. 1997 */ 1998 protected void addToken(XPath.Tokens tokens, int token) 1999 throws XPathException { 2000 tokens.addToken(token); 2001 } // addToken(int) 2002 2003 } // class Scanner 2004 2005 // 2006 // MAIN 2007 // 2008 2009 /** Main program entry. */ 2010 public static void main(String[] argv) throws Exception { 2011 2012 for (int i = 0; i < argv.length; i++) { 2013 final String expression = argv[i]; 2014 System.out.println("# XPath expression: \""+expression+'"'); 2015 try { 2016 SymbolTable symbolTable = new SymbolTable(); 2017 XPath xpath = new XPath(expression, symbolTable, null); 2018 System.out.println("expanded xpath: \""+xpath.toString()+'"'); 2019 } 2020 catch (XPathException e) { 2021 System.out.println("error: "+e.getMessage()); 2022 } 2023 } 2024 2025 } // main(String[]) 2026 2027} // class XPath 2028