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)
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 int len
= tests
.size();
89 this.tests
= new Test
[(len
== 0) ?
1 : len
];
91 tests
.toArray(this.tests
);
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()
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();
123 if (nodeType
== Node
.ATTRIBUTE_NODE
)
128 if (nodeType
!= Node
.ATTRIBUTE_NODE
)
131 case DESCENDANT_OR_SELF
:
136 for (int j
= 0; j
< tests
.length
&& len
> 0; j
++)
138 Test test
= tests
[j
];
139 if (!test
.matches(context
, pos
, len
))
145 private int getContextPosition(Node ctx
)
148 for (ctx
= ctx
.getPreviousSibling(); ctx
!= null;
149 ctx
= ctx
.getPreviousSibling())
151 if (tests
[0].matches(ctx
, 1, 1))
157 private int getContextSize(Node ctx
)
159 if (ctx
.getNodeType() == Node
.ATTRIBUTE_NODE
)
161 Node owner
= ((Attr
) ctx
).getOwnerElement();
162 return owner
.getAttributes().getLength();
165 Node sib
= ctx
.getPreviousSibling();
166 for (; sib
!= null; sib
= sib
.getPreviousSibling())
168 if (tests
[0].matches(ctx
, 1, 1))
171 sib
= ctx
.getNextSibling();
172 for (; sib
!= null; sib
= sib
.getNextSibling())
174 if (tests
[0].matches(ctx
, 1, 1))
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);
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);
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
);
218 // Documents and DocumentFragments should be considered
219 // if part of a location path where the axis involves
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
||
227 (tests
.length
== 1 &&
228 tests
[0] instanceof NodeTypeTest
&&
229 ((NodeTypeTest
) tests
[0]).type
== (short) 0))
231 successful
.add(node
);
235 if (test
.matches(node
, i
+ 1, len
))
236 successful
.add(node
);
238 candidates
= successful
;
239 len
= candidates
.size();
245 void addCandidates(Node context
, Collection candidates
)
247 // Build list of candidates
251 addChildNodes(context
, candidates
, false);
254 addChildNodes(context
, candidates
, true);
256 case DESCENDANT_OR_SELF
:
257 candidates
.add (context
);
258 addChildNodes(context
, candidates
, true);
261 addParentNode(context
, candidates
, false);
264 addParentNode(context
, candidates
, true);
266 case ANCESTOR_OR_SELF
:
267 candidates
.add(context
);
268 addParentNode(context
, candidates
, true);
270 case FOLLOWING_SIBLING
:
271 addFollowingNodes(context
, candidates
, false);
273 case PRECEDING_SIBLING
:
274 addPrecedingNodes(context
, candidates
, false);
277 addFollowingNodes(context
, candidates
, true);
280 addPrecedingNodes(context
, candidates
, true);
283 addAttributes(context
, candidates
);
286 addNamespaceAttributes(context
, candidates
);
289 candidates
.add(context
);
294 void addChildNodes(Node context
, Collection acc
, boolean recurse
)
296 Node child
= context
.getFirstChild();
297 while (child
!= null)
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();
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();
328 addChildNodes(cur
, acc
, true);
329 cur
= cur
.getNextSibling();
333 while (context
!= null)
335 context
= (context
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
336 ((Attr
) context
).getOwnerElement() : context
.getParentNode();
339 cur
= context
.getNextSibling();
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();
360 addChildNodes(cur
, acc
, true);
361 cur
= cur
.getPreviousSibling();
366 cur
= (cur
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
367 ((Attr
) cur
).getOwnerElement() : cur
.getParentNode();
369 addPrecedingNodes(cur
, acc
, recurse
);
373 void addAttributes(Node context
, Collection acc
)
375 NamedNodeMap attrs
= context
.getAttributes();
378 int attrLen
= attrs
.getLength();
379 for (int i
= 0; i
< attrLen
; i
++)
381 Node attr
= attrs
.item(i
);
382 if (!isNamespaceAttribute(attr
))
390 void addNamespaceAttributes(Node context
, Collection acc
)
392 NamedNodeMap attrs
= context
.getAttributes();
395 int attrLen
= attrs
.getLength();
396 for (int i
= 0; i
< attrLen
; i
++)
398 Node attr
= attrs
.item(i
);
399 if (isNamespaceAttribute(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
))
432 public String
toString()
434 StringBuffer buf
= new StringBuffer();
438 buf
.append("ancestor::");
440 case ANCESTOR_OR_SELF
:
441 buf
.append("ancestor-or-self::");
444 if (tests
.length
== 0 ||
445 (tests
[0] instanceof NameTest
))
448 buf
.append("attribute::");
451 //buf.append("child::");
454 buf
.append("descendant::");
456 case DESCENDANT_OR_SELF
:
457 buf
.append("descendant-or-self::");
460 buf
.append("following::");
462 case FOLLOWING_SIBLING
:
463 buf
.append("following-sibling::");
466 buf
.append("namespace::");
469 if (tests
.length
== 0 ||
470 (tests
[0] instanceof NodeTypeTest
&&
471 ((NodeTypeTest
) tests
[0]).type
== 0))
473 buf
.append("parent::");
476 buf
.append("preceding::");
478 case PRECEDING_SIBLING
:
479 buf
.append("preceding-sibling::");
482 if (tests
.length
== 0 ||
483 (tests
[0] instanceof NodeTypeTest
&&
484 ((NodeTypeTest
) tests
[0]).type
== 0))
486 buf
.append("self::");
489 if (tests
.length
== 0)
490 buf
.append("[error]");
493 for (int i
= 0; i
< tests
.length
; i
++)
494 buf
.append(tests
[i
]);
496 return buf
.toString();