KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > icl > saxon > jdom > NodeWrapper


1 package com.icl.saxon.jdom;
2 import com.icl.saxon.om.NodeInfo;
3 import com.icl.saxon.om.DocumentInfo;
4 import com.icl.saxon.om.AxisEnumeration;
5 import com.icl.saxon.om.NamePool;
6 import com.icl.saxon.om.Axis;
7 import com.icl.saxon.pattern.NodeTest;
8 import com.icl.saxon.pattern.AnyNodeTest;
9 import com.icl.saxon.output.Outputter;
10 import com.icl.saxon.om.EmptyEnumeration;
11 import com.icl.saxon.om.SingletonEnumeration;
12 import org.jdom.*;
13 import javax.xml.transform.TransformerException;
14
15 // NB: this module requires JDK 1.2
16

17 import java.util.List;
18 import java.util.Iterator;
19 import java.util.ListIterator;
20 import java.util.Stack;
21 import java.util.HashMap;
22
23 /**
24   * A node in the XML parse tree representing an XML element, character content, or attribute.<P>
25   * This is the top class in the interface hierarchy for nodes; see NodeImpl for the implementation
26   * hierarchy.
27   * @author <A HREF="mailto:mhkay@iclway.co.uk>Michael H. Kay</A>
28   */

29
30 public class NodeWrapper implements NodeInfo {
31
32     protected Object node;
33     protected short nodeType;
34     protected NodeWrapper parent;
35     protected DocumentWrapper docWrapper;
36     protected int index;
37         // the index is the only way of distinguishing
38
// two text nodes with the same parent!
39

40     public NodeWrapper(Object node, NodeWrapper parent, int index) {
41         this.node = node;
42         this.parent = parent;
43         this.index = index;
44     }
45     
46     public NodeWrapper makeWrapper(Object node, NodeWrapper parent, int index) {
47         NodeWrapper wrapper;
48         if (node instanceof Document) {
49             return docWrapper;
50         } else if (node instanceof Element) {
51             wrapper = new NodeWrapper(node, parent, index);
52             wrapper.nodeType = NodeInfo.ELEMENT;
53         } else if (node instanceof Attribute) {
54             wrapper = new NodeWrapper(node, parent, index);
55             wrapper.nodeType = NodeInfo.ATTRIBUTE;
56         } else if (node instanceof String || node instanceof Text) { // changed in JDOM beta 8
57
wrapper = new NodeWrapper(node, parent, index);
58             wrapper.nodeType = NodeInfo.TEXT;
59         } else if (node instanceof CDATA) {
60             wrapper = new NodeWrapper(node, parent, index);
61             wrapper.nodeType = NodeInfo.TEXT;
62         } else if (node instanceof Comment) {
63             wrapper = new NodeWrapper(node, parent, index);
64             wrapper.nodeType = NodeInfo.COMMENT;
65         } else if (node instanceof ProcessingInstruction) {
66             wrapper = new NodeWrapper(node, parent, index);
67             wrapper.nodeType = NodeInfo.PI;
68         } else {
69             throw new IllegalArgumentException("Bad node type in JDOM! " + node.getClass() + " instance " + node.toString());
70         }
71         wrapper.docWrapper = parent.docWrapper;
72         return wrapper;
73     }
74
75     /**
76     * Get the underlying JDOM node
77     */

78     
79     public Object getNode() {
80         return node;
81     }
82
83     /**
84     * Return the type of node.
85     * @return one of the values Node.ELEMENT, Node.TEXT, Node.ATTRIBUTE, etc.
86     */

87
88     public short getNodeType() {
89         return nodeType;
90     }
91     
92     /**
93     * Determine whether this is the same node as another node. <br />
94     * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
95     * @return true if this Node object and the supplied Node object represent the
96     * same node in the tree.
97     */

98
99     public boolean isSameNode(NodeInfo other) {
100         if (!(other instanceof NodeWrapper)) {
101             return false;
102         }
103         NodeWrapper ow = (NodeWrapper)other;
104         if (nodeType != ow.getNodeType()) {
105             return false;
106         }
107         if (index != ow.index) {
108             return false;
109         }
110         if (node instanceof String) {
111             return parent.isSameNode(ow.parent);
112         } else {
113             return node.equals(ow.node); // this test doesn't work for text nodes in beta 0.7
114
// but it's OK in beta 0.8
115
}
116     }
117
118     /**
119     * Get the System ID for the node.
120     * @return the System Identifier of the entity in the source document containing the node,
121     * or null if not known. Note this is not the same as the base URI: the base URI can be
122     * modified by xml:base, but the system ID cannot.
123     */

124
125     public String getSystemId() {
126         return docWrapper.baseURI;
127     }
128     
129     public void setSystemId(String uri) {
130         docWrapper.baseURI = uri;
131     }
132     
133     /**
134     * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
135     * in the node. This will be the same as the System ID unless xml:base has been used.
136     */

137     
138     public String getBaseURI() {
139         return docWrapper.baseURI;
140     }
141
142     /**
143     * Get line number
144     * @return the line number of the node in its original source document; or -1 if not available
145     */

146
147     public int getLineNumber() {
148         return -1;
149     }
150     
151     /**
152     * Determine the relative position of this node and another node, in document order.
153     * The other node will always be in the same document.
154     * @param other The other node, whose position is to be compared with this node
155     * @return -1 if this node precedes the other node, +1 if it follows the other
156     * node, or 0 if they are the same node. (In this case, isSameNode() will always
157     * return true, and the two nodes will produce the same result for generateId())
158     */

159     
160     public int compareOrder(NodeInfo other) {
161
162         NodeWrapper ow = (NodeWrapper)other;
163                 
164         // are they the same node?
165
if (this.isSameNode(other)) {
166             return 0;
167         }
168         
169         // are they siblings (common case)
170
if (this.getParent().isSameNode(other.getParent())) {
171             return this.index - ow.index;
172         }
173         
174         // find the depths of both nodes in the tree
175

176         int depth1 = 0;
177         int depth2 = 0;
178         NodeInfo p1 = this;
179         NodeInfo p2 = other;
180         while (p1 != null) {
181             depth1++;
182             p1 = p1.getParent();
183         }
184         while (p2 != null) {
185             depth2++;
186             p2 = p2.getParent();
187         }
188
189         // move up one branch of the tree so we have two nodes on the same level
190

191         p1 = this;
192         while (depth1>depth2) {
193             p1 = p1.getParent();
194             if (p1.isSameNode(ow)) {
195                 return +1;
196             }
197             depth1--;
198         }
199
200         p2 = ow;
201         while (depth2>depth1) {
202             p2 = p2.getParent();
203             if (p2.isSameNode(this)) {
204                 return -1;
205             }
206             depth2--;
207         }
208
209         // now move up both branches in sync until we find a common parent
210
while (true) {
211             NodeInfo par1 = p1.getParent();
212             NodeInfo par2 = p2.getParent();
213             if (par1==null || par2==null) {
214                 throw new NullPointerException("JDOM tree compare - internal error");
215             }
216             if (par1.isSameNode(par2)) {
217                 return ((NodeWrapper)p1).index - ((NodeWrapper)p2).index;
218             }
219             p1 = par1;
220             p2 = par2;
221         }
222     }
223     
224     /**
225     * Return the string value of the node. The interpretation of this depends on the type
226     * of node. For an element it is the accumulated character content of the element,
227     * including descendant elements.
228     * @return the string value of the node
229     */

230
231     public String getStringValue() {
232         switch (nodeType) {
233             case NodeInfo.ROOT:
234                 List children1 = ((Document)node).getContent();
235                 StringBuffer sb1 = new StringBuffer();
236                 expandStringValue(children1, sb1);
237                 return sb1.toString();
238                 
239             case NodeInfo.ELEMENT:
240                 List children2 = ((Element)node).getContent();
241                 StringBuffer sb2 = new StringBuffer();
242                 expandStringValue(children2, sb2);
243                 return sb2.toString();
244                 
245             case NodeInfo.ATTRIBUTE:
246                 return ((Attribute)node).getValue();
247                 
248             case NodeInfo.TEXT:
249                 if (node instanceof String) return (String)node;
250                 if (node instanceof Text) return ((Text)node).getText();
251                 if (node instanceof CDATA) return ((CDATA)node).getText();
252                 return "";
253                  
254             case NodeInfo.COMMENT:
255                 return ((Comment)node).getText();
256                 
257             case NodeInfo.PI:
258                 return ((ProcessingInstruction)node).getData();
259                 
260             case NodeInfo.NAMESPACE:
261                 return ((Namespace)node).getURI();
262                 
263             default:
264                 return "";
265         }
266     }
267     
268     private static void expandStringValue(List list, StringBuffer sb) {
269         Iterator iter = list.iterator();
270         while (iter.hasNext()) {
271             Object obj = iter.next();
272             if (obj instanceof Element) {
273                 expandStringValue(((Element)obj).getContent(), sb);
274             } else if (obj instanceof String) { // beta 0.7
275
sb.append((String)obj);
276             } else if (obj instanceof Text) { // beta 0.8
277
sb.append(((Text)obj).getText());
278             } else if (obj instanceof CDATA) {
279                 sb.append(((CDATA)obj).getText());
280             } else if (obj instanceof EntityRef) {
281                 throw new IllegalStateException("Unexpanded entity in JDOM tree");
282             }
283         }
284     }
285
286     /**
287     * Get name code. The name code is a coded form of the node name: two nodes
288     * with the same name code have the same namespace URI, the same local name,
289     * and the same prefix. By masking the name code with &0xfffff, you get a
290     * fingerprint: two nodes with the same fingerprint have the same local name
291     * and namespace URI.
292     * @see com.icl.saxon.om.NamePool#allocate allocate
293     */

294     
295     public int getNameCode() {
296         switch (nodeType) {
297             case ELEMENT:
298             case ATTRIBUTE:
299             case PI:
300                 return docWrapper.namePool.allocate(getPrefix(),
301                                                     getURI(),
302                                                     getLocalName());
303             default:
304                 return -1;
305         }
306     }
307
308     /**
309     * Get fingerprint. The fingerprint is a coded form of the expanded name
310     * of the node: two nodes
311     * with the same name code have the same namespace URI and the same local name.
312     * A fingerprint of -1 should be returned for a node with no name.
313     */

314     
315     public int getFingerprint() {
316         return getNameCode()&0xfffff;
317     }
318
319     /**
320     * Get the local part of the name of this node. This is the name after the ":" if any.
321     * @return the local part of the name. For an unnamed node, return an empty string.
322     */

323
324     public String getLocalName() {
325         switch (nodeType) {
326             case NodeInfo.ELEMENT:
327                 return ((Element)node).getName();
328             case NodeInfo.ATTRIBUTE:
329                 return ((Attribute)node).getName();
330             case NodeInfo.TEXT:
331             case NodeInfo.COMMENT:
332             case NodeInfo.ROOT:
333                 return "";
334             case NodeInfo.PI:
335                 return ((ProcessingInstruction)node).getTarget();
336             case NodeInfo.NAMESPACE:
337                 return ((Namespace)node).getPrefix();
338             default:
339                 return "";
340         }
341     }
342
343     /**
344     * Get the prefix part of the name of this node. This is the name before the ":" if any.
345     * @return the prefix part of the name. For an unnamed node, return an empty string.
346     */

347
348     public String getPrefix() {
349         switch (nodeType) {
350             case NodeInfo.ELEMENT:
351                 return ((Element)node).getNamespacePrefix();
352             case NodeInfo.ATTRIBUTE:
353                 return ((Attribute)node).getNamespacePrefix();
354             default:
355                 return "";
356         }
357     }
358
359     /**
360     * Get the URI part of the name of this node. This is the URI corresponding to the
361     * prefix, or the URI of the default namespace if appropriate.
362     * @return The URI of the namespace of this node. For an unnamed node, return null.
363     * For a node with an empty prefix, return an empty string.
364     */

365
366     public String getURI() {
367         switch (nodeType) {
368             case NodeInfo.ELEMENT:
369                 return ((Element)node).getNamespaceURI();
370             case NodeInfo.ATTRIBUTE:
371                 return ((Attribute)node).getNamespaceURI();
372             default:
373                 return "";
374         }
375     }
376
377     /**
378     * Get the display name of this node. For elements and attributes this is [prefix:]localname.
379     * For unnamed nodes, it is an empty string.
380     * @return The display name of this node.
381     * For a node with no name, return an empty string.
382     */

383
384     public String getDisplayName() {
385         switch (nodeType) {
386             case NodeInfo.ELEMENT:
387                 return ((Element)node).getQualifiedName();
388             case NodeInfo.ATTRIBUTE:
389                 return ((Attribute)node).getQualifiedName();
390             case NodeInfo.PI:
391             case NodeInfo.NAMESPACE:
392                 return getLocalName();
393             default:
394                 return "";
395
396         }
397     }
398
399     /**
400     * Get the NodeInfo object representing the parent of this node
401     */

402     
403     public NodeInfo getParent() {
404         return parent;
405     }
406
407     /**
408     * Return an enumeration over the nodes reached by the given axis from this node
409     * @param nodeType the type(s) of node to be included, e.g. NodeInfo.ELEMENT, NodeInfo.TEXT.
410     * The value NodeInfo.NODE means include any type of node.
411     * @param nodeTest A pattern to be matched by the returned nodes
412     * @return a NodeEnumeration that scans the nodes reached by the axis in turn.
413     */

414
415     public AxisEnumeration getEnumeration(byte axisNumber, NodeTest nodeTest) {
416         switch (axisNumber) {
417             case Axis.ANCESTOR:
418                 if (nodeType==ROOT) return EmptyEnumeration.getInstance();
419                 return new FilterEnumeration(
420                             new AncestorEnumeration(this, false),
421                             nodeTest);
422                  
423             case Axis.ANCESTOR_OR_SELF:
424                 if (nodeType==ROOT) return EmptyEnumeration.getInstance();
425                 return new FilterEnumeration(
426                             new AncestorEnumeration(this, true),
427                             nodeTest);
428                  
429             case Axis.ATTRIBUTE:
430                 if (nodeType!=ELEMENT) return EmptyEnumeration.getInstance();
431                 return new FilterEnumeration(
432                             new AttributeEnumeration(this),
433                             nodeTest);
434                  
435             case Axis.CHILD:
436                 if (hasChildNodes()) {
437                     return new FilterEnumeration(
438                             new ChildEnumeration(this, true, true),
439                             nodeTest);
440                 } else {
441                     return EmptyEnumeration.getInstance();
442                 }
443                  
444             case Axis.DESCENDANT:
445                 if (hasChildNodes()) {
446                     return new FilterEnumeration(
447                             new DescendantEnumeration(this, false, true),
448                             nodeTest);
449                 } else {
450                     return EmptyEnumeration.getInstance();
451                 }
452                  
453             case Axis.DESCENDANT_OR_SELF:
454                  return new FilterEnumeration(
455                             new DescendantEnumeration(this, true, true),
456                             nodeTest);
457                  
458             case Axis.FOLLOWING:
459                  return new FilterEnumeration(
460                             new FollowingEnumeration(this),
461                             nodeTest);
462                  
463             case Axis.FOLLOWING_SIBLING:
464                  switch (nodeType) {
465                     case ROOT:
466                     case ATTRIBUTE:
467                     case NAMESPACE:
468                         return EmptyEnumeration.getInstance();
469                     default:
470                         return new FilterEnumeration(
471                             new ChildEnumeration(this, false, true),
472                             nodeTest);
473                  }
474                  
475             case Axis.NAMESPACE:
476                  if (nodeType!=ELEMENT) return EmptyEnumeration.getInstance();
477                  return new FilterEnumeration(
478                                 new NamespaceEnumeration(this),
479                                 nodeTest);
480                  //throw new IllegalArgumentException(
481
// "namespace axis not implemented for JDOM");
482

483             case Axis.PARENT:
484                  if (parent==null) return EmptyEnumeration.getInstance();
485                  if (nodeTest.matches(parent)) return new SingletonEnumeration(parent);
486                  return EmptyEnumeration.getInstance();
487                  
488             case Axis.PRECEDING:
489                  return new FilterEnumeration(
490                             new PrecedingEnumeration(this, false),
491                             nodeTest);
492                                              
493             case Axis.PRECEDING_SIBLING:
494                  switch (nodeType) {
495                     case ROOT:
496                     case ATTRIBUTE:
497                     case NAMESPACE:
498                         return EmptyEnumeration.getInstance();
499                     default:
500                         return new FilterEnumeration(
501                             new ChildEnumeration(this, false, false),
502                             nodeTest);
503                  }
504                  
505             case Axis.SELF:
506                  if (nodeTest.matches(this)) return new SingletonEnumeration(this);
507                  return EmptyEnumeration.getInstance();
508                  
509             case Axis.PRECEDING_OR_ANCESTOR:
510                  return new FilterEnumeration(
511                             new PrecedingEnumeration(this, true),
512                             nodeTest);
513                                              
514             default:
515                  throw new IllegalArgumentException("Unknown axis number " + axisNumber);
516         }
517     }
518          
519     /**
520      * Find the value of a given attribute of this node. <BR>
521      * This method is defined on all nodes to meet XSL requirements, but for nodes
522      * other than elements it will always return null.
523      * @param uri the namespace uri of an attribute ("" if no namespace)
524      * @param localname the local name of the attribute
525      * @return the value of the attribute, if it exists, otherwise null
526      */

527
528     public String getAttributeValue(String uri, String localName) {
529         if (nodeType==NodeInfo.ELEMENT) {
530             Namespace ns = Namespace.getNamespace(uri);
531             return ((Element)node).getAttributeValue(localName, ns);
532         } else {
533             return "";
534         }
535     }
536    
537     /**
538     * Get the value of a given attribute of this node
539     * @param fingerprint The fingerprint of the attribute name
540     * @return the attribute value if it exists or null if not
541     */

542     
543     public String getAttributeValue(int fingerprint) {
544         if (nodeType==NodeInfo.ELEMENT) {
545             Iterator list = ((Element)node).getAttributes().iterator();
546             NamePool pool = docWrapper.getNamePool();
547             while (list.hasNext()) {
548                 Attribute att = (Attribute)list.next();
549                 int nameCode = pool.allocate(att.getNamespacePrefix(),
550                                              att.getNamespaceURI(),
551                                              att.getName());
552                 if (fingerprint == (nameCode & 0xfffff)) {
553                     return att.getValue();
554                 }
555             }
556         }
557         return null;
558     }
559
560     /**
561     * Get the root (document) node
562     * @return the DocumentInfo representing the containing document
563     */

564
565     public DocumentInfo getDocumentRoot() {
566         return docWrapper;
567     }
568
569     /**
570     * Determine whether the node has any children. <br />
571     * Note: the result is equivalent to <br />
572     * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasMoreElements()
573     */

574
575     public boolean hasChildNodes() {
576         switch (nodeType) {
577             case NodeInfo.ROOT:
578                 return true;
579             case NodeInfo.ELEMENT:
580                 return !((Element)node).getContent().isEmpty();
581             default:
582                 return false;
583         }
584     }
585     /**
586     * Get a character string that uniquely identifies this node.<br />
587     * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
588     * @return a string that uniquely identifies this node, within this
589     * document. The calling code prepends information to make the result
590     * unique across all documents.
591     */

592
593     public String generateId() {
594         if (node instanceof String || node instanceof Text) {
595             return parent.generateId() + "text" + index;
596         } else {
597             return "j" + node.hashCode();
598         }
599     }
600
601     /**
602     * Copy this node to a given outputter (deep copy)
603     */

604
605     public void copy(Outputter out) throws TransformerException {
606         
607         // TODO: this is a completely generic implementation: it ought to invoke a
608
// helper class.
609

610         switch (nodeType) {
611             case NodeInfo.ROOT:
612                 AxisEnumeration children0 = getEnumeration(Axis.CHILD, new AnyNodeTest());
613                 while (children0.hasMoreElements()) {
614                     children0.nextElement().copy(out);
615                 }
616                 return;
617                 
618             case NodeInfo.ELEMENT:
619                 int nc = getNameCode();
620                 out.writeStartTag(nc);
621
622                 // output the namespaces
623

624                 outputNamespaceNodes(out, true);
625         
626                 // output the attributes
627

628                 AxisEnumeration attributes = getEnumeration(Axis.ATTRIBUTE, new AnyNodeTest());
629                 while (attributes.hasMoreElements()) {
630                     attributes.nextElement().copy(out);
631                 }
632         
633                 // output the children
634

635                 AxisEnumeration children = getEnumeration(Axis.CHILD, new AnyNodeTest());
636                 while (children.hasMoreElements()) {
637                     children.nextElement().copy(out);
638                 }
639                 
640                 // finally the end tag
641

642                 out.writeEndTag(nc);
643                 return;
644                 
645             case NodeInfo.ATTRIBUTE:
646                 out.writeAttribute(getNameCode(), getStringValue());
647                 return;
648                 
649             case NodeInfo.TEXT:
650                 out.writeContent(getStringValue());
651                 return;
652                 
653             case NodeInfo.COMMENT:
654                 out.writeComment(getStringValue());
655                 return;
656                 
657             case NodeInfo.PI:
658                 out.writePI(getLocalName(), getStringValue());
659                 return;
660                 
661             case NodeInfo.NAMESPACE:
662                 throw new IllegalArgumentException("Copying Namespace node!");
663                 
664             default:
665
666         }
667     }
668
669     /**
670     * Copy the string-value of this node to a given outputter
671     */

672
673     public void copyStringValue(Outputter out) throws TransformerException {
674         out.writeContent(getStringValue());
675     }
676
677     /**
678     * Output all namespace nodes associated with this element. Does nothing if
679     * the node is not an element.
680     * @param out The relevant outputter
681     * @param includeAncestors True if namespaces declared on ancestor elements must
682     * be output; false if it is known that these are already on the result tree
683     */

684
685     public void outputNamespaceNodes(Outputter out, boolean includeAncestors)
686         throws TransformerException {
687             // TODO: use the includeAncestors flag for optimization
688
if (nodeType==ELEMENT) {
689             NamePool pool = docWrapper.getNamePool();
690             AxisEnumeration enum = getEnumeration(Axis.NAMESPACE,
691                                                   AnyNodeTest.getInstance());
692             while (enum.hasMoreElements()) {
693                 Namespace ns = (Namespace)((NodeWrapper)enum.nextElement()).node;
694                 int nscode = pool.allocateNamespaceCode(
695                                 ns.getPrefix(),
696                                 ns.getURI());
697                 out.writeNamespaceDeclaration(nscode);
698             }
699         }
700     }
701
702     ///////////////////////////////////////////////////////////////////////////////
703
// Axis enumeration classes
704
///////////////////////////////////////////////////////////////////////////////
705

706     private class FilterEnumeration implements AxisEnumeration {
707         private BaseEnumeration base;
708         private NodeTest nodeTest;
709         private NodeInfo next;
710         private int last = -1;
711         
712         public FilterEnumeration(BaseEnumeration base, NodeTest test) {
713             this.base = base;
714             this.nodeTest = test;
715             advance();
716         }
717
718         public void advance() {
719             while (base.hasMoreElements()) {
720                 next = base.nextElement();
721                 if (nodeTest.matches(next)) return;
722             }
723             next = null;
724         }
725         
726         public NodeInfo nextElement() {
727             NodeInfo n = next;
728             advance();
729             return n;
730         }
731                 
732         public boolean hasMoreElements() {
733             return next!=null;
734         }
735         
736         public int getLastPosition() {
737             
738             // To find out how many nodes there are in the axis, we
739
// make a copy of the original node enumeration, and run through
740
// the whole thing again, counting how many nodes match the filter.
741

742             if (last>=0) {
743                 return last;
744             }
745             last = 0;
746             BaseEnumeration b = base.copy();
747             while (b.hasMoreElements()) {
748                 NodeInfo n = b.nextElement();
749                 if (nodeTest.matches(n)) {
750                     last++;
751                 }
752             }
753             return last;
754         }
755
756         public boolean isSorted() {
757             return base.isSorted();
758         }
759
760         public boolean isReverseSorted() {
761             return base.isReverseSorted();
762         }
763
764         public boolean isPeer() {
765             return base.isPeer();
766         }
767     }
768     
769     private abstract class BaseEnumeration implements AxisEnumeration {
770
771         protected NodeWrapper next;
772
773         public final boolean hasMoreElements() {
774             return next!=null;
775         }
776         
777         public final NodeInfo nextElement() {
778             NodeInfo n = next;
779             advance();
780             return n;
781         }
782
783         public abstract void advance();
784         
785         public abstract BaseEnumeration copy();
786         
787         public final int getLastPosition() {// not used
788
return 1;
789         }
790         
791         public boolean isSorted() {
792             return true;
793         }
794         
795         public final boolean isReverseSorted() {
796             return !isSorted();
797         }
798         
799         public boolean isPeer() {
800             return false;
801         }
802     }
803         
804
805     final class AncestorEnumeration extends BaseEnumeration {
806     
807         private boolean includeSelf;
808         private NodeWrapper start;
809     
810         public AncestorEnumeration(NodeWrapper start, boolean includeSelf) {
811             this.start = start;
812             this.includeSelf = includeSelf;
813             this.next = start;
814             if (!includeSelf) {
815                 advance();
816             }
817         }
818
819         public void advance() {
820             next=(NodeWrapper)next.getParent();
821         }
822
823         public boolean isSorted() {
824             return false;
825         }
826         
827         public boolean isPeer() {
828             return false;
829         }
830             
831         public BaseEnumeration copy() {
832             return new AncestorEnumeration(start, includeSelf);
833         }
834     
835     } // end of class AncestorEnumeration
836

837     private final class AttributeEnumeration extends BaseEnumeration {
838     
839         private Iterator atts;
840         private int ix = 0;
841         private NodeWrapper start;
842     
843         public AttributeEnumeration(NodeWrapper start) {
844             this.start = start;
845             atts = ((Element)start.node).getAttributes().iterator();
846             advance();
847         }
848            
849         public void advance() {
850             if (atts.hasNext()) {
851                 next = makeWrapper(atts.next(), start, ix++);
852             } else {
853                 next = null;
854             }
855         }
856         
857         public boolean isPeer() {
858             return true;
859         }
860         
861         public BaseEnumeration copy() {
862             return new AttributeEnumeration(start);
863         }
864         
865     } // end of class AttributeEnumeration
866

867     private final class NamespaceEnumeration extends BaseEnumeration {
868     
869         private HashMap nslist = new HashMap();
870         private Iterator prefixes;
871         private int ix = 0;
872         private NodeWrapper start;
873     
874         public NamespaceEnumeration(NodeWrapper start) {
875             this.start = start;
876             NodeWrapper curr = start;
877             
878             // build the complete list of namespaces
879

880             do {
881                 Element elem = (Element)curr.node;
882                 Namespace ns = elem.getNamespace();
883                 if (nslist.containsKey(ns.getPrefix())) {
884                     nslist.put(ns.getPrefix(), ns);
885                 }
886                 List addl = elem.getAdditionalNamespaces();
887                 if (addl.size() > 0) {
888                     Iterator itr = addl.iterator();
889                     while (itr.hasNext()) {
890                         ns = (Namespace) itr.next();
891                         if (nslist.containsKey(ns.getPrefix())) {
892                             nslist.put(ns.getPrefix(), ns);
893                         }
894                     }
895                 }
896                 curr = (NodeWrapper)curr.getParent();
897             } while (curr.getNodeType()==ELEMENT);
898             
899             nslist.put("xml", Namespace.XML_NAMESPACE);
900             prefixes = nslist.keySet().iterator();
901         }
902         
903         public void advance() {
904             if (prefixes.hasNext()) {
905                 String prefix = (String)prefixes.next();
906                 Namespace ns = (Namespace)nslist.get(prefix);
907                 next = makeWrapper(ns, start, ix++);
908             } else {
909                 next = null;
910             }
911         }
912
913         public boolean isPeer() {
914             return true;
915         }
916
917         public BaseEnumeration copy() {
918             return new NamespaceEnumeration(start);
919         }
920         
921     } // end of class NamespaceEnumeration
922

923     
924     /**
925     * The class ChildEnumeration handles not only the child axis, but also the
926     * following-sibling and preceding-sibling axes. It can also enumerate the children
927     * of the start node in reverse order, something that is needed to support the
928     * preceding and preceding-or-ancestor axes (the latter being used by xsl:number)
929     */

930
931     private class ChildEnumeration extends BaseEnumeration {
932         
933         private NodeWrapper start;
934         private NodeWrapper commonParent;
935         private ListIterator children;
936         private int ix = 0;
937         private boolean downwards; // enumerate children of start node (not siblings)
938
private boolean forwards; // enumerate in document order (not reverse order)
939

940         public ChildEnumeration(NodeWrapper start,
941                                 boolean downwards, boolean forwards) {
942             this.start = start;
943             this.downwards = downwards;
944             this.forwards = forwards;
945
946             if (downwards) {
947                 commonParent = start;
948             } else {
949                 commonParent = (NodeWrapper)start.getParent();
950             }
951
952             if (commonParent.getNodeType()==ROOT) {
953                 children = ((Document)commonParent.node).getContent().listIterator();
954             } else {
955                 children = ((Element)commonParent.node).getContent().listIterator();
956             }
957
958             if (downwards) {
959                 if (!forwards) {
960                     // backwards enumeration: go to the end
961
while (children.hasNext()) {
962                         children.next();
963                         ix++;
964                     }
965                 }
966             } else {
967                 ix = start.index;
968                 // find the start node among the list of siblings
969
if (forwards) {
970                     for (int i=0; i<=ix; i++) {
971                         children.next();
972                     }
973                     ix++;
974                 } else {
975                     for (int i=0; i<ix; i++) {
976                         children.next();
977                     }
978                     ix--;
979                 }
980             }
981             advance();
982         }
983         
984
985             
986         public void advance() {
987             if (forwards) {
988                 if (children.hasNext()) {
989                     Object nextChild = children.next();
990                     if (nextChild instanceof EntityRef) {
991                         throw new IllegalStateException("Unexpanded entity in JDOM tree");
992                     } else {
993                         next = makeWrapper(nextChild, commonParent, ix++);
994                     }
995                 } else {
996                     next = null;
997                 }
998             } else { // backwards
999
if (children.hasPrevious()) {
1000                    Object nextChild = children.previous();
1001                    if (nextChild instanceof EntityRef) {
1002                        throw new IllegalStateException("Unexpanded entity in JDOM tree");
1003                    } else {
1004                        next = makeWrapper(nextChild, commonParent, ix--);
1005                    }
1006                } else {
1007                    next = null;
1008                }
1009            }
1010        }
1011    
1012        public boolean isSorted() {
1013            return forwards;
1014        }
1015    
1016        public boolean isPeer() {
1017            return true;
1018        }
1019
1020        public BaseEnumeration copy() {
1021            return new ChildEnumeration(start, downwards, forwards);
1022        }
1023    
1024    } // end of class ChildEnumeration
1025

1026    /**
1027    * The DescendantEnumeration class supports the XPath descendant axis.
1028    * But it also has the option to return the descendants in reverse document order;
1029    * this is used when evaluating the preceding axis. Note that the includeSelf option
1030    * should not be used when scanning in reverse order, as the self node will always be
1031    * returned first.
1032    */

1033
1034    private final class DescendantEnumeration extends BaseEnumeration {
1035        
1036        private AxisEnumeration children = null;
1037        private AxisEnumeration descendants = null;
1038        private NodeWrapper start;
1039        private boolean includeSelf;
1040        private boolean forwards;
1041        private boolean atEnd = false;
1042    
1043        public DescendantEnumeration(NodeWrapper start,
1044                                 boolean includeSelf, boolean forwards) {
1045            this.start = start;
1046            this.includeSelf = includeSelf;
1047            this.forwards = forwards;
1048            advance();
1049        }
1050    
1051        public void advance() {
1052            if (descendants!=null) {
1053                if (descendants.hasMoreElements()) {
1054                    next = (NodeWrapper)descendants.nextElement();
1055                    return;
1056                } else {
1057                    descendants = null;
1058                }
1059            }
1060            if (children!=null) {
1061                if (children.hasMoreElements()) {
1062                    NodeWrapper n = (NodeWrapper)children.nextElement();
1063                    if (n.hasChildNodes()) {
1064                        if (forwards) {
1065                            descendants = new DescendantEnumeration(n,
1066                                             false, forwards);
1067                            next = n;
1068                        } else {
1069                            descendants = new DescendantEnumeration(n, true, forwards);
1070                            advance();
1071                        }
1072                    } else {
1073                        next = n;
1074                    }
1075                } else {
1076                    if (forwards || !includeSelf) {
1077                        next = null;
1078                    } else {
1079                        atEnd = true;
1080                        children = null;
1081                        next = start;
1082                    }
1083                }
1084            } else if (atEnd) {
1085                // we're just finishing a backwards scan
1086
next = null;
1087            } else {
1088                // we're just starting...
1089
if (start.hasChildNodes()) {
1090                    children = new ChildEnumeration(start, true, forwards);
1091                } else {
1092                    children = EmptyEnumeration.getInstance();
1093                }
1094                if (forwards && includeSelf) {
1095                    next = start;
1096                } else {
1097                    advance();
1098                }
1099            }
1100        }
1101    
1102        public boolean isSorted() {
1103            return forwards;
1104        }
1105    
1106        public boolean isPeer() {
1107            return false;
1108        }
1109    
1110        public BaseEnumeration copy() {
1111            return new DescendantEnumeration(start, includeSelf, forwards);
1112        }
1113    
1114    } // end of class DescendantEnumeration
1115

1116    private class FollowingEnumeration extends BaseEnumeration {
1117        
1118        private NodeWrapper start;
1119        private AxisEnumeration ancestorEnum = null;
1120        private AxisEnumeration siblingEnum = null;
1121        private AxisEnumeration descendEnum = null;
1122    
1123        public FollowingEnumeration(NodeWrapper start) {
1124            this.start = start;
1125            ancestorEnum = new AncestorEnumeration(start, false);
1126            switch (start.getNodeType()) {
1127                case ELEMENT:
1128                case TEXT:
1129                case COMMENT:
1130                case PI:
1131                    siblingEnum = new ChildEnumeration(start, false, true);
1132                    break;
1133                case ATTRIBUTE:
1134                case NAMESPACE:
1135                    siblingEnum = new ChildEnumeration((NodeWrapper)start.getParent(), true, true);
1136                        // gets children of the attribute's parent node
1137
break;
1138                default:
1139                    siblingEnum = EmptyEnumeration.getInstance();
1140            }
1141            advance();
1142        }
1143            
1144        public void advance() {
1145            if (descendEnum!=null) {
1146                if (descendEnum.hasMoreElements()) {
1147                    next = (NodeWrapper)descendEnum.nextElement();
1148                    return;
1149                } else {
1150                    descendEnum = null;
1151                }
1152            }
1153            if (siblingEnum!=null) {
1154                if (siblingEnum.hasMoreElements()) {
1155                    next = (NodeWrapper)siblingEnum.nextElement();
1156                    if (next.hasChildNodes()) {
1157                        descendEnum = new DescendantEnumeration(next, false, true);
1158                    } else {
1159                        descendEnum = null;
1160                    }
1161                    return;
1162                } else {
1163                    descendEnum = null;
1164                    siblingEnum = null;
1165                }
1166            }
1167            if (ancestorEnum.hasMoreElements()) {
1168                next = (NodeWrapper)ancestorEnum.nextElement();
1169                if (next.getNodeType() == ROOT) {
1170                    siblingEnum = EmptyEnumeration.getInstance();
1171                } else {
1172                    siblingEnum = new ChildEnumeration(next, false, true);
1173                }
1174                advance();
1175            } else {
1176                next = null;
1177            }
1178        }
1179    
1180        public boolean isSorted() {
1181            return true;
1182        }
1183    
1184        public boolean isPeer() {
1185            return false;
1186        }
1187    
1188        public BaseEnumeration copy() {
1189            return new FollowingEnumeration(start);
1190        }
1191    
1192    } // end of class FollowingEnumeration
1193

1194    private class PrecedingEnumeration extends BaseEnumeration {
1195        
1196        private NodeWrapper start;
1197        private AxisEnumeration ancestorEnum = null;
1198        private AxisEnumeration siblingEnum = null;
1199        private AxisEnumeration descendEnum = null;
1200        private boolean includeAncestors;
1201    
1202        public PrecedingEnumeration(NodeWrapper start, boolean includeAncestors) {
1203            this.start = start;
1204            this.includeAncestors = includeAncestors;
1205            ancestorEnum = new AncestorEnumeration(start, false);
1206            switch (start.getNodeType()) {
1207                case ELEMENT:
1208                case TEXT:
1209                case COMMENT:
1210                case PI:
1211                    // get preceding-sibling enumeration
1212
siblingEnum = new ChildEnumeration(start, false, false);
1213                    break;
1214                default:
1215                    siblingEnum = EmptyEnumeration.getInstance();
1216            }
1217            advance();
1218        }
1219            
1220        public void advance() {
1221            if (descendEnum!=null) {
1222                if (descendEnum.hasMoreElements()) {
1223                    next = (NodeWrapper)descendEnum.nextElement();
1224                    return;
1225                } else {
1226                    descendEnum = null;
1227                }
1228            }
1229            if (siblingEnum!=null) {
1230                if (siblingEnum.hasMoreElements()) {
1231                    NodeWrapper sib = (NodeWrapper)siblingEnum.nextElement();
1232                    if (sib.hasChildNodes()) {
1233                        descendEnum = new DescendantEnumeration(sib, true, false);
1234                        advance();
1235                    } else {
1236                        descendEnum = null;
1237                        next = sib;
1238                    }
1239                    return;
1240                } else {
1241                    descendEnum = null;
1242                    siblingEnum = null;
1243                }
1244            }
1245            if (ancestorEnum.hasMoreElements()) {
1246                next = (NodeWrapper)ancestorEnum.nextElement();
1247                if (next.getNodeType() == ROOT) {
1248                    siblingEnum = EmptyEnumeration.getInstance();
1249                } else {
1250                    siblingEnum = new ChildEnumeration(next, false, false);
1251                }
1252                if (!includeAncestors) {
1253                    advance();
1254                }
1255            } else {
1256                next = null;
1257            }
1258        }
1259    
1260        public boolean isSorted() {
1261            return false;
1262        }
1263    
1264        public boolean isPeer() {
1265            return false;
1266        }
1267    
1268        public BaseEnumeration copy() {
1269            return new PrecedingEnumeration(start, includeAncestors);
1270        }
1271    
1272    } // end of class PrecedingEnumeration
1273

1274}
1275
1276//
1277
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1278
// you may not use this file except in compliance with the License. You may obtain a copy of the
1279
// License at http://www.mozilla.org/MPL/
1280
//
1281
// Software distributed under the License is distributed on an "AS IS" basis,
1282
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
1283
// See the License for the specific language governing rights and limitations under the License.
1284
//
1285
// The Original Code is: all this file.
1286
//
1287
// The Initial Developer of the Original Code is
1288
// Michael Kay (mhkay@iclway.co.uk).
1289
//
1290
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1291
//
1292
// Contributor(s): none.
1293
//
1294
Popular Tags