001: package net.sf.saxon.om;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.type.Type;
005: import net.sf.saxon.value.Value;
006: import net.sf.saxon.event.Receiver;
007: import net.sf.saxon.pattern.AnyNodeTest;
008: import net.sf.saxon.pattern.NodeTest;
009: import net.sf.saxon.trans.XPathException;
010:
011: /**
012: * This class represents a node that is a virtual copy of another node: that is, it behaves as a node that's the
013: * same as another node, but has different identity. It is implemented by means of a reference to the node of which
014: * it is a copy, but methods that are sensitive to node identity return a different result.
015: */
016:
017: public class VirtualCopy implements NodeInfo {
018:
019: protected String baseURI;
020: protected int documentNumber;
021: protected NodeInfo original;
022: protected VirtualCopy parent;
023: protected NodeInfo root; // the node forming the root of the subtree that was copied
024:
025: protected VirtualCopy(NodeInfo base) {
026: this .original = base;
027: }
028:
029: public static VirtualCopy makeVirtualCopy(NodeInfo original,
030: NodeInfo root) {
031:
032: VirtualCopy vc;
033: // Don't allow copies of copies of copies: define the new copy in terms of the original
034: while (original instanceof VirtualCopy) {
035: original = ((VirtualCopy) original).original;
036: }
037: while (root instanceof VirtualCopy) {
038: root = ((VirtualCopy) root).original;
039: }
040: if (original.getNodeKind() == Type.DOCUMENT) {
041: vc = new VirtualDocumentCopy((DocumentInfo) original);
042: } else {
043: vc = new VirtualCopy(original);
044: }
045: vc.root = root;
046: return vc;
047: }
048:
049: public void setDocumentNumber(int documentNumber) {
050: this .documentNumber = documentNumber;
051: }
052:
053: /**
054: * Get the kind of node. This will be a value such as Type.ELEMENT or Type.ATTRIBUTE
055: *
056: * @return an integer identifying the kind of node. These integer values are the
057: * same as those used in the DOM
058: * @see net.sf.saxon.type.Type
059: */
060:
061: public int getNodeKind() {
062: return original.getNodeKind();
063: }
064:
065: /**
066: * Determine whether this is the same node as another node.
067: * Note: a.isSameNodeInfo(b) if and only if generateId(a)==generateId(b).
068: * This method has the same semantics as isSameNode() in DOM Level 3, but
069: * works on Saxon NodeInfo objects rather than DOM Node objects.
070: *
071: * @param other the node to be compared with this node
072: * @return true if this NodeInfo object and the supplied NodeInfo object represent
073: * the same node in the tree.
074: */
075:
076: public boolean isSameNodeInfo(NodeInfo other) {
077: return other instanceof VirtualCopy
078: && documentNumber == other.getDocumentNumber()
079: && original
080: .isSameNodeInfo(((VirtualCopy) other).original);
081:
082: }
083:
084: /**
085: * Get the System ID for the node.
086: *
087: * @return the System Identifier of the entity in the source document
088: * containing the node, or null if not known. Note this is not the
089: * same as the base URI: the base URI can be modified by xml:base, but
090: * the system ID cannot.
091: */
092:
093: public String getSystemId() {
094: return baseURI;
095: }
096:
097: /**
098: * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
099: * in the node. This will be the same as the System ID unless xml:base has been used.
100: *
101: * @return the base URI of the node
102: */
103:
104: public String getBaseURI() {
105: return baseURI;
106: }
107:
108: /**
109: * Get line number
110: *
111: * @return the line number of the node in its original source document; or
112: * -1 if not available
113: */
114:
115: public int getLineNumber() {
116: return original.getLineNumber();
117: }
118:
119: /**
120: * Determine the relative position of this node and another node, in document order.
121: * The other node will always be in the same document.
122: *
123: * @param other The other node, whose position is to be compared with this
124: * node
125: * @return -1 if this node precedes the other node, +1 if it follows the
126: * other node, or 0 if they are the same node. (In this case,
127: * isSameNode() will always return true, and the two nodes will
128: * produce the same result for generateId())
129: */
130:
131: public int compareOrder(NodeInfo other) {
132: return original.compareOrder(((VirtualCopy) other).original);
133: }
134:
135: /**
136: * Return the string value of the node. The interpretation of this depends on the type
137: * of node. For an element it is the accumulated character content of the element,
138: * including descendant elements.
139: *
140: * @return the string value of the node
141: */
142:
143: public String getStringValue() {
144: return original.getStringValue();
145: }
146:
147: /**
148: * Get the value of the item as a CharSequence. This is in some cases more efficient than
149: * the version of the method that returns a String.
150: */
151:
152: public CharSequence getStringValueCS() {
153: return original.getStringValueCS();
154: }
155:
156: /**
157: * Get name code. The name code is a coded form of the node name: two nodes
158: * with the same name code have the same namespace URI, the same local name,
159: * and the same prefix. By masking the name code with &0xfffff, you get a
160: * fingerprint: two nodes with the same fingerprint have the same local name
161: * and namespace URI.
162: *
163: * @return an integer name code, which may be used to obtain the actual node
164: * name from the name pool
165: * @see NamePool#allocate allocate
166: * @see NamePool#getFingerprint getFingerprint
167: */
168:
169: public int getNameCode() {
170: return original.getNameCode();
171: }
172:
173: /**
174: * Get fingerprint. The fingerprint is a coded form of the expanded name
175: * of the node: two nodes
176: * with the same name code have the same namespace URI and the same local name.
177: * A fingerprint of -1 should be returned for a node with no name.
178: *
179: * @return an integer fingerprint; two nodes with the same fingerprint have
180: * the same expanded QName
181: */
182:
183: public int getFingerprint() {
184: return original.getFingerprint();
185: }
186:
187: /**
188: * Get the local part of the name of this node. This is the name after the ":" if any.
189: *
190: * @return the local part of the name. For an unnamed node, returns "". Unlike the DOM
191: * interface, this returns the full name in the case of a non-namespaced name.
192: */
193:
194: public String getLocalPart() {
195: return original.getLocalPart();
196: }
197:
198: /**
199: * Get the URI part of the name of this node. This is the URI corresponding to the
200: * prefix, or the URI of the default namespace if appropriate.
201: *
202: * @return The URI of the namespace of this node. For an unnamed node,
203: * or for a node with an empty prefix, return an empty
204: * string.
205: */
206:
207: public String getURI() {
208: return original.getURI();
209: }
210:
211: /**
212: * Get the prefix of the name of the node. This is defined only for elements and attributes.
213: * If the node has no prefix, or for other kinds of node, return a zero-length string.
214: *
215: * @return The prefix of the name of the node.
216: */
217:
218: public String getPrefix() {
219: return original.getPrefix();
220: }
221:
222: /**
223: * Get the display name of this node. For elements and attributes this is [prefix:]localname.
224: * For unnamed nodes, it is an empty string.
225: *
226: * @return The display name of this node. For a node with no name, return
227: * an empty string.
228: */
229:
230: public String getDisplayName() {
231: return original.getDisplayName();
232: }
233:
234: /**
235: * Get the configuration
236: */
237:
238: public Configuration getConfiguration() {
239: return original.getConfiguration();
240: }
241:
242: /**
243: * Get the NamePool that holds the namecode for this node
244: *
245: * @return the namepool
246: */
247:
248: public NamePool getNamePool() {
249: return original.getNamePool();
250: }
251:
252: /**
253: * Get the type annotation of this node, if any.
254: * Returns -1 for kinds of nodes that have no annotation, and for elements annotated as
255: * untyped, and attributes annotated as untypedAtomic.
256: *
257: * @return the type annotation of the node.
258: * @see net.sf.saxon.type.Type
259: */
260:
261: public int getTypeAnnotation() {
262: return original.getTypeAnnotation();
263: }
264:
265: /**
266: * Get the NodeInfo object representing the parent of this node
267: *
268: * @return the parent of this node; null if this node has no parent
269: */
270:
271: public NodeInfo getParent() {
272: if (original.isSameNodeInfo(root)) {
273: return null;
274: }
275: if (parent == null) {
276: NodeInfo basep = original.getParent();
277: if (basep == null) {
278: return null;
279: }
280: parent = new VirtualCopy(basep);
281: parent.setDocumentNumber(documentNumber);
282: }
283: return parent;
284: }
285:
286: /**
287: * Return an iteration over all the nodes reached by the given axis from this node
288: *
289: * @param axisNumber an integer identifying the axis; one of the constants
290: * defined in class net.sf.saxon.om.Axis
291: * @return an AxisIterator that scans the nodes reached by the axis in
292: * turn.
293: * @throws UnsupportedOperationException if the namespace axis is
294: * requested and this axis is not supported for this implementation.
295: * @see Axis
296: */
297:
298: public AxisIterator iterateAxis(byte axisNumber) {
299: return iterateAxis(axisNumber, AnyNodeTest.getInstance());
300: }
301:
302: /**
303: * Return an iteration over all the nodes reached by the given axis from this node
304: * that match a given NodeTest
305: *
306: * @param axisNumber an integer identifying the axis; one of the constants
307: * defined in class net.sf.saxon.om.Axis
308: * @param nodeTest A pattern to be matched by the returned nodes; nodes
309: * that do not match this pattern are not included in the result
310: * @return a NodeEnumeration that scans the nodes reached by the axis in
311: * turn.
312: * @throws UnsupportedOperationException if the namespace axis is
313: * requested and this axis is not supported for this implementation.
314: * @see Axis
315: */
316:
317: public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
318: VirtualCopy newParent = null;
319: if (axisNumber == Axis.CHILD || axisNumber == Axis.ATTRIBUTE
320: || axisNumber == Axis.NAMESPACE) {
321: newParent = this ;
322: } else if (axisNumber == Axis.SELF
323: || axisNumber == Axis.PRECEDING_SIBLING
324: || axisNumber == Axis.FOLLOWING_SIBLING) {
325: newParent = parent;
326: }
327: NodeInfo root;
328: if (Axis.isSubtreeAxis[axisNumber]) {
329: root = null;
330: } else {
331: root = this .root;
332: }
333: return new VirtualCopier(original.iterateAxis(axisNumber,
334: nodeTest), newParent, root);
335: }
336:
337: /**
338: * Get the value of a given attribute of this node
339: *
340: * @param fingerprint The fingerprint of the attribute name
341: * @return the attribute value if it exists or null if not
342: */
343:
344: public String getAttributeValue(int fingerprint) {
345: return original.getAttributeValue(fingerprint);
346: }
347:
348: /**
349: * Get the root node of the tree containing this node
350: *
351: * @return the NodeInfo representing the top-level ancestor of this node.
352: * This will not necessarily be a document node
353: */
354:
355: public NodeInfo getRoot() {
356: NodeInfo n = this ;
357: while (true) {
358: NodeInfo p = n.getParent();
359: if (p == null) {
360: return n;
361: }
362: n = p;
363: }
364: }
365:
366: /**
367: * Get the root node, if it is a document node.
368: *
369: * @return the DocumentInfo representing the containing document. If this
370: * node is part of a tree that does not have a document node as its
371: * root, return null.
372: */
373:
374: public DocumentInfo getDocumentRoot() {
375: NodeInfo root = getRoot();
376: if (root.getNodeKind() == Type.DOCUMENT) {
377: return (DocumentInfo) root;
378: }
379: return null;
380: }
381:
382: /**
383: * Determine whether the node has any children. <br />
384: * Note: the result is equivalent to <br />
385: * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasNext()
386: *
387: * @return True if the node has one or more children
388: */
389:
390: public boolean hasChildNodes() {
391: return original.hasChildNodes();
392: }
393:
394: /**
395: * Get a character string that uniquely identifies this node.
396: * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
397: *
398: * @return a string that uniquely identifies this node, across all
399: * documents. (Changed in Saxon 7.5. Previously this method returned
400: * an id that was unique within the current document, and the calling
401: * code prepended a document id).
402: */
403:
404: public String generateId() {
405: return "d" + documentNumber + original.generateId();
406: }
407:
408: /**
409: * Get the document number of the document containing this node. For a free-standing
410: * orphan node, just return the hashcode.
411: */
412:
413: public int getDocumentNumber() {
414: return documentNumber;
415: }
416:
417: /**
418: * Copy this node to a given outputter
419: *
420: * @param out the Receiver to which the node should be copied
421: * @param whichNamespaces in the case of an element, controls
422: * which namespace nodes should be copied. Values are NO_NAMESPACES,
423: * LOCAL_NAMESPACES, ALL_NAMESPACES
424: * @param copyAnnotations indicates whether the type annotations
425: * of element and attribute nodes should be copied
426: * @param locationId Identifies the location of the instruction
427: * that requested this copy. Pass zero if no other information is available
428: * @throws net.sf.saxon.trans.XPathException
429: *
430: */
431:
432: public void copy(Receiver out, int whichNamespaces,
433: boolean copyAnnotations, int locationId)
434: throws XPathException {
435: original
436: .copy(out, whichNamespaces, copyAnnotations, locationId);
437: }
438:
439: /**
440: * Output all namespace nodes associated with this element. Does nothing if
441: * the node is not an element.
442: *
443: * @param out The relevant outputter
444: * @param includeAncestors True if namespaces declared on ancestor
445: */
446:
447: public void sendNamespaceDeclarations(Receiver out,
448: boolean includeAncestors) throws XPathException {
449: original.sendNamespaceDeclarations(out, includeAncestors);
450: }
451:
452: /**
453: * Get all namespace undeclarations and undeclarations defined on this element.
454: *
455: * @param buffer If this is non-null, and the result array fits in this buffer, then the result
456: * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
457: * @return An array of integers representing the namespace declarations and undeclarations present on
458: * this element. For a node other than an element, return null. Otherwise, the returned array is a
459: * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
460: * top half word of each namespace code represents the prefix, the bottom half represents the URI.
461: * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
462: * The XML namespace is never included in the list. If the supplied array is larger than required,
463: * then the first unused entry will be set to -1.
464: * <p/>
465: * <p>For a node other than an element, the method returns null.</p>
466: */
467:
468: public int[] getDeclaredNamespaces(int[] buffer) {
469: return original.getDeclaredNamespaces(buffer);
470: }
471:
472: /**
473: * Set the system identifier for this Source.
474: * <p/>
475: * <p>The system identifier is optional if the source does not
476: * get its data from a URL, but it may still be useful to provide one.
477: * The application can use a system identifier, for example, to resolve
478: * relative URIs and to include in error messages and warnings.</p>
479: *
480: * @param systemId The system identifier as a URL string.
481: */
482: public void setSystemId(String systemId) {
483: baseURI = systemId;
484: }
485:
486: /**
487: * Get the typed value of the item
488: *
489: * @return the typed value of the item. In general this will be a sequence
490: * @throws net.sf.saxon.trans.XPathException
491: * where no typed value is available, e.g. for
492: * an element with complex content
493: */
494:
495: public SequenceIterator getTypedValue() throws XPathException {
496: return original.getTypedValue();
497: }
498:
499: /**
500: * Get the typed value. The result of this method will always be consistent with the method
501: * {@link Item#getTypedValue()}. However, this method is often more convenient and may be
502: * more efficient, especially in the common case where the value is expected to be a singleton.
503: *
504: * @return the typed value. If requireSingleton is set to true, the result will always be an
505: * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
506: * values.
507: * @since 8.5
508: */
509:
510: public Value atomize() throws XPathException {
511: return original.atomize();
512: }
513:
514: /**
515: * VirtualCopier implements the XPath axes as applied to a VirtualCopy node. It works by
516: * applying the requested axis to the node of which this is a copy. There are two
517: * complications: firstly, all nodes encountered must themselves be (virtually) copied
518: * to give them a new identity. Secondly, axes that stray outside the subtree rooted at
519: * the original copied node must be truncated.
520: */
521:
522: private class VirtualCopier implements AxisIterator,
523: AtomizableIterator {
524:
525: private AxisIterator base;
526: private VirtualCopy parent;
527: private NodeInfo subtreeRoot;
528: private Item current;
529:
530: public VirtualCopier(AxisIterator base, VirtualCopy parent,
531: NodeInfo subtreeRoot) {
532: this .base = base;
533: this .parent = parent;
534: this .subtreeRoot = subtreeRoot;
535: }
536:
537: /**
538: * Indicate that any nodes returned in the sequence will be atomized. This
539: * means that if it wishes to do so, the implementation can return the typed
540: * values of the nodes rather than the nodes themselves. The implementation
541: * is free to ignore this hint.
542: *
543: * @param atomizing true if the caller of this iterator will atomize any
544: * nodes that are returned, and is therefore willing to accept the typed
545: * value of the nodes instead of the nodes themselves.
546: */
547:
548: public void setIsAtomizing(boolean atomizing) {
549: if (base instanceof AtomizableIterator) {
550: ((AtomizableIterator) base).setIsAtomizing(atomizing);
551: }
552: }
553:
554: /**
555: * Get the next item in the sequence. <BR>
556: *
557: * @return the next Item. If there are no more nodes, return null.
558: */
559:
560: public Item next() {
561: Item next = base.next();
562:
563: if (next instanceof NodeInfo) {
564: if (subtreeRoot != null) {
565: // we're only interested in nodes within the subtree that was copied.
566: // Assert: once we find a node outside this subtree, all further nodes will also be outside
567: // the subtree.
568: if (!isAncestorOrSelf(subtreeRoot,
569: ((NodeInfo) next))) {
570: return null;
571: }
572: }
573: VirtualCopy vc = VirtualCopy.makeVirtualCopy(
574: ((NodeInfo) next), root);
575: vc.parent = parent;
576: vc.baseURI = baseURI;
577: vc.documentNumber = documentNumber;
578: next = vc;
579: }
580: current = next;
581: return next;
582: }
583:
584: /**
585: * Get the current item in the sequence.
586: *
587: * @return the current item, that is, the item most recently returned by
588: * next()
589: */
590:
591: public Item current() {
592: return current;
593: }
594:
595: /**
596: * Get the current position
597: *
598: * @return the position of the current item (the item most recently
599: * returned by next()), starting at 1 for the first node
600: */
601:
602: public int position() {
603: return base.position();
604: }
605:
606: /**
607: * Get another iterator over the same sequence of items, positioned at the
608: * start of the sequence
609: *
610: * @return a new iterator over the same sequence
611: */
612:
613: public SequenceIterator getAnother() {
614: return new VirtualCopier((AxisIterator) base.getAnother(),
615: parent, subtreeRoot);
616: }
617:
618: /**
619: * Get properties of this iterator, as a bit-significant integer.
620: *
621: * @return the properties of this iterator. This will be some combination of
622: * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
623: * and {@link LOOKAHEAD}. It is always
624: * acceptable to return the value zero, indicating that there are no known special properties.
625: * It is acceptable for the properties of the iterator to change depending on its state.
626: */
627:
628: public int getProperties() {
629: return ATOMIZABLE;
630: }
631:
632: /**
633: * Test whether a node is an ancestor-or-self of another
634: */
635:
636: private boolean isAncestorOrSelf(NodeInfo a, NodeInfo d) {
637: while (true) {
638: if (a.isSameNodeInfo(d)) {
639: return true;
640: }
641: d = d.getParent();
642: if (d == null) {
643: return false;
644: }
645: }
646: }
647:
648: }
649:
650: }
651:
652: //
653: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
654: // you may not use this file except in compliance with the License. You may obtain a copy of the
655: // License at http://www.mozilla.org/MPL/
656: //
657: // Software distributed under the License is distributed on an "AS IS" basis,
658: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
659: // See the License for the specific language governing rights and limitations under the License.
660: //
661: // The Original Code is: all this file.
662: //
663: // The Initial Developer of the Original Code is Michael H. Kay.
664: //
665: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
666: //
667: // Contributor(s): none.
668: //
|