    1   /*
    2    * Copyright 1999-2004 The Apache Software Foundation
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.jxpath.ri.model.dom;
    17
    18  import java.util.HashMap;
    19  import java.util.Locale;
    20  import java.util.Map;
    21
    22  import org.apache.commons.jxpath.AbstractFactory;
    23  import org.apache.commons.jxpath.JXPathContext;
    24  import org.apache.commons.jxpath.JXPathException;
    25  import org.apache.commons.jxpath.Pointer;
    26  import org.apache.commons.jxpath.ri.Compiler;
    27  import org.apache.commons.jxpath.ri.QName;
    28  import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
    29  import org.apache.commons.jxpath.ri.compiler.NodeTest;
    30  import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
    31  import org.apache.commons.jxpath.ri.compiler.ProcessingInstructionTest;
    32  import org.apache.commons.jxpath.ri.model.beans.NullPointer;
    33  import org.apache.commons.jxpath.ri.model.NodeIterator;
    34  import org.apache.commons.jxpath.ri.model.NodePointer;
    35  import org.apache.commons.jxpath.util.TypeUtils;
    36  import org.w3c.dom.Attr;
    37  import org.w3c.dom.Comment;
    38  import org.w3c.dom.Document;
    39  import org.w3c.dom.Element;
    40  import org.w3c.dom.NamedNodeMap;
    41  import org.w3c.dom.Node;
    42  import org.w3c.dom.NodeList;
    43  import org.w3c.dom.ProcessingInstruction;
    44
    45  /***
    46   * A Pointer that points to a DOM node.
    47   *
    48   * @author Dmitri Plotnikov
    49   * @version $Revision: 1.24 $ $Date: 2004/06/29 22:58:17 $
    50   */
    51  public class DOMNodePointer extends NodePointer {
    52      private Node node;
    53      private Map namespaces;
    54      private String defaultNamespace;
    55      private String id;
    56
    57      public static final String XML_NAMESPACE_URI =
    58              "http://www.w3.org/XML/1998/namespace";
    59      public static final String XMLNS_NAMESPACE_URI =
    60              "http://www.w3.org/2000/xmlns/";
    61
    62      public DOMNodePointer(Node node, Locale locale) {
    63          super(null, locale);
    64          this.node = node;
    65      }
    66
    67      public DOMNodePointer(Node node, Locale locale, String id) {
    68          super(null, locale);
    69          this.node = node;
    70          this.id = id;
    71      }
    72
    73      public DOMNodePointer(NodePointer parent, Node node) {
    74          super(parent);
    75          this.node = node;
    76      }
    77
    78      public boolean testNode(NodeTest test) {
    79          return testNode(node, test);
    80      }
    81
    82      public static boolean testNode(Node node, NodeTest test) {
    83          if (test == null) {
    84              return true;
    85          }
    86          else if (test instanceof NodeNameTest) {
    87              if (node.getNodeType() != Node.ELEMENT_NODE) {
    88                  return false;
    89              }
    90
    91              NodeNameTest nodeNameTest = (NodeNameTest) test;
    92              QName testName = nodeNameTest.getNodeName();
    93              String namespaceURI = nodeNameTest.getNamespaceURI();
    94              boolean wildcard = nodeNameTest.isWildcard();
    95              String testPrefix = testName.getPrefix();
    96              if (wildcard && testPrefix == null) {
    97                  return true;
    98              }
    99
    100             if (wildcard
    101                 || testName.getName()
    102                         .equals(DOMNodePointer.getLocalName(node))) {
    103                 String nodeNS = DOMNodePointer.getNamespaceURI(node);
    104                 return equalStrings(namespaceURI, nodeNS);
    105             }
    106         }
    107         else if (test instanceof NodeTypeTest) {
    108             int nodeType = node.getNodeType();
    109             switch (((NodeTypeTest) test).getNodeType()) {
    110                 case Compiler.NODE_TYPE_NODE :
    111                     return nodeType == Node.ELEMENT_NODE;
    112                 case Compiler.NODE_TYPE_TEXT :
    113                     return nodeType == Node.CDATA_SECTION_NODE
    114                         || nodeType == Node.TEXT_NODE;
    115                 case Compiler.NODE_TYPE_COMMENT :
    116                     return nodeType == Node.COMMENT_NODE;
    117                 case Compiler.NODE_TYPE_PI :
    118                     return nodeType == Node.PROCESSING_INSTRUCTION_NODE;
    119             }
    120             return false;
    121         }
    122         else if (test instanceof ProcessingInstructionTest) {
    123             if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
    124                 String testPI = ((ProcessingInstructionTest) test).getTarget();
    125                 String nodePI = ((ProcessingInstruction) node).getTarget();
    126                 return testPI.equals(nodePI);
    127             }
    128         }
    129         return false;
    130     }
    131
    132     private static boolean equalStrings(String s1, String s2) {
    133         if (s1 == null && s2 != null) {
    134             return false;
    135         }
    136         if (s1 != null && s2 == null) {
    137             return false;
    138         }
    139
    140         if (s1 != null && !s1.trim().equals(s2.trim())) {
    141             return false;
    142         }
    143
    144         return true;
    145     }
    146
    147     public QName getName() {
    148         String ln = null;
    149         String ns = null;
    150         int type = node.getNodeType();
    151         if (type == Node.ELEMENT_NODE) {
    152             ns = DOMNodePointer.getPrefix(node);
    153             ln = DOMNodePointer.getLocalName(node);
    154         }
    155         else if (type == Node.PROCESSING_INSTRUCTION_NODE) {
    156             ln = ((ProcessingInstruction) node).getTarget();
    157         }
    158         return new QName(ns, ln);
    159     }
    160
    161     public String getNamespaceURI() {
    162         return getNamespaceURI(node);
    163     }
    164
    165     public NodeIterator childIterator(
    166         NodeTest test,
    167         boolean reverse,
    168         NodePointer startWith)
    169     {
    170         return new DOMNodeIterator(this, test, reverse, startWith);
    171     }
    172
    173     public NodeIterator attributeIterator(QName name) {
    174         return new DOMAttributeIterator(this, name);
    175     }
    176
    177     public NodePointer namespacePointer(String prefix) {
    178         return new NamespacePointer(this, prefix);
    179     }
    180
    181     public NodeIterator namespaceIterator() {
    182         return new DOMNamespaceIterator(this);
    183     }
    184
    185     public String getNamespaceURI(String prefix) {
    186         if (prefix == null || prefix.equals("")) {
    187             return getDefaultNamespaceURI();
    188         }
    189
    190         if (prefix.equals("xml")) {
    191             return XML_NAMESPACE_URI;
    192         }
    193
    194         if (prefix.equals("xmlns")) {
    195             return XMLNS_NAMESPACE_URI;
    196         }
    197
    198         String namespace = null;
    199         if (namespaces == null) {
    200             namespaces = new HashMap();
    201         }
    202         else {
    203             namespace = (String) namespaces.get(prefix);
    204         }
    205
    206         if (namespace == null) {
    207             String qname = "xmlns:" + prefix;
    208             Node aNode = node;
    209             if (aNode instanceof Document) {
    210                 aNode = ((Document)aNode).getDocumentElement();
    211             }
    212             while (aNode != null) {
    213                 if (aNode.getNodeType() == Node.ELEMENT_NODE) {
    214                     Attr attr = ((Element) aNode).getAttributeNode(qname);
    215                     if (attr != null) {
    216                         namespace = attr.getValue();
    217                         break;
    218                     }
    219                 }
    220                 aNode = aNode.getParentNode();
    221             }
    222             if (namespace == null || namespace.equals("")) {
    223                 namespace = NodePointer.UNKNOWN_NAMESPACE;
    224             }
    225         }
    226
    227         namespaces.put(prefix, namespace);
    228         // TBD: We are supposed to resolve relative URIs to absolute ones.
    229         return namespace;
    230     }
    231
    232     private String getNamespaceURI(String prefix, String namespace) {
    233         String qname = "xmlns:" + prefix;
    234         Node aNode = node;
    235         if (aNode instanceof Document) {
    236             aNode = ((Document)aNode).getDocumentElement();
    237         }
    238         while (aNode != null) {
    239             if (aNode.getNodeType() == Node.ELEMENT_NODE) {
    240                 Attr attr = ((Element) aNode).getAttributeNode(qname);
    241                 if (attr != null) {
    242                     namespace = attr.getValue();
    243                     break;
    244                 }
    245             }
    246             aNode = aNode.getParentNode();
    247         }
    248         return namespace;
    249     }
    250
    251     public String getDefaultNamespaceURI() {
    252         if (defaultNamespace == null) {
    253             Node aNode = node;
    254             while (aNode != null) {
    255                 if (aNode.getNodeType() == Node.ELEMENT_NODE) {
    256                     Attr attr = ((Element) aNode).getAttributeNode("xmlns");
    257                     if (attr != null) {
    258                         defaultNamespace = attr.getValue();
    259                         break;
    260                     }
    261                 }
    262                 aNode = aNode.getParentNode();
    263             }
    264         }
    265         if (defaultNamespace == null) {
    266             defaultNamespace = "";
    267         }
    268         // TBD: We are supposed to resolve relative URIs to absolute ones.
    269         return defaultNamespace.equals("") ? null : defaultNamespace;
    270     }
    271
    272     public Object getBaseValue() {
    273         return node;
    274     }
    275
    276     public Object getImmediateNode() {
    277         return node;
    278     }
    279
    280     public boolean isActual() {
    281         return true;
    282     }
    283
    284     public boolean isCollection() {
    285         return false;
    286     }
    287
    288     public int getLength() {
    289         return 1;
    290     }
    291
    292     public boolean isLeaf() {
    293         return !node.hasChildNodes();
    294     }
    295
    296     /***
    297      * Returns true if the xml:lang attribute for the current node
    298      * or its parent has the specified prefix <i>lang</i>.
    299      * If no node has this prefix, calls <code>super.isLanguage(lang)</code>.
    300      */
    301     public boolean isLanguage(String lang) {
    302         String current = getLanguage();
    303         if (current == null) {
    304             return super.isLanguage(lang);
    305         }
    306         return current.toUpperCase().startsWith(lang.toUpperCase());
    307     }
    308
    309     protected String getLanguage() {
    310         Node n = node;
    311         while (n != null) {
    312             if (n.getNodeType() == Node.ELEMENT_NODE) {
    313                 Element e = (Element) n;
    314                 String attr = e.getAttribute("xml:lang");
    315                 if (attr != null && !attr.equals("")) {
    316                     return attr;
    317                 }
    318             }
    319             n = n.getParentNode();
    320         }
    321         return null;
    322     }
    323
    324     /***
    325      * Sets contents of the node to the specified value. If the value is
    326      * a String, the contents of the node are replaced with this text.
    327      * If the value is an Element or Document, the children of the
    328      * node are replaced with the children of the passed node.
    329      */
    330     public void setValue(Object value) {
    331         if (node.getNodeType() == Node.TEXT_NODE
    332             || node.getNodeType() == Node.CDATA_SECTION_NODE) {
    333             String string = (String) TypeUtils.convert(value, String.class);
    334             if (string != null && !string.equals("")) {
    335                 node.setNodeValue(string);
    336             }
    337             else {
    338                 node.getParentNode().removeChild(node);
    339             }
    340         }
    341         else {
    342             NodeList children = node.getChildNodes();
    343             int count = children.getLength();
    344             for (int i = count; --i >= 0;) {
    345                 Node child = children.item(i);
    346                 node.removeChild(child);
    347             }
    348
    349             if (value instanceof Node) {
    350                 Node valueNode = (Node) value;
    351                 if (valueNode instanceof Element
    352                     || valueNode instanceof Document) {
    353                     children = valueNode.getChildNodes();
    354                     for (int i = 0; i < children.getLength(); i++) {
    355                         Node child = children.item(i);
    356                         node.appendChild(child.cloneNode(true));
    357                     }
    358                 }
    359                 else {
    360                     node.appendChild(valueNode.cloneNode(true));
    361                 }
    362             }
    363             else {
    364                 String string = (String) TypeUtils.convert(value, String.class);
    365                 if (string != null && !string.equals("")) {
    366                     Node textNode =
    367                         node.getOwnerDocument().createTextNode(string);
    368                     node.appendChild(textNode);
    369                 }
    370             }
    371         }
    372     }
    373
    374     public NodePointer createChild(
    375         JXPathContext context,
    376         QName name,
    377         int index)
    378     {
    379         if (index == WHOLE_COLLECTION) {
    380             index = 0;
    381         }
    382         boolean success =
    383             getAbstractFactory(context).createObject(
    384                 context,
    385                 this,
    386                 node,
    387                 name.toString(),
    388                 index);
    389         if (success) {
    390             NodeTest nodeTest;
    391             String prefix = name.getPrefix();
    392             if (prefix != null) {
    393                 String namespaceURI = context.getNamespaceURI(prefix);
    394                 nodeTest = new NodeNameTest(name, namespaceURI);
    395             }
    396             else {
    397                 nodeTest = new NodeNameTest(name);
    398             }
    399
    400             NodeIterator it = childIterator(nodeTest, false, null);
    401             if (it != null && it.setPosition(index + 1)) {
    402                 return it.getNodePointer();
    403             }
    404         }
    405         throw new JXPathException(
    406             "Factory could not create a child node for path: "
    407                 + asPath()
    408                 + "/"
    409                 + name
    410                 + "["
    411                 + (index + 1)
    412                 + "]");
    413     }
    414
    415     public NodePointer createChild(JXPathContext context,
    416                 QName name, int index, Object value)
    417     {
    418         NodePointer ptr = createChild(context, name, index);
    419         ptr.setValue(value);
    420         return ptr;
    421     }
    422
    423     public NodePointer createAttribute(JXPathContext context, QName name) {
    424         if (!(node instanceof Element)) {
    425             return super.createAttribute(context, name);
    426         }
    427         Element element = (Element) node;
    428         String prefix = name.getPrefix();
    429         if (prefix != null) {
    430             String ns = getNamespaceURI(prefix);
    431             if (ns == null) {
    432                 throw new JXPathException(
    433                     "Unknown namespace prefix: " + prefix);
    434             }
    435             element.setAttributeNS(ns, name.toString(), "");
    436         }
    437         else {
    438             if (!element.hasAttribute(name.getName())) {
    439                 element.setAttribute(name.getName(), "");
    440             }
    441         }
    442         NodeIterator it = attributeIterator(name);
    443         it.setPosition(1);
    444         return it.getNodePointer();
    445     }
    446
    447     public void remove() {
    448         Node parent = node.getParentNode();
    449         if (parent == null) {
    450             throw new JXPathException("Cannot remove root DOM node");
    451         }
    452         parent.removeChild(node);
    453     }
    454
    455     public String asPath() {
    456         if (id != null) {
    457             return "id('" + escape(id) + "')";
    458         }
    459
    460         StringBuffer buffer = new StringBuffer();
    461         if (parent != null) {
    462             buffer.append(parent.asPath());
    463         }
    464         switch (node.getNodeType()) {
    465             case Node.ELEMENT_NODE :
    466                 // If the parent pointer is not a DOMNodePointer, it is
    467                 // the parent's responsibility to produce the node test part
    468                 // of the path
    469                 if (parent instanceof DOMNodePointer) {
    470                     if (buffer.length() == 0
    471                             || buffer.charAt(buffer.length() - 1) != '/') {
    472                         buffer.append('/');
    473                     }
    474                     String nsURI = getNamespaceURI();
    475                     String ln = DOMNodePointer.getLocalName(node);
    476
    477                     if (nsURI == null) {
    478                         buffer.append(ln);
    479                         buffer.append('[');
    480                         buffer.append(getRelativePositionByName()).append(']');
    481                     }
    482                     else {
    483                         String prefix = getNamespaceResolver().getPrefix(nsURI);
    484                         if (prefix != null) {
    485                             buffer.append(prefix);
    486                             buffer.append(':');
    487                             buffer.append(ln);
    488                             buffer.append('[');
    489                             buffer.append(getRelativePositionByName());
    490                             buffer.append(']');
    491                         }
    492                         else {
    493                             buffer.append("node()");
    494                             buffer.append('[');
    495                             buffer.append(getRelativePositionOfElement());
    496                             buffer.append(']');
    497                         }
    498                     }
    499                 }
    500             break;
    501             case Node.TEXT_NODE :
    502             case Node.CDATA_SECTION_NODE :
    503                 buffer.append("/text()");
    504                 buffer.append('[');
    505                 buffer.append(getRelativePositionOfTextNode()).append(']');
    506                 break;
    507             case Node.PROCESSING_INSTRUCTION_NODE :
    508                 String target = ((ProcessingInstruction) node).getTarget();
    509                 buffer.append("/processing-instruction(\'");
    510                 buffer.append(target).append("')");
    511                 buffer.append('[');
    512                 buffer.append(getRelativePositionOfPI(target)).append(']');
    513                 break;
    514             case Node.DOCUMENT_NODE :
    515                 // That'll be empty
    516         }
    517         return buffer.toString();
    518     }
    519
    520     private String escape(String string) {
    521         int index = string.indexOf('\'');
    522         while (index != -1) {
    523             string =
    524                 string.substring(0, index)
    525                     + "&apos;"
    526                     + string.substring(index + 1);
    527             index = string.indexOf('\'');
    528         }
    529         index = string.indexOf('\"');
    530         while (index != -1) {
    531             string =
    532                 string.substring(0, index)
    533                     + "&quot;"
    534                     + string.substring(index + 1);
    535             index = string.indexOf('\"');
    536         }
    537         return string;
    538     }
    539
    540     private int getRelativePositionByName() {
    541         int count = 1;
    542         Node n = node.getPreviousSibling();
    543         while (n != null) {
    544             if (n.getNodeType() == Node.ELEMENT_NODE) {
    545                 String nm = n.getNodeName();
    546                 if (nm.equals(node.getNodeName())) {
    547                     count++;
    548                 }
    549             }
    550             n = n.getPreviousSibling();
    551         }
    552         return count;
    553     }
    554
    555     private int getRelativePositionOfElement() {
    556         int count = 1;
    557         Node n = node.getPreviousSibling();
    558         while (n != null) {
    559             if (n.getNodeType() == Node.ELEMENT_NODE) {
    560                 count++;
    561             }
    562             n = n.getPreviousSibling();
    563         }
    564         return count;
    565     }
    566
    567     private int getRelativePositionOfTextNode() {
    568         int count = 1;
    569         Node n = node.getPreviousSibling();
    570         while (n != null) {
    571             if (n.getNodeType() == Node.TEXT_NODE
    572                 || n.getNodeType() == Node.CDATA_SECTION_NODE) {
    573                 count++;
    574             }
    575             n = n.getPreviousSibling();
    576         }
    577         return count;
    578     }
    579
    580     private int getRelativePositionOfPI(String target) {
    581         int count = 1;
    582         Node n = node.getPreviousSibling();
    583         while (n != null) {
    584             if (n.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE
    585                 && ((ProcessingInstruction) n).getTarget().equals(target)) {
    586                 count++;
    587             }
    588             n = n.getPreviousSibling();
    589         }
    590         return count;
    591     }
    592
    593     public int hashCode() {
    594         return System.identityHashCode(node);
    595     }
    596
    597     public boolean equals(Object object) {
    598         if (object == this) {
    599             return true;
    600         }
    601
    602         if (!(object instanceof DOMNodePointer)) {
    603             return false;
    604         }
    605
    606         DOMNodePointer other = (DOMNodePointer) object;
    607         return node == other.node;
    608     }
    609
    610     public static String getPrefix(Node node) {
    611         String prefix = node.getPrefix();
    612         if (prefix != null) {
    613             return prefix;
    614         }
    615
    616         String name = node.getNodeName();
    617         int index = name.lastIndexOf(':');
    618         if (index == -1) {
    619             return null;
    620         }
    621
    622         return name.substring(0, index);
    623     }
    624
    625     public static String getLocalName(Node node) {
    626         String localName = node.getLocalName();
    627         if (localName != null) {
    628             return localName;
    629         }
    630
    631         String name = node.getNodeName();
    632         int index = name.lastIndexOf(':');
    633         if (index == -1) {
    634             return name;
    635         }
    636
    637         return name.substring(index + 1);
    638     }
    639
    640     public static String getNamespaceURI(Node node) {
    641         if (node instanceof Document) {
    642             node = ((Document) node).getDocumentElement();
    643         }
    644
    645         Element element = (Element) node;
    646
    647         String uri = element.getNamespaceURI();
    648         if (uri != null) {
    649             return uri;
    650         }
    651
    652         String qname;
    653         String prefix = getPrefix(node);
    654         if (prefix == null) {
    655             qname = "xmlns";
    656         }
    657         else {
    658             qname = "xmlns:" + prefix;
    659         }
    660
    661         Node aNode = node;
    662         while (aNode != null) {
    663             if (aNode.getNodeType() == Node.ELEMENT_NODE) {
    664                 Attr attr = ((Element) aNode).getAttributeNode(qname);
    665                 if (attr != null) {
    666                     return attr.getValue();
    667                 }
    668             }
    669             aNode = aNode.getParentNode();
    670         }
    671         return null;
    672     }
    673
    674     public Object getValue() {
    675         return stringValue(node);
    676     }
    677
    678     private String stringValue(Node node) {
    679         int nodeType = node.getNodeType();
    680         if (nodeType == Node.COMMENT_NODE) {
    681             String text = ((Comment) node).getData();
    682             return text == null ? "" : text.trim();
    683         }
    684         else if (
    685             nodeType == Node.TEXT_NODE
    686                 || nodeType == Node.CDATA_SECTION_NODE) {
    687             String text = node.getNodeValue();
    688             return text == null ? "" : text.trim();
    689         }
    690         else if (nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
    691             String text = ((ProcessingInstruction) node).getData();
    692             return text == null ? "" : text.trim();
    693         }
    694         else {
    695             NodeList list = node.getChildNodes();
    696             StringBuffer buf = new StringBuffer(16);
    697             for (int i = 0; i < list.getLength(); i++) {
    698                 Node child = list.item(i);
    699                 if (child.getNodeType() == Node.TEXT_NODE) {
    700                     buf.append(child.getNodeValue());
    701                 }
    702                 else {
    703                     buf.append(stringValue(child));
    704                 }
    705             }
    706             return buf.toString().trim();
    707         }
    708     }
    709
    710     /***
    711      * Locates a node by ID.
    712      */
    713     public Pointer getPointerByID(JXPathContext context, String id) {
    714         Document document;
    715         if (node.getNodeType() == Node.DOCUMENT_NODE) {
    716             document = (Document) node;
    717         }
    718         else {
    719             document = node.getOwnerDocument();
    720         }
    721         Element element = document.getElementById(id);
    722         if (element != null) {
    723             return new DOMNodePointer(element, getLocale(), id);
    724         }
    725         else {
    726             return new NullPointer(getLocale(), id);
    727         }
    728     }
    729
    730     private AbstractFactory getAbstractFactory(JXPathContext context) {
    731         AbstractFactory factory = context.getFactory();
    732         if (factory == null) {
    733             throw new JXPathException(
    734                 "Factory is not set on the JXPathContext - "
    735                     + "cannot create path: "
    736                     + asPath());
    737         }
    738         return factory;
    739     }
    740
    741     public int compareChildNodePointers(
    742             NodePointer pointer1, NodePointer pointer2)
    743     {
    744         Node node1 = (Node) pointer1.getBaseValue();
    745         Node node2 = (Node) pointer2.getBaseValue();
    746         if (node1 == node2) {
    747             return 0;
    748         }
    749
    750         int t1 = node1.getNodeType();
    751         int t2 = node2.getNodeType();
    752         if (t1 == Node.ATTRIBUTE_NODE && t2 != Node.ATTRIBUTE_NODE) {
    753             return -1;
    754         }
    755         else if (t1 != Node.ATTRIBUTE_NODE && t2 == Node.ATTRIBUTE_NODE) {
    756             return 1;
    757         }
    758         else if (t1 == Node.ATTRIBUTE_NODE && t2 == Node.ATTRIBUTE_NODE) {
    759             NamedNodeMap map = ((Node) getNode()).getAttributes();
    760             int length = map.getLength();
    761             for (int i = 0; i < length; i++) {
    762                 Node n = map.item(i);
    763                 if (n == node1) {
    764                     return -1;
    765                 }
    766                 else if (n == node2) {
    767                     return 1;
    768                 }
    769             }
    770             return 0; // Should not happen
    771         }
    772
    773         Node current = node.getFirstChild();
    774         while (current != null) {
    775             if (current == node1) {
    776                 return -1;
    777             }
    778             else if (current == node2) {
    779                 return 1;
    780             }
    781             current = current.getNextSibling();
    782         }
    783
    784         return 0;
    785     }
    786 }