0001: package net.sf.saxon.xom;
0002:
0003: import net.sf.saxon.Configuration;
0004: import net.sf.saxon.trans.XPathException;
0005: import net.sf.saxon.event.Receiver;
0006: import net.sf.saxon.om.*;
0007: import net.sf.saxon.pattern.AnyNodeTest;
0008: import net.sf.saxon.pattern.NameTest;
0009: import net.sf.saxon.pattern.NodeKindTest;
0010: import net.sf.saxon.pattern.NodeTest;
0011: import net.sf.saxon.type.Type;
0012: import net.sf.saxon.value.UntypedAtomicValue;
0013: import net.sf.saxon.value.Value;
0014: import nu.xom.*;
0015:
0016: /**
0017: * A node in the XML parse tree representing an XML element, character content,
0018: * or attribute.
0019: * <P>
0020: * This is the implementation of the NodeInfo interface used as a wrapper for
0021: * XOM nodes.
0022: *
0023: * @author Michael H. Kay
0024: * @author Wolfgang Hoschek (ported net.sf.saxon.jdom to XOM)
0025: */
0026:
0027: public class NodeWrapper implements NodeInfo, VirtualNode,
0028: SiblingCountingNode {
0029:
0030: protected Node node;
0031:
0032: protected short nodeKind;
0033:
0034: private NodeWrapper parent; // null means unknown
0035:
0036: protected DocumentWrapper docWrapper;
0037:
0038: protected int index; // -1 means unknown
0039:
0040: /**
0041: * This constructor is protected: nodes should be created using the wrap
0042: * factory method on the DocumentWrapper class
0043: *
0044: * @param node
0045: * The XOM node to be wrapped
0046: * @param parent
0047: * The NodeWrapper that wraps the parent of this node
0048: * @param index
0049: * Position of this node among its siblings
0050: */
0051: protected NodeWrapper(Node node, NodeWrapper parent, int index) {
0052: short kind;
0053: if (node instanceof Element) {
0054: kind = Type.ELEMENT;
0055: } else if (node instanceof Text) {
0056: kind = Type.TEXT;
0057: } else if (node instanceof Attribute) {
0058: kind = Type.ATTRIBUTE;
0059: } else if (node instanceof Comment) {
0060: kind = Type.COMMENT;
0061: } else if (node instanceof ProcessingInstruction) {
0062: kind = Type.PROCESSING_INSTRUCTION;
0063: } else if (node instanceof Document) {
0064: kind = Type.DOCUMENT;
0065: } else {
0066: throwIllegalNode(node); // moved out of fast path to enable better inlining
0067: return; // keep compiler happy
0068: }
0069: this .nodeKind = kind;
0070: this .node = node;
0071: this .parent = parent;
0072: this .index = index;
0073: }
0074:
0075: /**
0076: * Factory method to wrap a XOM node with a wrapper that implements the
0077: * Saxon NodeInfo interface.
0078: *
0079: * @param node
0080: * The XOM node
0081: * @param docWrapper
0082: * The wrapper for the Document containing this node
0083: * @return The new wrapper for the supplied node
0084: */
0085: protected final NodeWrapper makeWrapper(Node node,
0086: DocumentWrapper docWrapper) {
0087: return makeWrapper(node, docWrapper, null, -1);
0088: }
0089:
0090: /**
0091: * Factory method to wrap a XOM node with a wrapper that implements the
0092: * Saxon NodeInfo interface.
0093: *
0094: * @param node
0095: * The XOM node
0096: * @param docWrapper
0097: * The wrapper for the Document containing this node
0098: * @param parent
0099: * The wrapper for the parent of the XOM node
0100: * @param index
0101: * The position of this node relative to its siblings
0102: * @return The new wrapper for the supplied node
0103: */
0104:
0105: protected final NodeWrapper makeWrapper(Node node,
0106: DocumentWrapper docWrapper, NodeWrapper parent, int index) {
0107:
0108: if (node == docWrapper.node)
0109: return docWrapper;
0110: NodeWrapper wrapper = new NodeWrapper(node, parent, index);
0111: wrapper.docWrapper = docWrapper;
0112: return wrapper;
0113: }
0114:
0115: private static void throwIllegalNode(Node node) {
0116: String str = node == null ? "NULL" : node.getClass()
0117: + " instance " + node.toString();
0118: throw new IllegalArgumentException("Bad node type in XOM! "
0119: + str);
0120: }
0121:
0122: /**
0123: * Get the configuration
0124: */
0125:
0126: public Configuration getConfiguration() {
0127: return docWrapper.getConfiguration();
0128: }
0129:
0130: /**
0131: * Get the underlying XOM node, to implement the VirtualNode interface
0132: */
0133:
0134: public Object getUnderlyingNode() {
0135: return node;
0136: }
0137:
0138: /**
0139: * Get the name pool for this node
0140: *
0141: * @return the NamePool
0142: */
0143:
0144: public NamePool getNamePool() {
0145: return docWrapper.getNamePool();
0146: }
0147:
0148: /**
0149: * Return the type of node.
0150: *
0151: * @return one of the values Node.ELEMENT, Node.TEXT, Node.ATTRIBUTE, etc.
0152: */
0153:
0154: public int getNodeKind() {
0155: return nodeKind;
0156: }
0157:
0158: /**
0159: * Get the typed value of the item
0160: */
0161:
0162: public SequenceIterator getTypedValue() {
0163: return SingletonIterator.makeIterator(new UntypedAtomicValue(
0164: getStringValueCS()));
0165: }
0166:
0167: /**
0168: * Get the typed value. The result of this method will always be consistent
0169: * with the method {@link net.sf.saxon.om.Item#getTypedValue()}. However,
0170: * this method is often more convenient and may be more efficient,
0171: * especially in the common case where the value is expected to be a
0172: * singleton.
0173: *
0174: * @return the typed value. If requireSingleton is set to true, the result
0175: * will always be an AtomicValue. In other cases it may be a Value
0176: * representing a sequence whose items are atomic values.
0177: * @since 8.5
0178: */
0179:
0180: public Value atomize() {
0181: return new UntypedAtomicValue(getStringValueCS());
0182: }
0183:
0184: /**
0185: * Get the type annotation of this node, if any. Returns -1 for kinds of
0186: * nodes that have no annotation, and for elements annotated as untyped, and
0187: * attributes annotated as untypedAtomic.
0188: *
0189: * @return -1 (there is no type annotation)
0190: * @return the type annotation of the node.
0191: * @see net.sf.saxon.type.Type
0192: */
0193:
0194: public int getTypeAnnotation() {
0195: return -1;
0196: }
0197:
0198: /**
0199: * Determine whether this is the same node as another node. <br />
0200: * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
0201: *
0202: * @return true if this Node object and the supplied Node object represent
0203: * the same node in the tree.
0204: */
0205:
0206: public boolean isSameNodeInfo(NodeInfo other) {
0207: if (other instanceof NodeWrapper) {
0208: return node == ((NodeWrapper) other).node; // In XOM equality means identity
0209: }
0210: return false;
0211: }
0212:
0213: /**
0214: * Get the System ID for the node.
0215: *
0216: * @return the System Identifier of the entity in the source document
0217: * containing the node, or null if not known. Note this is not the
0218: * same as the base URI: the base URI can be modified by xml:base,
0219: * but the system ID cannot.
0220: */
0221:
0222: public String getSystemId() {
0223: return docWrapper.baseURI;
0224: }
0225:
0226: public void setSystemId(String uri) {
0227: docWrapper.baseURI = uri;
0228: }
0229:
0230: /**
0231: * Get the Base URI for the node, that is, the URI used for resolving a
0232: * relative URI contained in the node.
0233: */
0234:
0235: public String getBaseURI() {
0236: return node.getBaseURI();
0237: }
0238:
0239: /**
0240: * Get line number
0241: *
0242: * @return the line number of the node in its original source document; or
0243: * -1 if not available
0244: */
0245:
0246: public int getLineNumber() {
0247: return -1;
0248: }
0249:
0250: /**
0251: * Determine the relative position of this node and another node, in
0252: * document order. The other node will always be in the same document.
0253: *
0254: * @param other
0255: * The other node, whose position is to be compared with this
0256: * node
0257: * @return -1 if this node precedes the other node, +1 if it follows the
0258: * other node, or 0 if they are the same node. (In this case,
0259: * isSameNode() will always return true, and the two nodes will
0260: * produce the same result for generateId())
0261: */
0262:
0263: public int compareOrder(NodeInfo other) {
0264: if (other instanceof NodeWrapper) {
0265: return compareOrderFast(node, ((NodeWrapper) other).node);
0266: // }
0267: // if (other instanceof SiblingCountingNode) {
0268: // return Navigator.compareOrder(this, (SiblingCountingNode) other);
0269: } else {
0270: // it must be a namespace node
0271: return -other.compareOrder(this );
0272: }
0273: }
0274:
0275: private static int compareOrderFast(Node first, Node second) {
0276: /*
0277: * Unfortunately we do not have a sequence number for each node at hand;
0278: * this would allow to turn the comparison into a simple sequence number
0279: * subtraction. Walking the entire tree and batch-generating sequence
0280: * numbers on the fly is no good option either. However, this rewritten
0281: * implementation turns out to be more than fast enough.
0282: */
0283:
0284: // assert first != null && second != null
0285: // assert first and second MUST NOT be namespace nodes
0286: if (first == second)
0287: return 0;
0288:
0289: ParentNode firstParent = first.getParent();
0290: ParentNode secondParent = second.getParent();
0291: if (firstParent == null) {
0292: if (secondParent != null)
0293: return -1; // first node is the root
0294: // both nodes are parentless, use arbitrary but fixed order:
0295: return first.hashCode() - second.hashCode();
0296: }
0297:
0298: if (secondParent == null)
0299: return +1; // second node is the root
0300:
0301: // do they have the same parent (common case)?
0302: if (firstParent == secondParent) {
0303: int i1 = firstParent.indexOf(first);
0304: int i2 = firstParent.indexOf(second);
0305:
0306: // note that attributes and namespaces are not children
0307: // of their own parent (i = -1).
0308: // attribute (if any) comes before child
0309: if (i1 != -1)
0310: return (i2 != -1) ? i1 - i2 : +1;
0311: if (i2 != -1)
0312: return -1;
0313:
0314: // assert: i1 == -1 && i2 == -1
0315: // i.e. both nodes are attributes
0316: Element elem = (Element) firstParent;
0317: for (int i = elem.getAttributeCount(); --i >= 0;) {
0318: Attribute attr = elem.getAttribute(i);
0319: if (attr == second)
0320: return -1;
0321: if (attr == first)
0322: return +1;
0323: }
0324: throw new IllegalStateException("should be unreachable");
0325: }
0326:
0327: // find the depths of both nodes in the tree
0328: int depth1 = 0;
0329: int depth2 = 0;
0330: Node p1 = first;
0331: Node p2 = second;
0332: while (p1 != null) {
0333: depth1++;
0334: p1 = p1.getParent();
0335: if (p1 == second)
0336: return +1;
0337: }
0338: while (p2 != null) {
0339: depth2++;
0340: p2 = p2.getParent();
0341: if (p2 == first)
0342: return -1;
0343: }
0344:
0345: // move up one branch of the tree so we have two nodes on the same level
0346: p1 = first;
0347: while (depth1 > depth2) {
0348: p1 = p1.getParent();
0349: depth1--;
0350: }
0351: p2 = second;
0352: while (depth2 > depth1) {
0353: p2 = p2.getParent();
0354: depth2--;
0355: }
0356:
0357: // now move up both branches in sync until we find a common parent
0358: while (true) {
0359: firstParent = p1.getParent();
0360: secondParent = p2.getParent();
0361: if (firstParent == null || secondParent == null) {
0362: // both nodes are documentless, use arbitrary but fixed order
0363: // based on their root elements
0364: return p1.hashCode() - p2.hashCode();
0365: // throw new NullPointerException("XOM tree compare - internal error");
0366: }
0367: if (firstParent == secondParent) {
0368: return firstParent.indexOf(p1)
0369: - firstParent.indexOf(p2);
0370: }
0371: p1 = firstParent;
0372: p2 = secondParent;
0373: }
0374: }
0375:
0376: /**
0377: * Return the string value of the node. The interpretation of this depends
0378: * on the type of node. For an element it is the accumulated character
0379: * content of the element, including descendant elements.
0380: *
0381: * @return the string value of the node
0382: */
0383:
0384: public String getStringValue() {
0385: return node.getValue();
0386: }
0387:
0388: /**
0389: * Get the value of the item as a CharSequence. This is in some cases more efficient than
0390: * the version of the method that returns a String.
0391: */
0392:
0393: public CharSequence getStringValueCS() {
0394: return node.getValue();
0395: }
0396:
0397: /**
0398: * Get name code. The name code is a coded form of the node name: two nodes
0399: * with the same name code have the same namespace URI, the same local name,
0400: * and the same prefix. By masking the name code with &0xfffff, you get a
0401: * fingerprint: two nodes with the same fingerprint have the same local name
0402: * and namespace URI.
0403: *
0404: * @see net.sf.saxon.om.NamePool#allocate allocate
0405: */
0406:
0407: public int getNameCode() {
0408: switch (nodeKind) {
0409: case Type.ELEMENT:
0410: case Type.ATTRIBUTE:
0411: case Type.PROCESSING_INSTRUCTION:
0412: return docWrapper.getNamePool().allocate(getPrefix(),
0413: getURI(), getLocalPart());
0414: default:
0415: return -1;
0416: }
0417: }
0418:
0419: /**
0420: * Get fingerprint. The fingerprint is a coded form of the expanded name of
0421: * the node: two nodes with the same name code have the same namespace URI
0422: * and the same local name. A fingerprint of -1 should be returned for a
0423: * node with no name.
0424: */
0425:
0426: public int getFingerprint() {
0427: int nc = getNameCode();
0428: if (nc == -1)
0429: return -1;
0430: return nc & 0xfffff;
0431: }
0432:
0433: /**
0434: * Get the local part of the name of this node. This is the name after the
0435: * ":" if any.
0436: *
0437: * @return the local part of the name. For an unnamed node, returns "".
0438: */
0439:
0440: public String getLocalPart() {
0441: switch (nodeKind) {
0442: case Type.ELEMENT:
0443: return ((Element) node).getLocalName();
0444: case Type.ATTRIBUTE:
0445: return ((Attribute) node).getLocalName();
0446: case Type.PROCESSING_INSTRUCTION:
0447: return ((ProcessingInstruction) node).getTarget();
0448: default:
0449: return "";
0450: }
0451: }
0452:
0453: /**
0454: * Get the prefix of the name of the node. This is defined only for elements and attributes.
0455: * If the node has no prefix, or for other kinds of node, return a zero-length string.
0456: * @return The prefix of the name of the node.
0457: */
0458:
0459: public String getPrefix() {
0460: switch (nodeKind) {
0461: case Type.ELEMENT:
0462: return ((Element) node).getNamespacePrefix();
0463: case Type.ATTRIBUTE:
0464: return ((Attribute) node).getNamespacePrefix();
0465: default:
0466: return "";
0467: }
0468: }
0469:
0470: /**
0471: * Get the URI part of the name of this node. This is the URI corresponding
0472: * to the prefix, or the URI of the default namespace if appropriate.
0473: *
0474: * @return The URI of the namespace of this node. For an unnamed node, or
0475: * for a node with an empty prefix, return an empty string.
0476: */
0477:
0478: public String getURI() {
0479: switch (nodeKind) {
0480: case Type.ELEMENT:
0481: return ((Element) node).getNamespaceURI();
0482: case Type.ATTRIBUTE:
0483: return ((Attribute) node).getNamespaceURI();
0484: default:
0485: return "";
0486: }
0487: }
0488:
0489: /**
0490: * Get the display name of this node. For elements and attributes this is
0491: * [prefix:]localname. For unnamed nodes, it is an empty string.
0492: *
0493: * @return The display name of this node. For a node with no name, return an
0494: * empty string.
0495: */
0496:
0497: public String getDisplayName() {
0498: switch (nodeKind) {
0499: case Type.ELEMENT:
0500: return ((Element) node).getQualifiedName();
0501: case Type.ATTRIBUTE:
0502: return ((Attribute) node).getQualifiedName();
0503: case Type.PROCESSING_INSTRUCTION:
0504: return ((ProcessingInstruction) node).getTarget();
0505: default:
0506: return "";
0507: }
0508: }
0509:
0510: /**
0511: * Get the NodeInfo object representing the parent of this node
0512: */
0513:
0514: public NodeInfo getParent() {
0515: if (parent == null) {
0516: ParentNode p = node.getParent();
0517: if (p != null)
0518: parent = makeWrapper(p, docWrapper);
0519: }
0520: return parent;
0521: }
0522:
0523: /**
0524: * Get the index position of this node among its siblings (starting from 0)
0525: */
0526:
0527: public int getSiblingPosition() {
0528: if (index != -1)
0529: return index;
0530: switch (nodeKind) {
0531: case Type.ATTRIBUTE: {
0532: Attribute att = (Attribute) node;
0533: Element p = (Element) att.getParent();
0534: if (p == null)
0535: return 0;
0536: for (int i = p.getAttributeCount(); --i >= 0;) {
0537: if (p.getAttribute(i) == att) {
0538: index = i;
0539: return i;
0540: }
0541: }
0542: throw new IllegalStateException(
0543: "XOM node not linked to parent node");
0544: }
0545:
0546: default: {
0547: ParentNode p = node.getParent();
0548: int i = (p == null ? 0 : p.indexOf(node));
0549: if (i == -1)
0550: throw new IllegalStateException(
0551: "XOM node not linked to parent node");
0552: index = i;
0553: return index;
0554: }
0555: }
0556: }
0557:
0558: /**
0559: * Return an iteration over the nodes reached by the given axis from this
0560: * node
0561: *
0562: * @param axisNumber
0563: * the axis to be used
0564: * @return a SequenceIterator that scans the nodes reached by the axis in
0565: * turn.
0566: */
0567:
0568: public AxisIterator iterateAxis(byte axisNumber) {
0569: return iterateAxis(axisNumber, AnyNodeTest.getInstance());
0570: }
0571:
0572: /**
0573: * Return an iteration over the nodes reached by the given axis from this
0574: * node
0575: *
0576: * @param axisNumber
0577: * the axis to be used
0578: * @param nodeTest
0579: * A pattern to be matched by the returned nodes
0580: * @return a SequenceIterator that scans the nodes reached by the axis in
0581: * turn.
0582: */
0583:
0584: public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
0585: // for clarifications, see the W3C specs or:
0586: // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk/html/xmrefaxes.asp
0587: switch (axisNumber) {
0588: case Axis.ANCESTOR:
0589: return new AncestorAxisIterator(this , false, nodeTest);
0590:
0591: case Axis.ANCESTOR_OR_SELF:
0592: return new AncestorAxisIterator(this , true, nodeTest);
0593:
0594: case Axis.ATTRIBUTE:
0595: if (nodeKind != Type.ELEMENT
0596: || ((Element) node).getAttributeCount() == 0) {
0597: return EmptyIterator.getInstance();
0598: } else {
0599: return new AttributeAxisIterator(this , nodeTest);
0600: }
0601:
0602: case Axis.CHILD:
0603: if (hasChildNodes()) {
0604: return new ChildAxisIterator(this , true, true, nodeTest);
0605: } else {
0606: return EmptyIterator.getInstance();
0607: }
0608:
0609: case Axis.DESCENDANT:
0610: if (hasChildNodes()) {
0611: return new DescendantAxisIterator(this , false, false,
0612: nodeTest);
0613: } else {
0614: return EmptyIterator.getInstance();
0615: }
0616:
0617: case Axis.DESCENDANT_OR_SELF:
0618: if (hasChildNodes()) {
0619: return new DescendantAxisIterator(this , true, false,
0620: nodeTest);
0621: } else {
0622: return makeSingleIterator(this , nodeTest);
0623: }
0624:
0625: case Axis.FOLLOWING:
0626: if (getParent() == null) {
0627: return EmptyIterator.getInstance();
0628: } else {
0629: return new DescendantAxisIterator(this , false, true,
0630: nodeTest);
0631: }
0632:
0633: case Axis.FOLLOWING_SIBLING:
0634: if (nodeKind == Type.ATTRIBUTE || getParent() == null) {
0635: return EmptyIterator.getInstance();
0636: } else {
0637: return new ChildAxisIterator(this , false, true,
0638: nodeTest);
0639: }
0640:
0641: case Axis.NAMESPACE:
0642: if (nodeKind == Type.ELEMENT) {
0643: return new NamespaceIterator(this , nodeTest);
0644: } else {
0645: return EmptyIterator.getInstance();
0646: }
0647:
0648: case Axis.PARENT:
0649: if (getParent() == null) {
0650: return EmptyIterator.getInstance();
0651: } else {
0652: return makeSingleIterator(parent, nodeTest);
0653: }
0654:
0655: case Axis.PRECEDING:
0656: return new PrecedingAxisIterator(this , false, nodeTest);
0657: // return new Navigator.AxisFilter(
0658: // new Navigator.PrecedingEnumeration(this, false), nodeTest);
0659:
0660: case Axis.PRECEDING_SIBLING:
0661: if (nodeKind == Type.ATTRIBUTE || getParent() == null) {
0662: return EmptyIterator.getInstance();
0663: } else {
0664: return new ChildAxisIterator(this , false, false,
0665: nodeTest);
0666: }
0667:
0668: case Axis.SELF:
0669: return makeSingleIterator(this , nodeTest);
0670:
0671: case Axis.PRECEDING_OR_ANCESTOR:
0672: // This axis is used internally by saxon for the xsl:number implementation,
0673: // it returns the union of the preceding axis and the ancestor axis.
0674: return new PrecedingAxisIterator(this , true, nodeTest);
0675: // return new Navigator.AxisFilter(new Navigator.PrecedingEnumeration(
0676: // this, true), nodeTest);
0677:
0678: default:
0679: throw new IllegalArgumentException("Unknown axis number "
0680: + axisNumber);
0681: }
0682: }
0683:
0684: private static AxisIterator makeSingleIterator(NodeWrapper wrapper,
0685: NodeTest nodeTest) {
0686: if (nodeTest == AnyNodeTest.getInstance()
0687: || nodeTest.matches(wrapper))
0688: return SingletonIterator.makeIterator(wrapper);
0689: else
0690: return EmptyIterator.getInstance();
0691: }
0692:
0693: /**
0694: * Get the value of a given attribute of this node
0695: *
0696: * @param fingerprint
0697: * The fingerprint of the attribute name
0698: * @return the attribute value if it exists or null if not
0699: */
0700:
0701: public String getAttributeValue(int fingerprint) {
0702: if (nodeKind == Type.ELEMENT) {
0703: NamePool pool = docWrapper.getNamePool();
0704: String localName = pool.getLocalName(fingerprint);
0705: String uri = pool.getURI(fingerprint);
0706: Attribute att = ((Element) node).getAttribute(localName,
0707: uri);
0708: if (att != null)
0709: return att.getValue();
0710: }
0711: return null;
0712: }
0713:
0714: /**
0715: * Get the root node of the tree containing this node
0716: *
0717: * @return the NodeInfo representing the top-level ancestor of this node.
0718: * This will not necessarily be a document node
0719: */
0720:
0721: public NodeInfo getRoot() {
0722: return docWrapper;
0723: }
0724:
0725: /**
0726: * Get the root node, if it is a document node.
0727: *
0728: * @return the DocumentInfo representing the containing document.
0729: */
0730:
0731: public DocumentInfo getDocumentRoot() {
0732: if (docWrapper.node instanceof Document) {
0733: return docWrapper;
0734: } else {
0735: return null;
0736: }
0737: }
0738:
0739: /**
0740: * Determine whether the node has any children. <br />
0741: * Note: the result is equivalent to <br />
0742: * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasNext()
0743: */
0744:
0745: public boolean hasChildNodes() {
0746: return node.getChildCount() > 0;
0747: }
0748:
0749: /**
0750: * Get a character string that uniquely identifies this node. Note:
0751: * a.isSameNode(b) if and only if generateId(a)==generateId(b)
0752: *
0753: * @return a string that uniquely identifies this node, across all documents
0754: */
0755:
0756: public String generateId() {
0757: return Navigator.getSequentialKey(this );
0758: }
0759:
0760: /**
0761: * Get the document number of the document containing this node. For a
0762: * free-standing orphan node, just return the hashcode.
0763: */
0764:
0765: public int getDocumentNumber() {
0766: return docWrapper.getDocumentNumber();
0767: }
0768:
0769: /**
0770: * Copy this node to a given outputter (deep copy)
0771: */
0772:
0773: public void copy(Receiver out, int whichNamespaces,
0774: boolean copyAnnotations, int locationId)
0775: throws XPathException {
0776: Navigator.copy(this , out, docWrapper.getNamePool(),
0777: whichNamespaces, copyAnnotations, locationId);
0778: }
0779:
0780: /**
0781: * Output all namespace nodes associated with this element. Does nothing if
0782: * the node is not an element.
0783: *
0784: * @param out
0785: * The relevant outputter
0786: * @param includeAncestors
0787: * True if namespaces declared on ancestor elements must be
0788: */
0789:
0790: public void sendNamespaceDeclarations(Receiver out,
0791: boolean includeAncestors) throws XPathException {
0792: Navigator
0793: .sendNamespaceDeclarations(this , out, includeAncestors);
0794: }
0795:
0796: /**
0797: * Get all namespace undeclarations and undeclarations defined on this element.
0798: *
0799: * @param buffer If this is non-null, and the result array fits in this buffer, then the result
0800: * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
0801: * @return An array of integers representing the namespace declarations and undeclarations present on
0802: * this element. For a node other than an element, return null. Otherwise, the returned array is a
0803: * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
0804: * top half word of each namespace code represents the prefix, the bottom half represents the URI.
0805: * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
0806: * The XML namespace is never included in the list. If the supplied array is larger than required,
0807: * then the first unused entry will be set to -1.
0808: * <p/>
0809: * <p>For a node other than an element, the method returns null.</p>
0810: */
0811:
0812: public int[] getDeclaredNamespaces(int[] buffer) {
0813: if (node instanceof Element) {
0814: Element elem = (Element) node;
0815: int size = elem.getNamespaceDeclarationCount();
0816: if (size == 0) {
0817: return EMPTY_NAMESPACE_LIST;
0818: }
0819: int[] result = (size <= buffer.length ? buffer
0820: : new int[size]);
0821: NamePool pool = getNamePool();
0822: for (int i = 0; i < size; i++) {
0823: String prefix = elem.getNamespacePrefix(i);
0824: String uri = elem.getNamespaceURI(prefix);
0825: result[i] = pool.allocateNamespaceCode(prefix, uri);
0826: }
0827: if (size < result.length) {
0828: result[size] = -1;
0829: }
0830: return result;
0831: } else {
0832: return null;
0833: }
0834: }
0835:
0836: ///////////////////////////////////////////////////////////////////////////////
0837: // Axis enumeration classes
0838: ///////////////////////////////////////////////////////////////////////////////
0839:
0840: /**
0841: * Handles the ancestor axis in a rather direct manner.
0842: */
0843: private final class AncestorAxisIterator implements AxisIterator {
0844:
0845: private NodeWrapper start;
0846: private boolean includeSelf;
0847:
0848: private NodeInfo current;
0849:
0850: private NodeTest nodeTest;
0851: private int position;
0852:
0853: public AncestorAxisIterator(NodeWrapper start,
0854: boolean includeSelf, NodeTest test) {
0855: // use lazy instead of eager materialization (performance)
0856: this .start = start;
0857: if (test == AnyNodeTest.getInstance())
0858: test = null;
0859: this .nodeTest = test;
0860: if (!includeSelf)
0861: this .current = start;
0862: this .includeSelf = includeSelf;
0863: this .position = 0;
0864: }
0865:
0866: public Item next() {
0867: NodeInfo curr;
0868: do { // until we find a match
0869: curr = advance();
0870: } while (curr != null && nodeTest != null
0871: && (!nodeTest.matches(curr)));
0872:
0873: if (curr != null)
0874: position++;
0875: current = curr;
0876: return curr;
0877: }
0878:
0879: private NodeInfo advance() {
0880: if (current == null)
0881: current = start;
0882: else
0883: current = current.getParent();
0884:
0885: return current;
0886: }
0887:
0888: public Item current() {
0889: return current;
0890: }
0891:
0892: public int position() {
0893: return position;
0894: }
0895:
0896: public SequenceIterator getAnother() {
0897: return new AncestorAxisIterator(start, includeSelf,
0898: nodeTest);
0899: }
0900:
0901: public int getProperties() {
0902: return 0;
0903: }
0904:
0905: } // end of class AncestorAxisIterator
0906:
0907: /**
0908: * Handles the attribute axis in a rather direct manner.
0909: */
0910: private final class AttributeAxisIterator implements AxisIterator {
0911:
0912: private NodeWrapper start;
0913:
0914: private NodeInfo current;
0915: private int cursor;
0916:
0917: private NodeTest nodeTest;
0918: private int position;
0919:
0920: public AttributeAxisIterator(NodeWrapper start, NodeTest test) {
0921: // use lazy instead of eager materialization (performance)
0922: this .start = start;
0923: if (test == AnyNodeTest.getInstance())
0924: test = null;
0925: this .nodeTest = test;
0926: this .position = 0;
0927: this .cursor = 0;
0928: }
0929:
0930: public Item next() {
0931: NodeInfo curr;
0932: do { // until we find a match
0933: curr = advance();
0934: } while (curr != null && nodeTest != null
0935: && (!nodeTest.matches(curr)));
0936:
0937: if (curr != null)
0938: position++;
0939: current = curr;
0940: return curr;
0941: }
0942:
0943: private NodeInfo advance() {
0944: Element elem = (Element) start.node;
0945: if (cursor == elem.getAttributeCount())
0946: return null;
0947: NodeInfo curr = makeWrapper(elem.getAttribute(cursor),
0948: docWrapper, start, cursor);
0949: cursor++;
0950: return curr;
0951: }
0952:
0953: public Item current() {
0954: return current;
0955: }
0956:
0957: public int position() {
0958: return position;
0959: }
0960:
0961: public SequenceIterator getAnother() {
0962: return new AttributeAxisIterator(start, nodeTest);
0963: }
0964:
0965: public int getProperties() {
0966: return 0;
0967: }
0968:
0969: } // end of class AttributeAxisIterator
0970:
0971: /**
0972: * The class ChildAxisIterator handles not only the child axis, but also the
0973: * following-sibling and preceding-sibling axes. It can also iterate the
0974: * children of the start node in reverse order, something that is needed to
0975: * support the preceding and preceding-or-ancestor axes (the latter being
0976: * used by xsl:number)
0977: */
0978: private final class ChildAxisIterator implements AxisIterator {
0979:
0980: private NodeWrapper start;
0981: private NodeWrapper commonParent;
0982: private int ix;
0983: private boolean downwards; // iterate children of start node (not siblings)
0984: private boolean forwards; // iterate in document order (not reverse order)
0985:
0986: private NodeInfo current;
0987: private ParentNode par;
0988: private int cursor;
0989:
0990: private NodeTest nodeTest;
0991: private int position;
0992:
0993: private ChildAxisIterator(NodeWrapper start, boolean downwards,
0994: boolean forwards, NodeTest test) {
0995: this .start = start;
0996: this .downwards = downwards;
0997: this .forwards = forwards;
0998:
0999: if (test == AnyNodeTest.getInstance())
1000: test = null;
1001: this .nodeTest = test;
1002: this .position = 0;
1003:
1004: if (downwards)
1005: commonParent = start;
1006: else
1007: commonParent = (NodeWrapper) start.getParent();
1008:
1009: par = (ParentNode) commonParent.node;
1010: if (downwards) {
1011: ix = (forwards ? 0 : par.getChildCount());
1012: } else {
1013: // find the start node among the list of siblings
1014: // ix = start.getSiblingPosition();
1015: ix = par.indexOf(start.node);
1016: if (forwards)
1017: ix++;
1018: }
1019: cursor = ix;
1020: if (!downwards && !forwards)
1021: ix--;
1022: }
1023:
1024: public Item next() {
1025: NodeInfo curr;
1026: do { // until we find a match
1027: curr = advance();
1028: } while (curr != null && nodeTest != null
1029: && (!nodeTest.matches(curr)));
1030:
1031: if (curr != null)
1032: position++;
1033: current = curr;
1034: return curr;
1035: }
1036:
1037: private NodeInfo advance() {
1038: Node nextChild;
1039: do {
1040: if (forwards) {
1041: if (cursor == par.getChildCount())
1042: return null;
1043: nextChild = par.getChild(cursor++);
1044: } else { // backwards
1045: if (cursor == 0)
1046: return null;
1047: nextChild = par.getChild(--cursor);
1048: }
1049: } while (nextChild instanceof DocType);
1050: // DocType is not an XPath node; can occur for /child::node()
1051:
1052: NodeInfo curr = makeWrapper(nextChild, docWrapper,
1053: commonParent, ix);
1054: ix += (forwards ? 1 : -1);
1055: return curr;
1056: }
1057:
1058: public Item current() {
1059: return current;
1060: }
1061:
1062: public int position() {
1063: return position;
1064: }
1065:
1066: public SequenceIterator getAnother() {
1067: return new ChildAxisIterator(start, downwards, forwards,
1068: nodeTest);
1069: }
1070:
1071: public int getProperties() {
1072: return 0;
1073: }
1074: }
1075:
1076: /**
1077: * A bit of a misnomer; efficiently takes care of descendants,
1078: * descentants-or-self as well as "following" axis.
1079: * "includeSelf" must be false for the following axis.
1080: * Uses simple and effective O(1) backtracking via indexOf().
1081: */
1082: private final class DescendantAxisIterator implements AxisIterator {
1083:
1084: private NodeWrapper start;
1085: private boolean includeSelf;
1086: private boolean following;
1087:
1088: private Node anchor; // so we know where to stop the scan
1089: private Node currNode;
1090: private boolean moveToNextSibling;
1091:
1092: private NodeInfo current;
1093: private NodeTest nodeTest;
1094: private int position;
1095:
1096: private String testLocalName;
1097: private String testURI;
1098:
1099: public DescendantAxisIterator(NodeWrapper start,
1100: boolean includeSelf, boolean following, NodeTest test) {
1101: this .start = start;
1102: this .includeSelf = includeSelf;
1103: this .following = following;
1104: this .moveToNextSibling = following;
1105:
1106: if (!following)
1107: anchor = start.node;
1108: if (!includeSelf)
1109: currNode = start.node;
1110:
1111: if (test == AnyNodeTest.getInstance()) { // performance hack
1112: test = null; // mark as AnyNodeTest
1113: } else if (test instanceof NameTest) {
1114: NameTest nt = (NameTest) test;
1115: if (nt.getPrimitiveType() == Type.ELEMENT) { // performance hack
1116: // mark as element name test
1117: NamePool pool = getNamePool();
1118: this .testLocalName = pool.getLocalName(nt
1119: .getFingerprint());
1120: this .testURI = pool.getURI(nt.getFingerprint());
1121: }
1122: } else if (test instanceof NodeKindTest) {
1123: if (test.getPrimitiveType() == Type.ELEMENT) { // performance hack
1124: // mark as element type test
1125: this .testLocalName = "";
1126: this .testURI = null;
1127: }
1128: }
1129: this .nodeTest = test;
1130: this .position = 0;
1131: }
1132:
1133: public Item next() {
1134: NodeInfo curr;
1135: do { // until we find a match
1136: curr = advance();
1137: } while (curr != null && nodeTest != null
1138: && (!nodeTest.matches(curr)));
1139:
1140: if (curr != null)
1141: position++;
1142: current = curr;
1143: return curr;
1144: }
1145:
1146: // might look expensive at first glance - but it's not
1147: private NodeInfo advance() {
1148: if (currNode == null) { // if includeSelf
1149: currNode = start.node;
1150: return start;
1151: }
1152:
1153: int i;
1154: do {
1155: i = 0;
1156: Node p = currNode;
1157:
1158: if (p.getChildCount() == 0 || moveToNextSibling) { // move to next sibling
1159:
1160: moveToNextSibling = false; // do it just once
1161: while (true) {
1162: // if we've reached the root we're done scanning
1163: p = currNode.getParent();
1164: if (p == null)
1165: return null;
1166:
1167: // Note: correct even if currNode is an attribute.
1168: // Performance is particularly good with the O(1) patch
1169: // for XOM's ParentNode.indexOf()
1170: i = currNode.getParent().indexOf(currNode) + 1;
1171:
1172: if (i < p.getChildCount()) {
1173: break; // break out of while(true) loop; move to next sibling
1174: } else { // reached last sibling; move up
1175: currNode = p;
1176: // if we've come all the way back to the start anchor we're done
1177: if (p == anchor)
1178: return null;
1179: }
1180: }
1181: }
1182: currNode = p.getChild(i);
1183: } while (!conforms(currNode));
1184:
1185: // note the null here: makeNodeWrapper(parent, ...) is fast, so it
1186: // doesn't really matter that we don't keep a link to it.
1187: // In fact, it makes objects more short lived, easing pressure on
1188: // the VM allocator and collector for tenured heaps.
1189: return makeWrapper(currNode, docWrapper, null, i);
1190: }
1191:
1192: // avoids NodeWrapper allocation when there's clearly a mismatch (common case)
1193: private boolean conforms(Node node) {
1194: if (this .testLocalName != null) { // element test?
1195: if (!(node instanceof Element))
1196: return false;
1197: if (this .testURI == null)
1198: return true; // pure element type test
1199:
1200: // element name test
1201: Element elem = (Element) node;
1202: return this .testLocalName.equals(elem.getLocalName())
1203: && this .testURI.equals(elem.getNamespaceURI());
1204: } else { // DocType is not an XPath node; can occur for /descendants::node()
1205: return !(node instanceof DocType);
1206: }
1207: }
1208:
1209: public Item current() {
1210: return current;
1211: }
1212:
1213: public int position() {
1214: return position;
1215: }
1216:
1217: public SequenceIterator getAnother() {
1218: return new DescendantAxisIterator(start, includeSelf,
1219: following, nodeTest);
1220: }
1221:
1222: public int getProperties() {
1223: return 0;
1224: }
1225: }
1226:
1227: /**
1228: * Efficiently takes care of preceding axis and Saxon internal preceding-or-ancestor axis.
1229: * Uses simple and effective O(1) backtracking via indexOf().
1230: * Implemented along similar lines as DescendantAxisIterator.
1231: */
1232: private final class PrecedingAxisIterator implements AxisIterator {
1233:
1234: private NodeWrapper start;
1235: private boolean includeAncestors;
1236:
1237: private Node currNode;
1238: private ParentNode nextAncestor; // next ancestors to skip if !includeAncestors
1239:
1240: private NodeInfo current;
1241: private NodeTest nodeTest;
1242: private int position;
1243:
1244: private String testLocalName;
1245: private String testURI;
1246:
1247: public PrecedingAxisIterator(NodeWrapper start,
1248: boolean includeAncestors, NodeTest test) {
1249: this .start = start;
1250: this .includeAncestors = includeAncestors;
1251: this .currNode = start.node;
1252: if (includeAncestors)
1253: nextAncestor = null;
1254: else
1255: nextAncestor = start.node.getParent();
1256:
1257: if (test == AnyNodeTest.getInstance()) { // performance hack
1258: test = null; // mark as AnyNodeTest
1259: } else if (test instanceof NameTest) {
1260: NameTest nt = (NameTest) test;
1261: if (nt.getPrimitiveType() == Type.ELEMENT) { // performance hack
1262: // mark as element name test
1263: NamePool pool = getNamePool();
1264: this .testLocalName = pool.getLocalName(nt
1265: .getFingerprint());
1266: this .testURI = pool.getURI(nt.getFingerprint());
1267: }
1268: } else if (test instanceof NodeKindTest) {
1269: if (test.getPrimitiveType() == Type.ELEMENT) { // performance hack
1270: // mark as element type test
1271: this .testLocalName = "";
1272: this .testURI = null;
1273: }
1274: }
1275: this .nodeTest = test;
1276: this .position = 0;
1277: }
1278:
1279: public Item next() {
1280: NodeInfo curr;
1281: do { // until we find a match
1282: curr = advance();
1283: } while (curr != null && nodeTest != null
1284: && (!nodeTest.matches(curr)));
1285:
1286: if (curr != null)
1287: position++;
1288: current = curr;
1289: return curr;
1290: }
1291:
1292: // might look expensive at first glance - but it's not
1293: private NodeInfo advance() {
1294: int i;
1295: do {
1296: Node p = currNode;
1297:
1298: while (true) {
1299: // if we've reached the root we're done scanning
1300: // System.out.println("p="+p);
1301: p = currNode.getParent();
1302: if (p == null)
1303: return null;
1304:
1305: // Note: correct even if currNode is an attribute.
1306: // Performance is particularly good with the O(1) patch
1307: // for XOM's ParentNode.indexOf()
1308: i = currNode.getParent().indexOf(currNode) - 1;
1309:
1310: if (i >= 0) { // move to next sibling's last descendant node
1311: p = p.getChild(i); // move to next sibling
1312: int j;
1313: while ((j = p.getChildCount() - 1) >= 0) { // move to last descendant node
1314: p = p.getChild(j);
1315: i = j;
1316: }
1317: break; // break out of while(true) loop
1318: } else { // there are no more siblings; move up
1319: // if !includeAncestors skip the ancestors of the start node
1320: // assert p != null
1321: if (p != nextAncestor)
1322: break; // break out of while(true) loop
1323:
1324: nextAncestor = nextAncestor.getParent();
1325: currNode = p;
1326: }
1327: }
1328: currNode = p;
1329:
1330: } while (!conforms(currNode));
1331:
1332: // note the null here: makeNodeWrapper(parent, ...) is fast, so it
1333: // doesn't really matter that we don't keep a link to it.
1334: // In fact, it makes objects more short lived, easing pressure on
1335: // the VM allocator and collector for tenured heaps.
1336: return makeWrapper(currNode, docWrapper, null, i);
1337: }
1338:
1339: // avoids NodeWrapper allocation when there's clearly a mismatch (common case)
1340: // same as for DescendantAxisIterator
1341: private boolean conforms(Node node) {
1342: if (this .testLocalName != null) { // element test?
1343: if (!(node instanceof Element))
1344: return false;
1345: if (this .testURI == null)
1346: return true; // pure element type test
1347:
1348: // element name test
1349: Element elem = (Element) node;
1350: return this .testLocalName.equals(elem.getLocalName())
1351: && this .testURI.equals(elem.getNamespaceURI());
1352: } else { // DocType is not an XPath node
1353: return !(node instanceof DocType);
1354: }
1355: }
1356:
1357: public Item current() {
1358: return current;
1359: }
1360:
1361: public int position() {
1362: return position;
1363: }
1364:
1365: public SequenceIterator getAnother() {
1366: return new PrecedingAxisIterator(start, includeAncestors,
1367: nodeTest);
1368: }
1369:
1370: public int getProperties() {
1371: return 0;
1372: }
1373: }
1374:
1375: }
1376:
1377: //
1378: // The contents of this file are subject to the Mozilla Public License Version
1379: // 1.0 (the "License");
1380: // you may not use this file except in compliance with the License. You may
1381: // obtain a copy of the
1382: // License at http://www.mozilla.org/MPL/
1383: //
1384: // Software distributed under the License is distributed on an "AS IS" basis,
1385: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
1386: // See the License for the specific language governing rights and limitations
1387: // under the License.
1388: //
1389: // The Original Code is: all this file.
1390: //
1391: // The Initial Developer of the Original Code is Michael Kay, with extensive
1392: // rewriting by Wolfgang Hoschek
1393: //
1394: // Portions created by (your name) are Copyright (C) (your legal entity). All
1395: // Rights Reserved.
1396: //
1397: // Contributor(s): none.
1398: //
|