001: /* Generated by Together */
002:
003: package com.rimfaxe.xml.xmlreader.xpath;
004:
005: import java.io.*;
006: import java.util.*;
007:
008: /** The root of the parse tree for an XPath expression. This is a
009: * participant in the Visitor Pattern [Gamma et al, #331]. You create
010: * an XPath object (which parses an XPath expression into a parse
011: * tree), create a Vistor object, and pass the visitor to the
012: * XPath.accept method.
013:
014: <blockquote><small> Copyright (C) 2002 Hewlett-Packard Company.
015: This file is part of Sparta, an XML Parser, DOM, and XPath library.
016: This library is free software; you can redistribute it and/or
017: modify it under the terms of the <a href="doc-files/LGPL.txt">GNU
018: Lesser General Public License</a> as published by the Free Software
019: Foundation; either version 2.1 of the License, or (at your option)
020: any later version. This library is distributed in the hope that it
021: will be useful, but WITHOUT ANY WARRANTY; without even the implied
022: warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
023: PURPOSE. </small></blockquote>
024: @version $Date: 2003/01/27 23:30:58 $ $Revision: 1.6 $
025: @author Eamonn O'Brien-Strain
026: */
027: public class XPath implements Cloneable {
028:
029: static private final int ASSERTION = 0;
030:
031: /** Create an XPath from some steps. steps.lenght must be >= 1 */
032: private XPath(boolean isAbsolute, Step[] steps) {
033: for (int i = 0; i < steps.length; ++i)
034: steps_.addElement(steps[i]);
035: absolute_ = isAbsolute;
036: string_ = null;
037: }
038:
039: /** Parse an XPath from a string. */
040: private XPath(String s) throws XPathException /*, IOException*/{
041: this (s, new StringReader(s));
042: }
043:
044: /** Parse an XPath from a character stream. */
045: private XPath(String s, Reader reader) throws XPathException {
046: try {
047: string_ = s;
048: StreamTokenizer toks = new StreamTokenizer(reader);
049: toks.ordinaryChar('/'); // '/' is not a comment
050: toks.ordinaryChar('.'); // '.' is not a part of a number
051: toks.wordChars(':', ':'); //Allow namespaces
052:
053: boolean multiLevel;
054: if (toks.nextToken() == '/') {
055: absolute_ = true;
056: if (toks.nextToken() == '/') {
057: multiLevel = true;
058: toks.nextToken();
059: } else
060: multiLevel = false;
061: } else {
062: absolute_ = false;
063: multiLevel = false;
064: }
065: //current token is first token of node_test production
066: steps_.push(new Step(this , multiLevel, toks));
067: //current token is token after last token of step production
068:
069: while (toks.ttype == '/') {
070: if (toks.nextToken() == '/') {
071: multiLevel = true;
072: toks.nextToken();
073: } else
074: multiLevel = false;
075: //current token is first token of node_test production
076: steps_.push(new Step(this , multiLevel, toks));
077: //current token is token after last token of step production
078: }
079:
080: if (toks.ttype != toks.TT_EOF)
081: throw new XPathException(this ,
082: "at end of XPATH expression", toks,
083: "end of expression");
084: } catch (IOException e) {
085: throw new XPathException(this , e);
086: }
087: if (ASSERTION >= 2)
088: if (!toString().equals(generateString()))
089: throw new Error("Postcondition failed");
090: }
091:
092: public String toString() {
093: if (string_ == null)
094: string_ = generateString();
095: return string_;
096: }
097:
098: private String generateString() {
099: StringBuffer result = new StringBuffer();
100: boolean first = true;
101: for (Enumeration i = steps_.elements(); i.hasMoreElements();) {
102: Step step = (Step) i.nextElement();
103: if (!first || absolute_) {
104: result.append('/');
105: if (step.isMultiLevel())
106: result.append('/');
107: }
108: result.append(step.toString());
109: first = false;
110: }
111: return result.toString();
112: }
113:
114: /** Does this path begin with a '/' or '//' ? */
115: public boolean isAbsolute() {
116: return absolute_;
117: }
118:
119: /** Does xpath evaluate to a string values (attribute values or
120: text() nodes)*/
121: public boolean isStringValue() {
122: Step lastStep = (Step) steps_.peek();
123: return lastStep.isStringValue();
124: }
125:
126: public Enumeration getSteps() {
127: return steps_.elements();
128: }
129:
130: /** Return the attribute name in a trailing [@attrName] predicate.
131: * For example if the Xpath expression was "/a/b[@p='pp']/c[@q]"
132: * then the indexing attribute name would be "q"*/
133: public String getIndexingAttrName() throws XPathException {
134: Step step = (Step) steps_.peek();
135: BooleanExpr predicate = step.getPredicate();
136: if (!(predicate instanceof AttrExistsExpr))
137: throw new XPathException(
138: this ,
139: "has no indexing attribute name (must end with predicate of the form [@attrName]");
140: return ((AttrExistsExpr) predicate).getAttrName();
141: }
142:
143: /** A one-level clone in which the steps list is cloned but not the steps objects themselves.
144: * It is OK for different XPaths to share Steps because they are immutable. */
145: public Object clone() {
146: try {
147: XPath result = (XPath) super .clone();
148: result.steps_ = (Stack) steps_.clone();
149: string_ = null;
150: return result;
151: } catch (CloneNotSupportedException e) {
152: throw new Error("Assertion failed");
153: }
154: }
155:
156: /**
157: *@link aggregation
158: * @associates <{Step}>
159: */
160: private Stack steps_ = new Stack();
161:
162: private/*final (JDK11 problems)*/
163: boolean absolute_;
164:
165: private String string_;
166:
167: ////////////////////////////////////////////////////////////////
168:
169: /** Return the xpath parse-tree object for this expression.
170: * This may be either a newly
171: * created object or a cached copy. */
172: static public XPath get(String xpathString) throws XPathException //, IOException
173: {
174: synchronized (cache_) {
175: XPath result = (XPath) cache_.get(xpathString);
176: if (result == null) {
177: result = new XPath(xpathString);
178: cache_.put(xpathString, result);
179: }
180: return result;
181: }
182: }
183:
184: /** Create an XPath from some steps.
185: The value of steps.length must be >= 1 */
186: static public XPath get(boolean isAbsolute, Step[] steps) {
187: XPath created = new XPath(isAbsolute, steps);
188: String xpathString = created.toString();
189: synchronized (cache_) {
190: XPath inCache = (XPath) cache_.get(xpathString);
191: if (inCache == null) {
192: cache_.put(xpathString, created);
193: return created;
194: } else
195: return inCache;
196:
197: }
198:
199: }
200:
201: /** Convenience function equivalent to get(xpathString).isStringValue() */
202: static public boolean isStringValue(String xpathString)
203: throws XPathException, IOException {
204: return get(xpathString).isStringValue();
205: }
206:
207: /** @associates XPath
208: * Warning grows without bound, no eviction.
209: * TODO: make LRU
210: */
211: static private Map cache_ = new HashMap();
212:
213: }
214:
215: // $Log: XPath.java,v $
216: // Revision 1.6 2003/01/27 23:30:58 yuhongx
217: // Replaced Hashtable with HashMap.
218: //
219: // Revision 1.5 2003/01/09 01:17:14 yuhongx
220: // Use JDK1.1 API to make code work with PersonalJava (use addElement()).
221: //
222: // Revision 1.4 2002/12/13 22:42:22 eobrain
223: // Fix javadoc.
224: //
225: // Revision 1.3 2002/12/13 18:09:34 eobrain
226: // Create XPath from a sequence of steps.
227: //
228: // Revision 1.2 2002/12/06 23:41:49 eobrain
229: // Add toString() which returns the original XPath.
230: //
231: // Revision 1.1.1.1 2002/08/19 05:04:02 eobrain
232: // import from HP Labs internal CVS
233: //
234: // Revision 1.9 2002/08/18 23:39:27 eob
235: // Add copyright and other formatting and commenting in preparation for
236: // release to SourceForge.
237: //
238: // Revision 1.8 2002/08/15 05:11:35 eob
239: // getIndexingAttrName
240: //
241: // Revision 1.7 2002/06/21 00:35:19 eob
242: // Make work with old JDK 1.1.*
243: //
244: // Revision 1.6 2002/06/04 05:27:08 eob
245: // Simplify use of visitor pattern to make code easier to understand.
246: //
247: // Revision 1.5 2002/05/23 21:14:51 eob
248: // Better error reporting.
249: //
250: // Revision 1.4 2002/05/10 19:42:48 eob
251: // Add static isStringValue
252: //
253: // Revision 1.3 2002/03/26 05:31:49 eob
254: // Making contructor private. Adding static factory method to manage
255: // cache of XPath objects.
256: //
257: // Revision 1.2 2002/02/04 22:12:59 eob
258: // Add boolean property to test whether this xpath returns a string.
259: //
260: // Revision 1.1 2002/02/01 01:59:09 eob
261: // initial
|