001: package com.rimfaxe.xml.xmlreader;
002:
003: import com.rimfaxe.xml.xmlreader.xpath.*;
004: import java.util.*;
005:
006: /**
007: * Visitor that evaluates an xpath expression relative to a context
008: * node by walking over the parse tree of the expression.
009:
010: <blockquote><small> Copyright (C) 2002 Hewlett-Packard Company.
011: This file is part of Sparta, an XML Parser, DOM, and XPath library.
012: This library is free software; you can redistribute it and/or
013: modify it under the terms of the <a href="doc-files/LGPL.txt">GNU
014: Lesser General Public License</a> as published by the Free Software
015: Foundation; either version 2.1 of the License, or (at your option)
016: any later version. This library is distributed in the hope that it
017: will be useful, but WITHOUT ANY WARRANTY; without even the implied
018: warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
019: PURPOSE. </small></blockquote>
020: @version $Date: 2003/01/27 23:30:58 $ $Revision: 1.6 $
021: @author Eamonn O'Brien-Strain
022: * @stereotype visitor */
023: class XPathVisitor implements Visitor {
024:
025: /** Evaluate a relative xpath expression relative to a context
026: * element by walking over the parse tree of th expression. */
027: private XPathVisitor(XPath xpath, Node context)
028: throws XPathException {
029: xpath_ = xpath;
030: contextNode_ = context;
031: nodelistFiltered_ = new Vector(1);
032: nodelistFiltered_.addElement(contextNode_);
033:
034: for (Enumeration i = xpath.getSteps(); i.hasMoreElements();) {
035: Step step = (Step) i.nextElement();
036: multiLevel_ = step.isMultiLevel();
037: nodesetIterator_ = null;
038: step.getNodeTest().accept(this );
039: nodesetIterator_ = nodelistRaw_.elements();
040: nodelistFiltered_.removeAllElements();
041: while (nodesetIterator_.hasMoreElements()) {
042: node_ = nodesetIterator_.nextElement();
043: step.getPredicate().accept(this );
044: Boolean expr = (Boolean) exprStack_.pop();
045: if (expr.booleanValue())
046: nodelistFiltered_.addElement(node_);
047: }
048: }
049: }
050:
051: /** Evaluate a relative xpath expression relative to a context
052: * element by walking over the parse tree of th expression. */
053: public XPathVisitor(Element context, XPath xpath)
054: throws XPathException {
055: this (xpath, context);
056: if (xpath.isAbsolute())
057: throw new XPathException(xpath,
058: "Cannot use element as context node for absolute xpath");
059: }
060:
061: /** Evaluate an absolute xpath expression in a document by walking
062: over the parse tree of th expression. */
063: public XPathVisitor(Document context, XPath xpath)
064: throws XPathException {
065: this (xpath, context);
066: }
067:
068: public void visit(ThisNodeTest a) {
069: nodelistRaw_.removeAllElements();
070: nodelistRaw_.add(contextNode_, ONE);
071: }
072:
073: /** @throws XPathException if ".." applied to node with no parent. */
074: public void visit(ParentNodeTest a) throws XPathException {
075: nodelistRaw_.removeAllElements();
076: Node parent = contextNode_.getParentNode();
077: if (parent == null)
078: throw new XPathException(xpath_,
079: "Illegal attempt to apply \"..\" to node with no parent.");
080: nodelistRaw_.add(parent, ONE);
081: }
082:
083: public void visit(AllElementTest a) {
084: Vector oldNodeList = nodelistFiltered_;
085: nodelistRaw_.removeAllElements();
086: for (Enumeration i = oldNodeList.elements(); i
087: .hasMoreElements();) {
088: Object node = i.nextElement();
089: if (node instanceof Element)
090: accumulateElements((Element) node);
091: else if (node instanceof Document)
092: accumulateElements((Document) node);
093: }
094: }
095:
096: private void accumulateElements(Document doc) {
097: Element child = doc.getDocumentElement();
098: nodelistRaw_.add(child, ONE);
099: if (multiLevel_)
100: accumulateElements(child); //recursive call
101: }
102:
103: private void accumulateElements(Element element) {
104: int position = 0;
105: for (Node n = element.getFirstChild(); n != null; n = n
106: .getNextSibling()) {
107: if (n instanceof Element) {
108: nodelistRaw_.add(n, ++position);
109: if (multiLevel_)
110: accumulateElements((Element) n); //recursive call
111: }
112: }
113: }
114:
115: public void visit(TextTest a) {
116: Vector oldNodeList = nodelistFiltered_;
117: nodelistRaw_.removeAllElements();
118: for (Enumeration i = oldNodeList.elements(); i
119: .hasMoreElements();) {
120: Object node = i.nextElement();
121: if (node instanceof Element) {
122: Element element = (Element) node;
123: for (Node n = element.getFirstChild(); n != null; n = n
124: .getNextSibling())
125: if (n instanceof Text)
126: nodelistRaw_.add(((Text) n).getData());
127: }
128: }
129: }
130:
131: private void emptyLists() {
132: nodelistRaw_.removeAllElements();
133: }
134:
135: public void visit(ElementTest test) {
136: String tagName = test.getTagName();
137: Vector oldNodeList = nodelistFiltered_;
138: int n = oldNodeList.size();
139: nodelistRaw_.removeAllElements();
140: for (int i = 0; i < n; ++i) {
141: Object node = oldNodeList.elementAt(i);
142: if (node instanceof Element)
143: accumulateMatchingElements((Element) node, tagName);
144: else if (node instanceof Document)
145: accumulateMatchingElements((Document) node, tagName);
146: }
147: }
148:
149: private void accumulateMatchingElements(Document document,
150: String tagName) {
151: Element child = document.getDocumentElement();
152: if (child == null)
153: return; //no document element
154: if (child.getTagName().equals(tagName))
155: nodelistRaw_.add(child, ONE);
156: if (multiLevel_)
157: accumulateMatchingElements(child, tagName); //recursive call
158: }
159:
160: private void accumulateMatchingElements(Element element,
161: String tagName) {
162: int position = 0;
163: for (Node n = element.getFirstChild(); n != null; n = n
164: .getNextSibling()) {
165: if (n instanceof Element) {
166: Element child = (Element) n;
167: if (child.getTagName().equals(tagName))
168: nodelistRaw_.add(child, ++position);
169: if (multiLevel_)
170: accumulateMatchingElements(child, tagName); //recursion
171: }
172: }
173: }
174:
175: public void visit(AttrTest test) {
176: Vector oldNodeList = nodelistFiltered_;
177: nodelistRaw_.removeAllElements();
178: for (Enumeration i = oldNodeList.elements(); i
179: .hasMoreElements();) {
180: Node node = (Node) i.nextElement();
181: if (node instanceof Element) {
182: Element element = (Element) node;
183: String attr = element.getAttribute(test.getAttrName());
184: if (attr != null)
185: nodelistRaw_.add(attr);
186: }
187: }
188: }
189:
190: static private final Integer ONE = new Integer(1);
191: static private final Boolean TRUE = new Boolean(true);
192: static private final Boolean FALSE = new Boolean(false);
193:
194: public void visit(TrueExpr a) {
195: exprStack_.push(TRUE);
196: }
197:
198: public void visit(AttrExistsExpr a) throws XPathException {
199: if (!(node_ instanceof Element))
200: throw new XPathException(xpath_,
201: "Cannot test attribute of document");
202: Element element = (Element) node_;
203: String attrValue = element.getAttribute(a.getAttrName());
204: boolean result = attrValue != null && attrValue.length() > 0;
205: exprStack_.push(result ? TRUE : FALSE);
206: }
207:
208: public void visit(AttrEqualsExpr a) throws XPathException {
209: if (!(node_ instanceof Element))
210: throw new XPathException(xpath_,
211: "Cannot test attribute of document");
212: Element element = (Element) node_;
213: String attrValue = element.getAttribute(a.getAttrName());
214: boolean result = a.getAttrValue().equals(attrValue);
215: exprStack_.push(result ? TRUE : FALSE);
216: }
217:
218: public void visit(AttrNotEqualsExpr a) throws XPathException {
219: if (!(node_ instanceof Element))
220: throw new XPathException(xpath_,
221: "Cannot test attribute of document");
222: Element element = (Element) node_;
223: String attrValue = element.getAttribute(a.getAttrName());
224: boolean result = !a.getAttrValue().equals(attrValue);
225: exprStack_.push(result ? TRUE : FALSE);
226: }
227:
228: public void visit(AttrLessExpr a) throws XPathException {
229: if (!(node_ instanceof Element))
230: throw new XPathException(xpath_,
231: "Cannot test attribute of document");
232: Element element = (Element) node_;
233: // Use jdk1.1 API to make the code work with PersonalJava.
234: // double attrValue = Double.parseDouble( element.getAttribute( a.getAttrName() ) );
235: double attrValue = Double.valueOf(
236: element.getAttribute(a.getAttrName())).doubleValue();
237: boolean result = attrValue < a.getAttrValue();
238: exprStack_.push(result ? TRUE : FALSE);
239: }
240:
241: public void visit(AttrGreaterExpr a) throws XPathException {
242: if (!(node_ instanceof Element))
243: throw new XPathException(xpath_,
244: "Cannot test attribute of document");
245: Element element = (Element) node_;
246: // Use jdk1.1 API to make the code work with PersonalJava.
247: // double attrValue = Double.parseDouble( element.getAttribute( a.getAttrName() ) );
248: double attrValue = Double.valueOf(
249: element.getAttribute(a.getAttrName())).doubleValue();
250: boolean result = attrValue > a.getAttrValue();
251: exprStack_.push(result ? TRUE : FALSE);
252: }
253:
254: public void visit(TextExistsExpr a) throws XPathException {
255: if (!(node_ instanceof Element))
256: throw new XPathException(xpath_,
257: "Cannot test attribute of document");
258: Element element = (Element) node_;
259: for (Node i = element.getFirstChild(); i != null; i = i
260: .getNextSibling()) {
261: if (i instanceof Text) {
262: exprStack_.push(TRUE);
263: return;
264: }
265: }
266: exprStack_.push(FALSE);
267: }
268:
269: public void visit(TextEqualsExpr a) throws XPathException {
270: if (!(node_ instanceof Element))
271: throw new XPathException(xpath_,
272: "Cannot test attribute of document");
273: Element element = (Element) node_;
274: for (Node i = element.getFirstChild(); i != null; i = i
275: .getNextSibling()) {
276: if (i instanceof Text) {
277: Text text = (Text) i;
278: if (text.getData().equals(a.getValue())) {
279: exprStack_.push(TRUE);
280: return;
281: }
282: }
283: }
284: exprStack_.push(FALSE);
285: }
286:
287: public void visit(TextNotEqualsExpr a) throws XPathException {
288: if (!(node_ instanceof Element))
289: throw new XPathException(xpath_,
290: "Cannot test attribute of document");
291: Element element = (Element) node_;
292: for (Node i = element.getFirstChild(); i != null; i = i
293: .getNextSibling()) {
294: if (i instanceof Text) {
295: Text text = (Text) i;
296: if (!text.getData().equals(a.getValue())) {
297: exprStack_.push(TRUE);
298: return;
299: }
300: }
301: }
302: exprStack_.push(FALSE);
303: }
304:
305: public void visit(PositionEqualsExpr a) throws XPathException {
306: if (!(node_ instanceof Element))
307: throw new XPathException(xpath_,
308: "Cannot test position of document");
309: Element element = (Element) node_;
310: boolean result = (nodelistRaw_.position(element) == a
311: .getPosition());
312: exprStack_.push(result ? TRUE : FALSE);
313: }
314:
315: /** Get all the elements or strings that match the xpath expression. */
316: public Enumeration getResultEnumeration() {
317: return nodelistFiltered_.elements();
318: }
319:
320: /** Get the first element that match the xpath expression, or null. */
321: public Element getFirstResultElement() {
322: return nodelistFiltered_.size() == 0 ? null
323: : (Element) nodelistFiltered_.elementAt(0);
324: }
325:
326: /** Get the first string that match the xpath expression, or null. */
327: public String getFirstResultString() {
328: return nodelistFiltered_.size() == 0 ? null
329: : (String) nodelistFiltered_.elementAt(0).toString();
330: }
331:
332: /** @associates Node. */
333: private final NodeListWithPosition nodelistRaw_ = new NodeListWithPosition();
334: private Vector nodelistFiltered_ = new Vector();
335: private Enumeration nodesetIterator_ = null;
336: private Object node_ = null; //String or Element
337: private final Stack exprStack_ = new Stack();
338:
339: /**
340: * @label context
341: */
342: private/*final (JDK11 problems)*/Node contextNode_;
343:
344: private boolean multiLevel_;
345:
346: private/*final (JDK11 problems)*/XPath xpath_;
347:
348: }
349:
350: /** A list of nodes, together with the positions in their context of
351: each node. */
352: class NodeListWithPosition {
353: Enumeration elements() {
354: return vector_.elements();
355: }
356:
357: void removeAllElements() {
358: vector_.removeAllElements();
359: }
360:
361: void add(String string) {
362: vector_.addElement(string);
363: }
364:
365: void add(Node node, int position) {
366: add(node, new Integer(position));
367: }
368:
369: void add(Node node, Integer position) {
370: vector_.addElement(node);
371: positions_.put(node, position);
372: }
373:
374: int position(Node node) {
375: return ((Integer) positions_.get(node)).intValue();
376: }
377:
378: private final Vector vector_ = new Vector();
379: private final Map positions_ = new HashMap();
380: }
381:
382: // $Log: XPathVisitor.java,v $
383: // Revision 1.6 2003/01/27 23:30:58 yuhongx
384: // Replaced Hashtable with HashMap.
385: //
386: // Revision 1.5 2003/01/09 01:10:50 yuhongx
387: // Use JDK1.1 API to make the code work with PersonalJava.
388: //
389: // Revision 1.4 2002/12/05 04:35:39 eobrain
390: // Add support for greater than and less than in predicates.
391: //
392: // Revision 1.3 2002/10/30 16:28:46 eobrain
393: // Feature request [ 630127 ] Support /a/b[text()='foo']
394: // http://sourceforge.net/projects/sparta-xml/
395: //
396: // Revision 1.2 2002/09/18 05:29:04 eobrain
397: // Support xpath predicates of the form [1], [2], ...
398: //
399: // Revision 1.1.1.1 2002/08/19 05:03:53 eobrain
400: // import from HP Labs internal CVS
401: //
402: // Revision 1.11 2002/08/18 04:47:47 eob
403: // Make class package-private so as not to clutter up the javadoc.
404: //
405: // Revision 1.10 2002/06/21 00:28:33 eob
406: // Make work with old JDK 1.1.*
407: //
408: // Revision 1.9 2002/06/14 19:34:50 eob
409: // Add handling of "text()" in XPath expressions.
410: //
411: // Revision 1.8 2002/06/04 05:29:28 eob
412: // Simplify use of visitor pattern to make code easier to understand.
413: // Fix bug when predicate in middle of XPath.
414: //
415: // Revision 1.7 2002/05/23 21:18:52 eob
416: // Better error reporting.
417: //
418: // Revision 1.6 2002/05/10 21:39:28 eob
419: // Allow documents to take relative xpaths.
420: //
421: // Revision 1.5 2002/03/26 01:49:53 eob
422: // Return different results depending on type. Return first result.
423: //
424: // Revision 1.4 2002/03/21 23:54:36 eob
425: // Fix handling of .. when no parent.
426: //
427: // Revision 1.3 2002/02/14 02:21:45 eob
428: // Handle attribute XPaths.
429: //
430: // Revision 1.2 2002/02/04 22:09:44 eob
431: // add visit(AttrTest)
432: //
433: // Revision 1.1 2002/02/01 22:46:12 eob
434: // initial
|