2 Copyright (C) 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu.xml.xpath;
40 import java.util.ArrayList;
41 import java.util.Collection;
42 import java.util.Iterator;
43 import java.util.LinkedHashSet;
44 import java.util.List;
46 import javax.xml.XMLConstants;
47 import javax.xml.namespace.QName;
48 import org.w3c.dom.Attr;
49 import org.w3c.dom.NamedNodeMap;
50 import org.w3c.dom.Node;
53 * A single component of a location path.
55 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
57 public final class Selector
61 public static final int ANCESTOR = 0;
62 public static final int ANCESTOR_OR_SELF = 1;
63 public static final int ATTRIBUTE = 2;
64 public static final int CHILD = 3;
65 public static final int DESCENDANT = 4;
66 public static final int DESCENDANT_OR_SELF = 5;
67 public static final int FOLLOWING = 6;
68 public static final int FOLLOWING_SIBLING = 7;
69 public static final int NAMESPACE = 8;
70 public static final int PARENT = 9;
71 public static final int PRECEDING = 10;
72 public static final int PRECEDING_SIBLING = 11;
73 public static final int SELF = 12;
76 * Axis to select nodes in.
81 * List of tests to perform on candidates.
85 public Selector(int axis, List tests)
88 this.tests = new Test[tests.size()];
89 tests.toArray(this.tests);
90 if (axis == NAMESPACE &&
91 this.tests.length > 0 &&
92 this.tests[0] instanceof NameTest)
94 NameTest nt = (NameTest) this.tests[0];
95 this.tests[0] = new NamespaceTest(nt.qName, nt.anyLocalName, nt.any);
100 * Returns the list of tests to perform on candidates.
102 public Test[] getTests()
107 public boolean matches(Node context)
109 short nodeType = context.getNodeType();
113 if (nodeType == Node.ATTRIBUTE_NODE)
120 if (nodeType != Node.ATTRIBUTE_NODE)
125 case DESCENDANT_OR_SELF:
130 int tlen = tests.length;
133 int pos = getContextPosition(context);
134 int len = getContextSize(context);
135 for (int j = 0; j < tlen && len > 0; j++)
137 Test test = tests[j];
138 if (!test.matches(context, pos, len))
147 private int getContextPosition(Node ctx)
150 for (ctx = ctx.getPreviousSibling(); ctx != null;
151 ctx = ctx.getPreviousSibling())
158 private int getContextSize(Node ctx)
160 if (ctx.getNodeType() == Node.ATTRIBUTE_NODE)
162 Node parent = ((Attr) ctx).getOwnerElement();
163 return parent.getAttributes().getLength();
165 Node parent = ctx.getParentNode();
168 return parent.getChildNodes().getLength();
173 public Object evaluate(Node context, int pos, int len)
175 Set acc = new LinkedHashSet();
176 addCandidates(context, acc);
177 List candidates = new ArrayList(acc);
178 //Collections.sort(candidates, documentOrderComparator);
179 List ret = filterCandidates(candidates, false);
183 Collection evaluate(Node context, Collection ns)
185 Set acc = new LinkedHashSet();
186 for (Iterator i = ns.iterator(); i.hasNext(); )
188 addCandidates((Node) i.next(), acc);
190 List candidates = new ArrayList(acc);
191 //Collections.sort(candidates, documentOrderComparator);
192 List ret = filterCandidates(candidates, true);
197 * Filter the given list of candidates according to the node tests.
199 List filterCandidates(List candidates, boolean cascade)
201 int len = candidates.size();
202 int tlen = tests.length;
203 if (tlen > 0 && len > 0)
205 // Present the result of each successful generation to the next test
206 for (int j = 0; j < tlen && len > 0; j++)
208 Test test = tests[j];
209 List successful = new ArrayList(len);
210 for (int i = 0; i < len; i++)
212 Node node = (Node) candidates.get(i);
215 // Documents and DocumentFragments should be considered
216 // if part of a location path where the axis involves
218 short nodeType = node.getNodeType();
219 if ((nodeType == Node.DOCUMENT_NODE ||
220 nodeType == Node.DOCUMENT_FRAGMENT_NODE) &&
221 (axis == DESCENDANT_OR_SELF ||
222 axis == ANCESTOR_OR_SELF ||
224 (tests.length == 1 &&
225 tests[0] instanceof NodeTypeTest &&
226 ((NodeTypeTest) tests[0]).type == (short) 0))
228 successful.add(node);
232 if (test.matches(node, i + 1, len))
234 successful.add(node);
237 System.err.println("Testing "+node);
238 int p = getContextPosition(node);
239 int l = getContextSize(node);
240 if (test.matches(node, p, l))
242 successful.add(node);
245 candidates = successful;
246 len = candidates.size();
252 void addCandidates(Node context, Collection candidates)
254 // Build list of candidates
258 addChildNodes(context, candidates, false);
261 addChildNodes(context, candidates, true);
263 case DESCENDANT_OR_SELF:
264 candidates.add (context);
265 addChildNodes(context, candidates, true);
268 addParentNode(context, candidates, false);
271 addParentNode(context, candidates, true);
273 case ANCESTOR_OR_SELF:
274 candidates.add(context);
275 addParentNode(context, candidates, true);
277 case FOLLOWING_SIBLING:
278 addFollowingNodes(context, candidates, false);
280 case PRECEDING_SIBLING:
281 addPrecedingNodes(context, candidates, false);
284 addFollowingNodes(context, candidates, true);
287 addPrecedingNodes(context, candidates, true);
290 addAttributes(context, candidates);
293 addNamespaceAttributes(context, candidates);
296 candidates.add(context);
301 void addChildNodes(Node context, Collection acc, boolean recurse)
303 Node child = context.getFirstChild();
304 while (child != null)
309 addChildNodes(child, acc, recurse);
311 child = child.getNextSibling();
315 void addParentNode(Node context, Collection acc, boolean recurse)
317 Node parent = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
318 ((Attr) context).getOwnerElement() : context.getParentNode();
324 addParentNode(parent, acc, recurse);
329 void addFollowingNodes(Node context, Collection acc, boolean recurse)
331 Node cur = context.getNextSibling();
337 addChildNodes(cur, acc, true);
339 cur = cur.getNextSibling();
343 context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
344 ((Attr) context).getOwnerElement() : context.getParentNode();
347 addFollowingNodes(context, acc, recurse);
352 void addPrecedingNodes(Node context, Collection acc, boolean recurse)
354 Node cur = context.getPreviousSibling();
360 addChildNodes(cur, acc, true);
362 cur = cur.getPreviousSibling();
366 context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
367 ((Attr) context).getOwnerElement() : context.getParentNode();
370 addPrecedingNodes(context, acc, recurse);
375 void addAttributes(Node context, Collection acc)
377 NamedNodeMap attrs = context.getAttributes();
380 int attrLen = attrs.getLength();
381 for (int i = 0; i < attrLen; i++)
383 Node attr = attrs.item(i);
384 if (!isNamespaceAttribute(attr))
392 void addNamespaceAttributes(Node context, Collection acc)
394 NamedNodeMap attrs = context.getAttributes();
397 int attrLen = attrs.getLength();
398 for (int i = 0; i < attrLen; i++)
400 Node attr = attrs.item(i);
401 if (isNamespaceAttribute(attr))
409 final boolean isNamespaceAttribute(Node node)
411 String uri = node.getNamespaceURI();
412 return (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
413 XMLConstants.XMLNS_ATTRIBUTE.equals(node.getPrefix()) ||
414 XMLConstants.XMLNS_ATTRIBUTE.equals(node.getNodeName()));
417 public Expr clone(Object context)
419 int len = tests.length;
420 List tests2 = new ArrayList(len);
421 for (int i = 0; i < len; i++)
423 tests2.add(tests[i].clone(context));
425 return new Selector(axis, tests2);
428 public boolean references(QName var)
430 for (int i = 0; i < tests.length; i++)
432 if (tests[i].references(var))
440 public String toString()
442 StringBuffer buf = new StringBuffer();
446 buf.append("ancestor::");
448 case ANCESTOR_OR_SELF:
449 buf.append("ancestor-or-self::");
452 if (tests.length == 0 ||
453 (tests[0] instanceof NameTest))
459 buf.append("attribute::");
463 //buf.append("child::");
466 buf.append("descendant::");
468 case DESCENDANT_OR_SELF:
469 buf.append("descendant-or-self::");
472 buf.append("following::");
474 case FOLLOWING_SIBLING:
475 buf.append("following-sibling::");
478 buf.append("namespace::");
481 if (tests.length == 0 ||
482 (tests[0] instanceof NodeTypeTest &&
483 ((NodeTypeTest) tests[0]).type == 0))
487 buf.append("parent::");
490 buf.append("preceding::");
492 case PRECEDING_SIBLING:
493 buf.append("preceding-sibling::");
496 if (tests.length == 0 ||
497 (tests[0] instanceof NodeTypeTest &&
498 ((NodeTypeTest) tests[0]).type == 0))
502 buf.append("self::");
505 if (tests.length == 0)
511 for (int i = 0; i < tests.length; i++)
513 buf.append(tests[i]);
516 return buf.toString();