Merge from the pain train
[official-gcc.git] / libjava / gnu / xml / xpath / Selector.java
blob5fd45b9117a0632cbbd4a494811b4934d7aaee82
1 /* Selector.java --
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)
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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 org.w3c.dom.Attr;
48 import org.w3c.dom.NamedNodeMap;
49 import org.w3c.dom.Node;
51 /**
52 * A single component of a location path.
54 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
56 public final class Selector
57 extends Path
60 public static final int ANCESTOR = 0;
61 public static final int ANCESTOR_OR_SELF = 1;
62 public static final int ATTRIBUTE = 2;
63 public static final int CHILD = 3;
64 public static final int DESCENDANT = 4;
65 public static final int DESCENDANT_OR_SELF = 5;
66 public static final int FOLLOWING = 6;
67 public static final int FOLLOWING_SIBLING = 7;
68 public static final int NAMESPACE = 8;
69 public static final int PARENT = 9;
70 public static final int PRECEDING = 10;
71 public static final int PRECEDING_SIBLING = 11;
72 public static final int SELF = 12;
74 /**
75 * Axis to select nodes in.
77 final int axis;
79 /**
80 * List of tests to perform on candidates.
82 final Test[] tests;
84 public Selector(int axis, List tests)
86 this.axis = axis;
87 this.tests = new Test[tests.size()];
88 tests.toArray(this.tests);
89 if (axis == NAMESPACE &&
90 this.tests.length > 0 &&
91 this.tests[0] instanceof NameTest)
93 NameTest nt = (NameTest) this.tests[0];
94 this.tests[0] = new NamespaceTest(nt.qName, nt.anyLocalName, nt.any);
98 /**
99 * Returns the list of tests to perform on candidates.
101 public Test[] getTests()
103 return tests;
106 public boolean matches(Node context)
108 short nodeType = context.getNodeType();
109 switch (axis)
111 case CHILD:
112 if (nodeType == Node.ATTRIBUTE_NODE)
114 return false;
116 break;
117 case ATTRIBUTE:
118 case NAMESPACE:
119 if (nodeType != Node.ATTRIBUTE_NODE)
121 return false;
123 break;
124 case DESCENDANT_OR_SELF:
125 return true;
126 default:
127 return false;
129 int tlen = tests.length;
130 if (tlen > 0)
132 int pos = getContextPosition(context);
133 int len = getContextSize(context);
134 for (int j = 0; j < tlen && len > 0; j++)
136 Test test = tests[j];
137 if (!test.matches(context, pos, len))
139 return false;
143 return true;
146 private int getContextPosition(Node ctx)
148 int pos = 1;
149 for (ctx = ctx.getPreviousSibling(); ctx != null;
150 ctx = ctx.getPreviousSibling())
152 pos++;
154 return pos;
157 private int getContextSize(Node ctx)
159 if (ctx.getNodeType() == Node.ATTRIBUTE_NODE)
161 Node parent = ((Attr) ctx).getOwnerElement();
162 return parent.getAttributes().getLength();
164 Node parent = ctx.getParentNode();
165 if (parent != null)
167 return parent.getChildNodes().getLength();
169 return 1;
172 public Object evaluate(Node context, int pos, int len)
174 Set acc = new LinkedHashSet();
175 addCandidates(context, acc);
176 List candidates = new ArrayList(acc);
177 //Collections.sort(candidates, documentOrderComparator);
178 List ret = filterCandidates(candidates, false);
179 return ret;
182 Collection evaluate(Node context, Collection ns)
184 Set acc = new LinkedHashSet();
185 for (Iterator i = ns.iterator(); i.hasNext(); )
187 addCandidates((Node) i.next(), acc);
189 List candidates = new ArrayList(acc);
190 //Collections.sort(candidates, documentOrderComparator);
191 List ret = filterCandidates(candidates, true);
192 return ret;
196 * Filter the given list of candidates according to the node tests.
198 List filterCandidates(List candidates, boolean cascade)
200 int len = candidates.size();
201 int tlen = tests.length;
202 if (tlen > 0 && len > 0)
204 // Present the result of each successful generation to the next test
205 for (int j = 0; j < tlen && len > 0; j++)
207 Test test = tests[j];
208 List successful = new ArrayList(len);
209 for (int i = 0; i < len; i++)
211 Node node = (Node) candidates.get(i);
212 if (cascade)
214 // Documents and DocumentFragments should be considered
215 // if part of a location path where the axis involves
216 // the SELF concept
217 short nodeType = node.getNodeType();
218 if ((nodeType == Node.DOCUMENT_NODE ||
219 nodeType == Node.DOCUMENT_FRAGMENT_NODE) &&
220 (axis == DESCENDANT_OR_SELF ||
221 axis == ANCESTOR_OR_SELF ||
222 axis == SELF) &&
223 (tests.length == 1 &&
224 tests[0] instanceof NodeTypeTest &&
225 ((NodeTypeTest) tests[0]).type == (short) 0))
227 successful.add(node);
228 continue;
231 if (test.matches(node, i + 1, len))
233 successful.add(node);
236 System.err.println("Testing "+node);
237 int p = getContextPosition(node);
238 int l = getContextSize(node);
239 if (test.matches(node, p, l))
241 successful.add(node);
244 candidates = successful;
245 len = candidates.size();
248 return candidates;
251 void addCandidates(Node context, Collection candidates)
253 // Build list of candidates
254 switch (axis)
256 case CHILD:
257 addChildNodes(context, candidates, false);
258 break;
259 case DESCENDANT:
260 addChildNodes(context, candidates, true);
261 break;
262 case DESCENDANT_OR_SELF:
263 candidates.add (context);
264 addChildNodes(context, candidates, true);
265 break;
266 case PARENT:
267 addParentNode(context, candidates, false);
268 break;
269 case ANCESTOR:
270 addParentNode(context, candidates, true);
271 break;
272 case ANCESTOR_OR_SELF:
273 candidates.add(context);
274 addParentNode(context, candidates, true);
275 break;
276 case FOLLOWING_SIBLING:
277 addFollowingNodes(context, candidates, false);
278 break;
279 case PRECEDING_SIBLING:
280 addPrecedingNodes(context, candidates, false);
281 break;
282 case FOLLOWING:
283 addFollowingNodes(context, candidates, true);
284 break;
285 case PRECEDING:
286 addPrecedingNodes(context, candidates, true);
287 break;
288 case ATTRIBUTE:
289 addAttributes(context, candidates);
290 break;
291 case NAMESPACE:
292 addNamespaceAttributes(context, candidates);
293 break;
294 case SELF:
295 candidates.add(context);
296 break;
300 void addChildNodes(Node context, Collection acc, boolean recurse)
302 Node child = context.getFirstChild();
303 while (child != null)
305 acc.add(child);
306 if (recurse)
308 addChildNodes(child, acc, recurse);
310 child = child.getNextSibling();
314 void addParentNode(Node context, Collection acc, boolean recurse)
316 Node parent = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
317 ((Attr) context).getOwnerElement() : context.getParentNode();
318 if (parent != null)
320 acc.add(parent);
321 if (recurse)
323 addParentNode(parent, acc, recurse);
328 void addFollowingNodes(Node context, Collection acc, boolean recurse)
330 Node cur = context.getNextSibling();
331 while (cur != null)
333 acc.add(cur);
334 if (recurse)
336 addChildNodes(cur, acc, true);
338 cur = cur.getNextSibling();
340 if (recurse)
342 context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
343 ((Attr) context).getOwnerElement() : context.getParentNode();
344 if (context != null)
346 addFollowingNodes(context, acc, recurse);
351 void addPrecedingNodes(Node context, Collection acc, boolean recurse)
353 Node cur = context.getPreviousSibling();
354 while (cur != null)
356 acc.add(cur);
357 if (recurse)
359 addChildNodes(cur, acc, true);
361 cur = cur.getPreviousSibling();
363 if (recurse)
365 context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
366 ((Attr) context).getOwnerElement() : context.getParentNode();
367 if (context != null)
369 addPrecedingNodes(context, acc, recurse);
374 void addAttributes(Node context, Collection acc)
376 NamedNodeMap attrs = context.getAttributes();
377 if (attrs != null)
379 int attrLen = attrs.getLength();
380 for (int i = 0; i < attrLen; i++)
382 Node attr = attrs.item(i);
383 if (!isNamespaceAttribute(attr))
385 acc.add(attr);
391 void addNamespaceAttributes(Node context, Collection acc)
393 NamedNodeMap attrs = context.getAttributes();
394 if (attrs != null)
396 int attrLen = attrs.getLength();
397 for (int i = 0; i < attrLen; i++)
399 Node attr = attrs.item(i);
400 if (isNamespaceAttribute(attr))
402 acc.add(attr);
408 final boolean isNamespaceAttribute(Node node)
410 String uri = node.getNamespaceURI();
411 return (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
412 XMLConstants.XMLNS_ATTRIBUTE.equals(node.getPrefix()) ||
413 XMLConstants.XMLNS_ATTRIBUTE.equals(node.getNodeName()));
416 public Expr clone(Object context)
418 int len = tests.length;
419 List tests2 = new ArrayList(len);
420 for (int i = 0; i < len; i++)
422 tests2.add(tests[i].clone(context));
424 return new Selector(axis, tests2);
427 public String toString()
429 StringBuffer buf = new StringBuffer();
430 switch (axis)
432 case ANCESTOR:
433 buf.append("ancestor::");
434 break;
435 case ANCESTOR_OR_SELF:
436 buf.append("ancestor-or-self::");
437 break;
438 case ATTRIBUTE:
439 buf.append("attribute::");
440 break;
441 case CHILD:
442 //buf.append("child::");
443 break;
444 case DESCENDANT:
445 buf.append("descendant::");
446 break;
447 case DESCENDANT_OR_SELF:
448 buf.append("descendant-or-self::");
449 break;
450 case FOLLOWING:
451 buf.append("following::");
452 break;
453 case FOLLOWING_SIBLING:
454 buf.append("following-sibling::");
455 break;
456 case NAMESPACE:
457 buf.append("namespace::");
458 break;
459 case PARENT:
460 if (tests.length == 0 ||
461 (tests[0] instanceof NodeTypeTest &&
462 ((NodeTypeTest) tests[0]).type == 0))
464 return "..";
466 buf.append("parent::");
467 break;
468 case PRECEDING:
469 buf.append("preceding::");
470 break;
471 case PRECEDING_SIBLING:
472 buf.append("preceding-sibling::");
473 break;
474 case SELF:
475 if (tests.length == 0 ||
476 (tests[0] instanceof NodeTypeTest &&
477 ((NodeTypeTest) tests[0]).type == 0))
479 return ".";
481 buf.append("self::");
482 break;
484 if (tests.length == 0)
486 buf.append('*');
488 else
490 for (int i = 0; i < tests.length; i++)
492 buf.append(tests[i]);
495 return buf.toString();