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