Clover coverage report - Clover results for XOM 1.2d1
Coverage timestamp: Wed Feb 8 2006 08:31:33 EST
file stats: LOC: 457   Methods: 16
NCLOC: 148   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ParentNode.java 100% 100% 100% 100%
coverage
 1    /* Copyright 2002-2005 Elliotte Rusty Harold
 2   
 3    This library is free software; you can redistribute it and/or modify
 4    it under the terms of version 2.1 of the GNU Lesser General Public
 5    License as published by the Free Software Foundation.
 6   
 7    This library is distributed in the hope that it will be useful,
 8    but WITHOUT ANY WARRANTY; without even the implied warranty of
 9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 10    GNU Lesser General Public License for more details.
 11   
 12    You should have received a copy of the GNU Lesser General Public
 13    License along with this library; if not, write to the
 14    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 15    Boston, MA 02111-1307 USA
 16   
 17    You can contact Elliotte Rusty Harold by sending e-mail to
 18    elharo@metalab.unc.edu. Please include the word "XOM" in the
 19    subject line. The XOM home page is located at http://www.xom.nu/
 20    */
 21   
 22    package nu.xom;
 23   
 24   
 25    /**
 26    *
 27    * <p>
 28    * The generic superclass for nodes that have children.
 29    * Not counting subclasses, there are exactly two such classes in XOM:
 30    * </p>
 31    *
 32    * <ul>
 33    * <li><code>Document</code></li>
 34    * <li><code>Element</code></li>
 35    * </ul>
 36    *
 37    * <p>
 38    * This class provides methods to add and remove child nodes.
 39    * </p>
 40    *
 41    *
 42    * @author Elliotte Rusty Harold
 43    * @version 1.1b4
 44    *
 45    */
 46    public abstract class ParentNode extends Node {
 47   
 48    Node[] children;
 49    int childCount = 0;
 50    String actualBaseURI;
 51   
 52    /**
 53    * <p>
 54    * Creates a new <code>ParentNode</code> object.
 55    * Can only be invoked by other members of
 56    * the <code>nu.xom</code> package.
 57    * </p>
 58    *
 59    */
 60  4381087 ParentNode() {}
 61   
 62   
 63    /**
 64    * <p>
 65    * Returns the number of child nodes this node contains.
 66    * This is always greater than or equal to 0.
 67    * </p>
 68    *
 69    * @return the number of children of this node
 70    */
 71  14588223 public int getChildCount() {
 72  14588223 return childCount;
 73    }
 74   
 75   
 76    /**
 77    * <p>
 78    * Inserts a child node at the specified position.
 79    * The child node previously at that position (if any)
 80    * and all subsequent child nodes are moved up by one.
 81    * That is, when inserting a node at 2, the old node at 2
 82    * is moved to 3, the old child at 3 is moved to 4, and so
 83    * forth. Inserting at position 0 makes the child the first
 84    * child of this node. Inserting at the position
 85    * <code>getChildCount()</code> makes the child the
 86    * last child of the node.
 87    * </p>
 88    *
 89    * <p>
 90    * All the other methods that add a node to the tree ultimately
 91    * invoke this method.
 92    * </p>
 93    *
 94    * @param position where to insert the child
 95    * @param child the node to insert
 96    *
 97    * @throws IllegalAddException if this node cannot have a child of
 98    * the argument's type
 99    * @throws MultipleParentException if <code>child</code> already
 100    * has a parent
 101    * @throws NullPointerException if <code>child</code> is null
 102    * @throws IndexOutOfBoundsException if the position is negative or
 103    * greater than the number of children of this node
 104    */
 105  4099295 public void insertChild(Node child, int position) {
 106  4099295 _insertChild(child, position);
 107    }
 108   
 109   
 110    // because this method is called from Document constructor and
 111    // constructors should not call overridable methods
 112  4139367 final void _insertChild(Node child, int position) {
 113  4139367 insertionAllowed(child, position);
 114  4139331 fastInsertChild(child, position);
 115    }
 116   
 117   
 118  8679142 void fastInsertChild(Node child, int position) {
 119  8679142 checkCapacity(this.childCount+1);
 120  8679142 if (position < childCount) {
 121  4529 System.arraycopy(children, position, children, position+1, childCount-position);
 122    }
 123  8679141 children[position] = child;
 124  8679140 childCount++;
 125  8679140 child.setParent(this);
 126    }
 127   
 128   
 129  8679142 private void checkCapacity(int position) {
 130   
 131  8679142 if (children == null) {
 132  4207462 children = new Node[1];
 133    }
 134  4471680 else if (position >= children.length) {
 135  455389 Node[] data = new Node[children.length * 2];
 136  455389 System.arraycopy(children, 0, data, 0, children.length);
 137  455389 this.children = data;
 138    }
 139   
 140    }
 141   
 142   
 143    abstract void insertionAllowed(Node child, int position);
 144   
 145   
 146    /**
 147    * <p>
 148    * Appends a node to the children of this node.
 149    * </p>
 150    *
 151    * @param child node to append to this node
 152    *
 153    * @throws IllegalAddException if this node cannot have children
 154    * of this type
 155    * @throws MultipleParentException if child already has a parent
 156    * @throws NullPointerException if <code>child</code> is null
 157    *
 158    */
 159  2185510 public void appendChild(Node child) {
 160  2185510 insertChild(child, childCount);
 161    }
 162   
 163   
 164    /**
 165    *<p>
 166    * Returns the child of this node at the specified position.
 167    * Indexes begin at 0 and count up to one less than the number
 168    * of children in this node.
 169    * </p>
 170    *
 171    * @param position index of the node to return
 172    *
 173    * @return the node at the requested position
 174    *
 175    * @throws IndexOutOfBoundsException if the index is negative or
 176    * greater than or equal to the number of children of this node
 177    */
 178  10374528 public Node getChild(int position) {
 179   
 180  10374528 if (children == null) {
 181  1 throw new IndexOutOfBoundsException(
 182    "This node has no children"
 183    );
 184    }
 185  10374527 return children[position];
 186   
 187    }
 188   
 189   
 190    // private int lastPosition = -1;
 191   
 192    /**
 193    *<p>
 194    * Returns the position of a node within the children of this
 195    * node. This is a number between 0 and one less than the number of
 196    * children of this node. It returns -1 if <code>child</code>
 197    * does not have this node as a parent.
 198    * </p>
 199    *
 200    * <p>
 201    * This method does a linear search through the node's children.
 202    * On average, it executes in O(N) where N is the number of
 203    * children of the node.
 204    * </p>
 205    *
 206    * @param child the node whose position is desired
 207    *
 208    * @return the position of the argument node among
 209    * the children of this node
 210    */
 211  147115 public int indexOf(Node child) {
 212   
 213  2 if (children == null) return -1;
 214   
 215    // Programs tend to iterate through in order so we store the
 216    // last index returned and check the one immediately after it
 217    // first; before searching the list from the beginning.
 218    /* lastPosition++;
 219    if (lastPosition != children.size()) {
 220    if (child == children.get(lastPosition)) {
 221    return lastPosition;
 222    }
 223    else lastPosition = -1;
 224    }
 225    lastPosition = children.indexOf(child);
 226    return lastPosition; */
 227  147113 for (int i = 0; i < childCount; i++) {
 228  147099 if (child == children[i]) return i;
 229    }
 230  14 return -1;
 231   
 232    }
 233   
 234   
 235    /**
 236    * <p>
 237    * Removes the child of this node at the specified position.
 238    * Indexes begin at 0 and count up to one less than the number
 239    * of children in this node.
 240    * </p>
 241    *
 242    * @param position index of the node to remove
 243    *
 244    * @return the node which was removed
 245    *
 246    * @throws IndexOutOfBoundsException if the index is negative or
 247    * greater than or equal to the number of children of this node
 248    */
 249  121891 public Node removeChild(int position) {
 250   
 251  121891 if (children == null) {
 252  1 throw new IndexOutOfBoundsException(
 253    "This node has no children"
 254    );
 255    }
 256  121890 Node removed = children[position];
 257    // fill in actual base URI
 258    // This way does add base URIs to elements created in memory
 259    // XXX but this is a HotSpot when building; we need a fastRemoveChild
 260  121831 if (removed.isElement()) fillInBaseURI((Element) removed);
 261   
 262  121887 int toCopy = childCount-position-1;
 263  121887 if (toCopy > 0) {
 264  17366 System.arraycopy(children, position+1, children, position, toCopy);
 265    }
 266  121887 childCount--;
 267  121887 children[childCount] = null;
 268  121887 removed.setParent(null);
 269   
 270  121887 return removed;
 271   
 272    }
 273   
 274   
 275  265754 void fillInBaseURI(Element removed) {
 276   
 277  265754 ParentNode parent = removed;
 278  265754 String actualBaseURI = "";
 279  265754 while (parent != null && actualBaseURI.equals("")) {
 280  773363 actualBaseURI = parent.getActualBaseURI();
 281  773363 parent = parent.getParent();
 282    }
 283  265754 removed.setActualBaseURI(actualBaseURI);
 284   
 285    }
 286   
 287   
 288    /**
 289    * <p>
 290    * Removes the specified child of this node.
 291    * </p>
 292    *
 293    * @param child child node to remove
 294    *
 295    * @return the node which was removed
 296    *
 297    * @throws NoSuchChildException if <code>child</code> is
 298    * not in fact a child of this node
 299    */
 300  121698 public Node removeChild(Node child) {
 301   
 302  121698 if (children == null) {
 303  2 throw new NoSuchChildException(
 304    "Child does not belong to this node"
 305    );
 306    }
 307    // This next line is a hot spot
 308  121696 int position = indexOf(child);
 309  121696 if (position == -1) {
 310  1 throw new NoSuchChildException(
 311    "Child does not belong to this node"
 312    );
 313    }
 314  121650 if (child.isElement()) fillInBaseURI((Element) child);
 315  121695 removeChild(position);
 316   
 317  121695 child.setParent(null);
 318   
 319  121695 return child;
 320   
 321    }
 322   
 323   
 324    /**
 325    * <p>
 326    * Replaces an existing child with a new child node.
 327    * If <code>oldChild</code> is not a child of this node,
 328    * then a <code>NoSuchChildException</code> is thrown.
 329    * </p>
 330    *
 331    * @param oldChild the node removed from the tree
 332    * @param newChild the node inserted into the tree
 333    *
 334    * @throws MultipleParentException if <code>newChild</code> already
 335    * has a parent
 336    * @throws NoSuchChildException if <code>oldChild</code>
 337    * is not a child of this node
 338    * @throws NullPointerException if either argument is null
 339    * @throws IllegalAddException if this node cannot have children
 340    * of the type of <code>newChild</code>
 341    */
 342  18 public void replaceChild(Node oldChild, Node newChild) {
 343   
 344  18 if (oldChild == null) {
 345  2 throw new NullPointerException(
 346    "Tried to replace null child"
 347    );
 348    }
 349  16 if (newChild == null) {
 350  2 throw new NullPointerException(
 351    "Tried to replace child with null"
 352    );
 353    }
 354  14 if (children == null) {
 355  1 throw new NoSuchChildException(
 356    "Reference node is not a child of this node."
 357    );
 358    }
 359  13 int position = indexOf(oldChild);
 360  13 if (position == -1) {
 361  2 throw new NoSuchChildException(
 362    "Reference node is not a child of this node."
 363    );
 364    }
 365   
 366  1 if (oldChild == newChild) return;
 367   
 368  10 insertionAllowed(newChild, position);
 369  5 removeChild(position);
 370  4 insertChild(newChild, position);
 371   
 372    }
 373   
 374   
 375    /**
 376    *
 377    * <p>
 378    * Sets the URI against which relative URIs in this node will be
 379    * resolved. Generally, it's only necessary to set this property if
 380    * it's different from a node's parent's base URI, as it may
 381    * be in a document assembled from multiple entities
 382    * or by XInclude.
 383    * </p>
 384    *
 385    * <p>
 386    * Relative URIs are not allowed here. Base URIs must be absolute.
 387    * However, the base URI may be set to null or the empty string
 388    * to indicate that the node has no explicit base URI. In this
 389    * case, it inherits the base URI of its parent node, if any.
 390    * </p>
 391    *
 392    * <p>
 393    * URIs with fragment identifiers are also not allowed. The value
 394    * passed to this method must be a pure URI, not a URI reference.
 395    * </p>
 396    *
 397    * <p>
 398    * You can also add an <code>xml:base</code> attribute to
 399    * an element in the same way you'd add any other namespaced
 400    * attribute to an element. If an element's base URI
 401    * conflicts with its <code>xml:base</code> attribute,
 402    * then the value found in the <code>xml:base</code> attribute
 403    * is used.
 404    * </p>
 405    *
 406    * <p>
 407    * If the base URI is null or the empty string and there is
 408    * no <code>xml:base</code> attribute, then the base URI is
 409    * determined by the nearest ancestor node which does have a
 410    * base URI. Moving such a node from one location to another
 411    * can change its base URI.
 412    * </p>
 413    *
 414    * @param URI the new base URI for this node
 415    *
 416    * @throws MalformedURIException if <code>URI</code> is
 417    * not a legal RFC 3986 absolute URI
 418    */
 419    public abstract void setBaseURI(String URI);
 420   
 421   
 422  3894293 String getActualBaseURI() {
 423  3476497 if (actualBaseURI == null) return "";
 424  417796 return actualBaseURI;
 425    }
 426   
 427   
 428  313754 void setActualBaseURI(String uri) {
 429  34797 if (uri == null) uri = "";
 430  52815 if (!"".equals(uri)) Verifier.checkAbsoluteURI(uri);
 431  313740 actualBaseURI = uri;
 432    }
 433   
 434   
 435  1911886 final String findActualBaseURI() {
 436   
 437  1911886 ParentNode current = this;
 438  1911886 while (true) {
 439  1917782 String actualBase = current.getActualBaseURI();
 440  1917782 ParentNode parent = current.getParent();
 441   
 442  1911859 if (parent == null) return actualBase;
 443   
 444  5923 if ("".equals(actualBase)) {
 445  5896 current = parent;
 446  5896 continue;
 447    }
 448   
 449    // The parent is loaded from a different entity.
 450    // Therefore just return the actual base.
 451  27 return actualBase;
 452    }
 453   
 454    }
 455   
 456   
 457    }