001: package net.sf.saxon.tinytree;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.Err;
005: import net.sf.saxon.style.StandardNames;
006: import net.sf.saxon.event.Receiver;
007: import net.sf.saxon.om.*;
008: import net.sf.saxon.pattern.AnyNodeTest;
009: import net.sf.saxon.pattern.NameTest;
010: import net.sf.saxon.pattern.NodeTest;
011: import net.sf.saxon.trans.DynamicError;
012: import net.sf.saxon.trans.XPathException;
013: import net.sf.saxon.type.SchemaType;
014: import net.sf.saxon.type.Type;
015: import net.sf.saxon.value.UntypedAtomicValue;
016: import net.sf.saxon.value.Value;
017:
018: import javax.xml.transform.SourceLocator;
019:
020: /**
021: * A node in a TinyTree representing an XML element, character content, or attribute.<P>
022: * This is the top-level class in the implementation class hierarchy; it essentially contains
023: * all those methods that can be defined using other primitive methods, without direct access
024: * to data.
025: * @author Michael H. Kay
026: */
027:
028: public abstract class TinyNodeImpl implements NodeInfo,
029: FingerprintedNode, SourceLocator {
030:
031: protected TinyTree tree;
032: protected int nodeNr;
033: protected TinyNodeImpl parent = null;
034:
035: /**
036: * Chararacteristic letters to identify each type of node, indexed using the node type
037: * values. These are used as the initial letter of the result of generate-id()
038: */
039:
040: public static final char[] NODE_LETTER = { 'x', 'e', 'a', 't', 'x',
041: 'x', 'x', 'p', 'c', 'r', 'x', 'x', 'x', 'n' };
042:
043: /**
044: * Get the value of the item as a CharSequence. This is in some cases more efficient than
045: * the version of the method that returns a String.
046: */
047:
048: public CharSequence getStringValueCS() {
049: return getStringValue();
050: }
051:
052: /**
053: * Get the type annotation of this node, if any
054: */
055:
056: public int getTypeAnnotation() {
057: return -1;
058: }
059:
060: /**
061: * Get the column number of the node.
062: * The default implementation returns -1, meaning unknown
063: */
064:
065: public int getColumnNumber() {
066: return -1;
067: }
068:
069: /**
070: * Get the public identifier of the document entity containing this node.
071: * The default implementation returns null, meaning unknown
072: */
073:
074: public String getPublicId() {
075: return null;
076: }
077:
078: /**
079: * Get the typed value of this node.
080: * If there is no type annotation, we return the string value, as an instance
081: * of xdt:untypedAtomic
082: */
083:
084: public SequenceIterator getTypedValue() throws XPathException {
085: int annotation = getTypeAnnotation();
086: if ((annotation & NodeInfo.IS_DTD_TYPE) != 0) {
087: annotation = StandardNames.XDT_UNTYPED_ATOMIC;
088: }
089: if (annotation == -1
090: || annotation == StandardNames.XDT_UNTYPED_ATOMIC
091: || annotation == StandardNames.XDT_UNTYPED) {
092: return SingletonIterator
093: .makeIterator(new UntypedAtomicValue(
094: getStringValueCS()));
095: } else {
096: SchemaType stype = getConfiguration().getSchemaType(
097: annotation);
098: if (stype == null) {
099: String typeName = getNamePool().getDisplayName(
100: annotation);
101: throw new DynamicError("Unknown type annotation "
102: + Err.wrap(typeName) + " in document instance");
103: } else {
104: return stype.getTypedValue(this );
105: }
106: }
107: }
108:
109: /**
110: * Get the typed value. The result of this method will always be consistent with the method
111: * {@link net.sf.saxon.om.Item#getTypedValue()}. However, this method is often more convenient and may be
112: * more efficient, especially in the common case where the value is expected to be a singleton.
113: *
114: * @return the typed value. If requireSingleton is set to true, the result will always be an
115: * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
116: * values.
117: * @since 8.5
118: */
119:
120: public Value atomize() throws XPathException {
121: int annotation = getTypeAnnotation();
122: if ((annotation & NodeInfo.IS_DTD_TYPE) != 0) {
123: annotation = StandardNames.XDT_UNTYPED_ATOMIC;
124: }
125: if (annotation == -1
126: || annotation == StandardNames.XDT_UNTYPED_ATOMIC
127: || annotation == StandardNames.XDT_UNTYPED) {
128: return new UntypedAtomicValue(getStringValueCS());
129: } else {
130: SchemaType stype = getConfiguration().getSchemaType(
131: annotation);
132: if (stype == null) {
133: String typeName = getNamePool().getDisplayName(
134: annotation);
135: throw new DynamicError("Unknown type annotation "
136: + Err.wrap(typeName) + " in document instance");
137: } else {
138: return stype.atomize(this );
139: }
140: }
141: }
142:
143: /**
144: * Set the system id of this node. <br />
145: * This method is present to ensure that
146: * the class implements the javax.xml.transform.Source interface, so a node can
147: * be used as the source of a transformation.
148: */
149:
150: public void setSystemId(String uri) {
151: short type = tree.nodeKind[nodeNr];
152: if (type == Type.ATTRIBUTE || type == Type.NAMESPACE) {
153: getParent().setSystemId(uri);
154: } else {
155: tree.setSystemId(nodeNr, uri);
156: }
157: }
158:
159: /**
160: * Set the parent of this node. Providing this information is useful,
161: * if it is known, because otherwise getParent() has to search backwards
162: * through the document.
163: */
164:
165: protected void setParentNode(TinyNodeImpl parent) {
166: this .parent = parent;
167: }
168:
169: /**
170: * Determine whether this is the same node as another node
171: * @return true if this Node object and the supplied Node object represent the
172: * same node in the tree.
173: */
174:
175: public boolean isSameNodeInfo(NodeInfo other) {
176: if (this == other)
177: return true;
178: if (!(other instanceof TinyNodeImpl))
179: return false;
180: if (this .tree != ((TinyNodeImpl) other).tree)
181: return false;
182: if (this .nodeNr != ((TinyNodeImpl) other).nodeNr)
183: return false;
184: if (this .getNodeKind() != other.getNodeKind())
185: return false;
186: return true;
187: }
188:
189: /**
190: * Get the system ID for the entity containing the node.
191: */
192:
193: public String getSystemId() {
194: return tree.getSystemId(nodeNr);
195: }
196:
197: /**
198: * Get the base URI for the node. Default implementation for child nodes gets
199: * the base URI of the parent node.
200: */
201:
202: public String getBaseURI() {
203: return (getParent()).getBaseURI();
204: }
205:
206: /**
207: * Get the line number of the node within its source document entity
208: */
209:
210: public int getLineNumber() {
211: return tree.getLineNumber(nodeNr);
212: }
213:
214: /**
215: * Get the node sequence number (in document order). Sequence numbers are monotonic but not
216: * consecutive. The sequence number must be unique within the document (not, as in
217: * previous releases, within the whole document collection).
218: * For document nodes, elements, text nodes, comment nodes, and PIs, the sequence number
219: * is a long with the sequential node number in the top half and zero in the bottom half.
220: * The bottom half is used only for attributes and namespace.
221: */
222:
223: protected long getSequenceNumber() {
224: return (long) nodeNr << 32;
225: }
226:
227: /**
228: * Determine the relative position of this node and another node, in document order.
229: * The other node will always be in the same document.
230: * @param other The other node, whose position is to be compared with this node
231: * @return -1 if this node precedes the other node, +1 if it follows the other
232: * node, or 0 if they are the same node. (In this case, isSameNode() will always
233: * return true, and the two nodes will produce the same result for generateId())
234: */
235:
236: public final int compareOrder(NodeInfo other) {
237: long a = getSequenceNumber();
238: if (other instanceof TinyNodeImpl) {
239: long b = ((TinyNodeImpl) other).getSequenceNumber();
240: if (a < b)
241: return -1;
242: if (a > b)
243: return +1;
244: return 0;
245: } else {
246: // it must be a namespace node
247: return 0 - other.compareOrder(this );
248: }
249: }
250:
251: /**
252: * Get the fingerprint of the node, used for matching names
253: */
254:
255: public int getFingerprint() {
256: int nc = getNameCode();
257: if (nc == -1)
258: return -1;
259: return nc & 0xfffff;
260: }
261:
262: /**
263: * Get the name code of the node, used for matching names
264: */
265:
266: public int getNameCode() {
267: // overridden for attributes and namespace nodes.
268: return tree.nameCode[nodeNr];
269: }
270:
271: /**
272: * Get the prefix part of the name of this node. This is the name before the ":" if any.
273: * @return the prefix part of the name. For an unnamed node, return "".
274: */
275:
276: public String getPrefix() {
277: int code = tree.nameCode[nodeNr];
278: if (code < 0)
279: return "";
280: if ((code >> 20 & 0xff) == 0)
281: return "";
282: return tree.getNamePool().getPrefix(code);
283: }
284:
285: /**
286: * Get the URI part of the name of this node. This is the URI corresponding to the
287: * prefix, or the URI of the default namespace if appropriate.
288: * @return The URI of the namespace of this node. For an unnamed node, or for
289: * an element or attribute in the default namespace, return an empty string.
290: */
291:
292: public String getURI() {
293: int code = tree.nameCode[nodeNr];
294: if (code < 0)
295: return "";
296: return tree.getNamePool().getURI(code);
297: }
298:
299: /**
300: * Get the display name of this node (a lexical QName). For elements and attributes this is [prefix:]localname.
301: * The original prefix is retained. For unnamed nodes, the result is an empty string.
302: * @return The display name of this node.
303: * For a node with no name, return an empty string.
304: */
305:
306: public String getDisplayName() {
307: int code = tree.nameCode[nodeNr];
308: if (code < 0)
309: return "";
310: return tree.getNamePool().getDisplayName(code);
311: }
312:
313: /**
314: * Get the local part of the name of this node.
315: * @return The local name of this node.
316: * For a node with no name, return "".
317: */
318:
319: public String getLocalPart() {
320: int code = tree.nameCode[nodeNr];
321: if (code < 0)
322: return "";
323: return tree.getNamePool().getLocalName(code);
324: }
325:
326: /**
327: * Return an iterator over all the nodes reached by the given axis from this node
328: * @param axisNumber Identifies the required axis, eg. Axis.CHILD or Axis.PARENT
329: * @return a AxisIteratorImpl that scans the nodes reached by the axis in turn.
330: */
331:
332: public AxisIterator iterateAxis(byte axisNumber) {
333: // fast path for child axis
334: if (axisNumber == Axis.CHILD) {
335: if (hasChildNodes()) {
336: return new SiblingEnumeration(tree, this , null, true);
337: } else {
338: return EmptyIterator.getInstance();
339: }
340: } else {
341: return iterateAxis(axisNumber, AnyNodeTest.getInstance());
342: }
343: }
344:
345: /**
346: * Return an iterator over the nodes reached by the given axis from this node
347: * @param axisNumber Identifies the required axis, eg. Axis.CHILD or Axis.PARENT
348: * @param nodeTest A pattern to be matched by the returned nodes.
349: * @return a AxisIteratorImpl that scans the nodes reached by the axis in turn.
350: */
351:
352: public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
353:
354: int type = getNodeKind();
355: switch (axisNumber) {
356: case Axis.ANCESTOR:
357: return new AncestorEnumeration(this , nodeTest, false);
358:
359: case Axis.ANCESTOR_OR_SELF:
360: return new AncestorEnumeration(this , nodeTest, true);
361:
362: case Axis.ATTRIBUTE:
363: if (type != Type.ELEMENT) {
364: return EmptyIterator.getInstance();
365: }
366: if (tree.alpha[nodeNr] < 0) {
367: return EmptyIterator.getInstance();
368: }
369: return new AttributeEnumeration(tree, nodeNr, nodeTest);
370:
371: case Axis.CHILD:
372: if (hasChildNodes()) {
373: return new SiblingEnumeration(tree, this , nodeTest,
374: true);
375: } else {
376: return EmptyIterator.getInstance();
377: }
378:
379: case Axis.DESCENDANT:
380: if (type == Type.DOCUMENT && nodeTest instanceof NameTest
381: && nodeTest.getPrimitiveType() == Type.ELEMENT) {
382: return ((TinyDocumentImpl) this )
383: .getAllElements(nodeTest.getFingerprint());
384: } else if (hasChildNodes()) {
385: return new DescendantEnumeration(tree, this , nodeTest,
386: false);
387: } else {
388: return EmptyIterator.getInstance();
389: }
390:
391: case Axis.DESCENDANT_OR_SELF:
392: if (hasChildNodes()) {
393: return new DescendantEnumeration(tree, this , nodeTest,
394: true);
395: } else {
396: if (nodeTest.matches(this )) {
397: return SingletonIterator.makeIterator(this );
398: } else {
399: return EmptyIterator.getInstance();
400: }
401: }
402:
403: case Axis.FOLLOWING:
404: if (type == Type.ATTRIBUTE || type == Type.NAMESPACE) {
405: return new FollowingEnumeration(tree,
406: (TinyNodeImpl) getParent(), nodeTest, true);
407: } else if (tree.depth[nodeNr] == 0) {
408: return EmptyIterator.getInstance();
409: } else {
410: return new FollowingEnumeration(tree, this , nodeTest,
411: false);
412: }
413:
414: case Axis.FOLLOWING_SIBLING:
415: if (type == Type.ATTRIBUTE || type == Type.NAMESPACE
416: || tree.depth[nodeNr] == 0) {
417: return EmptyIterator.getInstance();
418: } else {
419: return new SiblingEnumeration(tree, this , nodeTest,
420: false);
421: }
422:
423: case Axis.NAMESPACE:
424: if (type != Type.ELEMENT) {
425: return EmptyIterator.getInstance();
426: }
427: return new NamespaceIterator(this , nodeTest);
428:
429: case Axis.PARENT:
430: NodeInfo parent = getParent();
431: if (parent == null)
432: return EmptyIterator.getInstance();
433: if (nodeTest.matches(parent)) {
434: return SingletonIterator.makeIterator(parent);
435: }
436: return EmptyIterator.getInstance();
437:
438: case Axis.PRECEDING:
439: if (type == Type.ATTRIBUTE || type == Type.NAMESPACE) {
440: return new PrecedingEnumeration(tree,
441: (TinyNodeImpl) getParent(), nodeTest, false);
442: } else if (tree.depth[nodeNr] == 0) {
443: return EmptyIterator.getInstance();
444: } else {
445: return new PrecedingEnumeration(tree, this , nodeTest,
446: false);
447: }
448:
449: case Axis.PRECEDING_SIBLING:
450: if (type == Type.ATTRIBUTE || type == Type.NAMESPACE
451: || tree.depth[nodeNr] == 0) {
452: return EmptyIterator.getInstance();
453: } else {
454: return new PrecedingSiblingEnumeration(tree, this ,
455: nodeTest);
456: }
457:
458: case Axis.SELF:
459: if (nodeTest.matches(this )) {
460: return SingletonIterator.makeIterator(this );
461: }
462: return EmptyIterator.getInstance();
463:
464: case Axis.PRECEDING_OR_ANCESTOR:
465: if (type == Type.DOCUMENT) {
466: return EmptyIterator.getInstance();
467: } else if (type == Type.ATTRIBUTE || type == Type.NAMESPACE) {
468: // See test numb32.
469: TinyNodeImpl el = (TinyNodeImpl) getParent();
470: return new PrependIterator(el,
471: new PrecedingEnumeration(tree, el, nodeTest,
472: true));
473: } else {
474: return new PrecedingEnumeration(tree, this , nodeTest,
475: true);
476: }
477:
478: default:
479: throw new IllegalArgumentException("Unknown axis number "
480: + axisNumber);
481: }
482: }
483:
484: /**
485: * Find the parent node of this node.
486: * @return The Node object describing the containing element or root node.
487: */
488:
489: public NodeInfo getParent() {
490: if (parent != null) {
491: return parent;
492: }
493: int p = getParentNodeNr(tree, nodeNr);
494: if (p == -1) {
495: parent = null;
496: } else {
497: parent = tree.getNode(p);
498: }
499: return parent;
500: }
501:
502: /**
503: * Static method to get the parent of a given node, without instantiating the node as an object.
504: * The starting node is any node other than an attribute or namespace node.
505: * @param tree the tree containing the starting node
506: * @param nodeNr the node number of the starting node within the tree
507: * @return the node number of the parent node, or -1 if there is no parent.
508: */
509:
510: static final int getParentNodeNr(TinyTree tree, int nodeNr) {
511:
512: if (tree.depth[nodeNr] == 0) {
513: return -1;
514: }
515:
516: // follow the next-sibling pointers until we reach either a next sibling pointer that
517: // points backwards, or a parent-pointer pseudo-node
518: int p = tree.next[nodeNr];
519: while (p > nodeNr) {
520: if (tree.nodeKind[p] == Type.PARENT_POINTER) {
521: return tree.alpha[p];
522: }
523: p = tree.next[p];
524: }
525: return p;
526: }
527:
528: /**
529: * Determine whether the node has any children.
530: * @return <code>true</code> if this node has any attributes,
531: * <code>false</code> otherwise.
532: */
533:
534: public boolean hasChildNodes() {
535: // overridden in TinyParentNodeImpl
536: return false;
537: }
538:
539: /**
540: * Get the value of a given attribute of this node
541: * @param fingerprint The fingerprint of the attribute name
542: * @return the attribute value if it exists or null if not
543: */
544:
545: public String getAttributeValue(int fingerprint) {
546: // overridden in TinyElementImpl
547: return null;
548: }
549:
550: /**
551: * Get the root node of the tree (not necessarily a document node)
552: * @return the NodeInfo representing the root of this tree
553: */
554:
555: public NodeInfo getRoot() {
556: if (tree.depth[nodeNr] == 0) {
557: return this ;
558: }
559: if (parent != null) {
560: return parent.getRoot();
561: }
562: return tree.getNode(tree.getRootNode(nodeNr));
563: }
564:
565: /**
566: * Get the root (document) node
567: * @return the DocumentInfo representing the containing document
568: */
569:
570: public DocumentInfo getDocumentRoot() {
571: NodeInfo root = getRoot();
572: if (root.getNodeKind() == Type.DOCUMENT) {
573: return (DocumentInfo) root;
574: } else {
575: return null;
576: }
577: }
578:
579: /**
580: * Get the configuration
581: */
582:
583: public Configuration getConfiguration() {
584: return tree.getConfiguration();
585: }
586:
587: /**
588: * Get the NamePool for the tree containing this node
589: * @return the NamePool
590: */
591:
592: public NamePool getNamePool() {
593: return tree.getNamePool();
594: }
595:
596: /**
597: * Output all namespace nodes associated with this element. Does nothing if
598: * the node is not an element.
599: * @param out The relevant outputter
600: * @param includeAncestors True if namespaces declared on ancestor elements must
601: */
602:
603: public void sendNamespaceDeclarations(Receiver out,
604: boolean includeAncestors) throws XPathException {
605: }
606:
607: /**
608: * Get all namespace undeclarations and undeclarations defined on this element.
609: *
610: * @param buffer If this is non-null, and the result array fits in this buffer, then the result
611: * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
612: * @return An array of integers representing the namespace declarations and undeclarations present on
613: * this element. For a node other than an element, return null. Otherwise, the returned array is a
614: * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
615: * top half word of each namespace code represents the prefix, the bottom half represents the URI.
616: * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
617: * The XML namespace is never included in the list. If the supplied array is larger than required,
618: * then the first unused entry will be set to -1.
619: * <p/>
620: * <p>For a node other than an element, the method returns null.</p>
621: */
622:
623: public int[] getDeclaredNamespaces(int[] buffer) {
624: return null;
625: }
626:
627: /**
628: * Get a character string that uniquely identifies this node
629: * @return a string.
630: */
631:
632: public String generateId() {
633: return "d" + tree.getDocumentNumber()
634: + NODE_LETTER[getNodeKind()] + nodeNr;
635: }
636:
637: /**
638: * Get the document number of the document containing this node
639: * (Needed when the document isn't a real node, for sorting free-standing elements)
640: */
641:
642: public final int getDocumentNumber() {
643: return tree.getDocumentNumber();
644: }
645:
646: }
647:
648: //
649: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
650: // you may not use this file except in compliance with the License. You may obtain a copy of the
651: // License at http://www.mozilla.org/MPL/
652: //
653: // Software distributed under the License is distributed on an "AS IS" basis,
654: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
655: // See the License for the specific language governing rights and limitations under the License.
656: //
657: // The Original Code is: all this file.
658: //
659: // The Initial Developer of the Original Code is Michael H. Kay.
660: //
661: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
662: //
663: // Contributor(s): none.
664: //
|