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