1
/* Copyright 2005 Elliotte Rusty Harold
3
This library is free software; you can redistribute it and/or modify
4
it under the terms of version 2.1 of the GNU Lesser General Public
5
License as published by the Free Software Foundation.
7
This library is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU Lesser General Public License for more details.
12
You should have received a copy of the GNU Lesser General Public
13
License along with this library; if not, write to the
14
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15
Boston, MA 02111-1307 USA
17
You can contact Elliotte Rusty Harold by sending e-mail to
18
elharo@metalab.unc.edu. Please include the word "XOM" in the
19
subject line. The XOM home page is located at http://www.xom.nu/
26
* Interface between Jaxen and XOM.
29
* @author Elliotte Rusty Harold
34
import org.jaxen.DefaultNavigator;
35
import org.jaxen.FunctionCallException;
36
import org.jaxen.JaxenConstants;
37
import org.jaxen.JaxenException;
38
import org.jaxen.NamedAccessNavigator;
39
import org.jaxen.UnsupportedAxisException;
40
import org.jaxen.XPath;
41
import org.jaxen.util.SingleObjectIterator;
43
import java.util.Iterator;
44
import java.util.List;
45
import java.util.ArrayList;
47
import java.util.NoSuchElementException;
50
class JaxenNavigator extends DefaultNavigator implements NamedAccessNavigator {
53
private static final long serialVersionUID = 7008740797833836742L;
56
public Iterator getSelfAxisIterator(Object contextNode) {
58
if (contextNode instanceof Text) {
59
// wrap text nodes in a list
60
Text node = (Text) contextNode;
61
ArrayList temp = new ArrayList();
62
ParentNode parent = node.getParent();
63
// parent is never null here due to DocumentFragment
64
int index = parent.indexOf(node);
67
while (first > 0 && parent.getChild(first-1).isText()) {
70
while (last < parent.getChildCount()-1 && parent.getChild(last+1).isText()) {
73
for (int i = first; i <= last; i++) {
74
temp.add(parent.getChild(i));
78
return new SingleObjectIterator(contextNode);
83
public Object getElementById(Object node, String id) {
86
if (node instanceof ArrayList) {
87
original = (Node) ((List) node).get(0);
90
original = (Node) node;
93
if (original.isElement() || original.isDocument()) {
94
parent = (ParentNode) original;
97
parent = original.getParent();
100
// find highest parent node
101
ParentNode high = parent;
102
while (parent != null) {
104
parent = parent.getParent();
107
// Now search down from the highest point for the requested ID
109
if (high.isDocument()) {
110
root = ((Document) high).getRootElement();
112
else { // document fragment
113
Node first = high.getChild(0);
114
if (first.isElement()) {
115
root = (Element) high.getChild(0);
122
return findByID(root, id);
127
// ????remove recursion
128
public static Element findByID(Element top, String id) {
130
if (hasID(top, id)) return top;
132
Elements children = top.getChildElements();
133
for (int i = 0; i < children.size(); i++) {
134
Element result = findByID(children.get(i), id);
135
if (result != null) return result;
143
private static boolean hasID(Element top, String id) {
145
int count = top.getAttributeCount();
146
for (int i = 0; i < count; i++) {
147
Attribute a = top.getAttribute(i);
148
if (Attribute.Type.ID == a.getType()) {
149
// Do not need to fully normalize here
150
// because if the value passed to the id() function
151
// contains any spaces; then it is converted into a
152
// search for multiple IDs, none of which have spaces
153
return a.getValue().trim().equals(id);
161
public String getNamespacePrefix(Object o) {
162
Namespace ns = (Namespace) o;
163
return ns.getPrefix();
167
public String getNamespaceStringValue(Object o) {
168
Namespace ns = (Namespace) o;
169
return ns.getValue();
173
public Iterator getNamespaceAxisIterator(Object contextNode) {
176
Element element = (Element) contextNode;
177
// ???? can probably avoid this list copy
178
Map bindings = element.getNamespacePrefixesInScope();
179
Iterator iterator = bindings.entrySet().iterator();
180
List result = new ArrayList(bindings.size()+1);
181
result.add(new Namespace("xml",
182
"http://www.w3.org/XML/1998/namespace", element));
184
while (iterator.hasNext()) {
185
Map.Entry binding = (Map.Entry) iterator.next();
186
String prefix = (String) binding.getKey();
187
String uri = (String) binding.getValue();
188
if (! "".equals(prefix) || ! "".equals(uri)) {
189
Namespace ns = new Namespace(prefix, uri, element);
193
return result.iterator();
195
catch (ClassCastException ex) {
196
return JaxenConstants.EMPTY_ITERATOR;
202
public Iterator getParentAxisIterator(Object contextNode) {
204
Node parent = (Node) getParentNode(contextNode);
205
if (parent == null) return JaxenConstants.EMPTY_ITERATOR;
206
else return new SingleObjectIterator(parent);
211
public Object getDocumentNode(Object o) {
213
Node node = (Node) o;
214
return node.getRoot();
219
public Object getDocument(String url) throws FunctionCallException {
220
throw new FunctionCallException("document() function not supported");
223
public Iterator getAttributeAxisIterator(Object contextNode) {
226
Element element = (Element) contextNode;
227
return element.attributeIterator();
229
catch (ClassCastException ex) {
230
return JaxenConstants.EMPTY_ITERATOR;
236
public Iterator getChildAxisIterator(Object o) {
238
if (o instanceof ParentNode) {
239
return new ChildIterator((ParentNode) o);
242
return JaxenConstants.EMPTY_ITERATOR;
248
public Iterator getFollowingSiblingAxisIterator(Object o) {
251
if (o instanceof ArrayList) {
252
List l = (ArrayList) o;
253
start = (Node) l.get(l.size()-1);
258
ParentNode parent = start.getParent();
259
if (parent == null) return JaxenConstants.EMPTY_ITERATOR;
260
int startPos = parent.indexOf(start) + 1;
261
return new ChildIterator(parent, startPos);
266
public Object getParentNode(Object o) {
269
if (o instanceof ArrayList) {
270
n = (Node) ((List) o).get(0);
275
return n.getParent();
280
public String getTextStringValue(Object o) {
282
List texts = (List) o;
283
if (texts.size() == 1) {
284
return ((Text) texts.get(0)).getValue();
287
StringBuffer result = new StringBuffer();
288
Iterator iterator = texts.iterator();
289
while (iterator.hasNext()) {
290
Text text = (Text) iterator.next();
291
result.append(text.getValue());
293
return result.toString();
299
private static class ChildIterator implements Iterator {
301
private final ParentNode parent;
303
private int xomIndex = 0;
304
private final int xomCount;
306
ChildIterator(ParentNode parent) {
307
this.parent = parent;
308
this.xomCount = parent.getChildCount();
312
ChildIterator(ParentNode parent, int startNode) {
313
this.parent = parent;
314
this.xomIndex = startNode;
315
this.xomCount = parent.getChildCount();
319
public boolean hasNext() {
321
for (int i = xomIndex; i < xomCount; i++) {
322
Node next = parent.getChild(i);
324
if (! ((Text) next).isEmpty()) {
335
public Object next() {
338
Node next = parent.getChild(xomIndex++);
340
Text t = (Text) next;
341
// Is this an empty text node?
342
boolean empty = t.isEmpty();
343
List texts = new ArrayList(1);
345
while (xomIndex < xomCount) {
346
Node nextText = parent.getChild(xomIndex);
347
if (! nextText.isText()) break;
351
if (! ((Text) nextText).isEmpty()) {
356
// need to make sure at least one of these texts is non-empty
357
if (empty) return next();
360
else if (next.isDocType()) {
370
public void remove() {
371
throw new UnsupportedOperationException();
377
private static class NamedChildIterator implements Iterator {
379
private final ParentNode parent;
381
private int index = -1;
382
private final int xomCount;
383
private Element next;
384
private final String localName;
385
private final String URI;
387
NamedChildIterator(ParentNode parent, String localName, String prefix, String namespaceURI) {
388
this.parent = parent;
389
this.xomCount = parent.getChildCount();
390
this.localName = localName;
391
if (namespaceURI == null) this.URI = "";
392
else this.URI = namespaceURI;
397
private void findNext() {
399
while (++index < xomCount) {
400
Node next = parent.getChild(index);
401
if (next.isElement()) {
402
Element element = (Element) next;
403
String elementNamespace = element.getNamespaceURI();
404
if (elementNamespace.equals(URI)) {
405
// I don't need to worry about the prefix here because XPath only iterates
406
// by local name and namespace URI. The prefix doesn't matter.
407
// This does assume that this class is non-public.
408
if (element.getLocalName().equals(localName)) {
418
public boolean hasNext() {
423
public Object next() {
425
if (next == null) throw new NoSuchElementException(); // Correct? Yes. Necessary????
426
Object result = next;
431
public void remove() {
432
throw new UnsupportedOperationException();
438
public String getElementNamespaceUri(Object element) {
439
return ((Element) element).getNamespaceURI();
443
// In Jaxen, name means the local name only
444
public String getElementName(Object element) {
445
return ((Element) element).getLocalName();
448
public String getElementQName(Object element) {
449
return ((Element) element).getQualifiedName();
453
public String getAttributeNamespaceUri(Object attr) {
454
Attribute attribute = (Attribute) attr;
455
return attribute.getNamespaceURI();
459
// In Jaxen, name means the local name only
460
public String getAttributeName(Object attr) {
461
Attribute attribute = (Attribute) attr;
462
return attribute.getLocalName();
466
public String getAttributeQName(Object attr) {
467
Attribute attribute = (Attribute) attr;
468
return attribute.getQualifiedName();
471
public String getProcessingInstructionData(Object o) {
472
ProcessingInstruction pi = (ProcessingInstruction) o;
473
return pi.getValue();
477
public String getProcessingInstructionTarget(Object o) {
478
ProcessingInstruction pi = (ProcessingInstruction) o;
479
return pi.getTarget();
483
public boolean isDocument(Object object) {
484
return object instanceof Document || object instanceof DocumentFragment;
488
public boolean isElement(Object object) {
489
return object instanceof Element;
493
public boolean isAttribute(Object object) {
494
return object instanceof Attribute;
498
public boolean isNamespace(Object object) {
499
return object instanceof Namespace;
503
public boolean isComment(Object object) {
504
return object instanceof Comment;
508
public boolean isText(Object object) {
509
// ???? hack: need to use a separate special subclass of ArrayList I can identify.
510
// But may not be necessary since this is not a public API. I don't
511
// think there's any way to get a different ArrayList into this method.
512
if (object instanceof ArrayList) {
513
Iterator iterator = ((List) object).iterator();
514
while (iterator.hasNext()) {
515
if (! (iterator.next() instanceof Text)) return false;
523
public boolean isProcessingInstruction(Object object) {
524
return object instanceof ProcessingInstruction;
528
public String getCommentStringValue(Object comment) {
529
return ((Comment) comment).getValue();
533
public String getElementStringValue(Object element) {
534
return ((Element) element).getValue();
538
public String getAttributeStringValue(Object attribute) {
539
return ((Attribute) attribute).getValue();
543
public XPath parseXPath(String expression) throws JaxenException {
544
return new JaxenConnector(expression);
548
public Iterator getChildAxisIterator(Object parent, String localName, String namespacePrefix, String namespaceURI)
549
throws UnsupportedAxisException {
551
if (parent instanceof ParentNode) {
552
return new NamedChildIterator((ParentNode) parent, localName, namespacePrefix, namespaceURI);
554
return JaxenConstants.EMPTY_ITERATOR;
559
public Iterator getAttributeAxisIterator(Object contextNode, String localName, String namespacePrefix, String namespaceURI)
560
throws UnsupportedAxisException {
562
// I don't need to worry about the prefix here because XPath only iterates
563
// by local name and namespace URI. The prefix doesn't matter.
564
// This does assume that this class is non-public.
566
Element element = (Element) contextNode;
567
Attribute result = null;
568
if (namespaceURI == null) {
569
result = element.getAttribute(localName);
572
result = element.getAttribute(localName, namespaceURI);
575
if (result == null) return JaxenConstants.EMPTY_ITERATOR;
577
return new SingleObjectIterator(result);
579
catch (ClassCastException ex) {
580
return JaxenConstants.EMPTY_ITERATOR;