Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / gnu / xml / xpath / Selector.java
blobc7abb33e2ff893126dc9af863adfc49fa4bde705
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 java.util.ArrayList;
41 import java.util.Collection;
42 import java.util.Iterator;
43 import java.util.LinkedHashSet;
44 import java.util.List;
45 import java.util.Set;
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;
52 /**
53 * A single component of a location path.
55 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
57 public final class Selector
58 extends Path
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;
75 /**
76 * Axis to select nodes in.
78 final int axis;
80 /**
81 * List of tests to perform on candidates.
83 final Test[] tests;
85 public Selector(int axis, List tests)
87 this.axis = axis;
88 int len = tests.size();
89 this.tests = new Test[(len == 0) ? 1 : len];
90 if (len > 0)
91 tests.toArray(this.tests);
92 else
93 this.tests[0] = new NodeTypeTest((short) 0);
94 if (axis == NAMESPACE && this.tests[0] instanceof NameTest)
96 NameTest nt = (NameTest) this.tests[0];
97 this.tests[0] = new NamespaceTest(nt.qName, nt.anyLocalName, nt.any);
102 * Returns the list of tests to perform on candidates.
104 public Test[] getTests()
106 return tests;
109 public boolean matches(Node context)
111 // If called directly, selector is the top level of the path
112 return matches(context,
113 getContextPosition(context),
114 getContextSize(context));
117 boolean matches(Node context, int pos, int len)
119 short nodeType = context.getNodeType();
120 switch (axis)
122 case CHILD:
123 if (nodeType == Node.ATTRIBUTE_NODE)
124 return false;
125 break;
126 case ATTRIBUTE:
127 case NAMESPACE:
128 if (nodeType != Node.ATTRIBUTE_NODE)
129 return false;
130 break;
131 case DESCENDANT_OR_SELF:
132 return true;
133 default:
134 return false;
136 for (int j = 0; j < tests.length && len > 0; j++)
138 Test test = tests[j];
139 if (!test.matches(context, pos, len))
140 return false;
142 return true;
145 private int getContextPosition(Node ctx)
147 int pos = 1;
148 for (ctx = ctx.getPreviousSibling(); ctx != null;
149 ctx = ctx.getPreviousSibling())
151 if (tests[0].matches(ctx, 1, 1))
152 pos++;
154 return pos;
157 private int getContextSize(Node ctx)
159 if (ctx.getNodeType() == Node.ATTRIBUTE_NODE)
161 Node owner = ((Attr) ctx).getOwnerElement();
162 return owner.getAttributes().getLength();
164 int count = 1;
165 Node sib = ctx.getPreviousSibling();
166 for (; sib != null; sib = sib.getPreviousSibling())
168 if (tests[0].matches(ctx, 1, 1))
169 count++;
171 sib = ctx.getNextSibling();
172 for (; sib != null; sib = sib.getNextSibling())
174 if (tests[0].matches(ctx, 1, 1))
175 count++;
177 return count;
180 public Object evaluate(Node context, int pos, int len)
182 Set acc = new LinkedHashSet();
183 addCandidates(context, acc);
184 List candidates = new ArrayList(acc);
185 List ret = filterCandidates(candidates, false);
186 return ret;
189 Collection evaluate(Node context, Collection ns)
191 Set acc = new LinkedHashSet();
192 for (Iterator i = ns.iterator(); i.hasNext(); )
193 addCandidates((Node) i.next(), acc);
194 List candidates = new ArrayList(acc);
195 List ret = filterCandidates(candidates, true);
196 return ret;
200 * Filter the given list of candidates according to the node tests.
202 List filterCandidates(List candidates, boolean cascade)
204 int len = candidates.size();
205 int tlen = tests.length;
206 if (tlen > 0 && len > 0)
208 // Present the result of each successful generation to the next test
209 for (int j = 0; j < tlen && len > 0; j++)
211 Test test = tests[j];
212 List successful = new ArrayList(len);
213 for (int i = 0; i < len; i++)
215 Node node = (Node) candidates.get(i);
216 if (cascade)
218 // Documents and DocumentFragments should be considered
219 // if part of a location path where the axis involves
220 // the SELF concept
221 short nodeType = node.getNodeType();
222 if ((nodeType == Node.DOCUMENT_NODE ||
223 nodeType == Node.DOCUMENT_FRAGMENT_NODE) &&
224 (axis == DESCENDANT_OR_SELF ||
225 axis == ANCESTOR_OR_SELF ||
226 axis == SELF) &&
227 (tests.length == 1 &&
228 tests[0] instanceof NodeTypeTest &&
229 ((NodeTypeTest) tests[0]).type == (short) 0))
231 successful.add(node);
232 continue;
235 if (test.matches(node, i + 1, len))
236 successful.add(node);
238 candidates = successful;
239 len = candidates.size();
242 return candidates;
245 void addCandidates(Node context, Collection candidates)
247 // Build list of candidates
248 switch (axis)
250 case CHILD:
251 addChildNodes(context, candidates, false);
252 break;
253 case DESCENDANT:
254 addChildNodes(context, candidates, true);
255 break;
256 case DESCENDANT_OR_SELF:
257 candidates.add (context);
258 addChildNodes(context, candidates, true);
259 break;
260 case PARENT:
261 addParentNode(context, candidates, false);
262 break;
263 case ANCESTOR:
264 addParentNode(context, candidates, true);
265 break;
266 case ANCESTOR_OR_SELF:
267 candidates.add(context);
268 addParentNode(context, candidates, true);
269 break;
270 case FOLLOWING_SIBLING:
271 addFollowingNodes(context, candidates, false);
272 break;
273 case PRECEDING_SIBLING:
274 addPrecedingNodes(context, candidates, false);
275 break;
276 case FOLLOWING:
277 addFollowingNodes(context, candidates, true);
278 break;
279 case PRECEDING:
280 addPrecedingNodes(context, candidates, true);
281 break;
282 case ATTRIBUTE:
283 addAttributes(context, candidates);
284 break;
285 case NAMESPACE:
286 addNamespaceAttributes(context, candidates);
287 break;
288 case SELF:
289 candidates.add(context);
290 break;
294 void addChildNodes(Node context, Collection acc, boolean recurse)
296 Node child = context.getFirstChild();
297 while (child != null)
299 acc.add(child);
300 if (recurse)
301 addChildNodes(child, acc, recurse);
302 child = child.getNextSibling();
306 void addParentNode(Node context, Collection acc, boolean recurse)
308 Node parent = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
309 ((Attr) context).getOwnerElement() : context.getParentNode();
310 if (parent != null)
312 acc.add(parent);
313 if (recurse)
314 addParentNode(parent, acc, recurse);
318 void addFollowingNodes(Node context, Collection acc, boolean recurse)
320 if (context != null && recurse)
321 addChildNodes(context, acc, true);
322 Node cur = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? null :
323 context.getNextSibling();
324 while (cur != null)
326 acc.add(cur);
327 if (recurse)
328 addChildNodes(cur, acc, true);
329 cur = cur.getNextSibling();
331 if (recurse)
333 while (context != null)
335 context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
336 ((Attr) context).getOwnerElement() : context.getParentNode();
337 if (context != null)
339 cur = context.getNextSibling();
340 while (cur != null)
342 acc.add(cur);
343 if (recurse)
344 addChildNodes(cur, acc, true);
345 cur = cur.getNextSibling();
352 void addPrecedingNodes(Node context, Collection acc, boolean recurse)
354 Node cur = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? null :
355 context.getPreviousSibling();
356 while (cur != null)
358 acc.add(cur);
359 if (recurse)
360 addChildNodes(cur, acc, true);
361 cur = cur.getPreviousSibling();
363 if (recurse)
365 cur = context;
366 cur = (cur.getNodeType() == Node.ATTRIBUTE_NODE) ?
367 ((Attr) cur).getOwnerElement() : cur.getParentNode();
368 if (cur != null)
369 addPrecedingNodes(cur, acc, recurse);
373 void addAttributes(Node context, Collection acc)
375 NamedNodeMap attrs = context.getAttributes();
376 if (attrs != null)
378 int attrLen = attrs.getLength();
379 for (int i = 0; i < attrLen; i++)
381 Node attr = attrs.item(i);
382 if (!isNamespaceAttribute(attr))
384 acc.add(attr);
390 void addNamespaceAttributes(Node context, Collection acc)
392 NamedNodeMap attrs = context.getAttributes();
393 if (attrs != null)
395 int attrLen = attrs.getLength();
396 for (int i = 0; i < attrLen; i++)
398 Node attr = attrs.item(i);
399 if (isNamespaceAttribute(attr))
400 acc.add(attr);
405 final boolean isNamespaceAttribute(Node node)
407 String uri = node.getNamespaceURI();
408 return (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
409 XMLConstants.XMLNS_ATTRIBUTE.equals(node.getPrefix()) ||
410 XMLConstants.XMLNS_ATTRIBUTE.equals(node.getNodeName()));
413 public Expr clone(Object context)
415 int len = tests.length;
416 List tests2 = new ArrayList(len);
417 for (int i = 0; i < len; i++)
418 tests2.add(tests[i].clone(context));
419 return new Selector(axis, tests2);
422 public boolean references(QName var)
424 for (int i = 0; i < tests.length; i++)
426 if (tests[i].references(var))
427 return true;
429 return false;
432 public String toString()
434 StringBuffer buf = new StringBuffer();
435 switch (axis)
437 case ANCESTOR:
438 buf.append("ancestor::");
439 break;
440 case ANCESTOR_OR_SELF:
441 buf.append("ancestor-or-self::");
442 break;
443 case ATTRIBUTE:
444 if (tests.length == 0 ||
445 (tests[0] instanceof NameTest))
446 buf.append('@');
447 else
448 buf.append("attribute::");
449 break;
450 case CHILD:
451 //buf.append("child::");
452 break;
453 case DESCENDANT:
454 buf.append("descendant::");
455 break;
456 case DESCENDANT_OR_SELF:
457 buf.append("descendant-or-self::");
458 break;
459 case FOLLOWING:
460 buf.append("following::");
461 break;
462 case FOLLOWING_SIBLING:
463 buf.append("following-sibling::");
464 break;
465 case NAMESPACE:
466 buf.append("namespace::");
467 break;
468 case PARENT:
469 if (tests.length == 0 ||
470 (tests[0] instanceof NodeTypeTest &&
471 ((NodeTypeTest) tests[0]).type == 0))
472 return "..";
473 buf.append("parent::");
474 break;
475 case PRECEDING:
476 buf.append("preceding::");
477 break;
478 case PRECEDING_SIBLING:
479 buf.append("preceding-sibling::");
480 break;
481 case SELF:
482 if (tests.length == 0 ||
483 (tests[0] instanceof NodeTypeTest &&
484 ((NodeTypeTest) tests[0]).type == 0))
485 return ".";
486 buf.append("self::");
487 break;
489 if (tests.length == 0)
490 buf.append("[error]");
491 else
493 for (int i = 0; i < tests.length; i++)
494 buf.append(tests[i]);
496 return buf.toString();