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