KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > om > Navigator


1 package net.sf.saxon.om;
2
3 import net.sf.saxon.Controller;
4 import net.sf.saxon.event.Receiver;
5 import net.sf.saxon.expr.Expression;
6 import net.sf.saxon.expr.ReversibleIterator;
7 import net.sf.saxon.expr.XPathContext;
8 import net.sf.saxon.functions.EscapeURI;
9 import net.sf.saxon.pattern.*;
10 import net.sf.saxon.style.StandardNames;
11 import net.sf.saxon.trans.XPathException;
12 import net.sf.saxon.type.Type;
13 import net.sf.saxon.value.SequenceExtent;
14 import net.sf.saxon.value.Whitespace;
15
16 import java.net.URI JavaDoc;
17 import java.net.URISyntaxException JavaDoc;
18 import java.util.ArrayList JavaDoc;
19 import java.util.List JavaDoc;
20
21
22 /**
23  * The Navigator class provides helper classes for navigating a tree, irrespective
24  * of its implementation
25  *
26  * @author Michael H. Kay
27  */

28
29
30
31 public final class Navigator {
32
33     // Class is never instantiated
34
private Navigator() {
35     }
36
37     /**
38      * Get the string value of an attribute of a given element, given the URI and
39      * local part of the attribute name.
40      * @return the attribute value, or null if the attribute is not present
41      */

42
43     public static String JavaDoc getAttributeValue(NodeInfo element, String JavaDoc uri, String JavaDoc localName) {
44         int fingerprint = element.getNamePool().allocate("", uri, localName);
45         return element.getAttributeValue(fingerprint);
46     }
47
48     /**
49      * Helper method to get the base URI of an element node
50      */

51
52     public static String JavaDoc getBaseURI(NodeInfo element) {
53         String JavaDoc xmlBase = element.getAttributeValue(StandardNames.XML_BASE);
54         if (xmlBase!=null) {
55             String JavaDoc escaped = EscapeURI.escape(xmlBase, false).toString();
56             URI escapedURI;
57             try {
58                 escapedURI = new URI(escaped);
59                 if (!escapedURI.isAbsolute()) {
60                     NodeInfo parent = element.getParent();
61                     if (parent==null) {
62                         return escapedURI.toString();
63                     }
64                     String JavaDoc startSystemId = element.getSystemId();
65                     String JavaDoc parentSystemId = parent.getSystemId();
66                     if (startSystemId.equals(parentSystemId)) {
67                         URI base = new URI(element.getParent().getBaseURI());
68                         escapedURI = base.resolve(escapedURI);
69                     } else {
70                         URI base = new URI(startSystemId);
71                         escapedURI = base.resolve(escapedURI);
72                     }
73                 }
74             } catch (URISyntaxException JavaDoc e) {
75                 // xml:base is an invalid URI. Just return it as is: the operation that needs the base URI
76
// will probably fail as a result.
77
return xmlBase;
78             }
79             return escapedURI.toString();
80         }
81         String JavaDoc startSystemId = element.getSystemId();
82         NodeInfo parent = element.getParent();
83         if (parent==null) {
84             return startSystemId;
85         }
86         String JavaDoc parentSystemId = parent.getSystemId();
87         if (startSystemId.equals(parentSystemId)) {
88             return parent.getBaseURI();
89         } else {
90             return startSystemId;
91         }
92     }
93
94     /**
95      * Output all namespace nodes associated with this element. Does nothing if
96      * the node is not an element. This is a helper method to allow the method
97      * {@link NodeInfo#sendNamespaceDeclarations(net.sf.saxon.event.Receiver, boolean)} to be
98      * implemented if {@link NodeInfo#getDeclaredNamespaces(int[])} is available.
99      * @param out The relevant outputter
100      * @param includeAncestors True if namespaces declared on ancestor elements must be output
101      */

102
103     public static void sendNamespaceDeclarations(NodeInfo node, Receiver out, boolean includeAncestors)
104         throws XPathException {
105         if (node.getNodeKind() == Type.ELEMENT) {
106             int[] codes;
107             if (includeAncestors) {
108                 codes = new NamespaceIterator(node, null).getInScopeNamespaceCodes();
109             } else {
110                 codes = node.getDeclaredNamespaces(NodeInfo.EMPTY_NAMESPACE_LIST);
111             }
112             for (int i=0; i<codes.length; i++) {
113                 if (codes[i] == -1) break;
114                 out.namespace(codes[i], 0);
115             }
116         }
117     }
118
119     /**
120      * Get an absolute XPath expression that identifies a given node within its document
121      *
122      * @param node the node whose path is required. If null is supplied,
123      * an empty string is returned - this fact is used in making a recursive call
124      * for a parentless node.
125      * @return a path expression that can be used to retrieve the node
126      */

127
128     public static String JavaDoc getPath(NodeInfo node) {
129         if (node==null) {
130             return "";
131         }
132         String JavaDoc pre;
133         NodeInfo parent = node.getParent();
134         // System.err.println("node = " + node + " parent = " + parent);
135

136         switch (node.getNodeKind()) {
137             case Type.DOCUMENT:
138                 return "/";
139             case Type.ELEMENT:
140                 if (parent==null) {
141                     return node.getDisplayName();
142                 } else {
143                     pre = getPath(parent);
144                     if (pre.equals("/")) {
145                         return '/' + node.getDisplayName();
146                     } else {
147                         return pre + '/' + node.getDisplayName() + '[' + getNumberSimple(node) + ']';
148                     }
149                 }
150             case Type.ATTRIBUTE:
151                 return getPath(parent) + "/@" + node.getDisplayName();
152             case Type.TEXT:
153                 pre = getPath(parent);
154                 return (pre.equals("/") ? "" : pre) +
155                         "/text()[" + getNumberSimple(node) + ']';
156             case Type.COMMENT:
157                 pre = getPath(parent);
158                 return (pre.equals("/") ? "" : pre) +
159                     "/comment()[" + getNumberSimple(node) + ']';
160             case Type.PROCESSING_INSTRUCTION:
161                 pre = getPath(parent);
162                 return (pre.equals("/") ? "" : pre) +
163                     "/processing-instruction()[" + getNumberSimple(node) + ']';
164             case Type.NAMESPACE:
165                 String JavaDoc test = node.getLocalPart();
166                 if (test.equals("")) {
167                     // default namespace: need a node-test that selects unnamed nodes only
168
test="*[not(local-name()]";
169                 }
170                 return getPath(parent)+ "/namespace::" + test;
171             default:
172                 return "";
173         }
174     }
175
176     /**
177      * Get simple node number. This is defined as one plus the number of previous siblings of the
178      * same node type and name. It is not accessible directly in XSL.
179      *
180      * @param node The node whose number is required
181      * @param context Used for remembering previous result, for
182      * performance
183      * @exception XPathException if any error occurs
184      * @return the node number, as defined above
185      */

186
187     public static int getNumberSimple(NodeInfo node, XPathContext context) throws XPathException {
188
189         //checkNumberable(node);
190

191         int fingerprint = node.getFingerprint();
192         NodeTest same;
193
194         if (fingerprint==-1) {
195             same = NodeKindTest.makeNodeKindTest(node.getNodeKind());
196         } else {
197             same = new NameTest(node);
198         }
199
200         SequenceIterator preceding = node.iterateAxis(Axis.PRECEDING_SIBLING, same);
201
202         int i=1;
203         while (true) {
204             NodeInfo prev = (NodeInfo)preceding.next();
205             if (prev == null) {
206                 break;
207             }
208
209             Controller controller = context.getController();
210             int memo = controller.getRememberedNumber(prev);
211             if (memo>0) {
212                 memo += i;
213                 controller.setRememberedNumber(node, memo);
214                 return memo;
215             }
216
217             i++;
218         }
219
220         context.getController().setRememberedNumber(node, i);
221         return i;
222     }
223
224     /**
225      * Get simple node number. This is defined as one plus the number of previous siblings of the
226      * same node type and name. It is not accessible directly in XSL. This version doesn't require
227      * the controller, and therefore doesn't remember previous results. It is used only by getPath().
228      *
229      * @param node the node whose number is required
230      * @return the node number, as defined above
231      */

232
233     private static int getNumberSimple(NodeInfo node) {
234
235         int fingerprint = node.getFingerprint();
236         NodeTest same;
237
238         if (fingerprint==-1) {
239             same = NodeKindTest.makeNodeKindTest(node.getNodeKind());
240         } else {
241             same = new NameTest(node);
242         }
243
244         AxisIterator preceding = node.iterateAxis(Axis.PRECEDING_SIBLING, same);
245
246         int i=1;
247         while (preceding.next() != null) {
248             i++;
249         }
250
251         return i;
252     }
253
254     /**
255      * Get node number (level="single"). If the current node matches the supplied pattern, the returned
256      * number is one plus the number of previous siblings that match the pattern. Otherwise,
257      * return the element number of the nearest ancestor that matches the supplied pattern.
258      *
259      * @param node the current node, the one whose node number is required
260      * @param count Pattern that identifies which nodes should be
261      * counted. Default (null) is the element name if the current node is
262      * an element, or "node()" otherwise.
263      * @param from Pattern that specifies where counting starts from.
264      * Default (null) is the root node. (This parameter does not seem
265      * useful but is included for the sake of XSLT conformance.)
266      * @param context the dynamic context of the transformation, used if
267      * the patterns reference context values (e.g. variables)
268      * @exception XPathException when any error occurs in processing
269      * @return the node number established as follows: go to the nearest
270      * ancestor-or-self that matches the 'count' pattern and that is a
271      * descendant of the nearest ancestor that matches the 'from' pattern.
272      * Return one plus the nunber of preceding siblings of that ancestor
273      * that match the 'count' pattern. If there is no such ancestor,
274      * return 0.
275      */

276
277     public static int getNumberSingle(NodeInfo node, Pattern count,
278                     Pattern from, XPathContext context) throws XPathException {
279
280         // TODO: this assumes the change proposed in http://www.w3.org/Bugs/Public/show_bug.cgi?id=1846
281

282         if (count==null && from==null) {
283             return getNumberSimple(node, context);
284         }
285
286         boolean knownToMatch = false;
287         if (count==null) {
288             if (node.getFingerprint()==-1) { // unnamed node
289
count = new NodeTestPattern(NodeKindTest.makeNodeKindTest(node.getNodeKind()));
290             } else {
291                 count = new NodeTestPattern(new NameTest(node));
292             }
293             knownToMatch = true;
294         }
295
296         NodeInfo target = node;
297         while (!(knownToMatch || count.matches(target, context))) {
298             target = target.getParent();
299             if (target==null) {
300                 return 0;
301             }
302             if (from!=null && from.matches(target, context)) {
303                 return 0;
304             }
305         }
306
307         // we've found the ancestor to count from
308

309         SequenceIterator preceding =
310             target.iterateAxis(Axis.PRECEDING_SIBLING, count.getNodeTest());
311                         // pass the filter condition down to the axis enumeration where possible
312
boolean alreadyChecked = (count instanceof NodeTestPattern);
313         int i = 1;
314         while (true) {
315             NodeInfo p = (NodeInfo)preceding.next();
316             if (p == null) {
317                 return i;
318             }
319             if (alreadyChecked || count.matches(p, context)) {
320                 i++;
321             }
322         }
323     }
324
325     /**
326      * Get node number (level="any").
327      * Return one plus the number of previous nodes in the
328      * document that match the supplied pattern
329      *
330      * @exception net.sf.saxon.trans.XPathException
331      * @param inst Identifies the xsl:number expression; this is relevant
332      * when the function is memoised to support repeated use of the same
333      * instruction to number multiple nodes
334      * @param node The node being numbered
335      * @param count Pattern that identifies which nodes should be
336      * counted. Default (null) is the element name if the current node is
337      * an element, or "node()" otherwise.
338      * @param from Pattern that specifies where counting starts from.
339      * Default (null) is the root node. Only nodes after the first (most
340      * recent) node that matches the 'from' pattern are counted.
341      * @param context The dynamic context for the transformation
342      * @param hasVariablesInPatterns if the count or from patterns
343      * contain variables, then it's not safe to get the answer by adding
344      * one to the number of the most recent node that matches
345      * @return one plus the number of nodes that precede the current node,
346      * that match the count pattern, and that follow the first node that
347      * matches the from pattern if specified.
348      */

349
350     public static int getNumberAny(Expression inst, NodeInfo node, Pattern count,
351                     Pattern from, XPathContext context, boolean hasVariablesInPatterns) throws XPathException {
352
353         NodeInfo memoNode = null;
354         int memoNumber = 0;
355         Controller controller = context.getController();
356         boolean memoise = (!hasVariablesInPatterns && count!=null);
357         if (memoise) {
358             Object JavaDoc[] memo = (Object JavaDoc[])controller.getUserData(inst, "xsl:number");
359             if (memo != null) {
360                 memoNode = (NodeInfo)memo[0];
361                 memoNumber = ((Integer JavaDoc)memo[1]).intValue();
362             }
363         }
364
365         int num = 0;
366         if (count==null) {
367             if (node.getFingerprint()==-1) { // unnamed node
368
count = new NodeTestPattern(NodeKindTest.makeNodeKindTest(node.getNodeKind()));
369             } else {
370                 count = new NodeTestPattern(new NameTest(node));
371             }
372             num = 1;
373         } else if (count.matches(node, context)) {
374             num = 1;
375         }
376
377         // We use a special axis invented for the purpose: the union of the preceding and
378
// ancestor axes, but in reverse document order
379

380         // Pass part of the filtering down to the axis iterator if possible
381
NodeTest filter;
382         if (from==null) {
383             filter = count.getNodeTest();
384         } else if (from.getNodeKind()==Type.ELEMENT && count.getNodeKind()==Type.ELEMENT) {
385             filter = NodeKindTest.ELEMENT;
386         } else {
387             filter = AnyNodeTest.getInstance();
388         }
389
390         SequenceIterator preceding =
391             node.iterateAxis(Axis.PRECEDING_OR_ANCESTOR, filter);
392
393         while (true) {
394             NodeInfo prev = (NodeInfo)preceding.next();
395             if (prev == null) {
396                 break;
397             }
398             if (from!=null && from.matches(prev, context)) {
399                 return num;
400             }
401             if (count.matches(prev, context)) {
402                 if (num==1 && memoNode!=null && prev.isSameNodeInfo(memoNode)) {
403                     num = memoNumber + 1;
404                     break;
405                 }
406                 num++;
407             }
408         }
409         if (from != null) {
410             // we've got to the end without matching the from pattern - result is ()
411
return 0;
412         }
413         if (memoise) {
414             Object JavaDoc[] memo = new Object JavaDoc[2];
415             memo[0] = node;
416             memo[1] = new Integer JavaDoc(num);
417             controller.setUserData(inst, "xsl:number", memo);
418         }
419         return num;
420     }
421
422     /**
423      * Get node number (level="multiple").
424      * Return a vector giving the hierarchic position of this node. See the XSLT spec for details.
425      *
426      * @exception XPathException
427      * @param node The node to be numbered
428      * @param count Pattern that identifies which nodes (ancestors and
429      * their previous siblings) should be counted. Default (null) is the
430      * element name if the current node is an element, or "node()"
431      * otherwise.
432      * @param from Pattern that specifies where counting starts from.
433      * Default (null) is the root node. Only nodes below the first (most
434      * recent) node that matches the 'from' pattern are counted.
435      * @param context The dynamic context for the transformation
436      * @return a vector containing for each ancestor-or-self that matches the
437      * count pattern and that is below the nearest node that matches the
438      * from pattern, an Integer which is one greater than the number of
439      * previous siblings that match the count pattern.
440      */

441
442     public static List JavaDoc getNumberMulti(NodeInfo node, Pattern count,
443                     Pattern from, XPathContext context) throws XPathException {
444
445         //checkNumberable(node);
446

447         ArrayList JavaDoc v = new ArrayList JavaDoc(5);
448
449         if (count==null) {
450             if (node.getFingerprint()==-1) { // unnamed node
451
count = new NodeTestPattern(NodeKindTest.makeNodeKindTest(node.getNodeKind()));
452             } else {
453                 count = new NodeTestPattern(new NameTest(node));
454             }
455         }
456
457         NodeInfo curr = node;
458
459         while(true) {
460             if (count.matches(curr, context)) {
461                 int num = getNumberSingle(curr, count, null, context);
462                 v.add(0, new Long JavaDoc(num));
463             }
464             curr = curr.getParent();
465             if (curr==null) break;
466             if (from!=null && from.matches(curr, context)) break;
467         }
468
469         return v;
470     }
471
472      /**
473      * Generic (model-independent) implementation of deep copy algorithm for nodes.
474      * This is available for use by any node implementations that choose to use it.
475       * @param node The node to be copied
476       * @param out The receiver to which events will be sent
477       * @param namePool Namepool holding the name codes (used only to resolve namespace
478       * codes)
479       * @param whichNamespaces Indicates which namespace nodes for an element should
480       * be copied
481       * @param copyAnnotations Indicates whether type annotations should be copied
482       * @throws XPathException on any failure reported by the Receiver
483      */

484
485     public static void copy(NodeInfo node,
486                             Receiver out,
487                             NamePool namePool,
488                             int whichNamespaces,
489                             boolean copyAnnotations, int locationId) throws XPathException {
490
491         switch (node.getNodeKind()) {
492             case Type.DOCUMENT: {
493                 out.startDocument(0);
494                 AxisIterator children0 = node.iterateAxis(Axis.CHILD, new AnyNodeTest());
495                 while (true) {
496                     NodeInfo child = (NodeInfo)children0.next();
497                     if (child == null) {
498                         break;
499                     }
500                     child.copy(out, whichNamespaces, copyAnnotations, locationId);
501                 }
502                 out.endDocument();
503                 break;
504             }
505             case Type.ELEMENT: {
506                 out.startElement(node.getNameCode(),
507                         copyAnnotations? node.getTypeAnnotation(): StandardNames.XDT_UNTYPED,
508                         0, 0);
509
510                 // output the namespaces
511

512                 if (whichNamespaces != NodeInfo.NO_NAMESPACES) {
513                     node.sendNamespaceDeclarations(out, true);
514                 }
515
516                 // output the attributes
517

518                 AxisIterator attributes = node.iterateAxis(Axis.ATTRIBUTE, new AnyNodeTest());
519                 while (true) {
520                     NodeInfo att = (NodeInfo)attributes.next();
521                     if (att == null) {
522                         break;
523                     }
524                     att.copy(out, whichNamespaces, copyAnnotations, locationId);
525                 }
526
527                 // notify the start of content
528

529                 out.startContent();
530
531                 // output the children
532

533                 AxisIterator children = node.iterateAxis(Axis.CHILD, new AnyNodeTest());
534                 while (true) {
535                     NodeInfo child = (NodeInfo)children.next();
536                     if (child == null) {
537                         break;
538                     }
539                     child.copy(out, whichNamespaces, copyAnnotations, locationId);
540                 }
541
542                 // finally the end tag
543

544                 out.endElement();
545                 return;
546             }
547             case Type.ATTRIBUTE: {
548                 out.attribute(node.getNameCode(),
549                               copyAnnotations? node.getTypeAnnotation(): StandardNames.XDT_UNTYPED_ATOMIC,
550                               node.getStringValueCS(), 0, 0);
551                 return;
552             }
553             case Type.TEXT: {
554                 out.characters(node.getStringValueCS(), 0, 0);
555                 return;
556             }
557             case Type.COMMENT: {
558                 out.comment(node.getStringValueCS(), 0, 0);
559                 return;
560             }
561             case Type.PROCESSING_INSTRUCTION: {
562                 out.processingInstruction(node.getLocalPart(), node.getStringValueCS(), 0, 0);
563                 return;
564             }
565             case Type.NAMESPACE: {
566                 out.namespace(namePool.allocateNamespaceCode(node.getLocalPart(), node.getStringValue()),0);
567                 return;
568             }
569             default:
570
571         }
572     }
573
574     /**
575     * Generic (model-independent) method to determine the relative position of two
576     * node in document order. The nodes must be in the same tree.
577     * @param first The first node
578     * @param second The second node, whose position is to be compared with the first node
579     * @return -1 if this node precedes the other node, +1 if it follows the other
580     * node, or 0 if they are the same node. (In this case, isSameNode() will always
581     * return true, and the two nodes will produce the same result for generateId())
582     */

583
584     public static int compareOrder(SiblingCountingNode first, SiblingCountingNode second) {
585         NodeInfo ow = second;
586
587         // are they the same node?
588
if (first.isSameNodeInfo(second)) {
589             return 0;
590         }
591
592         NodeInfo firstParent = first.getParent();
593         if (firstParent == null) {
594             // first node is the root
595
return -1;
596         }
597
598         NodeInfo secondParent = second.getParent();
599         if (secondParent == null) {
600             // second node is the root
601
return +1;
602         }
603
604         // do they have the same parent (common case)?
605
if (firstParent.isSameNodeInfo(secondParent)) {
606             int cat1 = nodeCategories[first.getNodeKind()];
607             int cat2 = nodeCategories[second.getNodeKind()];
608             if (cat1 == cat2) {
609                 return first.getSiblingPosition() - second.getSiblingPosition();
610             } else {
611                 return cat1 - cat2;
612             }
613         }
614
615         // find the depths of both nodes in the tree
616
int depth1 = 0;
617         int depth2 = 0;
618         NodeInfo p1 = first;
619         NodeInfo p2 = second;
620         while (p1 != null) {
621             depth1++;
622             p1 = p1.getParent();
623         }
624         while (p2 != null) {
625             depth2++;
626             p2 = p2.getParent();
627         }
628         // move up one branch of the tree so we have two nodes on the same level
629

630         p1 = first;
631         while (depth1>depth2) {
632             p1 = p1.getParent();
633             if (p1.isSameNodeInfo(second)) {
634                 return +1;
635             }
636             depth1--;
637         }
638
639         p2 = ow;
640         while (depth2>depth1) {
641             p2 = p2.getParent();
642             if (p2.isSameNodeInfo(first)) {
643                 return -1;
644             }
645             depth2--;
646         }
647
648         // now move up both branches in sync until we find a common parent
649
while (true) {
650             NodeInfo par1 = p1.getParent();
651             NodeInfo par2 = p2.getParent();
652             if (par1==null || par2==null) {
653                 throw new NullPointerException JavaDoc("DOM/JDOM tree compare - internal error");
654             }
655             if (par1.isSameNodeInfo(par2)) {
656                 return ((SiblingCountingNode)p1).getSiblingPosition() -
657                         ((SiblingCountingNode)p2).getSiblingPosition();
658             }
659             p1 = par1;
660             p2 = par2;
661         }
662     }
663
664     /**
665      * Classify node kinds into categories for sorting into document order:
666      * 0 = document, 1 = namespace, 2 = attribute, 3 = (element, text, comment, pi)
667      */

668
669     private static int[] nodeCategories = {
670         -1, //0 = not used
671
3, //1 = element
672
2, //2 = attribute
673
3, //3 = text
674
-1, -1, -1, //4,5,6 = not used
675
3, //7 = processing-instruction
676
3, //8 = comment
677
0, //9 = document
678
-1, -1, -1, //10,11,12 = not used
679
1 //13 = namespace
680
};
681
682     /**
683     * Get a character string that uniquely identifies this node and that collates nodes
684     * into document order
685     * @return a string. The string is always interned so keys can be compared using "==".
686     */

687
688     public static String JavaDoc getSequentialKey(SiblingCountingNode node) {
689         // This was designed so it could be used for sorting nodes into document
690
// order, but is not currently used that way.
691
StringBuffer JavaDoc key = new StringBuffer JavaDoc(12);
692         int num = node.getDocumentNumber();
693         while(node != null && !(node instanceof DocumentInfo)) {
694             key.insert(0, alphaKey(node.getSiblingPosition()));
695             node = (SiblingCountingNode)node.getParent();
696         }
697         key.insert(0, "w" + num);
698         return key.toString().intern();
699     }
700
701     /**
702     * Construct an alphabetic key from an positive integer; the key collates in the same sequence
703     * as the integer
704     * @param value The positive integer key value (negative values are treated as zero).
705     */

706
707     public static String JavaDoc alphaKey(int value) {
708         if (value<1) return "a";
709         if (value<10) return "b" + value;
710         if (value<100) return "c" + value;
711         if (value<1000) return "d" + value;
712         if (value<10000) return "e" + value;
713         if (value<100000) return "f" + value;
714         if (value<1000000) return "g" + value;
715         if (value<10000000) return "h" + value;
716         if (value<100000000) return "i" + value;
717         if (value<1000000000) return "j" + value;
718         return "k" + value;
719     }
720
721      /**
722      * Determine if a string is all-whitespace
723      *
724      * @param content the string to be tested
725      * @return true if the supplied string contains no non-whitespace
726      * characters
727      * @deprecated since Saxon 8.5: use {@link Whitespace#isWhite}
728      */

729
730     public static final boolean isWhite(CharSequence JavaDoc content) {
731         return Whitespace.isWhite(content);
732     }
733
734
735     ///////////////////////////////////////////////////////////////////////////////
736
// Helper classes to support axis iteration
737
///////////////////////////////////////////////////////////////////////////////
738

739     /**
740      * AxisFilter is an iterator that applies a NodeTest filter to
741      * the nodes returned by an underlying AxisIterator.
742      */

743
744     public static class AxisFilter extends AxisIteratorImpl {
745         private AxisIterator base;
746         private NodeTest nodeTest;
747         //private int last = -1;
748

749         /** S
750          * Construct a AxisFilter
751          * @param base the underlying iterator that returns all the nodes on
752          * a required axis. This must not be an atomizing iterator!
753          * @param test a NodeTest that is applied to each node returned by the
754          * underlying AxisIterator; only those nodes that pass the NodeTest are
755          * returned by the AxisFilter
756          */

757
758         public AxisFilter(AxisIterator base, NodeTest test) {
759             this.base = base;
760             this.nodeTest = test;
761             position = 0;
762         }
763
764         public Item next() {
765             while (true) {
766                 current = base.next();
767                 if (current == null) {
768                     position = -1;
769                     return null;
770                 }
771                 if (nodeTest.matches((NodeInfo)current)) {
772                     position++;
773                     return current;
774                 }
775             }
776         }
777
778         public SequenceIterator getAnother() {
779             return new AxisFilter((AxisIterator)base.getAnother(), nodeTest);
780         }
781     }
782
783     /**
784      * BaseEnumeration is an abstract implementation of an AxisIterator, it
785      * simplifies the implementation of the underlying AxisIterator by requiring
786      * it to provide only two methods: advance(), and getAnother().
787      *
788      * NOTA BENE: BaseEnumeration does not maintain the value of the position variable.
789      * It must therefore either (a) be wrapped in an AxisFilter, which does maintain
790      * position, or (b) be subclassed by a class that maintains position itself.
791      */

792
793     public static abstract class BaseEnumeration extends AxisIteratorImpl {
794
795         public final Item next() {
796             advance();
797             return current;
798         }
799
800         /**
801          * The advance() method must be provided in each concrete implementation.
802          * It must leave the variable current set to the next node to be returned in the
803          * iteration, or to null if there are no more nodes to be returned.
804          */

805
806         public abstract void advance();
807
808         public abstract SequenceIterator getAnother();
809
810     }
811
812     /**
813      * General-purpose implementation of the ancestor and ancestor-or-self axes
814      */

815
816     public static final class AncestorEnumeration extends BaseEnumeration {
817
818         private boolean includeSelf;
819         private boolean atStart;
820         private NodeInfo start;
821
822         public AncestorEnumeration(NodeInfo start, boolean includeSelf) {
823             this.start = start;
824             this.includeSelf = includeSelf;
825             this.current = start;
826             atStart = true;
827         }
828
829         public void advance() {
830             if (atStart) {
831                 atStart = false;
832                 if (includeSelf) {
833                     return;
834                 }
835             }
836             current = ((NodeInfo)current).getParent();
837         }
838
839         public SequenceIterator getAnother() {
840             return new AncestorEnumeration(start, includeSelf);
841         }
842
843     } // end of class AncestorEnumeration
844

845     /**
846      * General-purpose implementation of the descendant and descendant-or-self axes,
847      * in terms of the child axis.
848     * But it also has the option to return the descendants in reverse document order;
849     * this is used when evaluating the preceding axis. Note that the includeSelf option
850     * should not be used when scanning in reverse order, as the self node will always be
851     * returned first.
852     */

853
854     public static final class DescendantEnumeration extends BaseEnumeration {
855
856         private AxisIterator children = null;
857         private AxisIterator descendants = null;
858         private NodeInfo start;
859         private boolean includeSelf;
860         private boolean forwards;
861         private boolean atEnd = false;
862
863         public DescendantEnumeration(NodeInfo start,
864                                  boolean includeSelf, boolean forwards) {
865             this.start = start;
866             this.includeSelf = includeSelf;
867             this.forwards = forwards;
868         }
869
870         public void advance() {
871             if (descendants!=null) {
872                 Item nextd = descendants.next();
873                 if (nextd != null) {
874                     current = nextd;
875                     return;
876                 } else {
877                     descendants = null;
878                 }
879             }
880             if (children!=null) {
881                 NodeInfo n = (NodeInfo)children.next();
882                 if (n != null) {
883                     if (n.hasChildNodes()) {
884                         if (forwards) {
885                             descendants = new DescendantEnumeration(n,
886                                              false, forwards);
887                             current = n;
888                         } else {
889                             descendants = new DescendantEnumeration(n, true, forwards);
890                             advance();
891                         }
892                     } else {
893                         current = n;
894                     }
895                 } else {
896                     if (forwards || !includeSelf) {
897                         current = null;
898                     } else {
899                         atEnd = true;
900                         children = null;
901                         current = start;
902                     }
903                 }
904             } else if (atEnd) {
905                 // we're just finishing a backwards scan
906
current = null;
907             } else {
908                 // we're just starting...
909
if (start.hasChildNodes()) {
910                     //children = new NodeWrapper.ChildEnumeration(start, true, forwards);
911
children = start.iterateAxis(Axis.CHILD);
912                     if (!forwards) {
913                         if (children instanceof ReversibleIterator) {
914                             children = (AxisIterator)((ReversibleIterator)children).getReverseIterator();
915                         } else {
916                             try {
917                                 children = new SequenceExtent(start.iterateAxis(Axis.CHILD)).reverseIterate();
918                             } catch (XPathException e) {
919                                 throw new AssertionError JavaDoc(
920                                         "Internal error in Navigator#descendantEnumeration: " + e.getMessage());
921                                 // shouldn't happen.
922
}
923                         }
924                     }
925                 } else {
926                     children = EmptyIterator.getInstance();
927                 }
928                 if (forwards && includeSelf) {
929                     current = start;
930                 } else {
931                     advance();
932                 }
933             }
934         }
935
936         public SequenceIterator getAnother() {
937             return new DescendantEnumeration(start, includeSelf, forwards);
938         }
939
940     } // end of class DescendantEnumeration
941

942     /**
943      * General purpose implementation of the following axis, in terms of the
944      * ancestor, child, and following-sibling axes
945      */

946
947     public static final class FollowingEnumeration extends BaseEnumeration {
948         private NodeInfo start;
949         private AxisIterator ancestorEnum = null;
950         private AxisIterator siblingEnum = null;
951         private AxisIterator descendEnum = null;
952
953         public FollowingEnumeration(NodeInfo start) {
954             this.start = start;
955             ancestorEnum = new AncestorEnumeration(start, false);
956             switch (start.getNodeKind()) {
957                 case Type.ELEMENT:
958                 case Type.TEXT:
959                 case Type.COMMENT:
960                 case Type.PROCESSING_INSTRUCTION:
961                     //siblingEnum = new NodeWrapper.ChildEnumeration(start, false, true);
962
// gets following siblings
963
siblingEnum = start.iterateAxis(Axis.FOLLOWING_SIBLING);
964                     break;
965                 case Type.ATTRIBUTE:
966                 case Type.NAMESPACE:
967                     //siblingEnum = new NodeWrapper.ChildEnumeration((NodeWrapper)start.getParent(), true, true);
968
// gets children of the attribute's parent node
969
NodeInfo parent = start.getParent();
970                     if (parent == null) {
971                         siblingEnum = EmptyIterator.getInstance();
972                     } else {
973                         siblingEnum = parent.iterateAxis(Axis.CHILD);
974                     }
975                     break;
976                 default:
977                     siblingEnum = EmptyIterator.getInstance();
978             }
979             //advance();
980
}
981
982         public void advance() {
983             if (descendEnum!=null) {
984                 Item nextd = descendEnum.next();
985                 if (nextd != null) {
986                     current = nextd;
987                     return;
988                 } else {
989                     descendEnum = null;
990                 }
991             }
992             if (siblingEnum!=null) {
993                 Item nexts = siblingEnum.next();
994                 if (nexts != null) {
995                     current = nexts;
996                     NodeInfo n = (NodeInfo)current;
997                     if (n.hasChildNodes()) {
998                         descendEnum = new DescendantEnumeration(n, false, true);
999                     } else {
1000                        descendEnum = null;
1001                    }
1002                    return;
1003                } else {
1004                    descendEnum = null;
1005                    siblingEnum = null;
1006                }
1007            }
1008            Item nexta = ancestorEnum.next();
1009            if (nexta != null) {
1010                current = nexta;
1011                NodeInfo n = (NodeInfo)current;
1012                if (n.getNodeKind() == Type.DOCUMENT) {
1013                    siblingEnum = EmptyIterator.getInstance();
1014                } else {
1015                    //siblingEnum = new NodeWrapper.ChildEnumeration(next, false, true);
1016
siblingEnum = n.iterateAxis(Axis.FOLLOWING_SIBLING);
1017                }
1018                advance();
1019            } else {
1020                current = null;
1021            }
1022        }
1023
1024        public SequenceIterator getAnother() {
1025            return new FollowingEnumeration(start);
1026        }
1027
1028    } // end of class FollowingEnumeration
1029

1030    public static final class PrecedingEnumeration extends BaseEnumeration {
1031
1032        private NodeInfo start;
1033        private AxisIterator ancestorEnum = null;
1034        private AxisIterator siblingEnum = null;
1035        private AxisIterator descendEnum = null;
1036        private boolean includeAncestors;
1037
1038        public PrecedingEnumeration(NodeInfo start, boolean includeAncestors) {
1039            this.start = start;
1040            this.includeAncestors = includeAncestors;
1041            ancestorEnum = new AncestorEnumeration(start, false);
1042            switch (start.getNodeKind()) {
1043                case Type.ELEMENT:
1044                case Type.TEXT:
1045                case Type.COMMENT:
1046                case Type.PROCESSING_INSTRUCTION:
1047                    // get preceding-sibling enumeration
1048
//siblingEnum = new NodeWrapper.ChildEnumeration(start, false, false);
1049
siblingEnum = start.iterateAxis(Axis.PRECEDING_SIBLING);
1050                    break;
1051                default:
1052                    siblingEnum = EmptyIterator.getInstance();
1053            }
1054            //advance();
1055
}
1056
1057        public void advance() {
1058            if (descendEnum!=null) {
1059                Item nextd = descendEnum.next();
1060                if (nextd != null) {
1061                    current = nextd;
1062                    return;
1063                } else {
1064                    descendEnum = null;
1065                }
1066            }
1067            if (siblingEnum!=null) {
1068                Item nexts = siblingEnum.next();
1069                if (nexts != null) {
1070                    NodeInfo sib = (NodeInfo)nexts;
1071                    if (sib.hasChildNodes()) {
1072                        descendEnum = new DescendantEnumeration(sib, true, false);
1073                        advance();
1074                    } else {
1075                        descendEnum = null;
1076                        current = sib;
1077                    }
1078                    return;
1079                } else {
1080                    descendEnum = null;
1081                    siblingEnum = null;
1082                }
1083            }
1084            Item nexta = ancestorEnum.next();
1085            if (nexta != null) {
1086                current = nexta;
1087                NodeInfo n = (NodeInfo)current;
1088                if (n.getNodeKind() == Type.DOCUMENT) {
1089                    siblingEnum = EmptyIterator.getInstance();
1090                } else {
1091                    siblingEnum = n.iterateAxis(Axis.PRECEDING_SIBLING);
1092                }
1093                if (!includeAncestors) {
1094                    advance();
1095                }
1096            } else {
1097                current = null;
1098            }
1099        }
1100
1101        public SequenceIterator getAnother() {
1102            return new PrecedingEnumeration(start, includeAncestors);
1103        }
1104
1105    } // end of class PrecedingEnumeration
1106

1107
1108}
1109
1110//
1111
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1112
// you may not use this file except in compliance with the License. You may obtain a copy of the
1113
// License at http://www.mozilla.org/MPL/
1114
//
1115
// Software distributed under the License is distributed on an "AS IS" basis,
1116
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
1117
// See the License for the specific language governing rights and limitations under the License.
1118
//
1119
// The Original Code is: all this file.
1120
//
1121
// The Initial Developer of the Original Code is Michael H. Kay.
1122
//
1123
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1124
//
1125
// Contributor(s): none.
1126
//
1127
Popular Tags