Clover coverage report -
Coverage timestamp: Sun Nov 1 2009 23:08:24 UTC
file stats: LOC: 544   Methods: 24
NCLOC: 341   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ContainerNodeImpl.java 57% 65% 75% 63.6%
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: ContainerNodeImpl.java 648455 2008-04-15 23:19:03Z 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.ByteArrayInput;
 25    import org.apache.xindice.xml.SymbolTable;
 26    import org.apache.xindice.xml.XMLCompressedInput;
 27   
 28    import org.w3c.dom.DOMException;
 29    import org.w3c.dom.Element;
 30    import org.w3c.dom.Node;
 31    import org.w3c.dom.NodeList;
 32    import org.w3c.dom.Text;
 33    import org.w3c.dom.traversal.DocumentTraversal;
 34    import org.w3c.dom.traversal.NodeFilter;
 35    import org.w3c.dom.traversal.NodeIterator;
 36   
 37    import java.io.IOException;
 38    import java.util.ArrayList;
 39    import java.util.List;
 40   
 41    /**
 42    * ContainerNodeImpl performs most of the child-rearing behavior of the
 43    * Element and Document implementations.
 44    *
 45    * @version $Revision: 648455 $, $Date: 2008-04-15 23:19:03 +0000 (Tue, 15 Apr 2008) $
 46    */
 47    public abstract class ContainerNodeImpl extends NodeImpl {
 48   
 49    private static final Log log = LogFactory.getLog(ContainerNodeImpl.class);
 50   
 51    protected NodeListImpl childNodes = new NodeListImpl(this);
 52   
 53   
 54  0 public ContainerNodeImpl() {
 55    }
 56   
 57  174792 public ContainerNodeImpl(NodeImpl parent, byte[] data, int pos, int len) {
 58  174792 super(parent, data, pos, len);
 59    }
 60   
 61  393937 public ContainerNodeImpl(NodeImpl parent, boolean dirty) {
 62  393937 super(parent, dirty);
 63    }
 64   
 65  36063 protected boolean isNodeTypeValid(short type) {
 66  36063 return true;
 67    }
 68   
 69  2 protected final Node getPreviousSibling(Node node) {
 70  2 checkLoaded();
 71  2 int pos = childNodes.indexOf(node) - 1;
 72  2 return pos >= 0 ? childNodes.item(pos) : null;
 73    }
 74   
 75  526502 protected final Node getNextSibling(Node node) {
 76  526502 checkLoaded();
 77  526502 int pos = childNodes.indexOf(node) + 1;
 78  526502 return pos < childNodes.getLength() ? childNodes.item(pos) : null;
 79    }
 80   
 81  1501969 protected void checkLoaded() {
 82  1501970 if (loaded) {
 83  1283937 return;
 84    }
 85   
 86  218027 loaded = true;
 87  218027 try {
 88  218027 if (data != null) {
 89  0 DocumentImpl doc = (DocumentImpl) getOwnerDocument();
 90  0 loadChildren(doc.getSymbols());
 91    }
 92    } catch (Exception e) {
 93  0 if (log.isWarnEnabled()) {
 94  0 log.warn("ignored exception", e);
 95    }
 96    }
 97    }
 98   
 99  163167 protected final void loadChildren(SymbolTable st) throws IOException {
 100  163167 ByteArrayInput bis = new ByteArrayInput(data, pos, len);
 101  163167 XMLCompressedInput in = new XMLCompressedInput(bis, st);
 102   
 103  163167 if (getNodeType() == Node.ELEMENT_NODE) {
 104    // Have to skip the attributes
 105  130901 in.readSignature();
 106  130901 in.readContentSize();
 107  130901 in.readShort(); // Element Symbol
 108  130901 int attrCount = in.readAttributeCount();
 109  130901 for (int i = 0; i < attrCount; i++) {
 110  80694 in.readShort(); // Attribute Symbol
 111  80694 in.skip(in.readShort()); // Attribute Length
 112    }
 113    } else {
 114  32266 in.readInt();
 115    }
 116   
 117  163167 while (in.available() > 0) {
 118  306825 int pos = bis.getPos();
 119  306825 in.readSignature(); // Skip signature
 120  306825 int len = in.readContentSize();
 121  306825 if (len == 0) {
 122  0 len = 1;
 123    }
 124   
 125  306825 switch (in.getNodeType()) {
 126   
 127  130979 case Node.ELEMENT_NODE:
 128  130979 childNodes.add(new ElementImpl(this, data, pos, len));
 129  130979 break;
 130   
 131  173079 case Node.TEXT_NODE:
 132  173079 childNodes.add(new TextImpl(this, data, pos, len));
 133  173079 break;
 134   
 135  4 case Node.CDATA_SECTION_NODE:
 136  4 childNodes.add(new CDATASectionImpl(this, data, pos, len));
 137  4 break;
 138   
 139  0 case Node.ENTITY_REFERENCE_NODE:
 140  0 childNodes.add(new EntityReferenceImpl(this, data, pos, len));
 141  0 break;
 142   
 143  0 case Node.ENTITY_NODE:
 144  0 childNodes.add(new EntityImpl(this, data, pos, len));
 145  0 break;
 146   
 147  2658 case Node.PROCESSING_INSTRUCTION_NODE:
 148  2658 childNodes.add(new ProcessingInstructionImpl(this, data, pos, len));
 149  2658 break;
 150   
 151  105 case Node.COMMENT_NODE:
 152  105 childNodes.add(new CommentImpl(this, data, pos, len));
 153  105 break;
 154   
 155  0 case Node.NOTATION_NODE:
 156  0 childNodes.add(new NotationImpl(this, data, pos, len));
 157  0 break;
 158   
 159  0 default:
 160  0 if (log.isWarnEnabled()) {
 161  0 log.warn("invalid node type : " + in.getNodeType());
 162    }
 163    }
 164   
 165  306825 bis.setPos(pos);
 166  306825 bis.skip(len);
 167    }
 168    }
 169   
 170    /**
 171    * This is a convenience method to allow easy determination of whether a
 172    * node has any children.
 173    * @return <code>true</code> if the node has any children,
 174    * <code>false</code> if the node has no children.
 175    */
 176  339046 public final boolean hasChildNodes() {
 177  339046 checkLoaded();
 178  339046 return childNodes.getLength() > 0;
 179    }
 180   
 181  224144 public final NodeList getChildNodes() {
 182  224144 checkLoaded();
 183  224143 return childNodes;
 184    }
 185   
 186    /**
 187    * The first child of this node. If there is no such node, this returns
 188    * <code>null</code>.
 189    */
 190  170345 public final Node getFirstChild() {
 191  170345 checkLoaded();
 192  170345 if (childNodes.size() > 0) {
 193  169838 return childNodes.item(0);
 194    } else {
 195  507 return null;
 196    }
 197    }
 198   
 199    /**
 200    * The last child of this node. If there is no such node, this returns
 201    * <code>null</code>.
 202    */
 203  2 public final Node getLastChild() {
 204  2 checkLoaded();
 205  2 if (childNodes.size() > 0) {
 206  2 return childNodes.item(childNodes.getLength() - 1);
 207    } else {
 208  0 return null;
 209    }
 210    }
 211   
 212    /**
 213    * Replaces the child node <code>oldChild</code> with <code>newChild</code>
 214    * in the list of children, and returns the <code>oldChild</code> node. If
 215    * the <code>newChild</code> is already in the tree, it is first removed.
 216    * @param newChild The new node to put in the child list.
 217    * @param oldChild The node being replaced in the list.
 218    * @return The node replaced.
 219    * @exception DOMException
 220    * HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
 221    * allow children of the type of the <code>newChild</code> node, or it
 222    * the node to put in is one of this node's ancestors.
 223    * <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
 224    * from a different document than the one that created this node.
 225    * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 226    * <br>NOT_FOUND_ERR: Raised if <code>oldChild</code> is not a child of
 227    * this node.
 228    */
 229  0 public final synchronized Node replaceChild(Node newChild, Node oldChild) throws DOMException {
 230  0 checkLoaded();
 231  0 checkReadOnly();
 232  0 if (!isNodeTypeValid(newChild.getNodeType())) {
 233  0 throw EX_HIERARCHY_REQUEST;
 234    }
 235  0 int idx = childNodes.indexOf(oldChild);
 236  0 if (idx >= 0) {
 237  0 if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) {
 238  0 childNodes.remove(idx);
 239  0 NodeList nl = newChild.getChildNodes();
 240  0 for (int i = 0; i < nl.getLength(); i++) {
 241  0 NodeImpl impl = (NodeImpl) nl.item(i);
 242  0 impl.setParentNode(this);
 243  0 childNodes.add(idx + i, impl);
 244    }
 245    } else {
 246  0 NodeImpl impl = (NodeImpl) newChild;
 247  0 impl.setParentNode(this);
 248  0 childNodes.set(idx, impl);
 249    }
 250    }
 251  0 setDirty();
 252  0 return oldChild;
 253    }
 254   
 255    /**
 256    * Inserts the node <code>newChild</code> before the existing child node
 257    * <code>refChild</code>. If <code>refChild</code> is <code>null</code>,
 258    * insert <code>newChild</code> at the end of the list of children.
 259    * <br>If <code>newChild</code> is a <code>DocumentFragment</code> object,
 260    * all of its children are inserted, in the same order, before
 261    * <code>refChild</code>. If the <code>newChild</code> is already in the
 262    * tree, it is first removed.
 263    * @param newChild The node to insert.
 264    * @param refChild The reference node, i.e., the node before which the new
 265    * node must be inserted.
 266    * @return The node being inserted.
 267    * @exception DOMException
 268    * HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
 269    * allow children of the type of the <code>newChild</code> node, or if
 270    * the node to insert is one of this node's ancestors.
 271    * <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
 272    * from a different document than the one that created this node.
 273    * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 274    * <br>NOT_FOUND_ERR: Raised if <code>refChild</code> is not a child of
 275    * this node.
 276    */
 277  0 public final synchronized Node insertBefore(Node newChild, Node refChild) throws DOMException {
 278  0 checkLoaded();
 279  0 checkReadOnly();
 280  0 int idx = childNodes.indexOf(refChild);
 281  0 if (idx >= 0) {
 282  0 if (!isNodeTypeValid(newChild.getNodeType())) {
 283  0 throw EX_HIERARCHY_REQUEST;
 284    }
 285  0 if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) {
 286  0 NodeList nl = newChild.getChildNodes();
 287  0 for (int i = 0; i < nl.getLength(); i++) {
 288  0 NodeImpl impl = (NodeImpl) nl.item(i);
 289  0 impl.setParentNode(this);
 290  0 childNodes.add(idx + i, impl);
 291    }
 292    } else {
 293  0 NodeImpl impl = (NodeImpl) newChild;
 294  0 impl.setParentNode(this);
 295  0 childNodes.add(idx, impl);
 296    }
 297    }
 298  0 setDirty();
 299  0 return newChild;
 300    }
 301   
 302    /**
 303    * Removes the child node indicated by <code>oldChild</code> from the list
 304    * of children, and returns it.
 305    * @param oldChild The node being removed.
 306    * @return The node removed.
 307    * @exception DOMException
 308    * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 309    * <br>NOT_FOUND_ERR: Raised if <code>oldChild</code> is not a child of
 310    * this node.
 311    */
 312  763 public final synchronized Node removeChild(Node oldChild) throws DOMException {
 313  763 checkLoaded();
 314  763 checkReadOnly();
 315  763 if (!childNodes.remove(oldChild)) {
 316  0 throw EX_NOT_FOUND;
 317    }
 318  763 setDirty();
 319  763 return oldChild;
 320    }
 321   
 322    /**
 323    * Adds the node <code>newChild</code> to the end of the list of children of
 324    * this node. If the <code>newChild</code> is already in the tree, it is
 325    * first removed.
 326    * @param newChild The node to add.If it is a <code>DocumentFragment</code>
 327    * object, the entire contents of the document fragment are moved into
 328    * the child list of this node
 329    * @return The node added.
 330    * @exception DOMException
 331    * HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
 332    * allow children of the type of the <code>newChild</code> node, or if
 333    * the node to append is one of this node's ancestors.
 334    * <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
 335    * from a different document than the one that created this node.
 336    * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
 337    */
 338  272753 public final synchronized Node appendChild(Node newChild) throws DOMException {
 339  272753 checkLoaded();
 340  272753 checkReadOnly();
 341  272753 if (!isNodeTypeValid(newChild.getNodeType())) {
 342  0 throw EX_HIERARCHY_REQUEST;
 343    }
 344  272753 if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) {
 345  0 NodeList nl = newChild.getChildNodes();
 346  0 for (int i = 0; i < nl.getLength(); i++) {
 347  0 NodeImpl impl = (NodeImpl) nl.item(i);
 348  0 impl.setParentNode(this);
 349  0 childNodes.add(impl);
 350    }
 351    } else {
 352  272753 NodeImpl impl = (NodeImpl) newChild;
 353  272753 impl.setParentNode(this);
 354  272753 childNodes.add(impl);
 355    }
 356  272753 setDirty();
 357  272753 return newChild;
 358    }
 359   
 360    /**
 361    * Puts all <code>Text</code> nodes in the full depth of the sub-tree
 362    * underneath this <code>Node</code> , including attribute nodes, into a
 363    * "normal" form where only markup (e.g., tags, comments, processing
 364    * instructions, CDATA sections, and entity references) separates
 365    * <code>Text</code> nodes, i.e., there are neither adjacent
 366    * <code>Text</code> nodes nor empty <code>Text</code> nodes. This can be
 367    * used to ensure that the DOM view of a document is the same as if it
 368    * were saved and re-loaded, and is useful when operations (such as
 369    * XPointer lookups) that depend on a particular document tree structure
 370    * are to be used. In cases where the document contains
 371    * <code>CDATASections</code> , the normalize operation alone may not be
 372    * sufficient, since XPointers do not differentiate between
 373    * <code>Text</code> nodes and <code>CDATASection</code> nodes.
 374    * @since DOM Level 2
 375    */
 376  191040 public final synchronized void normalize() {
 377  191040 checkLoaded();
 378  191038 checkReadOnly();
 379  191040 List set = new ArrayList();
 380   
 381  191040 NodeListImpl newList = new NodeListImpl(this);
 382  191040 boolean modified = false;
 383   
 384  191040 int size = childNodes.size();
 385  191040 for (int i = 0; i < size; i++) {
 386  326067 Node add = null;
 387  326067 Node n = (Node) childNodes.get(i);
 388  326066 short type = n.getNodeType();
 389   
 390  326066 switch (type) {
 391  164533 case Node.TEXT_NODE:
 392  164533 set.add(n);
 393  164534 break;
 394  155499 case Node.ELEMENT_NODE:
 395  155499 n.normalize();
 396  6035 default :
 397  161535 add = n;
 398    }
 399   
 400  326069 if (!set.isEmpty() && (type != Node.TEXT_NODE || i == size - 1)) {
 401  163016 Text s = (Text) set.get(0);
 402  163016 StringBuffer buf = new StringBuffer(s.getData());
 403  163017 int len = set.size();
 404  163017 for (int j = 1; j < len; j++) {
 405  1517 modified = true;
 406  1517 Text a = (Text) set.get(j);
 407  1517 buf.append(a.getData());
 408    }
 409  163017 s.setData(buf.toString());
 410  163015 newList.add(s);
 411  163017 set.clear();
 412    }
 413  326069 if (add != null) {
 414  161533 newList.add(add);
 415    }
 416    }
 417   
 418  191040 if (modified) {
 419  329 childNodes = newList;
 420  329 setDirty();
 421    }
 422    }
 423   
 424    /**
 425    * Returns a <code>NodeList</code> of all descendant elements with a given
 426    * tag name, in the order in which they would be encountered in a preorder
 427    * traversal of the <code>Element</code> tree.
 428    *
 429    * @param name The name of the tag to match on. The special value "*"
 430    * matches all tags.
 431    * @return A list of matching <code>Element</code> nodes.
 432    */
 433  471 public final NodeList getElementsByTagName(final String name) {
 434  471 checkLoaded();
 435  471 NodeListImpl list = new NodeListImpl(this);
 436   
 437  471 NodeFilter filter = new NodeFilter() {
 438  4115 public short acceptNode(Node node) {
 439  4115 if (node.getNodeName().equals(name)) {
 440  3817 return NodeFilter.FILTER_ACCEPT;
 441    } else {
 442  298 return NodeFilter.FILTER_SKIP;
 443    }
 444    }
 445    };
 446   
 447  471 NodeIterator iter = ((DocumentTraversal) getOwnerDocument()).createNodeIterator(this, NodeFilter.SHOW_ELEMENT, filter, false);
 448  471 Node node;
 449  471 do {
 450  4288 node = iter.nextNode();
 451  4288 if (node != null) {
 452  3817 list.add(node);
 453    }
 454  4288 } while (node != null);
 455   
 456  471 return list;
 457    }
 458   
 459  0 public final NodeList getElementsByTagNameNS(final String namespaceURI, final String localName) {
 460  0 checkLoaded();
 461  0 NodeListImpl list = new NodeListImpl(this);
 462   
 463  0 NodeFilter filter = new NodeFilter() {
 464  0 public short acceptNode(Node node) {
 465  0 if (node.getLocalName().equals(localName) && node.getNamespaceURI().equals(namespaceURI)) {
 466  0 return NodeFilter.FILTER_ACCEPT;
 467    } else {
 468  0 return NodeFilter.FILTER_SKIP;
 469    }
 470    }
 471    };
 472   
 473  0 NodeIterator iter = ((DocumentTraversal) getOwnerDocument()).createNodeIterator(this, NodeFilter.SHOW_ELEMENT, filter, false);
 474  0 Node node;
 475  0 do {
 476  0 node = iter.nextNode();
 477  0 if (node != null) {
 478  0 list.add(node);
 479    }
 480  0 } while (node != null);
 481   
 482  0 return list;
 483    }
 484   
 485  0 public final Element getElementById(String elementId) {
 486  0 return null;
 487    }
 488   
 489    //
 490    // DOM Level 3 Implementation
 491    //
 492   
 493    /**
 494    * @since DOM Level 3
 495    */
 496  121 public String getTextContent() {
 497  121 StringBuffer val = new StringBuffer();
 498   
 499  121 NodeList children = getChildNodes();
 500  121 if (children == null || children.getLength() == 0) {
 501  12 return "";
 502    }
 503   
 504  109 for (int i = 0; i < children.getLength(); i++ ) {
 505  240 val.append(((NodeImpl) children.item(i)).getTextContent());
 506    }
 507  109 return val.toString();
 508    }
 509   
 510    /**
 511    * @since DOM Level 3
 512    */
 513  18 public boolean isEqualNode(Node other) {
 514  18 if (!super.isEqualNode(other)) {
 515  2 return false;
 516    }
 517   
 518    // The childNodes NodeLists have to be equal
 519  16 other.normalize();
 520  16 normalize();
 521   
 522  16 if (!hasChildNodes() && !other.hasChildNodes()) {
 523  8 return true;
 524    }
 525   
 526  8 if (!hasChildNodes() || !other.hasChildNodes()) {
 527  0 return false;
 528    }
 529   
 530  8 NodeList thisChildren = getChildNodes();
 531  8 NodeList otherChildren = other.getChildNodes();
 532  8 if (thisChildren.getLength() != otherChildren.getLength()) {
 533  0 return false;
 534    }
 535   
 536  8 for (int i = 0; i < thisChildren.getLength(); i++) {
 537  8 if (!((NodeImpl) thisChildren.item(i)).isEqualNode(otherChildren.item(i))) {
 538  2 return false;
 539    }
 540    }
 541   
 542  6 return true;
 543    }
 544    }