lmartin 2003/01/23 15:24:03 Modified: java/src/org/apache/xerces/dom NodeImpl.java Log: added initial impl for compareDocPosition Revision Changes Path 1.63 +271 -1 xml-xerces/java/src/org/apache/xerces/dom/NodeImpl.java Index: NodeImpl.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/NodeImpl.java,v retrieving revision 1.62 retrieving revision 1.63 diff -u -r1.62 -r1.63 --- NodeImpl.java 17 Jan 2003 14:29:10 -0000 1.62 +++ NodeImpl.java 23 Jan 2003 23:23:59 -0000 1.63 @@ -69,6 +69,7 @@ import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import org.w3c.dom.DocumentType; import org.w3c.dom.events.Event; import org.w3c.dom.events.EventListener; @@ -161,6 +162,14 @@ public static final short TREE_POSITION_DISCONNECTED = 0x00; + // DocumentPosition + public static final short DOCUMENT_POSITION_DISCONNECTED = 0x01; + public static final short DOCUMENT_POSITION_PRECEDING = 0x02; + public static final short DOCUMENT_POSITION_FOLLOWING = 0x04; + public static final short DOCUMENT_POSITION_CONTAINS = 0x08; + public static final short DOCUMENT_POSITION_IS_CONTAINED = 0x10; + public static final short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; + /** Serialization version. */ static final long serialVersionUID = -6316591992167219696L; @@ -781,6 +790,7 @@ * @return Returns how the given node is positioned relatively to this * node. * @since DOM Level 3 + * @deprecated */ public short compareTreePosition(Node other) { // Questions of clarification for this method - to be answered by the @@ -979,6 +989,258 @@ return 0; } + /** + * Compares a node with this node with regard to their position in the + * document. + * @param other The node to compare against this node. + * @return Returns how the given node is positioned relatively to this + * node. + * @since DOM Level 3 + */ + public short compareDocumentPosition(Node other) { + + // If the nodes are the same, no flags should be set + if (this==other) + return 0; + + Document thisOwnerDoc, otherOwnerDoc; + // get the respective Document owners. + if (this.getNodeType() == Node.DOCUMENT_NODE) + thisOwnerDoc = (Document)this; + else + thisOwnerDoc = this.getOwnerDocument(); + if (other.getNodeType() == Node.DOCUMENT_NODE) + otherOwnerDoc = (Document)other; + else + otherOwnerDoc = other.getOwnerDocument(); + + // If from different documents, we know they are disconnected. + // Set DISCONNECTED for now. + // TODO: set order of Document nodes by assigning doc numbers to them + if (thisOwnerDoc != otherOwnerDoc) + return DOCUMENT_POSITION_DISCONNECTED; + + // Find the ancestor of each node, and the distance each node is from + // its ancestor. + // During this traversal, look for ancestor/descendent relationships + // between the 2 nodes in question. + // We do this now, so that we get this info correct for attribute nodes + // and their children. + + Node node; + Node thisAncestor = this; + Node otherAncestor = other; + + int thisDepth=0; + int otherDepth=0; + for (node=this; node != null; node = node.getParentNode()) { + thisDepth +=1; + if (node == other) + // The other node is an ancestor of this one. + return (DOCUMENT_POSITION_CONTAINS | + DOCUMENT_POSITION_PRECEDING); + thisAncestor = node; + } + + for (node=other; node!=null; node=node.getParentNode()) { + otherDepth +=1; + if (node == this) + // The other node is a descendent of the reference node. + return (DOCUMENT_POSITION_IS_CONTAINED | + DOCUMENT_POSITION_FOLLOWING); + otherAncestor = node; + } + + + + int thisAncestorType = thisAncestor.getNodeType(); + int otherAncestorType = otherAncestor.getNodeType(); + Node thisNode = this; + Node otherNode = other; + + // Special casing for ENTITY, NOTATION, DOCTYPE and ATTRIBUTES + // LM: should rewrite this. + switch (thisAncestorType) { + case Node.NOTATION_NODE: + case Node.ENTITY_NODE: { + DocumentType container = thisOwnerDoc.getDoctype(); + if (container == otherAncestor) return + (DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING); + switch (otherAncestorType) { + case Node.NOTATION_NODE: + case Node.ENTITY_NODE: { + if (thisAncestorType != otherAncestorType) + // the nodes are of different types + return ((thisAncestorType>otherAncestorType) ? + DOCUMENT_POSITION_PRECEDING:DOCUMENT_POSITION_FOLLOWING); + else { + // the nodes are of the same type. Find order. + if (thisAncestorType == Node.NOTATION_NODE) + + if (((NamedNodeMapImpl)container.getNotations()).precedes(otherAncestor,thisAncestor)) + return (DOCUMENT_POSITION_PRECEDING | + DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC); + else + return (DOCUMENT_POSITION_FOLLOWING | + DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC); + else + if (((NamedNodeMapImpl)container.getEntities()).precedes(otherAncestor,thisAncestor)) + return (DOCUMENT_POSITION_PRECEDING | + DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC); + else + return (DOCUMENT_POSITION_FOLLOWING | + DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC); + } + } + } + thisNode = thisAncestor = thisOwnerDoc; + break; + } + case Node.DOCUMENT_TYPE_NODE: { + if (otherNode == thisOwnerDoc) + return (DOCUMENT_POSITION_PRECEDING | + DOCUMENT_POSITION_CONTAINS); + else + return (DOCUMENT_POSITION_FOLLOWING); + } + case Node.ATTRIBUTE_NODE: { + thisNode = ((AttrImpl)thisAncestor).getOwnerElement(); + if (otherAncestorType==Node.ATTRIBUTE_NODE) { + otherNode = ((AttrImpl)otherAncestor).getOwnerElement(); + if (otherNode == thisNode) { + if (((NamedNodeMapImpl)thisNode.getAttributes()).precedes(other,this)) + return (DOCUMENT_POSITION_PRECEDING | + DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC); + else + return (DOCUMENT_POSITION_FOLLOWING | + DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC); + } + } + + // Now, find the ancestor of the element + thisDepth=0; + for (node=thisNode; node != null; node=node.getParentNode()) { + thisDepth +=1; + if (node == otherNode) + // The other node is an ancestor of the owning element + { + return DOCUMENT_POSITION_PRECEDING; + } + thisAncestor = node; + } + } + } + switch (otherAncestorType) { + case Node.NOTATION_NODE: + case Node.ENTITY_NODE: { + DocumentType container = thisOwnerDoc.getDoctype(); + if (container == this) return (DOCUMENT_POSITION_IS_CONTAINED | + DOCUMENT_POSITION_FOLLOWING); + otherNode = otherAncestor = thisOwnerDoc; + break; + } + case Node.DOCUMENT_TYPE_NODE: { + if (thisNode == otherOwnerDoc) + return (DOCUMENT_POSITION_FOLLOWING | + DOCUMENT_POSITION_IS_CONTAINED); + else + return (DOCUMENT_POSITION_PRECEDING); + } + case Node.ATTRIBUTE_NODE: { + otherDepth=0; + otherNode = ((AttrImpl)otherAncestor).getOwnerElement(); + for (node=otherNode; node != null; node=node.getParentNode()) { + otherDepth +=1; + if (node == thisNode) + // The other node is a descendent of the reference + // node's element + return DOCUMENT_POSITION_FOLLOWING | + DOCUMENT_POSITION_IS_CONTAINED; + otherAncestor = node; + } + + } + } + + // thisAncestor and otherAncestor must be the same at this point, + // otherwise, we are not in the same document fragment + // TODO: need to look at ordering document fragments as well? + if (thisAncestor != otherAncestor) + return DOCUMENT_POSITION_DISCONNECTED; + + + // Go up the parent chain of the deeper node, until we find a node + // with the same depth as the shallower node + + if (thisDepth > otherDepth) { + for (int i=0; i<thisDepth - otherDepth; i++) + thisNode = thisNode.getParentNode(); + // Check if the node we have reached is in fact "otherNode". This can + // happen in the case of attributes. In this case, otherNode + // "precedes" this. + if (thisNode == otherNode) + return DOCUMENT_POSITION_PRECEDING; + } + + else { + for (int i=0; i<otherDepth - thisDepth; i++) + otherNode = otherNode.getParentNode(); + // Check if the node we have reached is in fact "thisNode". This can + // happen in the case of attributes. In this case, otherNode + // "follows" this. + if (otherNode == thisNode) + return DOCUMENT_POSITION_FOLLOWING; + } + + // We now have nodes at the same depth in the tree. Find a common + // ancestor. + Node thisNodeP, otherNodeP; + for (thisNodeP=thisNode.getParentNode(), + otherNodeP=otherNode.getParentNode(); + thisNodeP!=otherNodeP;) { + thisNode = thisNodeP; + otherNode = otherNodeP; + thisNodeP = thisNodeP.getParentNode(); + otherNodeP = otherNodeP.getParentNode(); + } + + // At this point, thisNode and otherNode are direct children of + // the common ancestor. + // See whether thisNode or otherNode is the leftmost + + for (Node current=thisNodeP.getFirstChild(); + current!=null; + current=current.getNextSibling()) { + if (current==otherNode) { + return DOCUMENT_POSITION_PRECEDING; + } + else if (current==thisNode) { + return DOCUMENT_POSITION_FOLLOWING; + } + } + // REVISIT: shouldn't get here. Should probably throw an + // exception + return 0; + + } + + /** + * This attribute returns the text content of this node and its + * descendants. When it is defined to be null, setting it has no effect. + * When set, any possible children this node may have are removed and + * replaced by a single <code>Text</code> node containing the string + * this attribute is set to. On getting, no serialization is performed, + * the returned string does not contain any markup. No whitespace + * normalization is performed, the returned string does not contain the + * element content whitespaces . Similarly, on setting, no parsing is + * performed either, the input string is taken as pure textual content. + * <br>The string returned is made of the text content of this node + * depending on its type, as defined below: + * <table border='1'> + * <tr> + * <th>Node type</th> + * <th>Content</th> + * </tr> /** * This attribute returns the text content of this node and its @@ -1640,6 +1902,14 @@ protected void synchronizeData() { // By default just change the flag to avoid calling this method again needsSyncData(false); + } + + /** + * For non-child nodes, the node which "points" to this node. + * For example, the owning element for an attribute + */ + protected Node getContainer() { + return null; }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]