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