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 NameTest(null, true, true);
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 short nodeType
= context
.getNodeType();
115 if (nodeType
== Node
.ATTRIBUTE_NODE
)
120 if (nodeType
!= Node
.ATTRIBUTE_NODE
)
123 case DESCENDANT_OR_SELF
:
128 int tlen
= tests
.length
;
131 int pos
= getContextPosition(context
);
132 int len
= getContextSize(context
);
134 System
.err
.println("WARNING: context size is 0");
135 for (int j
= 0; j
< tlen
&& len
> 0; j
++)
137 Test test
= tests
[j
];
138 if (!test
.matches(context
, pos
, len
))
145 private int getContextPosition(Node ctx
)
148 for (ctx
= ctx
.getPreviousSibling(); ctx
!= null;
149 ctx
= ctx
.getPreviousSibling())
154 private int getContextSize(Node ctx
)
156 if (ctx
.getNodeType() == Node
.ATTRIBUTE_NODE
)
158 Node owner
= ((Attr
) ctx
).getOwnerElement();
159 return owner
.getAttributes().getLength();
162 Node sib
= ctx
.getPreviousSibling();
163 for (; sib
!= null; sib
= sib
.getPreviousSibling())
165 sib
= ctx
.getNextSibling();
166 for (; sib
!= null; sib
= sib
.getNextSibling())
171 public Object
evaluate(Node context
, int pos
, int len
)
173 Set acc
= new LinkedHashSet();
174 addCandidates(context
, acc
);
175 List candidates
= new ArrayList(acc
);
176 List ret
= filterCandidates(candidates
, false);
180 Collection
evaluate(Node context
, Collection ns
)
182 Set acc
= new LinkedHashSet();
183 for (Iterator i
= ns
.iterator(); i
.hasNext(); )
184 addCandidates((Node
) i
.next(), acc
);
185 List candidates
= new ArrayList(acc
);
186 List ret
= filterCandidates(candidates
, true);
191 * Filter the given list of candidates according to the node tests.
193 List
filterCandidates(List candidates
, boolean cascade
)
195 int len
= candidates
.size();
196 int tlen
= tests
.length
;
197 if (tlen
> 0 && len
> 0)
199 // Present the result of each successful generation to the next test
200 for (int j
= 0; j
< tlen
&& len
> 0; j
++)
202 Test test
= tests
[j
];
203 List successful
= new ArrayList(len
);
204 for (int i
= 0; i
< len
; i
++)
206 Node node
= (Node
) candidates
.get(i
);
209 // Documents and DocumentFragments should be considered
210 // if part of a location path where the axis involves
212 short nodeType
= node
.getNodeType();
213 if ((nodeType
== Node
.DOCUMENT_NODE
||
214 nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
) &&
215 (axis
== DESCENDANT_OR_SELF
||
216 axis
== ANCESTOR_OR_SELF
||
218 (tests
.length
== 1 &&
219 tests
[0] instanceof NodeTypeTest
&&
220 ((NodeTypeTest
) tests
[0]).type
== (short) 0))
222 successful
.add(node
);
226 if (test
.matches(node
, i
+ 1, len
))
227 successful
.add(node
);
229 candidates
= successful
;
230 len
= candidates
.size();
236 void addCandidates(Node context
, Collection candidates
)
238 // Build list of candidates
242 addChildNodes(context
, candidates
, false);
245 addChildNodes(context
, candidates
, true);
247 case DESCENDANT_OR_SELF
:
248 candidates
.add (context
);
249 addChildNodes(context
, candidates
, true);
252 addParentNode(context
, candidates
, false);
255 addParentNode(context
, candidates
, true);
257 case ANCESTOR_OR_SELF
:
258 candidates
.add(context
);
259 addParentNode(context
, candidates
, true);
261 case FOLLOWING_SIBLING
:
262 addFollowingNodes(context
, candidates
, false);
264 case PRECEDING_SIBLING
:
265 addPrecedingNodes(context
, candidates
, false);
268 addFollowingNodes(context
, candidates
, true);
271 addPrecedingNodes(context
, candidates
, true);
274 addAttributes(context
, candidates
);
277 addNamespaceAttributes(context
, candidates
);
280 candidates
.add(context
);
285 void addChildNodes(Node context
, Collection acc
, boolean recurse
)
287 Node child
= context
.getFirstChild();
288 while (child
!= null)
292 addChildNodes(child
, acc
, recurse
);
293 child
= child
.getNextSibling();
297 void addParentNode(Node context
, Collection acc
, boolean recurse
)
299 Node parent
= (context
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
300 ((Attr
) context
).getOwnerElement() : context
.getParentNode();
305 addParentNode(parent
, acc
, recurse
);
309 void addFollowingNodes(Node context
, Collection acc
, boolean recurse
)
311 if (context
!= null && recurse
)
312 addChildNodes(context
, acc
, true);
313 Node cur
= (context
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
null :
314 context
.getNextSibling();
319 addChildNodes(cur
, acc
, true);
320 cur
= cur
.getNextSibling();
324 while (context
!= null)
326 context
= (context
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
327 ((Attr
) context
).getOwnerElement() : context
.getParentNode();
330 cur
= context
.getNextSibling();
335 addChildNodes(cur
, acc
, true);
336 cur
= cur
.getNextSibling();
343 void addPrecedingNodes(Node context
, Collection acc
, boolean recurse
)
345 Node cur
= (context
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
null :
346 context
.getPreviousSibling();
351 addChildNodes(cur
, acc
, true);
352 cur
= cur
.getPreviousSibling();
357 cur
= (cur
.getNodeType() == Node
.ATTRIBUTE_NODE
) ?
358 ((Attr
) cur
).getOwnerElement() : cur
.getParentNode();
360 addPrecedingNodes(cur
, acc
, recurse
);
364 void addAttributes(Node context
, Collection acc
)
366 NamedNodeMap attrs
= context
.getAttributes();
369 int attrLen
= attrs
.getLength();
370 for (int i
= 0; i
< attrLen
; i
++)
372 Node attr
= attrs
.item(i
);
373 if (!isNamespaceAttribute(attr
))
381 void addNamespaceAttributes(Node context
, Collection acc
)
383 NamedNodeMap attrs
= context
.getAttributes();
386 int attrLen
= attrs
.getLength();
387 for (int i
= 0; i
< attrLen
; i
++)
389 Node attr
= attrs
.item(i
);
390 if (isNamespaceAttribute(attr
))
396 final boolean isNamespaceAttribute(Node node
)
398 String uri
= node
.getNamespaceURI();
399 return (XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
.equals(uri
) ||
400 XMLConstants
.XMLNS_ATTRIBUTE
.equals(node
.getPrefix()) ||
401 XMLConstants
.XMLNS_ATTRIBUTE
.equals(node
.getNodeName()));
404 public Expr
clone(Object context
)
406 int len
= tests
.length
;
407 List tests2
= new ArrayList(len
);
408 for (int i
= 0; i
< len
; i
++)
409 tests2
.add(tests
[i
].clone(context
));
410 return new Selector(axis
, tests2
);
413 public boolean references(QName var
)
415 for (int i
= 0; i
< tests
.length
; i
++)
417 if (tests
[i
].references(var
))
423 public String
toString()
425 StringBuffer buf
= new StringBuffer();
429 buf
.append("ancestor::");
431 case ANCESTOR_OR_SELF
:
432 buf
.append("ancestor-or-self::");
435 if (tests
.length
== 0 ||
436 (tests
[0] instanceof NameTest
))
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))
464 buf
.append("parent::");
467 buf
.append("preceding::");
469 case PRECEDING_SIBLING
:
470 buf
.append("preceding-sibling::");
473 if (tests
.length
== 0 ||
474 (tests
[0] instanceof NodeTypeTest
&&
475 ((NodeTypeTest
) tests
[0]).type
== 0))
477 buf
.append("self::");
480 if (tests
.length
== 0)
481 buf
.append("[error]");
484 for (int i
= 0; i
< tests
.length
; i
++)
485 buf
.append(tests
[i
]);
487 return buf
.toString();