Clover coverage report -
Coverage timestamp: Sun Nov 16 2008 23:05:30 GMT
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  95383 public ContainerNodeImpl(NodeImpl parent, byte[] data, int pos, int len) {
 58  95383 super(parent, data, pos, len);
 59    }
 60   
 61  310554 public ContainerNodeImpl(NodeImpl parent, boolean dirty) {
 62  310554 super(parent, dirty);
 63    }
 64   
 65  31214 protected boolean isNodeTypeValid(short type) {
 66  31214 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  350259 protected final Node getNextSibling(Node node) {
 76  350259 checkLoaded();
 77  350259 int pos = childNodes.indexOf(node) + 1;
 78  350259 return pos < childNodes.getLength() ? childNodes.item(pos) : null;
 79    }
 80   
 81  1050842 protected void checkLoaded() {
 82  1050842 if (loaded) {
 83  891909 return;
 84    }
 85   
 86  158928 loaded = true;
 87  158928 try {
 88  158928 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  86805 protected final void loadChildren(SymbolTable st) throws IOException {
 100  86805 ByteArrayInput bis = new ByteArrayInput(data, pos, len);
 101  86805 XMLCompressedInput in = new XMLCompressedInput(bis, st);
 102   
 103  86805 if (getNodeType() == Node.ELEMENT_NODE) {
 104    // Have to skip the attributes
 105  69720 in.readSignature();
 106  69720 in.readContentSize();
 107  69720 in.readShort(); // Element Symbol
 108  69720 int attrCount = in.readAttributeCount();
 109  69720 for (int i = 0; i < attrCount; i++) {
 110  47371 in.readShort(); // Attribute Symbol
 111  47371 in.skip(in.readShort()); // Attribute Length
 112    }
 113    } else {
 114  17085 in.readInt();
 115    }
 116   
 117  86805 while (in.available() > 0) {
 118  157995 int pos = bis.getPos();
 119  157995 in.readSignature(); // Skip signature
 120  157995 int len = in.readContentSize();
 121  157995 if (len == 0) {
 122  0 len = 1;
 123    }
 124   
 125  157995 switch (in.getNodeType()) {
 126   
 127  69790 case Node.ELEMENT_NODE:
 128  69790 childNodes.add(new ElementImpl(this, data, pos, len));
 129  69790 break;
 130   
 131  86098 case Node.TEXT_NODE:
 132  86098 childNodes.add(new TextImpl(this, data, pos, len));
 133  86098 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  2046 case Node.PROCESSING_INSTRUCTION_NODE:
 148  2046 childNodes.add(new ProcessingInstructionImpl(this, data, pos, len));
 149  2046 break;
 150   
 151  57 case Node.COMMENT_NODE:
 152  57 childNodes.add(new CommentImpl(this, data, pos, len));
 153  57 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  157995 bis.setPos(pos);
 166  157995 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  205536 public final boolean hasChildNodes() {
 177  205536 checkLoaded();
 178  205536 return childNodes.getLength() > 0;
 179    }
 180   
 181  125165 public final NodeList getChildNodes() {
 182  125165 checkLoaded();
 183  125164 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  113025 public final Node getFirstChild() {
 191  113025 checkLoaded();
 192  113025 if (childNodes.size() > 0) {
 193  112688 return childNodes.item(0);
 194    } else {
 195  337 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  622 public final synchronized Node removeChild(Node oldChild) throws DOMException {
 313  622 checkLoaded();
 314  622 checkReadOnly();
 315  622 if (!childNodes.remove(oldChild)) {
 316  0 throw EX_NOT_FOUND;
 317    }
 318  622 setDirty();
 319  622 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  242677 public final synchronized Node appendChild(Node newChild) throws DOMException {
 339  242677 checkLoaded();
 340  242677 checkReadOnly();
 341  242677 if (!isNodeTypeValid(newChild.getNodeType())) {
 342  0 throw EX_HIERARCHY_REQUEST;
 343    }
 344  242677 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  242677 NodeImpl impl = (NodeImpl) newChild;
 353  242677 impl.setParentNode(this);
 354  242677 childNodes.add(impl);
 355    }
 356  242676 setDirty();
 357  242677 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  98243 public final synchronized void normalize() {
 377  98243 checkLoaded();
 378  98243 checkReadOnly();
 379  98243 List set = new ArrayList();
 380   
 381  98243 NodeListImpl newList = new NodeListImpl(this);
 382  98243 boolean modified = false;
 383   
 384  98243 int size = childNodes.size();
 385  98243 for (int i = 0; i < size; i++) {
 386  153424 Node add = null;
 387  153424 Node n = (Node) childNodes.get(i);
 388  153424 short type = n.getNodeType();
 389   
 390  153424 switch (type) {
 391  68523 case Node.TEXT_NODE:
 392  68523 set.add(n);
 393  68523 break;
 394  80295 case Node.ELEMENT_NODE:
 395  80295 n.normalize();
 396  4606 default :
 397  84901 add = n;
 398    }
 399   
 400  153423 if (!set.isEmpty() && (type != Node.TEXT_NODE || i == size - 1)) {
 401  67389 Text s = (Text) set.get(0);
 402  67389 StringBuffer buf = new StringBuffer(s.getData());
 403  67389 int len = set.size();
 404  67389 for (int j = 1; j < len; j++) {
 405  1134 modified = true;
 406  1134 Text a = (Text) set.get(j);
 407  1134 buf.append(a.getData());
 408    }
 409  67389 s.setData(buf.toString());
 410  67389 newList.add(s);
 411  67389 set.clear();
 412    }
 413  153424 if (add != null) {
 414  84901 newList.add(add);
 415    }
 416    }
 417   
 418  98242 if (modified) {
 419  253 childNodes = newList;
 420  253 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  463 public final NodeList getElementsByTagName(final String name) {
 434  463 checkLoaded();
 435  463 NodeListImpl list = new NodeListImpl(this);
 436   
 437  463 NodeFilter filter = new NodeFilter() {
 438  4071 public short acceptNode(Node node) {
 439  4071 if (node.getNodeName().equals(name)) {
 440  3773 return NodeFilter.FILTER_ACCEPT;
 441    } else {
 442  298 return NodeFilter.FILTER_SKIP;
 443    }
 444    }
 445    };
 446   
 447