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 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
;
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
;
55 * A single component of a location path.
57 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
59 public final class Selector
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;
78 * Axis to select nodes in.
83 * List of tests to perform on candidates.
87 public Selector(int axis
, List
<?
extends Test
> tests
)
90 int len
= tests
.size();
91 this.tests
= new Test
[(len
== 0) ?
1 : len
];
93 tests
.toArray(this.tests
);
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()
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();
125 if (nodeType
== Node
.ATTRIBUTE_NODE
)
130 if (nodeType
!= Node
.ATTRIBUTE_NODE
)
133 case DESCENDANT_OR_SELF
:
138 for (int j
= 0; j
< tests
.length
&& len
> 0; j
++)
140 Test test
= tests
[j
];
141 if (!test
.matches(context
, pos
, len
))
147 private int getContextPosition(Node ctx
)
150 for (ctx
= ctx
.getPreviousSibling(); ctx
!= null;
151 ctx
= ctx
.getPreviousSibling())
153 if (tests
[0].matches(ctx
, 1, 1))
159 private int getContextSize(Node ctx
)
161 if (ctx
.getNodeType() == Node
.ATTRIBUTE_NODE
)
163 Node owner
= ((Attr
) ctx
).getOwnerElement();
164 return owner
.getAttributes().getLength();
167 Node sib
= ctx
.getPreviousSibling();
168 for (; sib
!= null; sib
= sib
.getPreviousSibling())
170 if (tests
[0].matches(ctx
, 1, 1))
173 sib
= ctx
.getNextSibling();
174 for (; sib
!= null; sib
= sib
.getNextSibling())
176 if (tests
[0].matches(ctx
, 1, 1))
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);
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);
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
);
222 // Documents and DocumentFragments should be considered
223 // if part of a location path where the axis involves
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
||
231 (tests
.length
== 1 &&
232 tests
[0] instanceof NodeTypeTest
&&
233 ((NodeTypeTest
) tests
[0]).type
== (short) 0))
235 successful
.add(node
);
239 if (test
.matches(node
, i
+ 1, len
))
240 successful
.add(node
);
242 candidates
= successful
;
243 len
= candidates
.size();
249 void addCandidates(Node context
, Collection
<Node
> candidates
)
251 // Build list of candidates
255 addChildNodes(context
, candidates
, false);
258 addChildNodes(context
, candidates
, true);
260 case DESCENDANT_OR_SELF
:
261 candidates
.add (context
);
262 addChildNodes(context
, candidates
, true);
265 addParentNode(context
, candidates
, false);
268 addParentNode(context
, candidates
, true);
270 case ANCESTOR_OR_SELF
:
271 candidates
.add(context
);
272 addParentNode(context
, candidates
, true);
274 case FOLLOWING_SIBLING
:
275 addFollowingNodes(context
, candidates
, false);
277 case PRECEDING_SIBLING
:
278 addPrecedingNodes(context
, candidates
, false);
281 addFollowingNodes(context
, candidates
, true);
284 addPrecedingNodes(context
, candidates
, true);
287 addAttributes(context
, candidates
);
290 addNamespaceAttributes(context
, candidates
);
293 candidates
.add(context
);
298 void addChildNodes(Node context
, Collection
<Node
> acc
, boolean recurse
)
300 Node child
= context
.getFirstChild();
301 while (child
!= null)
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();
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();
332 addChildNodes(cur
, acc
, true);
333 cur
= cur
.getNextSibling();
337 while (context
!= null)
339 context
= (context
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
340 ((Attr
) context
).getOwnerElement() : context
.getParentNode();
343 cur
= context
.getNextSibling();
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();
364 addChildNodes(cur
, acc
, true);
365 cur
= cur
.getPreviousSibling();
370 cur
= (cur
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
371 ((Attr
) cur
).getOwnerElement() : cur
.getParentNode();
373 addPrecedingNodes(cur
, acc
, recurse
);
377 void addAttributes(Node context
, Collection
<Node
> acc
)
379 NamedNodeMap attrs
= context
.getAttributes();
382 int attrLen
= attrs
.getLength();
383 for (int i
= 0; i
< attrLen
; i
++)
385 Node attr
= attrs
.item(i
);
386 if (!isNamespaceAttribute(attr
))
394 void addNamespaceAttributes(Node context
, Collection
<Node
> acc
)
396 NamedNodeMap attrs
= context
.getAttributes();
399 int attrLen
= attrs
.getLength();
400 for (int i
= 0; i
< attrLen
; i
++)
402 Node attr
= attrs
.item(i
);
403 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
<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
))
436 public String
toString()
438 CPStringBuilder buf
= new CPStringBuilder();
442 buf
.append("ancestor::");
444 case ANCESTOR_OR_SELF
:
445 buf
.append("ancestor-or-self::");
448 if (tests
.length
== 0 ||
449 (tests
[0] instanceof NameTest
))
452 buf
.append("attribute::");
455 //buf.append("child::");
458 buf
.append("descendant::");
460 case DESCENDANT_OR_SELF
:
461 buf
.append("descendant-or-self::");
464 buf
.append("following::");
466 case FOLLOWING_SIBLING
:
467 buf
.append("following-sibling::");
470 buf
.append("namespace::");
473 if (tests
.length
== 0 ||
474 (tests
[0] instanceof NodeTypeTest
&&
475 ((NodeTypeTest
) tests
[0]).type
== 0))
477 buf
.append("parent::");
480 buf
.append("preceding::");
482 case PRECEDING_SIBLING
:
483 buf
.append("preceding-sibling::");
486 if (tests
.length
== 0 ||
487 (tests
[0] instanceof NodeTypeTest
&&
488 ((NodeTypeTest
) tests
[0]).type
== 0))
490 buf
.append("self::");
493 if (tests
.length
== 0)
494 buf
.append("[error]");
497 for (int i
= 0; i
< tests
.length
; i
++)
498 buf
.append(tests
[i
]);
500 return buf
.toString();