Clover coverage report -
Coverage timestamp: Sun Nov 1 2009 23:08:24 UTC
file stats: LOC: 1,349   Methods: 70
NCLOC: 638   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
NodeImpl.java 64% 61.2% 54.3% 61.2%
coverage coverage
 1    /*
 2    * Licensed to the Apache Software Foundation (ASF) under one or more
 3    * contributor license agreements. See the NOTICE file distributed with
 4    * this work for additional information regarding copyright ownership.
 5    * The ASF licenses this file to You under the Apache License, Version 2.0
 6    * (the "License"); you may not use this file except in compliance with
 7    * the License. You may obtain a copy of the License at
 8    *
 9    * http://www.apache.org/licenses/LICENSE-2.0
 10    *
 11    * Unless required by applicable law or agreed to in writing, software
 12    * distributed under the License is distributed on an "AS IS" BASIS,
 13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14    * See the License for the specific language governing permissions and
 15    * limitations under the License.
 16    *
 17    * $Id: NodeImpl.java 632207 2008-02-29 02:13:21Z natalia $
 18    */
 19   
 20    package org.apache.xindice.xml.dom;
 21   
 22    import org.apache.commons.logging.Log;
 23    import org.apache.commons.logging.LogFactory;
 24    import org.apache.xindice.util.StringUtilities;
 25    import org.apache.xindice.xml.NodeSource;
 26    import org.apache.xindice.xml.TextWriter;
 27   
 28    import org.w3c.dom.Attr;
 29    import org.w3c.dom.DOMException;
 30    import org.w3c.dom.Document;
 31    import org.w3c.dom.Element;
 32    import org.w3c.dom.NamedNodeMap;
 33    import org.w3c.dom.Node;
 34    import org.w3c.dom.NodeList;
 35    import org.w3c.dom.UserDataHandler;
 36   
 37    import java.util.ArrayList;
 38    import java.util.HashMap;
 39    import java.util.HashSet;
 40    import java.util.Iterator;
 41    import java.util.Map;
 42    import java.util.Set;
 43   
 44    /**
 45    * NodeImpl implements the foundation of the Xindice compressed DOM.
 46    *
 47    * @version $Revision: 632207 $, $Date: 2008-02-29 02:13:21 +0000 (Fri, 29 Feb 2008) $
 48    */
 49    public abstract class NodeImpl implements CompressedNode, DBNode {
 50   
 51    private static final Log log = LogFactory.getLog(NodeImpl.class);
 52   
 53    public static final String XMLNS_PREFIX = "xmlns";
 54    public static final String XML_PREFIX = "xml";
 55   
 56    public static final String OBJECT_NS = "http://xml.apache.org/xindice/XMLObject";
 57    public static final String OBJECT_HREF = "href";
 58    public static final String OBJECT_TYPE = "type";
 59   
 60    public static final String TYPE_CONTENT = "content";
 61    public static final String TYPE_REPLACE = "replace";
 62    public static final String TYPE_INSERT = "insert";
 63    public static final String TYPE_APPEND = "append";
 64   
 65   
 66    public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
 67    public static final String XML_URI = "http://www.w3.org/XML/1998/namespace";
 68   
 69    // Static Exception Instances
 70    public static final DOMException EX_NO_MODIFICATION_ALLOWED =
 71    new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "This Node Is Read-Only");
 72   
 73    public static final DOMException EX_INUSE_ATTRIBUTE =
 74    new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, "This Attribute Belongs To Another Element");
 75   
 76    public static final DOMException EX_WRONG_DOCUMENT =
 77    new DOMException(DOMException.WRONG_DOCUMENT_ERR, "This Attribute Belongs To Another Document");
 78   
 79    public static final DOMException EX_NOT_FOUND =
 80    new DOMException(DOMException.NOT_FOUND_ERR, "This Node Does Not Belong To This Element");
 81   
 82    public static final DOMException EX_HIERARCHY_REQUEST =
 83    new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "This Node Cannot Contain This Child");
 84   
 85    public static final DOMException EX_NO_DATA_ALLOWED =
 86    new DOMException(DOMException.NO_DATA_ALLOWED_ERR, "This Node Has No Value");
 87   
 88    public static final DOMException EX_INVALID_STATE =
 89    new DOMException(DOMException.INVALID_STATE_ERR, "NodeIterator Has Been Detached");
 90   
 91    public static final DOMException EX_DOMSTRING_SIZE =
 92    new DOMException(DOMException.DOMSTRING_SIZE_ERR, "String Too Large For Type");
 93   
 94    public static final DOMException EX_INDEX_SIZE =
 95    new DOMException(DOMException.INDEX_SIZE_ERR, "Index Out Of Bounds");
 96   
 97    // DocumentPosition flags
 98    /**
 99    * The two nodes are disconnected. Order between disconnected nodes is
 100    * always implementation-specific.
 101    */
 102    public static final short DOCUMENT_POSITION_DISCONNECTED = 0x01;
 103   
 104    /**
 105    * The second node precedes the reference node.
 106    */
 107    public static final short DOCUMENT_POSITION_PRECEDING = 0x02;
 108   
 109    /**
 110    * The node follows the reference node.
 111    */
 112    public static final short DOCUMENT_POSITION_FOLLOWING = 0x04;
 113   
 114    /**
 115    * The node contains the reference node. A node which contains is always
 116    * preceding, too.
 117    */
 118    public static final short DOCUMENT_POSITION_CONTAINS = 0x08;
 119   
 120    /**
 121    * The node is contained by the reference node. A node which is contained
 122    * is always following, too.
 123    */
 124    public static final short DOCUMENT_POSITION_CONTAINED_BY = 0x10;
 125   
 126    /**
 127    * The determination of preceding versus following is
 128    * implementation-specific.
 129    */
 130    public static final short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
 131   
 132   
 133    // Serialization Stuff
 134    protected NodeSource source;
 135    protected byte[] data;
 136    protected int pos;
 137    protected int len;
 138   
 139    protected boolean loaded; // Have the definitions for this node been loaded?
 140    protected boolean dirty; // Has this node been modified?
 141   
 142    // DOM Level 1 Stuff
 143    protected NodeImpl parentNode;
 144    protected String nodeName;
 145    protected String nodeValue;
 146   
 147    // DOM Level 2 Stuff
 148    protected Document ownerDocument; // The Document object associated with this node
 149    protected String nsURI; // Just for caching so we don't have to walk the tree
 150   
 151    // DOM Level 3 Stuff
 152    protected HashMap userData;
 153    protected HashMap handlers;
 154    protected Object key;
 155   
 156    /**
 157    * Create empty node
 158    */
 159  0 public NodeImpl() {
 160    }
 161   
 162    /**
 163    * Create node from compressed data
 164    *
 165    * @param parent the parent node
 166    * @param data compressed document data byte array
 167    * @param pos offset in the data byte array
 168    * @param len length of node's data
 169    */
 170  350638 public NodeImpl(NodeImpl parent, byte[] data, int pos, int len) {
 171  350638 this.parentNode = parent;
 172  350638 this.data = data;
 173  350638 this.pos = pos;
 174  350638 this.len = len;
 175   
 176  350638 if (parent == null) {
 177  43811 ownerDocument = null;
 178  306827 } else if (parent.getNodeType() == DOCUMENT_TYPE_NODE) {
 179  0 this.ownerDocument = (Document) parent;
 180    } else {
 181  306827 this.ownerDocument = parent.getOwnerDocument();
 182    }
 183    }
 184   
 185  739034 public NodeImpl(NodeImpl parent, boolean dirty) {
 186  739034 this.parentNode = parent;
 187   
 188  739034 if (parent == null) {
 189  30213 this.ownerDocument = null;
 190  708821 } else if (parent.getNodeType() == DOCUMENT_TYPE_NODE) {
 191  0 this.ownerDocument = (Document) parent;
 192    } else {
 193  708821 this.ownerDocument = parent.getOwnerDocument();
 194    }
 195   
 196  739034 if (dirty) {
 197  440280 setDirty();
 198    }
 199    }
 200   
 201  0 public final boolean isLoaded() {
 202  0 return loaded;
 203    }
 204   
 205  0 protected void checkLoaded() {
 206  0 if (!loaded) {
 207  0 loaded = true;
 208    }
 209    }
 210   
 211  0 public void load() {
 212  0 checkLoaded();
 213    }
 214   
 215  0 public void unload() {
 216  0 if (loaded) {
 217  0 loaded = false;
 218    }
 219    }
 220   
 221  0 public short getSymbolID() {
 222  0 return -1;
 223    }
 224   
 225  902128 public final void checkReadOnly() throws DOMException {
 226  902128 DocumentImpl doc = (DocumentImpl) getOwnerDocument();
 227  902132 if (doc != null && doc.isReadOnly()) {
 228  0 throw EX_NO_MODIFICATION_ALLOWED;
 229    }
 230    }
 231   
 232  0 public final boolean isDefined() {
 233  0 return data != null;
 234    }
 235   
 236  18 public final boolean isDirty() {
 237  18 return dirty;
 238    }
 239   
 240  2711072 public final void setDirty() {
 241  2711071 dirty = true;
 242  2711071 if (parentNode != null) {
 243  1559398 parentNode.setDirty();
 244    }
 245    }
 246   
 247  849 public byte[] getDataBytes() {
 248  849 return data;
 249    }
 250   
 251  0 public void setDataBytes(byte[] data, int pos, int len) {
 252  0 this.data = data;
 253  0 this.pos = pos;
 254  0 this.len = len;
 255    }
 256   
 257  0 public void setDataBytes(byte[] data) {
 258  0 this.data = data;
 259    }
 260   
 261  0 public int getDataPos() {
 262  0 return pos;
 263    }
 264   
 265  0 public void setDataPos(int pos) {
 266  0 this.pos = pos;
 267    }
 268   
 269  0 public int getDataLen() {
 270  0 return len;
 271    }
 272   
 273  0 public void setDataLen(int len) {
 274  0 this.len = len;
 275    }
 276   
 277  2 public final void setNodeName(String nodeName) {
 278  2 checkLoaded();
 279  2 this.nodeName = nodeName;
 280  2 setDirty();
 281    }
 282   
 283  410035 public final void setParentNode(NodeImpl parentNode) {
 284  410035 this.parentNode = parentNode;
 285    }
 286   
 287  0 protected Node getPreviousSibling(Node node) {
 288  0 return null;
 289    }
 290   
 291  0 protected Node getNextSibling(Node node) {
 292  0 return null;
 293    }
 294   
 295  27623 public final void setSource(NodeSource source) {
 296  27623 this.source = source;
 297    }
 298   
 299  11048 public final NodeSource getSource() {
 300  11048 if (source == null && parentNode != null) {
 301  1103 return parentNode.getSource();
 302    } else {
 303  9945 return source;
 304    }
 305    }
 306   
 307  0 public void expandSource() {
 308    }
 309   
 310    // Some DOM Level 1 Core Methods
 311   
 312    /**
 313    * A code representing the type of the underlying object, as defined above.
 314    */
 315    public abstract short getNodeType();
 316   
 317    /**
 318    * The last child of this node. If there is no such node, this returns
 319    * <code>null</code>.
 320    */
 321  0 public Node getLastChild() {
 322  0 return null;
 323    }
 324   
 325    /**
 326    * The node immediately preceding this node. If there is no such node, this
 327    * returns <code>null</code>.
 328    */
 329  2 public final Node getPreviousSibling() {
 330  2 return parentNode != null ? parentNode.getPreviousSibling(this)
 331    : null;
 332    }
 333   
 334    /**
 335    * The node immediately following this node. If there is no such node, this
 336    * returns <code>null</code>.
 337    */
 338  526502 public final Node getNextSibling() {
 339  526502 return parentNode != null ? parentNode.getNextSibling(this)
 340    : null;
 341    }
 342   
 343    /**
 344    * A <code>NamedNodeMap</code> containing the attributes of this node (if it
 345    * is an <code>Element</code>) or <code>null</code> otherwise.
 346    */
 347  0 public NamedNodeMap getAttributes() {
 348  0 return null;
 349    }
 350   
 351  3 public boolean hasAttributes() {
 352  3 return false;
 353    }
 354   
 355    /**
 356    * The <code>Document</code> object associated with this node. This is also
 357    * the <code>Document</code> object used to create new nodes. When this
 358    * node is a <code>Document</code> this is <code>null</code>.
 359    */
 360  3285796 public final Document getOwnerDocument() {
 361  3285797 if (getNodeType() == Node.DOCUMENT_NODE) {
 362  552038 return (Document) this;
 363    }
 364   
 365  2733785 return ownerDocument;
 366    }
 367   
 368    /**
 369    * Inserts the node <code>newChild</code> before the existing child node
 370    * <code>refChild</code>. If <code>refChild</code> is <code>null</code>,
 371    * insert <code>newChild</code> at the end of the list of children.
 372    * <br>If <code>newChild</code> is a <code>DocumentFragment</code> object,
 373    * all of its children are inserted, in the same order, before
 374    * <code>refChild</code>. If the <code>newChild</code> is already in the
 375    * tree, it is first removed.
 376    * @param newChild The node to insert.
 377    * @param refChild The reference node, i.e., the node before which the new
 378    * node must be inserted.
 379    * @return The node being inserted.
 380    * @exception DOMException <ul>
 381    * <li>HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
 382    * allow children of the type of the <code>newChild</code> node, or if
 383    * the node to insert is one of this node's ancestors.
 384    * <li>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
 385    * from a different document than the one that created this node.
 386    * <li>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 387    * <li>NOT_FOUND_ERR: Raised if <code>refChild</code> is not a child of
 388    * this node.
 389    * </ul>
 390    */
 391  0 public Node insertBefore(Node newChild, Node refChild) throws DOMException {
 392  0 throw EX_HIERARCHY_REQUEST;
 393    }
 394   
 395    /**
 396    * A <code>NodeList</code> that contains all children of this node. If there
 397    * are no children, this is a <code>NodeList</code> containing no nodes.
 398    * The content of the returned <code>NodeList</code> is "live" in the sense
 399    * that, for instance, changes to the children of the node object that
 400    * it was created from are immediately reflected in the nodes returned by
 401    * the <code>NodeList</code> accessors; it is not a static snapshot of the
 402    * content of the node. This is true for every <code>NodeList</code>,
 403    * including the ones returned by the <code>getElementsByTagName</code>
 404    * method.
 405    */
 406  1788 public NodeList getChildNodes() {
 407  1788 return new NodeListImpl(this);
 408    }
 409   
 410    /**
 411    * The value of this node, depending on its type; see the table above.
 412    * When it is defined to be <code>null</code> , setting it has no effect.
 413    * @exception DOMException <ul>
 414    * <li>NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
 415    * <li>DOMSTRING_SIZE_ERR: Raised when it would return more characters
 416    * than fit in a <code>DOMString</code> variable on the implementation
 417    * platform.
 418    * </ul>
 419    */
 420  0 public void setNodeValue(String nodeValue) throws DOMException {
 421  0 throw EX_NO_DATA_ALLOWED;
 422    }
 423   
 424    /**
 425    * The parent of this node. All nodes, except <code>Document</code>,
 426    * <code>DocumentFragment</code>, and <code>Attr</code> may have a parent.
 427    * However, if a node has just been created and not yet added to the tree,
 428    * or if it has been removed from the tree, this is <code>null</code>.
 429    */
 430  612936 public final Node getParentNode() {
 431  612936 return parentNode;
 432    }
 433   
 434    /**
 435    * The first child of this node. If there is no such node, this returns
 436    * <code>null</code>.
 437    */
 438  2217 public Node getFirstChild() {
 439  2217 return null;
 440    }
 441   
 442    /**
 443    * The name of this node, depending on its type; see the table above.
 444    */
 445  1537838 public String getNodeName() {
 446  1537838 checkLoaded();
 447  1537834 return nodeName;
 448    }
 449   
 450    /**
 451    * The value of this node, depending on its type; see the table above.
 452    * @exception DOMException <ul>
 453    * <li>NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
 454    * <li>DOMSTRING_SIZE_ERR: Raised when it would return more characters than
 455    * fit in a <code>DOMString</code> variable on the implementation
 456    * platform.
 457    * </ul>
 458    */
 459  739705 public String getNodeValue() throws DOMException {
 460  739705 checkLoaded();
 461  739704 return nodeValue;
 462    }
 463   
 464    /**
 465    * Replaces the child node <code>oldChild</code> with <code>newChild</code>
 466    * in the list of children, and returns the <code>oldChild</code> node. If
 467    * the <code>newChild</code> is already in the tree, it is first removed.
 468    * @param newChild The new node to put in the child list.
 469    * @param oldChild The node being replaced in the list.
 470    * @return The node replaced.
 471    * @exception DOMException
 472    * HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
 473    * allow children of the type of the <code>newChild</code> node, or it
 474    * the node to put in is one of this node's ancestors.
 475    * <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
 476    * from a different document than the one that created this node.
 477    * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 478    * <br>NOT_FOUND_ERR: Raised if <code>oldChild</code> is not a child of
 479    * this node.
 480    */
 481  0 public synchronized Node replaceChild(Node newChild, Node oldChild) throws DOMException {
 482  0 throw EX_HIERARCHY_REQUEST;
 483    }
 484   
 485    /**
 486    * Removes the child node indicated by <code>oldChild</code> from the list
 487    * of children, and returns it.
 488    * @param oldChild The node being removed.
 489    * @return The node removed.
 490    * @exception DOMException
 491    * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 492    * <br>NOT_FOUND_ERR: Raised if <code>oldChild</code> is not a child of
 493    * this node.
 494    */
 495  0 public synchronized Node removeChild(Node oldChild) throws DOMException {
 496  0 throw EX_NOT_FOUND;
 497    }
 498   
 499    /**
 500    * Adds the node <code>newChild</code> to the end of the list of children of
 501    * this node. If the <code>newChild</code> is already in the tree, it is
 502    * first removed.
 503    * @param newChild The node to add.If it is a <code>DocumentFragment</code>
 504    * object, the entire contents of the document fragment are moved into
 505    * the child list of this node
 506    * @return The node added.
 507    * @exception DOMException
 508    * HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
 509    * allow children of the type of the <code>newChild</code> node, or if
 510    * the node to append is one of this node's ancestors.
 511    * <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
 512    * from a different document than the one that created this node.
 513    * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 514    */
 515  0 public synchronized Node appendChild(Node newChild) throws DOMException {
 516  0 throw EX_HIERARCHY_REQUEST;
 517    }
 518   
 519    /**
 520    * This is a convenience method to allow easy determination of whether a
 521    * node has any children.
 522    * @return <code>true</code> if the node has any children,
 523    * <code>false</code> if the node has no children.
 524    */
 525  193944 public boolean hasChildNodes() {
 526  193944 return false;
 527    }
 528   
 529    /**
 530    * Returns a duplicate of this node, i.e., serves as a generic copy
 531    * constructor for nodes. The duplicate node has no parent (
 532    * <code>parentNode</code> returns <code>null</code>.).
 533    * <br>Cloning an <code>Element</code> copies all attributes and their
 534    * values, including those generated by the XML processor to represent
 535    * defaulted attributes, but this method does not copy any text it contains
 536    * unless it is a deep clone, since the text is contained in a child
 537    * <code>Text</code> node. Cloning any other type of node simply returns a
 538    * copy of this node.
 539    * @param deep If <code>true</code>, recursively clone the subtree under the
 540    * specified node; if <code>false</code>, clone only the node itself (and
 541    * its attributes, if it is an <code>Element</code>).
 542    * @return The duplicate node.
 543    */
 544  21 public final Node cloneNode(boolean deep) {
 545  21 return cloneNode(deep, true);
 546    }
 547   
 548  21 protected final synchronized Node cloneNode(boolean deep, boolean invokeHandler) {
 549  21 DocumentImpl doc = (DocumentImpl) getOwnerDocument();
 550   
 551    // compressed documents
 552  21 if (deep && this.data != null) {
 553  2 byte[] data = this.data;
 554  2 int pos = this.pos;
 555  2 int len = this.len;
 556   
 557  2 if (dirty) {
 558  0 data = DOMCompressor.compress(this, doc.getSymbols());
 559  0 pos = 0;
 560  0 len = data.length;
 561    }
 562   
 563  2 NodeImpl newNode = null;
 564  2 switch (getNodeType()) {
 565   
 566  0 case Node.ATTRIBUTE_NODE:
 567  0 newNode = new AttrImpl(this, true);
 568  0 newNode.setNodeName(getNodeName());
 569  0 newNode.setNodeValue(getNodeValue());
 570  0 break;
 571   
 572  0 case Node.CDATA_SECTION_NODE:
 573  0 newNode = new CDATASectionImpl(this, data, pos, len);
 574  0 break;
 575   
 576  0 case Node.COMMENT_NODE:
 577  0 newNode = new CommentImpl(this, data, pos, len);
 578  0 break;
 579   
 580  0 case Node.DOCUMENT_FRAGMENT_NODE:
 581  0 DocumentFragmentImpl df = new DocumentFragmentImpl(this);
 582  0 NodeList nl = getChildNodes();
 583  0 for (int i = 0; i < nl.getLength(); i++) {
 584  0 df.appendChild(nl.item(i).cloneNode(deep));
 585    }
 586  0 newNode = df;
 587  0 break;
 588   
 589  2 case Node.ELEMENT_NODE:
 590  2 newNode = new ElementImpl(this, data, pos, len);
 591  2 newNode.setNodeName(getNodeName());
 592  2 break;
 593   
 594  0 case Node.ENTITY_REFERENCE_NODE:
 595  0 newNode = new EntityReferenceImpl(this, data, pos, len);
 596  0 break;
 597   
 598  0 case Node.NOTATION_NODE:
 599  0 newNode = new NotationImpl(this, data, pos, len);
 600  0 break;
 601   
 602  0 case Node.PROCESSING_INSTRUCTION_NODE:
 603  0 newNode = new ProcessingInstructionImpl(this, data, pos, len);
 604  0 break;
 605   
 606  0 case Node.TEXT_NODE:
 607  0 newNode = new TextImpl(this, data, pos, len);
 608  0 break;
 609   
 610  0 default:
 611  0 if (log.isWarnEnabled()) {
 612  0 log.warn("invalid node type : " + getNodeType());
 613    }
 614    }
 615   
 616  2 if (newNode != null) {
 617    // cloned node must not have a parent
 618  2 newNode.parentNode = null;
 619  2 if (invokeHandler) {
 620  2 invokeHandlers(UserDataHandler.NODE_CLONED, this, newNode);
 621    }
 622  2 return newNode;
 623    }
 624    }
 625   
 626  19 checkLoaded();
 627  19 NodeImpl node = null;
 628  19 switch (getNodeType()) {
 629  0 case Node.ATTRIBUTE_NODE:
 630  0 AttrImpl attr = (AttrImpl) doc.createAttribute(nodeName);
 631  0 attr.setValue(nodeValue);
 632  0 node = attr;
 633  0 break;
 634   
 635  0 case Node.CDATA_SECTION_NODE:
 636  0 node = (NodeImpl) doc.createCDATASection(nodeValue);
 637  0 break;
 638   
 639  0 case Node.COMMENT_NODE:
 640  0 node = (NodeImpl) doc.createComment(nodeValue);
 641  0 break;
 642   
 643  0 case Node.DOCUMENT_FRAGMENT_NODE:
 644  0 node = (NodeImpl) doc.createDocumentFragment();
 645  0 break;
 646   
 647  19 case Node.ELEMENT_NODE:
 648  19 ElementImpl elem = (ElementImpl) doc.createElement(nodeName);
 649  19 NamedNodeMap attrs = getAttributes();
 650  19 int size = attrs.getLength();
 651  19 for (int i = 0; i < size; i++) {
 652  11 Attr a = (Attr) attrs.item(i);
 653  11 elem.setAttribute(a.getName(), a.getValue());
 654    }
 655   
 656  19 if (getPrefix() != null) {
 657  2 elem.nsURI = lookupNamespaceURI(getPrefix());
 658    } else {
 659  17 elem.nsURI = lookupDefaultNamespaceURI();
 660    }
 661   
 662  19 node = elem;
 663  19 break;
 664   
 665  0 case Node.ENTITY_REFERENCE_NODE:
 666  0 node = (NodeImpl) doc.createEntityReference(nodeValue);
 667  0 break;
 668   
 669  0 case Node.PROCESSING_INSTRUCTION_NODE:
 670  0 node = (NodeImpl) doc.createProcessingInstruction(nodeName, nodeValue);
 671  0 break;
 672   
 673  0 case Node.TEXT_NODE:
 674  0 node = (NodeImpl) doc.createTextNode(nodeValue);
 675  0 break;
 676   
 677  0 default:
 678  0 if (log.isWarnEnabled()) {
 679  0 log.warn("invalid node type : " + getNodeType());
 680    }
 681  0 break;
 682    }
 683   
 684  19 if (node != null && deep) {
 685  19 NodeList list = getChildNodes();
 686  19 for (int i = 0; i < list.getLength(); i++) {
 687  2 node.appendChild(list.item(i).cloneNode(deep));
 688    }
 689    }
 690   
 691  19 if (node != null && invokeHandler) {
 692  19 node.parentNode = null;
 693  19 invokeHandlers(UserDataHandler.NODE_CLONED, this, node);
 694    }
 695   
 696  19 return node;
 697    }
 698   
 699    // Some DOM Level 2 Core Methods
 700   
 701    /**
 702    * Puts all <code>Text</code> nodes in the full depth of the sub-tree
 703    * underneath this <code>Node</code> , including attribute nodes, into a
 704    * "normal" form where only markup (e.g., tags, comments, processing
 705    * instructions, CDATA sections, and entity references) separates
 706    * <code>Text</code> nodes, i.e., there are neither adjacent
 707    * <code>Text</code> nodes nor empty <code>Text</code> nodes. This can be
 708    * used to ensure that the DOM view of a document is the same as if it
 709    * were saved and re-loaded, and is useful when operations (such as
 710    * XPointer lookups) that depend on a particular document tree structure
 711    * are to be used. In cases where the document contains
 712    * <code>CDATASections</code> , the normalize operation alone may not be
 713    * sufficient, since XPointers do not differentiate between
 714    * <code>Text</code> nodes and <code>CDATASection</code> nodes.
 715    * @since DOM Level 2
 716    */
 717  0 public void normalize() {
 718    }
 719   
 720    /**
 721    * Tests whether the DOM implementation implements a specific feature and
 722    * that feature is supported by this node.
 723    * @param feature The name of the feature to test. This is the same name
 724    * which can be passed to the method <code>hasFeature</code> on
 725    * <code>DOMImplementation</code> .
 726    * @param version This is the version number of the feature to test. In
 727    * Level 2, version 1, this is the string "2.0". If the version is not
 728    * specified, supporting any version of the feature will cause the
 729    * method to return <code>true</code> .
 730    * @return Returns <code>true</code> if the specified feature is supported
 731    * on this node, <code>false</code> otherwise.
 732    * @since DOM Level 2
 733    */
 734  0 public final boolean isSupported(String feature, String version) {
 735  0 return DOMImplementationImpl.HasFeature(feature, version);
 736    }
 737   
 738    /**
 739    * The namespace URI of this node, or <code>null</code> if it is
 740    * unspecified.
 741    * <br> This is not a computed value that is the result of a namespace
 742    * lookup based on an examination of the namespace declarations in scope.
 743    * It is merely the namespace URI given at creation time.
 744    * <br> For nodes of any type other than <code>ELEMENT_NODE</code> and
 745    * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1
 746    * method, such as <code>createElement</code> from the
 747    * <code>Document</code> interface, this is always <code>null</code> .
 748    * Per the Namespaces in XML Specification an attribute does not
 749    * inherit its namespace from the element it is attached to. If an
 750    * attribute is not explicitly given a namespace, it simply has no
 751    * namespace.
 752    * @since DOM Level 2
 753    */
 754  811610 public final String getNamespaceURI() {
 755   
 756  811610 short nodeType = getNodeType();
 757  811612 if (nodeType == Node.ATTRIBUTE_NODE) {
 758  215819 if (nodeName.equals(XMLNS_PREFIX) || nodeName.startsWith(XMLNS_PREFIX + ":")) {
 759    // defined by specification, must not be declared
 760  5434 return XMLNS_URI;
 761  210387 } else if (nodeName.startsWith(XML_PREFIX + ":")) {
 762    // defined by specification, does not need to be declared
 763  4 return XML_URI;
 764    }
 765    }
 766  806174 if (nsURI != null) {
 767  67 return nsURI;
 768    }
 769   
 770  806107 if (!(nodeType == Node.ELEMENT_NODE || nodeType == Node.ATTRIBUTE_NODE)) {
 771  225139 return null;
 772    }
 773   
 774  580968 String prefix = getPrefix();
 775  580968 return prefix != null ? lookupNamespaceURI(prefix)
 776    : lookupDefaultNamespaceURI();
 777    }
 778   
 779    /**
 780    * The namespace prefix of this node, or <code>null</code> if it is
 781    * unspecified.
 782    * <br> Note that setting this attribute, when permitted, changes the
 783    * <code>nodeName</code> attribute, which holds the qualified name , as
 784    * well as the <code>tagName</code> and <code>name</code> attributes of
 785    * the <code>Element</code> and <code>Attr</code> interfaces, when
 786    * applicable.
 787    * <br> Note also that changing the prefix of an attribute that is known to
 788    * have a default value, does not make a new attribute with the default
 789    * value and the original prefix appear, since the
 790    * <code>namespaceURI</code> and <code>localName</code> do not change.
 791    * @exception DOMException
 792    * INVALID_CHARACTER_ERR: Raised if the specified prefix contains an
 793    * illegal character.
 794    * <br> NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 795    * <br> NAMESPACE_ERR: Raised if the specified <code>prefix</code> is
 796    * malformed, if the <code>namespaceURI</code> of this node is
 797    * <code>null</code> , if the specified prefix is "xml" and the
 798    * <code>namespaceURI</code> of this node is different from
 799    * "http://www.w3.org/XML/1998/namespace", if this node is an attribute
 800    * and the specified prefix is "xmlns" and the <code>namespaceURI</code>
 801    * of this node is different from "http://www.w3.org/2000/xmlns/", or
 802    * if this node is an attribute and the <code>qualifiedName</code> of
 803    * this node is "xmlns" .
 804    * @since DOM Level 2
 805    */
 806  1090250 public final String getPrefix() {
 807  1090250 short nodeType = getNodeType();
 808  1090250 if (!(nodeType == Node.ELEMENT_NODE || nodeType == Node.ATTRIBUTE_NODE)) {
 809  0 return null;
 810    }
 811  1090250 checkLoaded();
 812  1090250 int idx = nodeName.indexOf(':');
 813  1090250 return idx != -1 ? nodeName.substring(0, idx)
 814    : null;
 815    }
 816   
 817    /**
 818    * The namespace prefix of this node, or <code>null</code> if it is
 819    * unspecified.
 820    * <br> Note that setting this attribute, when permitted, changes the
 821    * <code>nodeName</code> attribute, which holds the qualified name , as
 822    * well as the <code>tagName</code> and <code>name</code> attributes of
 823    * the <code>Element</code> and <code>Attr</code> interfaces, when
 824    * applicable.
 825    * <br> Note also that changing the prefix of an attribute that is known to
 826    * have a default value, does not make a new attribute with the default
 827    * value and the original prefix appear, since the
 828    * <code>namespaceURI</code> and <code>localName</code> do not change.
 829    * @exception DOMException
 830    * INVALID_CHARACTER_ERR: Raised if the specified prefix contains an
 831    * illegal character.
 832    * <br> NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 833    * <br> NAMESPACE_ERR: Raised if the specified <code>prefix</code> is
 834    * malformed, if the <code>namespaceURI</code> of this node is
 835    * <code>null</code> , if the specified prefix is "xml" and the
 836    * <code>namespaceURI</code> of this node is different from
 837    * "http://www.w3.org/XML/1998/namespace", if this node is an attribute
 838    * and the specified prefix is "xmlns" and the <code>namespaceURI</code>
 839    * of this node is different from "http://www.w3.org/2000/xmlns/", or
 840    * if this node is an attribute and the <code>qualifiedName</code> of
 841    * this node is "xmlns" .
 842    * @since DOM Level 2
 843    */
 844  0 public final void setPrefix(String prefix) {
 845  0 short nodeType = getNodeType();
 846  0 if (!(nodeType == Node.ELEMENT_NODE || nodeType == Node.ATTRIBUTE_NODE)) {
 847  0 return;
 848    }
 849  0 checkReadOnly();
 850  0 String ln = getLocalName();
 851  0 if (prefix != null) {
 852  0 setNodeName(prefix + ':' + ln);
 853    } else {
 854  0 setNodeName(ln);
 855    }
 856    }
 857   
 858    /**
 859    * Returns the local part of the qualified name of this node.
 860    * <br> For nodes created with a DOM Level 1 method, such as
 861    * <code>createElement</code> from the <code>Document</code> interface,
 862    * it is <code>null</code> .
 863    * @since DOM Level 2
 864    */
 865  959512 public final String getLocalName() {
 866  959512 short nodeType = getNodeType();
 867  959512 if (!(nodeType == Node.ELEMENT_NODE || nodeType == Node.ATTRIBUTE_NODE)) {
 868  450278 return null;
 869    }
 870  509234 String prefix = getPrefix();
 871  509234 return prefix != null ? getNodeName().substring(prefix.length() + 1)
 872    : getNodeName();
 873    }
 874   
 875    /**
 876    * getDefaultNamespaceURI returns the default Namespace URI in this
 877    * scope.
 878    *
 879    * @return The URI (or null)
 880    */
 881  1279643 public final String lookupDefaultNamespaceURI() {
 882  1279644 if (getNodeType() != Node.ELEMENT_NODE) {
 883  560180 return null;
 884    }
 885   
 886  719464 String uri = ((Element) this).getAttribute(XMLNS_PREFIX);
 887  719465 if (uri != null && uri.length() > 0) {
 888  19767 return uri;
 889    }
 890   
 891  699698 return parentNode != null ? parentNode.lookupDefaultNamespaceURI()
 892    : null;
 893    }
 894   
 895    //
 896    // DOM Level 3 Implementation
 897    //
 898   
 899    /**
 900    * Returns whether this node is the same node as the given one.
 901    * @param other The node to test against.
 902    * @return Returns <code>true</code> if the nodes are the same,
 903    * <code>false</code> otherwise.
 904    * @since DOM Level 3
 905    */
 906  22 public final boolean isSameNode(Node other) {
 907  22 return this == other;
 908    }
 909   
 910    /**
 911    * Look up the prefix associated to the given namespace URI, starting from
 912    * this node. The default namespace declarations are ignored by this method.
 913    * @param namespaceURI The namespace URI to look for.
 914    * @return Returns the associated namespace prefix or <code>null</code>
 915    * if none is found.
 916    * @since DOM Level 3
 917    */
 918  0 public final String lookupPrefix(String namespaceURI) {
 919  0 if (getNodeType() == Node.ELEMENT_NODE) {
 920  0 NamedNodeMap map = getAttributes();
 921  0 int size = map.getLength();
 922  0 for (int i = 0; i < size; i++) {
 923  0 Attr attr = (Attr) map.item(i);
 924  0 String name = attr.getName();
 925  0 if (name.startsWith(XMLNS_PREFIX + ':')
 926    && attr.getValue().equals(namespaceURI)) {
 927  0 return name.substring(XMLNS_PREFIX.length() + 1);
 928    }
 929    }
 930    }
 931  0 return parentNode != null ? parentNode.lookupPrefix(namespaceURI) : null;
 932    }
 933   
 934    /**
 935    * Look up the namespace URI associated to the given prefix.
 936    *
 937    * Starts from this node.Name. May need to change depending on ending of the
 938    * relative namespace URI reference nightmare.
 939    * @param prefix The prefix to look for.
 940    * @return Returns the associated namespace URI or <code>null</code> if
 941    * none is found.
 942    * @since DOM Level 3
 943    */
 944  2017 public final String lookupNamespaceURI(String prefix) {
 945  2017 String uri = null;
 946  2017 if (getNodeType() == Node.ELEMENT_NODE) {
 947  1299 uri = ((Element) this).getAttribute(XMLNS_PREFIX + ':' + prefix);
 948    }
 949  2017 if (uri != null && uri.length() > 0) {
 950  1037 return uri;
 951    }
 952   
 953  980 return parentNode != null ? parentNode.lookupNamespaceURI(prefix) : null;
 954    }
 955   
 956    /**
 957    * This method allows to attach a user object to a Node. This object can
 958    * then be retreived using <code>getUserData</code>.Is this really worth
 959    * it?Could we live without a key?What happens if the node is cloned,
 960    * imported, adopted? Should some event mechanism be specified to notify
 961    * the application?What happens if the node is cloned?What should Object
 962    * be mapped to in ECMAScript? For IDL we need to define this type
 963    * somehow.
 964    * @param data The piece of data to attach to this node.
 965    * @param key The key to associate this data to.
 966    * @param handler user data handler
 967    * @return The object previously associated to this node and the given
 968    * key or <code>null</code>.
 969    * @since DOM Level 3
 970    */
 971  2 public final synchronized Object setUserData(String key, Object data, UserDataHandler handler) {
 972  2 if (userData == null) {
 973  2 userData = new HashMap();
 974    }
 975  2 if (handlers == null) {
 976  2 handlers = new HashMap();
 977    }
 978   
 979  2 Object oldData = userData.get(key);
 980  2 if (data != null) {
 981  2 userData.put(key, data);
 982  2 if (handler != null) {
 983  2 handlers.put(key, handler);
 984    }
 985    } else {
 986  0 userData.remove(key);
 987  0 handlers.remove(key);
 988    }
 989   
 990  2 return oldData;
 991    }
 992   
 993    /**
 994    * This method allows to retreive a user object previously attached to a
 995    * Node with <code>setUserData</code>.
 996    * @param key The key to look for.
 997    * @return The object associated to this node and the given key or
 998    * <code>null</code>.
 999    * @since DOM Level 3
 1000    */
 1001  0 public final synchronized Object getUserData(String key) {
 1002  0 if (userData == null) {
 1003  0 return null;
 1004    }
 1005  0 return userData.get(key);
 1006    }
 1007   
 1008  0 Node renameNode(String namespaceURI, String qualifiedName, String prefix) throws DOMException {
 1009  0 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Only Element and Attribute nodes can be renamed.");
 1010    }
 1011   
 1012    /**
 1013    * Not implemented yet.
 1014    *
 1015    * @return null
 1016    * @since DOM Level 3
 1017    */
 1018  0 public String getBaseURI() {
 1019  0 return null;
 1020    }
 1021   
 1022    /**
 1023    * This method returns the text content of this node and its descendants.
 1024    * No serialization is performed, the returned string does not contain any
 1025    * markup. No whitespace normalization is performed and the returned string
 1026    * does not contain the white spaces in element content (see the attribute
 1027    * Text.isElementContentWhitespace).
 1028    * <br>
 1029    * The string returned is made of the text content of this node depending on
 1030    * its type, as defined below:
 1031    * <table border=1><tr><th>Node type</th><th>Content</th></tr>
 1032    * <tr><td>ELEMENT_NODE, ATTRIBUTE_NODE, ENTITY_NODE, ENTITY_REFERENCE_NODE,
 1033    * DOCUMENT_FRAGMENT_NODE</td>
 1034    * <td>concatenation of the textContent attribute value of every child node,
 1035    * excluding COMMENT_NODE and PROCESSING_INSTRUCTION_NODE nodes. This is the
 1036    * empty string if the node has no children.</td></tr>
 1037    * <tr><td>TEXT_NODE, CDATA_SECTION_NODE, COMMENT_NODE,
 1038    * PROCESSING_INSTRUCTION_NODE</td><td>nodeValue</td></tr>
 1039    * <tr><td>DOCUMENT_NODE, DOCUMENT_TYPE_NODE, NOTATION_NODE</td>
 1040    * <td>null</td></tr></table>
 1041    * @return the text content of this node and its descendants
 1042    * @since DOM Level 3
 1043    */
 1044  203 public String getTextContent() {
 1045  203 return getNodeValue();
 1046    }
 1047   
 1048    /**
 1049    * This method changes the text content of this node. When it is defined to be
 1050    * null, setting it has no effect. On setting, any possible children this node
 1051    * may have are removed and, if it the new string is not empty or null, replaced
 1052    * by a single Text node containing the string this attribute is set to.
 1053    * No parsing is performed, the input string is taken as pure textual content.
 1054    * @param textContent new text content
 1055    * @throws DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
 1056    */
 1057  4 public void setTextContent(String textContent) throws DOMException {
 1058  4 checkReadOnly();
 1059  4 checkLoaded();
 1060  2 if (textContent == null || "".equals(textContent)) return;
 1061   
 1062    // remove all child nodes, if any
 1063  2 if (hasChildNodes()) {
 1064  2 int num = getChildNodes().getLength();
 1065  2 for (int i = 0; i < num; i++) {
 1066  8 removeChild(getChildNodes().item(0));
 1067    }
 1068    }
 1069   
 1070  2 appendChild(new TextImpl(this, textContent));
 1071    }
 1072   
 1073    /**
 1074    * Tests whether two nodes are equal.
 1075    * This method tests for equality of nodes, not sameness (i.e., whether
 1076    * the two nodes are references to the same object) which can be tested with
 1077    * <code>Node.isSameNode()</code>. All nodes that are the same will also be
 1078    * equal, though the reverse may not be true.
 1079    * <br>
 1080    * Two nodes are equal if and only if the following conditions are satisfied:
 1081    * <ul><li>The two nodes are of the same type.</li>
 1082    * <li>The following string attributes are equal: nodeName, localName,
 1083    * namespaceURI, prefix, nodeValue. This is: they are both null, or they have
 1084    * the same length and are character for character identical.</li>
 1085    * <li>The attributes <code>NamedNodeMaps</code> are equal. This is: they are both null,
 1086    * or they have the same length and for each node that exists in one map there
 1087    * is a node that exists in the other map and is equal, although not
 1088    * necessarily at the same index.</li>
 1089    * <li>The childNodes NodeLists are equal. This is: they are both null, or they
 1090    * have the same length and contain equal nodes at the same index. Note that
 1091    * normalization can affect equality; to avoid this, nodes should be normalized
 1092    * before being compared.</li></ul>
 1093    * <br>
 1094    * For two DocumentType nodes to be equal, the following conditions must also
 1095    * be satisfied:
 1096    * <ul><li>The following string attributes are equal: publicId, systemId,
 1097    * internalSubset.</li>
 1098    * <li>The entities <code>NamedNodeMaps</code> are equal.</li>
 1099    * <li>The notations <code>NamedNodeMaps</code> are equal.</li></ul>
 1100    * <br>
 1101    * On the other hand, the following do not affect equality: the ownerDocument,
 1102    * baseURI, and parentNode attributes, the specified attribute for Attr nodes,
 1103    * the schemaTypeInfo attribute for Attr and Element nodes,
 1104    * the Text.isElementContentWhitespace attribute for Text nodes, as well as any
 1105    * user data or event listeners registered on the nodes.
 1106    * @param other Node to test againts
 1107    * @return true if nodes are equal, false otherwise
 1108    * @since DOM Level 3
 1109    */
 1110  18 public boolean isEqualNode(Node other) {
 1111  4 if (this.isSameNode(other)) return true;
 1112   
 1113    // Node has to be of the same type
 1114  14 if (other == null || other.getNodeType() != this.getNodeType()) {
 1115  0 return false;
 1116    }
 1117   
 1118    // The following string attributes have to be equal: nodeName, localName,
 1119    // namespaceURI, prefix, nodeValue
 1120  14 return StringUtilities.equals(getNodeName(), other.getNodeName()) &&
 1121    StringUtilities.equals(getLocalName(), other.getLocalName()) &&
 1122    StringUtilities.equals(getNamespaceURI(), other.getNamespaceURI()) &&
 1123    StringUtilities.equals(getPrefix(), other.getPrefix()) &&
 1124    StringUtilities.equals(getNodeValue(), other.getNodeValue());
 1125   
 1126    }
 1127   
 1128    /**
 1129    * @param feature feature name
 1130    * @param version feature version
 1131    * @return requested feature value, if any
 1132    * @since DOM Level 3
 1133    */
 1134  0 public Object getFeature(String feature, String version) {
 1135  0 if (DOMImplementationImpl.HasFeature(feature, version)) {
 1136  0 return this;
 1137    }
 1138   
 1139  0 return null;
 1140    }
 1141   
 1142    /**
 1143    * @param namespaceURI namespace URI to check
 1144    * @return true if specified namespace is the default namespace
 1145    * @since DOM Level 3
 1146    */
 1147  0 public boolean isDefaultNamespace(String namespaceURI) {
 1148  0 return namespaceURI.equals(lookupDefaultNamespaceURI());
 1149    }
 1150   
 1151    /**
 1152    * Compares the reference node, i.e. the node on which this method is being
 1153    * called, with a node, i.e. the one passed as a parameter, with regard to
 1154    * their position in the document and according to the document order.
 1155    * @param other The node to compare against the reference node.
 1156    * @return Returns how the node is positioned relatively to the reference
 1157    * node.
 1158    * @throws DOMException NOT_SUPPORTED_ERR: when the compared nodes are
 1159    * from different DOM implementations that do not coordinate to return
 1160    * consistent implementation-specific results.
 1161    * @since DOM Level 3
 1162    */
 1163  12 public short compareDocumentPosition(Node other) throws DOMException {
 1164  12 if (this == other) {
 1165  0 return 0;
 1166    }
 1167   
 1168  12 if (!(other instanceof NodeImpl)) {
 1169  0 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Nodes are from different DOM implementations");
 1170    }
 1171   
 1172  12 if (getOwnerDocument() != other.getOwnerDocument()) {
 1173  2 return DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
 1174    }
 1175   
 1176  10 ArrayList ancestors = new ArrayList();
 1177  10 Node root = findAncestors(this, other, ancestors);
 1178  10 Node ancNode = (Node) ancestors.get(0);
 1179  10 Node ancOther = (Node) ancestors.get(1);
 1180   
 1181  10 if (root == null) {
 1182  0 return DOCUMENT_POSITION_DISCONNECTED;
 1183    }
 1184   
 1185  10 if (root == other) {
 1186  2 return DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING;
 1187    }
 1188   
 1189  8 if (root == this) {
 1190  4 return DOCUMENT_POSITION_CONTAINED_BY | DOCUMENT_POSITION_FOLLOWING;
 1191    }
 1192   
 1193  4 short ancNodeType = ancNode.getNodeType();
 1194  4 short ancOtherType = ancOther.getNodeType();
 1195   
 1196  4 boolean nodeIsChild = ancNodeType != ATTRIBUTE_NODE && ancNodeType != NOTATION_NODE;
 1197  4 boolean otherIsChild = ancOtherType != ATTRIBUTE_NODE && ancOtherType != NOTATION_NODE;
 1198   
 1199  4 if (nodeIsChild && otherIsChild) {
 1200  2 Node child = root.getFirstChild();
 1201   
 1202  4 while (child != null) {
 1203  4 if (child == ancNode) {
 1204  2 return DOCUMENT_POSITION_FOLLOWING;
 1205  2 } else if (child == ancOther) {
 1206  0 return DOCUMENT_POSITION_PRECEDING;
 1207    }
 1208   
 1209  2 child = child.getNextSibling();
 1210    }
 1211   
 1212  0 return 0;
 1213  2 } else if (nodeIsChild ^ otherIsChild) {
 1214  2 return nodeIsChild ? DOCUMENT_POSITION_PRECEDING : DOCUMENT_POSITION_FOLLOWING;
 1215    } else {
 1216  0 if (ancNodeType != ancOtherType) {
 1217  0 return ancNodeType > ancOtherType ? DOCUMENT_POSITION_PRECEDING : DOCUMENT_POSITION_FOLLOWING;
 1218    } else {
 1219  0 return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
 1220    }
 1221    }
 1222    }
 1223   
 1224  28 private Node findParent(Set seen, Node node, ArrayList path) {
 1225  28 if (node == null) {
 1226  0 return null;
 1227    }
 1228   
 1229  28 seen.add(node);
 1230  28 path.add(node);
 1231  28 return node.getParentNode();
 1232    }
 1233   
 1234  10 private Node findAncestors(Node node1, Node node2, ArrayList ancestors) {
 1235  10 ArrayList path1 = new ArrayList();
 1236  10 ArrayList path2 = new ArrayList();
 1237  10 HashSet seen = new HashSet();
 1238   
 1239  10 Node root = null;
 1240  10 do {
 1241  14 node1 = findParent(seen, node1, path1);
 1242  14 node2 = findParent(seen, node2, path2);
 1243   
 1244  14 if (seen.contains(node1)) {
 1245  2 root = node1;
 1246  2 ancestors.add(path1.get(path1.size() - 1));
 1247  2 int idx = path2.indexOf(node1);
 1248  2 ancestors.add(path2.get(idx > 0 ? idx - 1 : 0));
 1249   
 1250  2 break;
 1251  12 } else if (seen.contains(node2)) {
 1252  6 root = node2;
 1253  6 int idx = path1.indexOf(node2);
 1254  6 ancestors.add(path1.get(idx > 0 ? idx - 1 : 0));
 1255  6 ancestors.add(path2.get(path2.size() - 1));
 1256   
 1257  6 break;
 1258  6 } else if (node1 != null && node1 == node2) {
 1259  2 root = node1;
 1260  2 ancestors.add(path1.get(path1.size() - 1));
 1261  2 ancestors.add(path2.get(path2.size() - 1));
 1262   
 1263  2 break;
 1264    }
 1265  4 } while (node1 != null || node2 != null);
 1266   
 1267  10 return root;
 1268    }
 1269   
 1270    //
 1271    // Implementation Methods
 1272    //
 1273   
 1274    /**
 1275    * This method walks down the tree, starting from this node, and adds
 1276    * namespace declarations where needed so that every namespace being
 1277    * used is properly declared. It also changes or assign prefixes when
 1278    * needed. This effectively makes this node subtree is "namespace
 1279    * wellformed".
 1280    * <br>What the generated prefixes are and/or how prefixes are changed to
 1281    * achieve this is implementation dependent.Any other name?How specific
 1282    * should this be? Should we not even specify that this should be done
 1283    * by walking down the tree?What does this do on attribute nodes?Doesn't
 1284    * do anything (F2F 1 Aug 2000).How does it work with entity reference
 1285    * subtree which may be broken?This doesn't affect entity references
 1286    * which are not visited in this operation (F2F 1 Aug 2000).Should this
 1287    * be really be on Node?Yes, but this only works on Document, Element,
 1288    * and DocumentFragment. On other types it is a no-op. (F2F 1 Aug 2000).
 1289    * What happens with read-only nodes?What/how errors should be reported?
 1290    * Are there any?
 1291    public void normalizeNS() throws DOMException {
 1292    }
 1293    */
 1294   
 1295    /**
 1296    * This attribute returns a unique key identifying this node.
 1297    *
 1298    * What type should this really be? In what space is this key
 1299    * unique (Document, DOMImplementation)? What is the lifetime of
 1300    * the uniqueness of this key (Node, Document, ...)?
 1301    public final synchronized Object getKey() {
 1302    if (key == null) {
 1303    key = new Object();
 1304    }
 1305    return key;
 1306    }
 1307    */
 1308   
 1309    /**
 1310    * Invoke user data handlers with provided parameters.
 1311    *
 1312    * @param op operation code
 1313    * @param src source node
 1314    * @param dst destination node
 1315    */
 1316  4462 protected void invokeHandlers(short op, Node src, Node dst) {
 1317  4462 if (!(src instanceof NodeImpl)) {
 1318  0 return;
 1319    }
 1320   
 1321  4462 ((NodeImpl) src).invokeHandlers(op, dst);
 1322    }
 1323   
 1324  4462 protected synchronized void invokeHandlers(short op, Node dst) {
 1325  4462 if (handlers == null || handlers.isEmpty()) {
 1326  4458 return;
 1327    }
 1328   
 1329  4 for (Iterator i = handlers.entrySet().iterator(); i.hasNext(); ) {
 1330  4 final Map.Entry entry = (Map.Entry) i.next();
 1331  4 final String key = (String) entry.getKey();
 1332  4 final UserDataHandler handler = (UserDataHandler) entry.getValue();
 1333   
 1334  4 try {
 1335  4 handler.handle(op, key, userData.get(key), this, dst);
 1336    } catch (Exception e) {
 1337  0 log.error("User data handler '" + key + "' failed for operation " + op, e);
 1338    }
 1339    }
 1340   
 1341    }
 1342   
 1343    /**
 1344    * Converts this node into its textual representation.
 1345    */
 1346  9 public String toString() {
 1347  9 return TextWriter.toString(this);
 1348    }
 1349    }