Coverage Report - org.apache.commons.configuration.tree.DefaultConfigurationNode
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultConfigurationNode
97%
72/74
100%
4/4
1,604
DefaultConfigurationNode$SubNodes
100%
58/58
100%
18/18
1,604
 
 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  
 package org.apache.commons.configuration.tree;
 18  
 
 19  
 import java.util.ArrayList;
 20  
 import java.util.Collection;
 21  
 import java.util.Collections;
 22  
 import java.util.HashMap;
 23  
 import java.util.Iterator;
 24  
 import java.util.LinkedList;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 
 28  
 import org.apache.commons.configuration.ConfigurationRuntimeException;
 29  
 
 30  
 /**
 31  
  * <p>
 32  
  * A default implementation of the <code>ConfigurationNode</code> interface.
 33  
  * </p>
 34  
  *
 35  
  * @since 1.3
 36  
  * @author Oliver Heger
 37  
  */
 38  
 public class DefaultConfigurationNode implements ConfigurationNode, Cloneable
 39  
 {
 40  
     /** Stores the children of this node. */
 41  
     private SubNodes children;
 42  
 
 43  
     /** Stores the attributes of this node. */
 44  
     private SubNodes attributes;
 45  
 
 46  
     /** Stores a reference to this node's parent. */
 47  
     private ConfigurationNode parent;
 48  
 
 49  
     /** Stores the value of this node. */
 50  
     private Object value;
 51  
 
 52  
     /** Stores the reference. */
 53  
     private Object reference;
 54  
 
 55  
     /** Stores the name of this node. */
 56  
     private String name;
 57  
 
 58  
     /** Stores a flag if this is an attribute. */
 59  
     private boolean attribute;
 60  
 
 61  
     /**
 62  
      * Creates a new uninitialized instance of
 63  
      * <code>DefaultConfigurationNode</code>.
 64  
      */
 65  
     public DefaultConfigurationNode()
 66  
     {
 67  3482
         this(null);
 68  3482
     }
 69  
 
 70  
     /**
 71  
      * Creates a new instance of <code>DefaultConfigurationNode</code> and
 72  
      * initializes it with the node name.
 73  
      *
 74  
      * @param name the name of this node
 75  
      */
 76  
     public DefaultConfigurationNode(String name)
 77  
     {
 78  45019
         this(name, null);
 79  45019
     }
 80  
 
 81  
     /**
 82  
      * Creates a new instance of <code>DefaultConfigurationNode</code> and
 83  
      * initializes it with the name and a value.
 84  
      *
 85  
      * @param name the node's name
 86  
      * @param value the node's value
 87  
      */
 88  
     public DefaultConfigurationNode(String name, Object value)
 89  90927
     {
 90  90927
         setName(name);
 91  90927
         setValue(value);
 92  90927
         initSubNodes();
 93  90927
     }
 94  
 
 95  
     /**
 96  
      * Returns the name of this node.
 97  
      *
 98  
      * @return the name of this node
 99  
      */
 100  
     public String getName()
 101  
     {
 102  291644
         return name;
 103  
     }
 104  
 
 105  
     /**
 106  
      * Sets the name of this node.
 107  
      *
 108  
      * @param name the new name
 109  
      */
 110  
     public void setName(String name)
 111  
     {
 112  93119
         checkState();
 113  93119
         this.name = name;
 114  93119
     }
 115  
 
 116  
     /**
 117  
      * Returns the value of this node.
 118  
      *
 119  
      * @return the value of this node
 120  
      */
 121  
     public Object getValue()
 122  
     {
 123  108204
         return value;
 124  
     }
 125  
 
 126  
     /**
 127  
      * Sets the value of this node.
 128  
      *
 129  
      * @param val the value of this node
 130  
      */
 131  
     public void setValue(Object val)
 132  
     {
 133  146255
         value = val;
 134  146255
     }
 135  
 
 136  
     /**
 137  
      * Returns the reference.
 138  
      *
 139  
      * @return the reference
 140  
      */
 141  
     public Object getReference()
 142  
     {
 143  114063
         return reference;
 144  
     }
 145  
 
 146  
     /**
 147  
      * Sets the reference.
 148  
      *
 149  
      * @param reference the reference object
 150  
      */
 151  
     public void setReference(Object reference)
 152  
     {
 153  40788
         this.reference = reference;
 154  40788
     }
 155  
 
 156  
     /**
 157  
      * Returns a reference to this node's parent.
 158  
      *
 159  
      * @return the parent node or <b>null </b> if this is the root
 160  
      */
 161  
     public ConfigurationNode getParentNode()
 162  
     {
 163  200924
         return parent;
 164  
     }
 165  
 
 166  
     /**
 167  
      * Sets the parent of this node.
 168  
      *
 169  
      * @param parent the parent of this node
 170  
      */
 171  
     public void setParentNode(ConfigurationNode parent)
 172  
     {
 173  198090
         this.parent = parent;
 174  198090
     }
 175  
 
 176  
     /**
 177  
      * Adds a new child to this node.
 178  
      *
 179  
      * @param child the new child
 180  
      */
 181  
     public void addChild(ConfigurationNode child)
 182  
     {
 183  60729
         children.addNode(child);
 184  60727
         child.setAttribute(false);
 185  60727
         child.setParentNode(this);
 186  60727
     }
 187  
 
 188  
     /**
 189  
      * Returns a list with all children of this node.
 190  
      *
 191  
      * @return a list with all child nodes
 192  
      */
 193  
     public List getChildren()
 194  
     {
 195  14194
         return children.getSubNodes();
 196  
     }
 197  
 
 198  
     /**
 199  
      * Returns the number of all children of this node.
 200  
      *
 201  
      * @return the number of all children
 202  
      */
 203  
     public int getChildrenCount()
 204  
     {
 205  11711
         return children.getSubNodes().size();
 206  
     }
 207  
 
 208  
     /**
 209  
      * Returns a list of all children with the given name.
 210  
      *
 211  
      * @param name the name; can be <b>null </b>, then all children are returned
 212  
      * @return a list of all children with the given name
 213  
      */
 214  
     public List getChildren(String name)
 215  
     {
 216  25571
         return children.getSubNodes(name);
 217  
     }
 218  
 
 219  
     /**
 220  
      * Returns the number of children with the given name.
 221  
      *
 222  
      * @param name the name; can be <b>null </b>, then the number of all
 223  
      * children is returned
 224  
      * @return the number of child nodes with this name
 225  
      */
 226  
     public int getChildrenCount(String name)
 227  
     {
 228  12450
         return children.getSubNodes(name).size();
 229  
     }
 230  
 
 231  
     /**
 232  
      * Returns the child node with the given index.
 233  
      *
 234  
      * @param index the index (0-based)
 235  
      * @return the child with this index
 236  
      */
 237  
     public ConfigurationNode getChild(int index)
 238  
     {
 239  861
         return children.getNode(index);
 240  
     }
 241  
 
 242  
     /**
 243  
      * Removes the specified child node from this node.
 244  
      *
 245  
      * @param child the node to be removed
 246  
      * @return a flag if a node was removed
 247  
      */
 248  
     public boolean removeChild(ConfigurationNode child)
 249  
     {
 250  10185
         return children.removeNode(child);
 251  
     }
 252  
 
 253  
     /**
 254  
      * Removes all children with the given name.
 255  
      *
 256  
      * @param childName the name of the children to be removed
 257  
      * @return a flag if at least one child node was removed
 258  
      */
 259  
     public boolean removeChild(String childName)
 260  
     {
 261  9
         return children.removeNodes(childName);
 262  
     }
 263  
 
 264  
     /**
 265  
      * Removes all child nodes of this node.
 266  
      */
 267  
     public void removeChildren()
 268  
     {
 269  3
         children.clear();
 270  3
     }
 271  
 
 272  
     /**
 273  
      * Checks if this node is an attribute node.
 274  
      *
 275  
      * @return a flag if this is an attribute node
 276  
      */
 277  
     public boolean isAttribute()
 278  
     {
 279  29400
         return attribute;
 280  
     }
 281  
 
 282  
     /**
 283  
      * Sets the attribute flag. Note: this method can only be called if the node
 284  
      * is not already part of a node hierarchy.
 285  
      *
 286  
      * @param f the attribute flag
 287  
      */
 288  
     public void setAttribute(boolean f)
 289  
     {
 290  93538
         checkState();
 291  93537
         attribute = f;
 292  93537
     }
 293  
 
 294  
     /**
 295  
      * Adds the specified attribute to this node.
 296  
      *
 297  
      * @param attr the attribute to be added
 298  
      */
 299  
     public void addAttribute(ConfigurationNode attr)
 300  
     {
 301  32798
         attributes.addNode(attr);
 302  32798
         attr.setAttribute(true);
 303  32798
         attr.setParentNode(this);
 304  32798
     }
 305  
 
 306  
     /**
 307  
      * Returns a list with the attributes of this node. This list contains
 308  
      * <code>ConfigurationNode</code> objects, too.
 309  
      *
 310  
      * @return the attribute list, never <b>null </b>
 311  
      */
 312  
     public List getAttributes()
 313  
     {
 314  11112
         return attributes.getSubNodes();
 315  
     }
 316  
 
 317  
     /**
 318  
      * Returns the number of attributes contained in this node.
 319  
      *
 320  
      * @return the number of attributes
 321  
      */
 322  
     public int getAttributeCount()
 323  
     {
 324  3616
         return attributes.getSubNodes().size();
 325  
     }
 326  
 
 327  
     /**
 328  
      * Returns a list with all attributes of this node with the given name.
 329  
      *
 330  
      * @param name the attribute's name
 331  
      * @return all attributes with this name
 332  
      */
 333  
     public List getAttributes(String name)
 334  
     {
 335  7675
         return attributes.getSubNodes(name);
 336  
     }
 337  
 
 338  
     /**
 339  
      * Returns the number of attributes of this node with the given name.
 340  
      *
 341  
      * @param name the name
 342  
      * @return the number of attributes with this name
 343  
      */
 344  
     public int getAttributeCount(String name)
 345  
     {
 346  39
         return getAttributes(name).size();
 347  
     }
 348  
 
 349  
     /**
 350  
      * Removes the specified attribute.
 351  
      *
 352  
      * @param node the attribute node to be removed
 353  
      * @return a flag if the attribute could be removed
 354  
      */
 355  
     public boolean removeAttribute(ConfigurationNode node)
 356  
     {
 357  1
         return attributes.removeNode(node);
 358  
     }
 359  
 
 360  
     /**
 361  
      * Removes all attributes with the specified name.
 362  
      *
 363  
      * @param name the name
 364  
      * @return a flag if at least one attribute was removed
 365  
      */
 366  
     public boolean removeAttribute(String name)
 367  
     {
 368  7
         return attributes.removeNodes(name);
 369  
     }
 370  
 
 371  
     /**
 372  
      * Returns the attribute with the given index.
 373  
      *
 374  
      * @param index the index (0-based)
 375  
      * @return the attribute with this index
 376  
      */
 377  
     public ConfigurationNode getAttribute(int index)
 378  
     {
 379  791
         return attributes.getNode(index);
 380  
     }
 381  
 
 382  
     /**
 383  
      * Removes all attributes of this node.
 384  
      */
 385  
     public void removeAttributes()
 386  
     {
 387  1
         attributes.clear();
 388  1
     }
 389  
 
 390  
     /**
 391  
      * Returns a flag if this node is defined. This means that the node contains
 392  
      * some data.
 393  
      *
 394  
      * @return a flag whether this node is defined
 395  
      */
 396  
     public boolean isDefined()
 397  
     {
 398  1
         return getValue() != null || getChildrenCount() > 0
 399  
                 || getAttributeCount() > 0;
 400  
     }
 401  
 
 402  
     /**
 403  
      * Visits this node and all its sub nodes.
 404  
      *
 405  
      * @param visitor the visitor
 406  
      */
 407  
     public void visit(ConfigurationNodeVisitor visitor)
 408  
     {
 409  45574
         if (visitor == null)
 410  
         {
 411  1
             throw new IllegalArgumentException("Visitor must not be null!");
 412  
         }
 413  
 
 414  45573
         if (!visitor.terminate())
 415  
         {
 416  45573
             visitor.visitBeforeChildren(this);
 417  45573
             children.visit(visitor);
 418  45573
             attributes.visit(visitor);
 419  45573
             visitor.visitAfterChildren(this);
 420  
         } /* if */
 421  45573
     }
 422  
 
 423  
     /**
 424  
      * Creates a copy of this object. This is not a deep copy, the children are
 425  
      * not cloned.
 426  
      *
 427  
      * @return a copy of this object
 428  
      */
 429  
     public Object clone()
 430  
     {
 431  
         try
 432  
         {
 433  827
             DefaultConfigurationNode copy = (DefaultConfigurationNode) super
 434  
                     .clone();
 435  827
             copy.initSubNodes();
 436  827
             return copy;
 437  
         }
 438  0
         catch (CloneNotSupportedException cex)
 439  
         {
 440  
             // should not happen
 441  0
             throw new ConfigurationRuntimeException("Cannot clone " + getClass());
 442  
         }
 443  
     }
 444  
 
 445  
     /**
 446  
      * Checks if a modification of this node is allowed. Some properties of a
 447  
      * node must not be changed when the node has a parent. This method checks
 448  
      * this and throws a runtime exception if necessary.
 449  
      */
 450  
     protected void checkState()
 451  
     {
 452  186657
         if (getParentNode() != null)
 453  
         {
 454  1
             throw new IllegalStateException(
 455  
                     "Node cannot be modified when added to a parent!");
 456  
         }
 457  186656
     }
 458  
 
 459  
     /**
 460  
      * Creates a <code>SubNodes</code> instance that is used for storing
 461  
      * either this node's children or attributes.
 462  
      *
 463  
      * @param attributes <b>true</b> if the returned instance is used for
 464  
      * storing attributes, <b>false</b> for storing child nodes
 465  
      * @return the <code>SubNodes</code> object to use
 466  
      */
 467  
     protected SubNodes createSubNodes(boolean attributes)
 468  
     {
 469  183508
         return new SubNodes();
 470  
     }
 471  
 
 472  
     /**
 473  
      * Deals with the reference when a node is removed. This method is called
 474  
      * for each removed child node or attribute. It can be overloaded in sub
 475  
      * classes, for which the reference has a concrete meaning and remove
 476  
      * operations need some update actions. This default implementation is
 477  
      * empty.
 478  
      */
 479  
     protected void removeReference()
 480  
     {
 481  252
     }
 482  
 
 483  
     /**
 484  
      * Helper method for initializing the sub nodes objects.
 485  
      */
 486  
     private void initSubNodes()
 487  
     {
 488  91754
         children = createSubNodes(false);
 489  91754
         attributes = createSubNodes(true);
 490  91754
     }
 491  
 
 492  
     /**
 493  
      * An internally used helper class for managing a collection of sub nodes.
 494  
      */
 495  183508
     protected static class SubNodes
 496  
     {
 497  
         /** Stores a list for the sub nodes. */
 498  
         private List nodes;
 499  
 
 500  
         /** Stores a map for accessing subnodes by name. */
 501  
         private Map namedNodes;
 502  
 
 503  
         /**
 504  
          * Adds a new sub node.
 505  
          *
 506  
          * @param node the node to add
 507  
          */
 508  
         public void addNode(ConfigurationNode node)
 509  
         {
 510  93527
             if (node == null || node.getName() == null)
 511  
             {
 512  2
                 throw new IllegalArgumentException(
 513  
                         "Node to add must have a defined name!");
 514  
             }
 515  93525
             node.setParentNode(null);  // reset, will later be set
 516  
 
 517  93525
             if (nodes == null)
 518  
             {
 519  45317
                 nodes = new ArrayList();
 520  45317
                 namedNodes = new HashMap();
 521  
             }
 522  
 
 523  93525
             nodes.add(node);
 524  93525
             List lst = (List) namedNodes.get(node.getName());
 525  93525
             if (lst == null)
 526  
             {
 527  66461
                 lst = new LinkedList();
 528  66461
                 namedNodes.put(node.getName(), lst);
 529  
             }
 530  93525
             lst.add(node);
 531  93525
         }
 532  
 
 533  
         /**
 534  
          * Removes a sub node.
 535  
          *
 536  
          * @param node the node to remove
 537  
          * @return a flag if the node could be removed
 538  
          */
 539  
         public boolean removeNode(ConfigurationNode node)
 540  
         {
 541  10186
             if (nodes != null && node != null && nodes.contains(node))
 542  
             {
 543  7588
                 detachNode(node);
 544  7588
                 nodes.remove(node);
 545  
 
 546  7588
                 List lst = (List) namedNodes.get(node.getName());
 547  7588
                 if (lst != null)
 548  
                 {
 549  7588
                     lst.remove(node);
 550  7588
                     if (lst.isEmpty())
 551  
                     {
 552  4939
                         namedNodes.remove(node.getName());
 553  
                     }
 554  
                 }
 555  7588
                 return true;
 556  
             }
 557  
 
 558  
             else
 559  
             {
 560  2598
                 return false;
 561  
             }
 562  
         }
 563  
 
 564  
         /**
 565  
          * Removes all sub nodes with the given name.
 566  
          *
 567  
          * @param name the name
 568  
          * @return a flag if at least on sub node was removed
 569  
          */
 570  
         public boolean removeNodes(String name)
 571  
         {
 572  16
             if (nodes != null && name != null)
 573  
             {
 574  9
                 List lst = (List) namedNodes.remove(name);
 575  9
                 if (lst != null)
 576  
                 {
 577  7
                     detachNodes(lst);
 578  7
                     nodes.removeAll(lst);
 579  7
                     return true;
 580  
                 }
 581  
             }
 582  9
             return false;
 583  
         }
 584  
 
 585  
         /**
 586  
          * Removes all sub nodes.
 587  
          */
 588  
         public void clear()
 589  
         {
 590  4
             if (nodes != null)
 591  
             {
 592  3
                 detachNodes(nodes);
 593  3
                 nodes = null;
 594  3
                 namedNodes = null;
 595  
             }
 596  4
         }
 597  
 
 598  
         /**
 599  
          * Returns the node with the given index. If this index cannot be found,
 600  
          * an <code>IndexOutOfBoundException</code> exception will be thrown.
 601  
          *
 602  
          * @param index the index (0-based)
 603  
          * @return the sub node at the specified index
 604  
          */
 605  
         public ConfigurationNode getNode(int index)
 606  
         {
 607  1652
             if (nodes == null)
 608  
             {
 609  1
                 throw new IndexOutOfBoundsException("No sub nodes available!");
 610  
             }
 611  1651
             return (ConfigurationNode) nodes.get(index);
 612  
         }
 613  
 
 614  
         /**
 615  
          * Returns a list with all stored sub nodes. The return value is never
 616  
          * <b>null</b>.
 617  
          *
 618  
          * @return a list with the sub nodes
 619  
          */
 620  
         public List getSubNodes()
 621  
         {
 622  40634
             return (nodes == null) ? Collections.EMPTY_LIST : Collections
 623  
                     .unmodifiableList(nodes);
 624  
         }
 625  
 
 626  
         /**
 627  
          * Returns a list of the sub nodes with the given name. The return value
 628  
          * is never <b>null</b>.
 629  
          *
 630  
          * @param name the name; if <b>null</b> is passed, all sub nodes will
 631  
          * be returned
 632  
          * @return all sub nodes with this name
 633  
          */
 634  
         public List getSubNodes(String name)
 635  
         {
 636  45696
             if (name == null)
 637  
             {
 638  1
                 return getSubNodes();
 639  
             }
 640  
 
 641  
             List result;
 642  45695
             if (nodes == null)
 643  
             {
 644  1242
                 result = null;
 645  
             }
 646  
             else
 647  
             {
 648  44453
                 result = (List) namedNodes.get(name);
 649  
             }
 650  
 
 651  45695
             return (result == null) ? Collections.EMPTY_LIST : Collections
 652  
                     .unmodifiableList(result);
 653  
         }
 654  
 
 655  
         /**
 656  
          * Let the passed in visitor visit all sub nodes.
 657  
          *
 658  
          * @param visitor the visitor
 659  
          */
 660  
         public void visit(ConfigurationNodeVisitor visitor)
 661  
         {
 662  91146
             if (nodes != null)
 663  
             {
 664  22235
                 for (Iterator it = nodes.iterator(); it.hasNext()
 665  50483
                         && !visitor.terminate();)
 666  
                 {
 667  28248
                     ((ConfigurationNode) it.next()).visit(visitor);
 668  
                 }
 669  
             }
 670  91146
         }
 671  
 
 672  
         /**
 673  
          * This method is called whenever a sub node is removed from this
 674  
          * object. It ensures that the removed node's parent is reset and its
 675  
          * <code>removeReference()</code> method gets called.
 676  
          *
 677  
          * @param subNode the node to be removed
 678  
          */
 679  
         protected void detachNode(ConfigurationNode subNode)
 680  
         {
 681  7613
             subNode.setParentNode(null);
 682  7613
             if (subNode instanceof DefaultConfigurationNode)
 683  
             {
 684  7613
                 ((DefaultConfigurationNode) subNode).removeReference();
 685  
             }
 686  7613
         }
 687  
 
 688  
         /**
 689  
          * Detaches a list of sub nodes. This method calls
 690  
          * <code>detachNode()</code> for each node contained in the list.
 691  
          *
 692  
          * @param subNodes the list with nodes to be detached
 693  
          */
 694  
         protected void detachNodes(Collection subNodes)
 695  
         {
 696  10
             for (Iterator it = subNodes.iterator(); it.hasNext();)
 697  
             {
 698  25
                 detachNode((ConfigurationNode) it.next());
 699  
             }
 700  10
         }
 701  
     }
 702  
 }