~ubuntu-branches/ubuntu/precise/xom/precise

« back to all changes in this revision

Viewing changes to src/nu/xom/JaxenNavigator.java

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2007-11-25 15:50:40 UTC
  • Revision ID: james.westby@ubuntu.com-20071125155040-r75ikcqf1vu0cei7
Tags: upstream-1.1
ImportĀ upstreamĀ versionĀ 1.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2005 Elliotte Rusty Harold
 
2
   
 
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.
 
6
   
 
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.
 
11
   
 
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
 
16
   
 
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/
 
20
*/
 
21
 
 
22
package nu.xom;
 
23
 
 
24
/**
 
25
 * <p>
 
26
 * Interface between Jaxen and XOM.
 
27
 * </p>
 
28
 * 
 
29
 * @author Elliotte Rusty Harold
 
30
 * @version 1.1b6
 
31
 *
 
32
 */
 
33
 
 
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;
 
42
 
 
43
import java.util.Iterator;
 
44
import java.util.List;
 
45
import java.util.ArrayList;
 
46
import java.util.Map;
 
47
import java.util.NoSuchElementException;
 
48
 
 
49
 
 
50
class JaxenNavigator extends DefaultNavigator implements NamedAccessNavigator {
 
51
 
 
52
 
 
53
    private static final long serialVersionUID = 7008740797833836742L;
 
54
 
 
55
 
 
56
    public Iterator getSelfAxisIterator(Object contextNode) {
 
57
        
 
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);
 
65
            int first = index;
 
66
            int last = index;
 
67
            while (first > 0 && parent.getChild(first-1).isText()) {
 
68
                first--;
 
69
            }
 
70
            while (last < parent.getChildCount()-1 && parent.getChild(last+1).isText()) {
 
71
                last++;
 
72
            }
 
73
            for (int i = first; i <= last; i++) {
 
74
                temp.add(parent.getChild(i));
 
75
            }
 
76
            contextNode = temp;
 
77
        }
 
78
        return new SingleObjectIterator(contextNode);
 
79
        
 
80
    }
 
81
    
 
82
    
 
83
    public Object getElementById(Object node, String id) {
 
84
        
 
85
        Node original;
 
86
        if (node instanceof ArrayList) {
 
87
            original = (Node) ((List) node).get(0);
 
88
        }
 
89
        else {
 
90
            original = (Node) node;
 
91
        }
 
92
        ParentNode parent;
 
93
        if (original.isElement() || original.isDocument()) {
 
94
            parent = (ParentNode) original;
 
95
        }
 
96
        else {
 
97
            parent = original.getParent();
 
98
        }
 
99
        
 
100
        // find highest parent node
 
101
        ParentNode high = parent;
 
102
        while (parent != null) {
 
103
            high = parent;
 
104
            parent = parent.getParent();
 
105
        }
 
106
        
 
107
        // Now search down from the highest point for the requested ID
 
108
        Element root;
 
109
        if (high.isDocument()) {
 
110
            root = ((Document) high).getRootElement();
 
111
        }
 
112
        else { // document fragment
 
113
            Node first = high.getChild(0);
 
114
            if (first.isElement()) {
 
115
                root = (Element) high.getChild(0);
 
116
            }
 
117
            else {
 
118
                return null;
 
119
            }
 
120
        }
 
121
        
 
122
        return findByID(root, id);
 
123
        
 
124
    }
 
125
    
 
126
    
 
127
    // ????remove recursion
 
128
    public static Element findByID(Element top, String id) {
 
129
        
 
130
        if (hasID(top, id)) return top;
 
131
        else {
 
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;
 
136
            }
 
137
        }
 
138
        return null;
 
139
        
 
140
    }
 
141
    
 
142
    
 
143
    private static boolean hasID(Element top, String id) {
 
144
 
 
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);
 
154
            }
 
155
        }
 
156
        return false;
 
157
        
 
158
    }
 
159
 
 
160
 
 
161
    public String getNamespacePrefix(Object o) {
 
162
        Namespace ns = (Namespace) o;
 
163
        return ns.getPrefix();
 
164
    }
 
165
    
 
166
    
 
167
    public String getNamespaceStringValue(Object o) {
 
168
        Namespace ns = (Namespace) o;
 
169
        return ns.getValue();
 
170
    }
 
171
 
 
172
    
 
173
    public Iterator getNamespaceAxisIterator(Object contextNode) {
 
174
        
 
175
        try {
 
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));
 
183
 
 
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);
 
190
                    result.add(ns);
 
191
                }
 
192
            }
 
193
            return result.iterator();
 
194
        }
 
195
        catch (ClassCastException ex) {
 
196
            return JaxenConstants.EMPTY_ITERATOR;
 
197
        }
 
198
        
 
199
    }
 
200
    
 
201
    
 
202
    public Iterator getParentAxisIterator(Object contextNode)  {
 
203
        
 
204
        Node parent = (Node) getParentNode(contextNode);
 
205
        if (parent == null) return JaxenConstants.EMPTY_ITERATOR;
 
206
        else return new SingleObjectIterator(parent);
 
207
        
 
208
    }
 
209
    
 
210
    
 
211
    public Object getDocumentNode(Object o) {
 
212
    
 
213
        Node node = (Node) o;
 
214
        return node.getRoot();
 
215
        
 
216
    }
 
217
    
 
218
        
 
219
    public Object getDocument(String url) throws FunctionCallException {
 
220
        throw new FunctionCallException("document() function not supported");
 
221
    }
 
222
    
 
223
    public Iterator getAttributeAxisIterator(Object contextNode) {
 
224
        
 
225
        try {
 
226
            Element element = (Element) contextNode;
 
227
            return element.attributeIterator();
 
228
        }
 
229
        catch (ClassCastException ex) {
 
230
            return JaxenConstants.EMPTY_ITERATOR;
 
231
        }
 
232
        
 
233
    }
 
234
    
 
235
    
 
236
    public Iterator getChildAxisIterator(Object o) {
 
237
        
 
238
        if (o instanceof ParentNode) {
 
239
            return new ChildIterator((ParentNode) o);
 
240
        }
 
241
        else {
 
242
            return JaxenConstants.EMPTY_ITERATOR;
 
243
        }
 
244
        
 
245
    }
 
246
    
 
247
    
 
248
    public Iterator getFollowingSiblingAxisIterator(Object o) {
 
249
        
 
250
        Node start;
 
251
        if (o instanceof ArrayList) {
 
252
            List l = (ArrayList) o;
 
253
            start = (Node) l.get(l.size()-1);
 
254
        }
 
255
        else {
 
256
            start = (Node) o;
 
257
        }
 
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);
 
262
        
 
263
    }
 
264
    
 
265
    
 
266
    public Object getParentNode(Object o) {
 
267
        
 
268
        Node n;
 
269
        if (o instanceof ArrayList) {
 
270
            n = (Node) ((List) o).get(0);
 
271
        }
 
272
        else {
 
273
            n = (Node) o;
 
274
        }
 
275
        return n.getParent();
 
276
        
 
277
    }
 
278
 
 
279
    
 
280
    public String getTextStringValue(Object o) {
 
281
 
 
282
        List texts = (List) o;
 
283
        if (texts.size() == 1) {
 
284
            return ((Text) texts.get(0)).getValue();
 
285
        }
 
286
        else {
 
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());
 
292
            }
 
293
            return result.toString();
 
294
        }
 
295
        
 
296
    }
 
297
    
 
298
 
 
299
    private static class ChildIterator implements Iterator {
 
300
    
 
301
        private final ParentNode parent;
 
302
 
 
303
        private int xomIndex = 0;
 
304
        private final int xomCount;
 
305
        
 
306
        ChildIterator(ParentNode parent) {
 
307
            this.parent = parent;
 
308
            this.xomCount = parent.getChildCount();
 
309
        }
 
310
      
 
311
        
 
312
        ChildIterator(ParentNode parent, int startNode) {
 
313
            this.parent = parent;
 
314
            this.xomIndex = startNode;
 
315
            this.xomCount = parent.getChildCount();
 
316
        }
 
317
      
 
318
        
 
319
        public boolean hasNext() {
 
320
            
 
321
            for (int i = xomIndex; i < xomCount; i++) {
 
322
                Node next = parent.getChild(i); 
 
323
                if (next.isText()) {
 
324
                    if (! ((Text) next).isEmpty()) {
 
325
                        return true;
 
326
                    }
 
327
                }
 
328
                else return true;
 
329
            }
 
330
            return false;
 
331
            
 
332
        }
 
333
        
 
334
 
 
335
        public Object next() {
 
336
            
 
337
            Object result;
 
338
            Node next = parent.getChild(xomIndex++);
 
339
            if (next.isText()) {
 
340
                Text t = (Text) next;
 
341
                // Is this an empty text node?
 
342
                boolean empty = t.isEmpty();
 
343
                List texts = new ArrayList(1);
 
344
                texts.add(t);
 
345
                while (xomIndex < xomCount) {
 
346
                    Node nextText = parent.getChild(xomIndex);
 
347
                    if (! nextText.isText()) break;
 
348
                    xomIndex++;
 
349
                    texts.add(nextText);
 
350
                    if (empty) {
 
351
                        if (! ((Text) nextText).isEmpty()) {
 
352
                            empty = false;
 
353
                        }
 
354
                    }
 
355
                }
 
356
                // need to make sure at least one of these texts is non-empty
 
357
                if (empty) return next();
 
358
                else result = texts;
 
359
            }
 
360
            else if (next.isDocType()) {
 
361
                return next();
 
362
            }
 
363
            else {
 
364
                result = next;
 
365
            }
 
366
            return result;
 
367
            
 
368
        }
 
369
 
 
370
        public void remove() {
 
371
            throw new UnsupportedOperationException();
 
372
        }
 
373
        
 
374
    }
 
375
    
 
376
 
 
377
    private static class NamedChildIterator implements Iterator {
 
378
    
 
379
        private final ParentNode parent;
 
380
 
 
381
        private int index = -1;
 
382
        private final int xomCount;
 
383
        private Element next;
 
384
        private final String localName;
 
385
        private final String URI;
 
386
        
 
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;
 
393
            
 
394
            findNext();
 
395
        }
 
396
      
 
397
        private void findNext() {
 
398
            
 
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)) {
 
409
                            this.next = element;
 
410
                            return;
 
411
                        }
 
412
                    }
 
413
                }
 
414
            }
 
415
            next = null;
 
416
        }
 
417
        
 
418
        public boolean hasNext() {
 
419
            return next != null;
 
420
        }
 
421
        
 
422
 
 
423
        public Object next() {
 
424
            
 
425
            if (next == null) throw new NoSuchElementException(); // Correct? Yes. Necessary????
 
426
            Object result = next;
 
427
            findNext();
 
428
            return result;
 
429
        }
 
430
 
 
431
        public void remove() {
 
432
            throw new UnsupportedOperationException();
 
433
        }
 
434
        
 
435
    }
 
436
 
 
437
    
 
438
    public String getElementNamespaceUri(Object element) {
 
439
        return ((Element) element).getNamespaceURI();
 
440
    }
 
441
 
 
442
    
 
443
    // In Jaxen, name means the local name only 
 
444
    public String getElementName(Object element) {
 
445
        return ((Element) element).getLocalName();
 
446
    }
 
447
 
 
448
    public String getElementQName(Object element) {
 
449
        return ((Element) element).getQualifiedName();
 
450
    }
 
451
 
 
452
    
 
453
    public String getAttributeNamespaceUri(Object attr) {
 
454
        Attribute attribute = (Attribute) attr;
 
455
        return attribute.getNamespaceURI();
 
456
    }
 
457
 
 
458
    
 
459
    // In Jaxen, name means the local name only 
 
460
    public String getAttributeName(Object attr) {
 
461
        Attribute attribute = (Attribute) attr;
 
462
        return attribute.getLocalName();
 
463
    }
 
464
 
 
465
    
 
466
    public String getAttributeQName(Object attr) {
 
467
        Attribute attribute = (Attribute) attr;
 
468
        return attribute.getQualifiedName();
 
469
    }
 
470
 
 
471
    public String getProcessingInstructionData(Object o) {
 
472
        ProcessingInstruction pi = (ProcessingInstruction) o;
 
473
        return pi.getValue();
 
474
    }
 
475
 
 
476
   
 
477
    public String getProcessingInstructionTarget(Object o) {
 
478
        ProcessingInstruction pi = (ProcessingInstruction) o;
 
479
        return pi.getTarget();
 
480
    }
 
481
 
 
482
    
 
483
    public boolean isDocument(Object object) {
 
484
        return object instanceof Document || object instanceof DocumentFragment;
 
485
    }
 
486
 
 
487
    
 
488
    public boolean isElement(Object object) {
 
489
        return object instanceof Element;
 
490
    }
 
491
 
 
492
    
 
493
    public boolean isAttribute(Object object) {
 
494
        return object instanceof Attribute;
 
495
    }
 
496
 
 
497
    
 
498
    public boolean isNamespace(Object object) {
 
499
        return object instanceof Namespace;
 
500
    }
 
501
 
 
502
    
 
503
    public boolean isComment(Object object) {
 
504
        return object instanceof Comment;
 
505
    }
 
506
 
 
507
    
 
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;
 
516
            }
 
517
            return true;
 
518
        }
 
519
        return false;
 
520
    }
 
521
 
 
522
    
 
523
    public boolean isProcessingInstruction(Object object) {
 
524
        return object instanceof ProcessingInstruction;
 
525
    }
 
526
 
 
527
    
 
528
    public String getCommentStringValue(Object comment) {
 
529
        return ((Comment) comment).getValue();
 
530
    }
 
531
 
 
532
    
 
533
    public String getElementStringValue(Object element) {
 
534
        return ((Element) element).getValue();
 
535
    }
 
536
 
 
537
    
 
538
    public String getAttributeStringValue(Object attribute) {
 
539
        return ((Attribute) attribute).getValue();
 
540
    }
 
541
    
 
542
 
 
543
    public XPath parseXPath(String expression) throws JaxenException {
 
544
        return new JaxenConnector(expression);
 
545
    }
 
546
 
 
547
 
 
548
    public Iterator getChildAxisIterator(Object parent, String localName, String namespacePrefix, String namespaceURI) 
 
549
      throws UnsupportedAxisException {
 
550
        
 
551
        if (parent instanceof ParentNode) {
 
552
            return new NamedChildIterator((ParentNode) parent, localName, namespacePrefix, namespaceURI);
 
553
        }
 
554
        return JaxenConstants.EMPTY_ITERATOR;
 
555
        
 
556
    }
 
557
 
 
558
 
 
559
    public Iterator getAttributeAxisIterator(Object contextNode, String localName, String namespacePrefix, String namespaceURI) 
 
560
      throws UnsupportedAxisException {
 
561
 
 
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.
 
565
        try {
 
566
            Element element = (Element) contextNode;
 
567
            Attribute result = null;
 
568
            if (namespaceURI == null) {
 
569
                result = element.getAttribute(localName);
 
570
            }
 
571
            else {
 
572
                result = element.getAttribute(localName, namespaceURI);
 
573
            }
 
574
            
 
575
            if (result == null) return JaxenConstants.EMPTY_ITERATOR;
 
576
            
 
577
            return new SingleObjectIterator(result);
 
578
        }
 
579
        catch (ClassCastException ex) {
 
580
            return JaxenConstants.EMPTY_ITERATOR;
 
581
        }
 
582
        
 
583
    }
 
584
    
 
585
    
 
586
}