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)
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
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 org
.w3c
.dom
.Attr
;
48 import org
.w3c
.dom
.NamedNodeMap
;
49 import org
.w3c
.dom
.Node
;
52 * A single component of a location path.
54 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
56 public final class Selector
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;
75 * Axis to select nodes in.
80 * List of tests to perform on candidates.
84 public Selector(int axis
, List tests
)
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
);
99 * Returns the list of tests to perform on candidates.
101 public Test
[] getTests()
106 public boolean matches(Node context
)
108 short nodeType
= context
.getNodeType();
112 if (nodeType
== Node
.ATTRIBUTE_NODE
)
119 if (nodeType
!= Node
.ATTRIBUTE_NODE
)
124 case DESCENDANT_OR_SELF
:
129 int tlen
= tests
.length
;
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
))
146 private int getContextPosition(Node ctx
)
149 for (ctx
= ctx
.getPreviousSibling(); ctx
!= null;
150 ctx
= ctx
.getPreviousSibling())
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();
167 return parent
.getChildNodes().getLength();
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);
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);
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
);
214 // Documents and DocumentFragments should be considered
215 // if part of a location path where the axis involves
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
||
223 (tests
.length
== 1 &&
224 tests
[0] instanceof NodeTypeTest
&&
225 ((NodeTypeTest
) tests
[0]).type
== (short) 0))
227 successful
.add(node
);
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();
251 void addCandidates(Node context
, Collection candidates
)
253 // Build list of candidates
257 addChildNodes(context
, candidates
, false);
260 addChildNodes(context
, candidates
, true);
262 case DESCENDANT_OR_SELF
:
263 candidates
.add (context
);
264 addChildNodes(context
, candidates
, true);
267 addParentNode(context
, candidates
, false);
270 addParentNode(context
, candidates
, true);
272 case ANCESTOR_OR_SELF
:
273 candidates
.add(context
);
274 addParentNode(context
, candidates
, true);
276 case FOLLOWING_SIBLING
:
277 addFollowingNodes(context
, candidates
, false);
279 case PRECEDING_SIBLING
:
280 addPrecedingNodes(context
, candidates
, false);
283 addFollowingNodes(context
, candidates
, true);
286 addPrecedingNodes(context
, candidates
, true);
289 addAttributes(context
, candidates
);
292 addNamespaceAttributes(context
, candidates
);
295 candidates
.add(context
);
300 void addChildNodes(Node context
, Collection acc
, boolean recurse
)
302 Node child
= context
.getFirstChild();
303 while (child
!= null)
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();
323 addParentNode(parent
, acc
, recurse
);
328 void addFollowingNodes(Node context
, Collection acc
, boolean recurse
)
330 Node cur
= context
.getNextSibling();
336 addChildNodes(cur
, acc
, true);
338 cur
= cur
.getNextSibling();
342 context
= (context
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
343 ((Attr
) context
).getOwnerElement() : context
.getParentNode();
346 addFollowingNodes(context
, acc
, recurse
);
351 void addPrecedingNodes(Node context
, Collection acc
, boolean recurse
)
353 Node cur
= context
.getPreviousSibling();
359 addChildNodes(cur
, acc
, true);
361 cur
= cur
.getPreviousSibling();
365 context
= (context
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
366 ((Attr
) context
).getOwnerElement() : context
.getParentNode();
369 addPrecedingNodes(context
, acc
, recurse
);
374 void addAttributes(Node context
, Collection acc
)
376 NamedNodeMap attrs
= context
.getAttributes();
379 int attrLen
= attrs
.getLength();
380 for (int i
= 0; i
< attrLen
; i
++)
382 Node attr
= attrs
.item(i
);
383 if (!isNamespaceAttribute(attr
))
391 void addNamespaceAttributes(Node context
, Collection acc
)
393 NamedNodeMap attrs
= context
.getAttributes();
396 int attrLen
= attrs
.getLength();
397 for (int i
= 0; i
< attrLen
; i
++)
399 Node attr
= attrs
.item(i
);
400 if (isNamespaceAttribute(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();
433 buf
.append("ancestor::");
435 case ANCESTOR_OR_SELF
:
436 buf
.append("ancestor-or-self::");
439 buf
.append("attribute::");
442 //buf.append("child::");
445 buf
.append("descendant::");
447 case DESCENDANT_OR_SELF
:
448 buf
.append("descendant-or-self::");
451 buf
.append("following::");
453 case FOLLOWING_SIBLING
:
454 buf
.append("following-sibling::");
457 buf
.append("namespace::");
460 if (tests
.length
== 0 ||
461 (tests
[0] instanceof NodeTypeTest
&&
462 ((NodeTypeTest
) tests
[0]).type
== 0))
466 buf
.append("parent::");
469 buf
.append("preceding::");
471 case PRECEDING_SIBLING
:
472 buf
.append("preceding-sibling::");
475 if (tests
.length
== 0 ||
476 (tests
[0] instanceof NodeTypeTest
&&
477 ((NodeTypeTest
) tests
[0]).type
== 0))
481 buf
.append("self::");
484 if (tests
.length
== 0)
490 for (int i
= 0; i
< tests
.length
; i
++)
492 buf
.append(tests
[i
]);
495 return buf
.toString();