libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / gnu / xml / xpath / Selector.java
blob429b3f7e20b01e8e0350ab057e6fe6935246c978
1 /* Selector.java --
2 Copyright (C) 2004,2006 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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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 gnu.java.lang.CPStringBuilder;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.Iterator;
45 import java.util.LinkedHashSet;
46 import java.util.List;
47 import java.util.Set;
48 import javax.xml.XMLConstants;
49 import javax.xml.namespace.QName;
50 import org.w3c.dom.Attr;
51 import org.w3c.dom.NamedNodeMap;
52 import org.w3c.dom.Node;
54 /**
55 * A single component of a location path.
57 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
59 public final class Selector
60 extends Path
63 public static final int ANCESTOR = 0;
64 public static final int ANCESTOR_OR_SELF = 1;
65 public static final int ATTRIBUTE = 2;
66 public static final int CHILD = 3;
67 public static final int DESCENDANT = 4;
68 public static final int DESCENDANT_OR_SELF = 5;
69 public static final int FOLLOWING = 6;
70 public static final int FOLLOWING_SIBLING = 7;
71 public static final int NAMESPACE = 8;
72 public static final int PARENT = 9;
73 public static final int PRECEDING = 10;
74 public static final int PRECEDING_SIBLING = 11;
75 public static final int SELF = 12;
77 /**
78 * Axis to select nodes in.
80 final int axis;
82 /**
83 * List of tests to perform on candidates.
85 final Test[] tests;
87 public Selector(int axis, List<? extends Test> tests)
89 this.axis = axis;
90 int len = tests.size();
91 this.tests = new Test[(len == 0) ? 1 : len];
92 if (len > 0)
93 tests.toArray(this.tests);
94 else
95 this.tests[0] = new NodeTypeTest((short) 0);
96 if (axis == NAMESPACE && this.tests[0] instanceof NameTest)
98 NameTest nt = (NameTest) this.tests[0];
99 this.tests[0] = new NamespaceTest(nt.qName, nt.anyLocalName, nt.any);
104 * Returns the list of tests to perform on candidates.
106 public Test[] getTests()
108 return tests;
111 public boolean matches(Node context)
113 // If called directly, selector is the top level of the path
114 return matches(context,
115 getContextPosition(context),
116 getContextSize(context));
119 boolean matches(Node context, int pos, int len)
121 short nodeType = context.getNodeType();
122 switch (axis)
124 case CHILD:
125 if (nodeType == Node.ATTRIBUTE_NODE)
126 return false;
127 break;
128 case ATTRIBUTE:
129 case NAMESPACE:
130 if (nodeType != Node.ATTRIBUTE_NODE)
131 return false;
132 break;
133 case DESCENDANT_OR_SELF:
134 return true;
135 default:
136 return false;
138 for (int j = 0; j < tests.length && len > 0; j++)
140 Test test = tests[j];
141 if (!test.matches(context, pos, len))
142 return false;
144 return true;
147 private int getContextPosition(Node ctx)
149 int pos = 1;
150 for (ctx = ctx.getPreviousSibling(); ctx != null;
151 ctx = ctx.getPreviousSibling())
153 if (tests[0].matches(ctx, 1, 1))
154 pos++;
156 return pos;
159 private int getContextSize(Node ctx)
161 if (ctx.getNodeType() == Node.ATTRIBUTE_NODE)
163 Node owner = ((Attr) ctx).getOwnerElement();
164 return owner.getAttributes().getLength();
166 int count = 1;
167 Node sib = ctx.getPreviousSibling();
168 for (; sib != null; sib = sib.getPreviousSibling())
170 if (tests[0].matches(ctx, 1, 1))
171 count++;
173 sib = ctx.getNextSibling();
174 for (; sib != null; sib = sib.getNextSibling())
176 if (tests[0].matches(ctx, 1, 1))
177 count++;
179 return count;
183 @Override
184 public Object evaluate(Node context, int pos, int len)
186 Set<Node> acc = new LinkedHashSet<Node>();
187 addCandidates(context, acc);
188 List<Node> candidates = new ArrayList<Node>(acc);
189 List<Node> ret = filterCandidates(candidates, false);
190 return ret;
193 Collection<Node> evaluate(Node context, Collection<Node> ns)
195 Set<Node> acc = new LinkedHashSet<Node>();
196 for (Iterator<Node> i = ns.iterator(); i.hasNext(); )
197 addCandidates(i.next(), acc);
198 List<Node> candidates = new ArrayList<Node>(acc);
199 List<Node> ret = filterCandidates(candidates, true);
200 return ret;
204 * Filter the given list of candidates according to the node tests.
206 List<Node> filterCandidates(List<Node> candidates, boolean cascade)
208 int len = candidates.size();
209 int tlen = tests.length;
210 if (tlen > 0 && len > 0)
212 // Present the result of each successful generation to the next test
213 for (int j = 0; j < tlen && len > 0; j++)
215 Test test = tests[j];
216 List<Node> successful = new ArrayList<Node>(len);
217 for (int i = 0; i < len; i++)
219 Node node = candidates.get(i);
220 if (cascade)
222 // Documents and DocumentFragments should be considered
223 // if part of a location path where the axis involves
224 // the SELF concept
225 short nodeType = node.getNodeType();
226 if ((nodeType == Node.DOCUMENT_NODE ||
227 nodeType == Node.DOCUMENT_FRAGMENT_NODE) &&
228 (axis == DESCENDANT_OR_SELF ||
229 axis == ANCESTOR_OR_SELF ||
230 axis == SELF) &&
231 (tests.length == 1 &&
232 tests[0] instanceof NodeTypeTest &&
233 ((NodeTypeTest) tests[0]).type == (short) 0))
235 successful.add(node);
236 continue;
239 if (test.matches(node, i + 1, len))
240 successful.add(node);
242 candidates = successful;
243 len = candidates.size();
246 return candidates;
249 void addCandidates(Node context, Collection<Node> candidates)
251 // Build list of candidates
252 switch (axis)
254 case CHILD:
255 addChildNodes(context, candidates, false);
256 break;
257 case DESCENDANT:
258 addChildNodes(context, candidates, true);
259 break;
260 case DESCENDANT_OR_SELF:
261 candidates.add (context);
262 addChildNodes(context, candidates, true);
263 break;
264 case PARENT:
265 addParentNode(context, candidates, false);
266 break;
267 case ANCESTOR:
268 addParentNode(context, candidates, true);
269 break;
270 case ANCESTOR_OR_SELF:
271 candidates.add(context);
272 addParentNode(context, candidates, true);
273 break;
274 case FOLLOWING_SIBLING:
275 addFollowingNodes(context, candidates, false);
276 break;
277 case PRECEDING_SIBLING:
278 addPrecedingNodes(context, candidates, false);
279 break;
280 case FOLLOWING:
281 addFollowingNodes(context, candidates, true);
282 break;
283 case PRECEDING:
284 addPrecedingNodes(context, candidates, true);
285 break;
286 case ATTRIBUTE:
287 addAttributes(context, candidates);
288 break;
289 case NAMESPACE:
290 addNamespaceAttributes(context, candidates);
291 break;
292 case SELF:
293 candidates.add(context);
294 break;
298 void addChildNodes(Node context, Collection<Node> acc, boolean recurse)
300 Node child = context.getFirstChild();
301 while (child != null)
303 acc.add(child);
304 if (recurse)
305 addChildNodes(child, acc, recurse);
306 child = child.getNextSibling();
310 void addParentNode(Node context, Collection<Node> acc, boolean recurse)
312 Node parent = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
313 ((Attr) context).getOwnerElement() : context.getParentNode();
314 if (parent != null)
316 acc.add(parent);
317 if (recurse)
318 addParentNode(parent, acc, recurse);
322 void addFollowingNodes(Node context, Collection<Node> acc, boolean recurse)
324 if (context != null && recurse)
325 addChildNodes(context, acc, true);
326 Node cur = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? null :
327 context.getNextSibling();
328 while (cur != null)
330 acc.add(cur);
331 if (recurse)
332 addChildNodes(cur, acc, true);
333 cur = cur.getNextSibling();
335 if (recurse)
337 while (context != null)
339 context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
340 ((Attr) context).getOwnerElement() : context.getParentNode();
341 if (context != null)
343 cur = context.getNextSibling();
344 while (cur != null)
346 acc.add(cur);
347 if (recurse)
348 addChildNodes(cur, acc, true);
349 cur = cur.getNextSibling();
356 void addPrecedingNodes(Node context, Collection<Node> acc, boolean recurse)
358 Node cur = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? null :
359 context.getPreviousSibling();
360 while (cur != null)
362 acc.add(cur);
363 if (recurse)
364 addChildNodes(cur, acc, true);
365 cur = cur.getPreviousSibling();
367 if (recurse)
369 cur = context;
370 cur = (cur.getNodeType() == Node.ATTRIBUTE_NODE) ?
371 ((Attr) cur).getOwnerElement() : cur.getParentNode();
372 if (cur != null)
373 addPrecedingNodes(cur, acc, recurse);
377 void addAttributes(Node context, Collection<Node> acc)
379 NamedNodeMap attrs = context.getAttributes();
380 if (attrs != null)
382 int attrLen = attrs.getLength();
383 for (int i = 0; i < attrLen; i++)
385 Node attr = attrs.item(i);
386 if (!isNamespaceAttribute(attr))
388 acc.add(attr);
394 void addNamespaceAttributes(Node context, Collection<Node> acc)
396 NamedNodeMap attrs = context.getAttributes();
397 if (attrs != null)
399 int attrLen = attrs.getLength();
400 for (int i = 0; i < attrLen; i++)
402 Node attr = attrs.item(i);
403 if (isNamespaceAttribute(attr))
404 acc.add(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<Test> tests2 = new ArrayList<Test>(len);
421 for (int i = 0; i < len; i++)
422 tests2.add(tests[i].clone(context));
423 return new Selector(axis, tests2);
426 public boolean references(QName var)
428 for (int i = 0; i < tests.length; i++)
430 if (tests[i].references(var))
431 return true;
433 return false;
436 public String toString()
438 CPStringBuilder buf = new CPStringBuilder();
439 switch (axis)
441 case ANCESTOR:
442 buf.append("ancestor::");
443 break;
444 case ANCESTOR_OR_SELF:
445 buf.append("ancestor-or-self::");
446 break;
447 case ATTRIBUTE:
448 if (tests.length == 0 ||
449 (tests[0] instanceof NameTest))
450 buf.append('@');
451 else
452 buf.append("attribute::");
453 break;
454 case CHILD:
455 //buf.append("child::");
456 break;
457 case DESCENDANT:
458 buf.append("descendant::");
459 break;
460 case DESCENDANT_OR_SELF:
461 buf.append("descendant-or-self::");
462 break;
463 case FOLLOWING:
464 buf.append("following::");
465 break;
466 case FOLLOWING_SIBLING:
467 buf.append("following-sibling::");
468 break;
469 case NAMESPACE:
470 buf.append("namespace::");
471 break;
472 case PARENT:
473 if (tests.length == 0 ||
474 (tests[0] instanceof NodeTypeTest &&
475 ((NodeTypeTest) tests[0]).type == 0))
476 return "..";
477 buf.append("parent::");
478 break;
479 case PRECEDING:
480 buf.append("preceding::");
481 break;
482 case PRECEDING_SIBLING:
483 buf.append("preceding-sibling::");
484 break;
485 case SELF:
486 if (tests.length == 0 ||
487 (tests[0] instanceof NodeTypeTest &&
488 ((NodeTypeTest) tests[0]).type == 0))
489 return ".";
490 buf.append("self::");
491 break;
493 if (tests.length == 0)
494 buf.append("[error]");
495 else
497 for (int i = 0; i < tests.length; i++)
498 buf.append(tests[i]);
500 return buf.toString();