KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.om;
2 import net.sf.saxon.Configuration;
3 import net.sf.saxon.value.Whitespace;
4 import net.sf.saxon.value.Value;
5 import net.sf.saxon.style.StandardNames;
6 import net.sf.saxon.event.Receiver;
7 import net.sf.saxon.event.Stripper;
8 import net.sf.saxon.pattern.NodeKindTest;
9 import net.sf.saxon.pattern.NodeTest;
10 import net.sf.saxon.trans.XPathException;
11 import net.sf.saxon.type.Type;
12
13
14 /**
15  * A StrippedNode is a view of a node, in a virtual tree that has whitespace
16  * text nodes stripped from it. All operations on the node produce the same result
17  * as operations on the real underlying node, except that iterations over the axes
18  * take care to skip whitespace-only text nodes that are supposed to be stripped.
19  * Note that this class is only used in cases where a pre-built tree is supplied as
20  * the input to a transformation, and where the stylesheet does whitespace stripping;
21  * if a SAXSource or StreamSource is supplied, whitespace is stripped as the tree
22  * is built.
23 */

24
25 public class StrippedNode implements NodeInfo, VirtualNode {
26
27     protected NodeInfo node;
28     protected StrippedNode parent; // null means unknown
29
protected StrippedDocument docWrapper;
30
31     protected StrippedNode() {}
32
33     /**
34      * This constructor is protected: nodes should be created using the makeWrapper
35      * factory method
36      * @param node The node to be wrapped
37      * @param parent The StrippedNode that wraps the parent of this node
38      */

39
40     protected StrippedNode(NodeInfo node, StrippedNode parent) {
41         this.node = node;
42         this.parent = parent;
43     }
44
45     /**
46      * Factory method to wrap a node with a wrapper that implements the Saxon
47      * NodeInfo interface.
48      * @param node The underlying node
49      * @param docWrapper The wrapper for the document node (must be supplied)
50      * @param parent The wrapper for the parent of the node (null if unknown)
51      * @return The new wrapper for the supplied node
52      */

53
54     protected StrippedNode makeWrapper(NodeInfo node,
55                                        StrippedDocument docWrapper,
56                                        StrippedNode parent) {
57         StrippedNode wrapper = new StrippedNode(node, parent);
58         wrapper.docWrapper = docWrapper;
59         return wrapper;
60     }
61
62     /**
63     * Get the underlying DOM node, to implement the VirtualNode interface
64     */

65
66     public Object JavaDoc getUnderlyingNode() {
67         return node;
68     }
69
70     /**
71      * Get the configuration
72      */

73
74     public Configuration getConfiguration() {
75         return node.getConfiguration();
76     }
77
78     /**
79      * Get the name pool for this node
80      * @return the NamePool
81      */

82
83     public NamePool getNamePool() {
84         return node.getNamePool();
85     }
86
87     /**
88     * Return the type of node.
89     * @return one of the values Node.ELEMENT, Node.TEXT, Node.ATTRIBUTE, etc.
90     */

91
92     public int getNodeKind() {
93         return node.getNodeKind();
94     }
95
96     /**
97     * Get the typed value of the item
98     */

99
100     public SequenceIterator getTypedValue() throws XPathException {
101         return node.getTypedValue();
102     }
103
104     /**
105      * Get the typed value. The result of this method will always be consistent with the method
106      * {@link Item#getTypedValue()}. However, this method is often more convenient and may be
107      * more efficient, especially in the common case where the value is expected to be a singleton.
108      *
109      * @return the typed value. If requireSingleton is set to true, the result will always be an
110      * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
111      * values.
112      * @since 8.5
113      */

114
115     public Value atomize() throws XPathException {
116         return node.atomize();
117     }
118
119     /**
120     * Get the type annotation
121     * @return 0 (there is no type annotation)
122     */

123
124     public int getTypeAnnotation() {
125         return node.getTypeAnnotation();
126     }
127
128     /**
129     * Determine whether this is the same node as another node. <br />
130     * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
131     * @return true if this Node object and the supplied Node object represent the
132     * same node in the tree.
133     */

134
135     public boolean isSameNodeInfo(NodeInfo other) {
136         if (other instanceof StrippedNode) {
137             return node.isSameNodeInfo(((StrippedNode)other).node);
138         } else {
139             return node.isSameNodeInfo(other);
140         }
141     }
142
143     /**
144     * Get the System ID for the node.
145     * @return the System Identifier of the entity in the source document containing the node,
146     * or null if not known. Note this is not the same as the base URI: the base URI can be
147     * modified by xml:base, but the system ID cannot.
148     */

149
150     public String JavaDoc getSystemId() {
151         return node.getSystemId();
152     }
153
154     public void setSystemId(String JavaDoc uri) {
155         node.setSystemId(uri);
156     }
157
158     /**
159     * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
160      * in the node. In the JDOM model, base URIs are held only an the document level. We don't
161      * currently take any account of xml:base attributes.
162     */

163
164     public String JavaDoc getBaseURI() {
165         return node.getBaseURI();
166     }
167
168     /**
169     * Get line number
170     * @return the line number of the node in its original source document; or -1 if not available
171     */

172
173     public int getLineNumber() {
174         return node.getLineNumber();
175     }
176
177     /**
178     * Determine the relative position of this node and another node, in document order.
179     * The other node will always be in the same document.
180     * @param other The other node, whose position is to be compared with this node
181     * @return -1 if this node precedes the other node, +1 if it follows the other
182     * node, or 0 if they are the same node. (In this case, isSameNode() will always
183     * return true, and the two nodes will produce the same result for generateId())
184     */

185
186     public int compareOrder(NodeInfo other) {
187         if (other instanceof StrippedNode) {
188             return node.compareOrder(((StrippedNode)other).node);
189         } else {
190             return node.compareOrder(other);
191         }
192     }
193
194     /**
195     * Return the string value of the node. The interpretation of this depends on the type
196     * of node. For an element it is the accumulated character content of the element,
197     * including descendant elements.
198     * @return the string value of the node
199     */

200
201     public String JavaDoc getStringValue() {
202         return getStringValueCS().toString();
203     }
204
205     /**
206      * Get the value of the item as a CharSequence. This is in some cases more efficient than
207      * the version of the method that returns a String.
208      */

209
210     public CharSequence JavaDoc getStringValueCS() {
211         // Might not be the same as the string value of the underlying node because of space stripping
212
switch (getNodeKind()) {
213             case Type.DOCUMENT:
214             case Type.ELEMENT:
215                 AxisIterator iter = iterateAxis(Axis.DESCENDANT, NodeKindTest.makeNodeKindTest(Type.TEXT));
216                 FastStringBuffer sb = new FastStringBuffer(1024);
217                 while(true) {
218                     NodeInfo it = (NodeInfo)iter.next();
219                     if (it == null) {
220                         break;
221                     }
222                     sb.append(it.getStringValueCS());
223                 }
224                 return sb.condense();
225             default:
226                 return node.getStringValueCS();
227         }
228     }
229
230     /**
231     * Get name code. The name code is a coded form of the node name: two nodes
232     * with the same name code have the same namespace URI, the same local name,
233     * and the same prefix. By masking the name code with &0xfffff, you get a
234     * fingerprint: two nodes with the same fingerprint have the same local name
235     * and namespace URI.
236     * @see NamePool#allocate allocate
237     */

238
239     public int getNameCode() {
240         return node.getNameCode();
241     }
242
243     /**
244     * Get fingerprint. The fingerprint is a coded form of the expanded name
245     * of the node: two nodes
246     * with the same name code have the same namespace URI and the same local name.
247     * A fingerprint of -1 should be returned for a node with no name.
248     */

249
250     public int getFingerprint() {
251         return node.getFingerprint();
252     }
253
254     /**
255     * Get the local part of the name of this node. This is the name after the ":" if any.
256     * @return the local part of the name. For an unnamed node, returns null, except for
257      * un unnamed namespace node, which returns "".
258     */

259
260     public String JavaDoc getLocalPart() {
261         return node.getLocalPart();
262     }
263
264    /**
265     * Get the URI part of the name of this node. This is the URI corresponding to the
266     * prefix, or the URI of the default namespace if appropriate.
267     * @return The URI of the namespace of this node. For an unnamed node, return null.
268     * For a node with an empty prefix, return an empty string.
269     */

270
271     public String JavaDoc getURI() {
272         return node.getURI();
273     }
274
275     /**
276      * Get the prefix of the name of the node. This is defined only for elements and attributes.
277      * If the node has no prefix, or for other kinds of node, return a zero-length string.
278      *
279      * @return The prefix of the name of the node.
280      */

281
282     public String JavaDoc getPrefix() {
283         return node.getPrefix();
284     }
285
286     /**
287     * Get the display name of this node. For elements and attributes this is [prefix:]localname.
288     * For unnamed nodes, it is an empty string.
289     * @return The display name of this node.
290     * For a node with no name, return an empty string.
291     */

292
293     public String JavaDoc getDisplayName() {
294         return node.getDisplayName();
295     }
296
297     /**
298     * Get the NodeInfo object representing the parent of this node
299     */

300
301     public NodeInfo getParent() {
302         if (parent==null) {
303             NodeInfo realParent = node.getParent();
304             if (realParent != null) {
305                 parent = makeWrapper(realParent, docWrapper, null);
306             }
307         }
308         return parent;
309     }
310
311     /**
312     * Return an iteration over the nodes reached by the given axis from this node
313     * @param axisNumber the axis to be used
314     * @return a SequenceIterator that scans the nodes reached by the axis in turn.
315     */

316
317     public AxisIterator iterateAxis(byte axisNumber) {
318         switch (axisNumber) {
319             case Axis.ATTRIBUTE:
320             case Axis.NAMESPACE:
321                 return new WrappingIterator(node.iterateAxis(axisNumber), this);
322             case Axis.CHILD:
323                 return new StrippingIterator(node.iterateAxis(axisNumber), this);
324             case Axis.FOLLOWING_SIBLING:
325             case Axis.PRECEDING_SIBLING:
326                 StrippedNode parent = (StrippedNode)getParent();
327                 if (parent == null) {
328                     return EmptyIterator.getInstance();
329                 } else {
330                     return new StrippingIterator(node.iterateAxis(axisNumber), parent);
331                 }
332             default:
333                 return new StrippingIterator(node.iterateAxis(axisNumber), null);
334         }
335     }
336
337     /**
338     * Return an iteration over the nodes reached by the given axis from this node
339     * @param axisNumber the axis to be used
340     * @param nodeTest A pattern to be matched by the returned nodes
341     * @return a SequenceIterator that scans the nodes reached by the axis in turn.
342     */

343
344     public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
345         return new Navigator.AxisFilter(iterateAxis(axisNumber), nodeTest);
346     }
347
348     /**
349     * Get the value of a given attribute of this node
350     * @param fingerprint The fingerprint of the attribute name
351     * @return the attribute value if it exists or null if not
352     */

353
354     public String JavaDoc getAttributeValue(int fingerprint) {
355         return node.getAttributeValue(fingerprint);
356     }
357
358     /**
359     * Get the root node - always a document node with this tree implementation
360     * @return the NodeInfo representing the containing document
361     */

362
363     public NodeInfo getRoot() {
364         return docWrapper;
365     }
366
367     /**
368     * Get the root (document) node
369     * @return the DocumentInfo representing the containing document
370     */

371
372     public DocumentInfo getDocumentRoot() {
373         return docWrapper;
374     }
375
376     /**
377     * Determine whether the node has any children. <br />
378     * Note: the result is equivalent to <br />
379     * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasNext()
380     */

381
382     public boolean hasChildNodes() {
383         return node.hasChildNodes();
384     }
385
386     /**
387     * Get a character string that uniquely identifies this node.
388     * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
389     * @return a string that uniquely identifies this node, within this
390     * document. The calling code prepends information to make the result
391     * unique across all documents.
392     */

393
394     public String JavaDoc generateId() {
395         return node.generateId();
396     }
397
398     /**
399      * Get the document number of the document containing this node. For a free-standing
400      * orphan node, just return the hashcode.
401      */

402
403     public int getDocumentNumber() {
404         return docWrapper.getDocumentNumber();
405     }
406
407     /**
408     * Copy this node to a given outputter (deep copy)
409     */

410
411     public void copy(Receiver out, int whichNamespaces, boolean copyAnnotations, int locationId) throws XPathException {
412         // The underlying code does not do whitespace stripping. So we need to interpose
413
// a stripper.
414
Stripper stripper = docWrapper.getStripper().getAnother();
415         stripper.setUnderlyingReceiver(out);
416         node.copy(stripper, whichNamespaces, copyAnnotations, locationId);
417     }
418
419     /**
420     * Output all namespace nodes associated with this element. Does nothing if
421     * the node is not an element.
422     * @param out The relevant outputter
423      * @param includeAncestors True if namespaces declared on ancestor elements must
424      */

425
426     public void sendNamespaceDeclarations(Receiver out, boolean includeAncestors)
427     throws XPathException {
428         node.sendNamespaceDeclarations(out, includeAncestors);
429     }
430
431     /**
432      * Get all namespace undeclarations and undeclarations defined on this element.
433      *
434      * @param buffer If this is non-null, and the result array fits in this buffer, then the result
435      * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
436      * @return An array of integers representing the namespace declarations and undeclarations present on
437      * this element. For a node other than an element, return null. Otherwise, the returned array is a
438      * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
439      * top half word of each namespace code represents the prefix, the bottom half represents the URI.
440      * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
441      * The XML namespace is never included in the list. If the supplied array is larger than required,
442      * then the first unused entry will be set to -1.
443      * <p/>
444      * <p>For a node other than an element, the method returns null.</p>
445      */

446
447     public int[] getDeclaredNamespaces(int[] buffer) {
448         return node.getDeclaredNamespaces(buffer);
449     }
450
451     /**
452      * A WrappingIterator delivers wrappers for the nodes delivered
453      * by its underlying iterator. It is used when no whitespace stripping
454      * is actually needed, e.g. for the attribute axis. But we still need to
455      * create wrappers, so that further iteration remains in the virtual layer
456      * rather than switching to the real nodes.
457      */

458
459     private final class WrappingIterator implements AxisIterator, AtomizableIterator {
460
461         AxisIterator base;
462         StrippedNode parent;
463         Item current;
464         boolean atomizing = false;
465
466         /**
467          * Create a WrappingIterator
468          * @param base The underlying iterator
469          * @param parent If all the nodes to be wrapped have the same parent,
470          * it can be specified here. Otherwise specify null.
471          */

472
473         public WrappingIterator(AxisIterator base, StrippedNode parent) {
474             this.base = base;
475             this.parent = parent;
476         }
477
478         public Item next() {
479             Item n = base.next();
480             if (n instanceof NodeInfo && !atomizing) {
481                 current = makeWrapper((NodeInfo)n, docWrapper, parent);
482             } else {
483                 current = n;
484             }
485             return current;
486         }
487
488         public Item current() {
489             return current;
490         }
491
492         public int position() {
493             return base.position();
494         }
495
496         public SequenceIterator getAnother() {
497             return new WrappingIterator((AxisIterator)base.getAnother(), parent);
498         }
499
500         /**
501          * Get properties of this iterator, as a bit-significant integer.
502          *
503          * @return the properties of this iterator. This will be some combination of
504          * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
505          * and {@link LOOKAHEAD}. It is always
506          * acceptable to return the value zero, indicating that there are no known special properties.
507          * It is acceptable for the properties of the iterator to change depending on its state.
508          */

509
510         public int getProperties() {
511             return 0;
512         }
513
514         /**
515          * Indicate that any nodes returned in the sequence will be atomized. This
516          * means that if it wishes to do so, the implementation can return the typed
517          * values of the nodes rather than the nodes themselves. The implementation
518          * is free to ignore this hint.
519          * @param atomizing true if the caller of this iterator will atomize any
520          * nodes that are returned, and is therefore willing to accept the typed
521          * value of the nodes instead of the nodes themselves.
522          */

523
524         public void setIsAtomizing(boolean atomizing) {
525             this.atomizing = true;
526             if (base instanceof AtomizableIterator) {
527                 ((AtomizableIterator)base).setIsAtomizing(atomizing);
528             }
529         }
530
531     } // end of class WrappingIterator
532

533     /**
534      * A StrippingIterator delivers wrappers for the nodes delivered
535      * by its underlying iterator. It is used when whitespace stripping
536      * may be needed, e.g. for the child axis. It examines all text nodes
537      * encountered to see if they need to be stripped, and if so, it
538      * skips them.
539      */

540
541     private final class StrippingIterator implements AxisIterator {
542
543         AxisIterator base;
544         StrippedNode parent;
545         NodeInfo currentVirtualNode;
546         int position;
547
548         /**
549          * Create a StrippingIterator
550          * @param base The underlying iterator
551          * @param parent If all the nodes to be wrapped have the same parent,
552          * it can be specified here. Otherwise specify null.
553          */

554
555         public StrippingIterator(AxisIterator base, StrippedNode parent) {
556             this.base = base;
557             this.parent = parent;
558             position = 0;
559         }
560
561         public Item next() {
562             NodeInfo nextRealNode;
563             while (true) {
564                 nextRealNode = (NodeInfo)base.next();
565                 if (nextRealNode==null) {
566                     return null;
567                 }
568                 if (isPreserved(nextRealNode)) {
569                     break;
570                 }
571                 // otherwise skip this whitespace text node
572
}
573
574             currentVirtualNode = makeWrapper(nextRealNode, docWrapper, parent);
575             position++;
576             return currentVirtualNode;
577         }
578
579         private boolean isPreserved(NodeInfo nextRealNode) {
580             if (nextRealNode.getNodeKind() != Type.TEXT) {
581                 return true;
582             }
583             if (!Whitespace.isWhite(nextRealNode.getStringValueCS())) {
584                 return true;
585             }
586             NodeInfo actualParent =
587                     (parent==null ? nextRealNode.getParent() : parent.node);
588
589             if (docWrapper.containsPreserveSpace()) {
590                 NodeInfo p = actualParent;
591                 // the document contains one or more xml:space="preserve" attributes, so we need to see
592
// if one of them is on an ancestor of this node
593
while (p.getNodeKind() == Type.ELEMENT) {
594                     String JavaDoc val = p.getAttributeValue(StandardNames.XML_SPACE);
595                     if (val != null) {
596                         if ("preserve".equals(val)) {
597                             return true;
598                         } else if ("default".equals(val)) {
599                             break;
600                         }
601                     }
602                     p = p.getParent();
603                 }
604             }
605
606             if (docWrapper.getStripper().isSpacePreserving(actualParent) == Stripper.ALWAYS_PRESERVE) {
607                 return true;
608             }
609             return false;
610         }
611
612         public Item current() {
613             return currentVirtualNode;
614         }
615
616         public int position() {
617             return position;
618         }
619
620         public SequenceIterator getAnother() {
621             return new StrippingIterator((AxisIterator)base.getAnother(), parent);
622         }
623
624         /**
625          * Get properties of this iterator, as a bit-significant integer.
626          *
627          * @return the properties of this iterator. This will be some combination of
628          * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
629          * and {@link LOOKAHEAD}. It is always
630          * acceptable to return the value zero, indicating that there are no known special properties.
631          * It is acceptable for the properties of the iterator to change depending on its state.
632          */

633
634         public int getProperties() {
635             return 0;
636         }
637
638     } // end of class StrippingIterator
639

640
641
642 }
643
644 //
645
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
646
// you may not use this file except in compliance with the License. You may obtain a copy of the
647
// License at http://www.mozilla.org/MPL/
648
//
649
// Software distributed under the License is distributed on an "AS IS" basis,
650
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
651
// See the License for the specific language governing rights and limitations under the License.
652
//
653
// The Original Code is: all this file.
654
//
655
// The Initial Developer of the Original Code is
656
// Michael H. Kay.
657
//
658
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
659
//
660
// Contributor(s): none.
661
//
662
Popular Tags