Coverage report

  %line %branch
org.apache.commons.configuration.HierarchicalConfiguration$NodeVisitor
100% 
100% 

 1  
 /*
 2  
  * Copyright 2001-2005 The Apache Software Foundation.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License")
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 
 17  
 package org.apache.commons.configuration;
 18  
 
 19  
 import java.io.Serializable;
 20  
 import java.util.ArrayList;
 21  
 import java.util.Collection;
 22  
 import java.util.Iterator;
 23  
 import java.util.LinkedList;
 24  
 import java.util.List;
 25  
 import java.util.Set;
 26  
 import java.util.Stack;
 27  
 
 28  
 import org.apache.commons.collections.map.LinkedMap;
 29  
 import org.apache.commons.collections.set.ListOrderedSet;
 30  
 import org.apache.commons.lang.StringUtils;
 31  
 
 32  
 /**
 33  
  * <p>A specialized configuration class that extends its base class by the
 34  
  * ability of keeping more structure in the stored properties.</p><p>There
 35  
  * are some sources of configuration data that cannot be stored very well in a
 36  
  * <code>BaseConfiguration</code> object because then their structure is lost.
 37  
  * This is especially true for XML documents. This class can deal with such
 38  
  * structured configuration sources by storing the properties in a tree-like
 39  
  * organization.</p><p>The internal used storage form allows for a more
 40  
  * sophisticated access to single properties. As an example consider the
 41  
  * following XML document:</p><p>
 42  
  *
 43  
  * <pre>
 44  
  * &lt;database&gt;
 45  
  *   &lt;tables&gt;
 46  
  *     &lt;table&gt;
 47  
  *       &lt;name&gt;users&lt;/name&gt;
 48  
  *       &lt;fields&gt;
 49  
  *         &lt;field&gt;
 50  
  *           &lt;name&gt;lid&lt;/name&gt;
 51  
  *           &lt;type&gt;long&lt;/name&gt;
 52  
  *         &lt;/field&gt;
 53  
  *         &lt;field&gt;
 54  
  *           &lt;name&gt;usrName&lt;/name&gt;
 55  
  *           &lt;type&gt;java.lang.String&lt;/type&gt;
 56  
  *         &lt;/field&gt;
 57  
  *        ...
 58  
  *       &lt;/fields&gt;
 59  
  *     &lt;/table&gt;
 60  
  *     &lt;table&gt;
 61  
  *       &lt;name&gt;documents&lt;/name&gt;
 62  
  *       &lt;fields&gt;
 63  
  *         &lt;field&gt;
 64  
  *           &lt;name&gt;docid&lt;/name&gt;
 65  
  *           &lt;type&gt;long&lt;/type&gt;
 66  
  *         &lt;/field&gt;
 67  
  *         ...
 68  
  *       &lt;/fields&gt;
 69  
  *     &lt;/table&gt;
 70  
  *     ...
 71  
  *   &lt;/tables&gt;
 72  
  * &lt;/database&gt;
 73  
  * </pre>
 74  
  *
 75  
  * </p><p>If this document is parsed and stored in a
 76  
  * <code>HierarchicalConfiguration</code> object (which can be done by one of
 77  
  * the sub classes), there are enhanced possibilities of accessing properties.
 78  
  * The keys for querying information can contain indices that select a certain
 79  
  * element if there are multiple hits.</p><p>For instance the key
 80  
  * <code>tables.table(0).name</code> can be used to find out the name of the
 81  
  * first table. In opposite <code>tables.table.name</code> would return a
 82  
  * collection with the names of all available tables. Similarily the key
 83  
  * <code>tables.table(1).fields.field.name</code> returns a collection with
 84  
  * the names of all fields of the second table. If another index is added after
 85  
  * the <code>field</code> element, a single field can be accessed:
 86  
  * <code>tables.table(1).fields.field(0).name</code>.</p><p>There is a
 87  
  * <code>getMaxIndex()</code> method that returns the maximum allowed index
 88  
  * that can be added to a given property key. This method can be used to iterate
 89  
  * over all values defined for a certain property.</p>
 90  
  *
 91  
  * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger </a>
 92  
  * @version $Id: HierarchicalConfiguration.java,v 1.14 2004/12/02 22:05:52
 93  
  * ebourg Exp $
 94  
  */
 95  
 public class HierarchicalConfiguration extends AbstractConfiguration implements Serializable, Cloneable
 96  
 {
 97  
     /** Constant for a new dummy key. */
 98  
     private static final String NEW_KEY = "newKey";
 99  
 
 100  
     /** Stores the root node of this configuration. */
 101  
     private Node root = new Node();
 102  
 
 103  
     /**
 104  
      * Returns the root node of this hierarchical configuration.
 105  
      *
 106  
      * @return the root node
 107  
      */
 108  
     public Node getRoot()
 109  
     {
 110  
         return root;
 111  
     }
 112  
 
 113  
     /**
 114  
      * Sets the root node of this hierarchical configuration.
 115  
      *
 116  
      * @param node the root node
 117  
      */
 118  
     public void setRoot(Node node)
 119  
     {
 120  
         if (node == null)
 121  
         {
 122  
             throw new IllegalArgumentException("Root node must not be null!");
 123  
         }
 124  
         root = node;
 125  
     }
 126  
 
 127  
     /**
 128  
      * Fetches the specified property. Performs a recursive lookup in the tree
 129  
      * with the configuration properties.
 130  
      *
 131  
      * @param key the key to be looked up
 132  
      * @return the found value
 133  
      */
 134  
     public Object getProperty(String key)
 135  
     {
 136  
         List nodes = fetchNodeList(key);
 137  
 
 138  
         if (nodes.size() == 0)
 139  
         {
 140  
             return null;
 141  
         }
 142  
         else
 143  
         {
 144  
             List list = new ArrayList();
 145  
             for (Iterator it = nodes.iterator(); it.hasNext();)
 146  
             {
 147  
                 Node node = (Node) it.next();
 148  
                 if (node.getValue() != null)
 149  
                 {
 150  
                     list.add(node.getValue());
 151  
                 }
 152  
             }
 153  
 
 154  
             if (list.size() < 1)
 155  
             {
 156  
                 return null;
 157  
             }
 158  
             else
 159  
             {
 160  
                 return (list.size() == 1) ? list.get(0) : list;
 161  
             }
 162  
         }
 163  
     }
 164  
 
 165  
     /**
 166  
      * <p>Adds the property with the specified key.</p><p>To be able to deal
 167  
      * with the structure supported by this configuration implementation the
 168  
      * passed in key is of importance, especially the indices it might contain.
 169  
      * The following example should clearify this: Suppose the actual
 170  
      * configuration contains the following elements:</p><p>
 171  
      *
 172  
      * <pre>
 173  
      * tables
 174  
      *    +-- table
 175  
      *            +-- name = user
 176  
      *            +-- fields
 177  
      *                    +-- field
 178  
      *                            +-- name = uid
 179  
      *                    +-- field
 180  
      *                            +-- name = firstName
 181  
      *                    ...
 182  
      *    +-- table
 183  
      *            +-- name = documents
 184  
      *            +-- fields
 185  
      *                   ...
 186  
      * </pre>
 187  
      *
 188  
      * </p><p>In this example a database structure is defined, e.g. all fields
 189  
      * of the first table could be accessed using the key
 190  
      * <code>tables.table(0).fields.field.name</code>. If now properties are
 191  
      * to be added, it must be exactly specified at which position in the
 192  
      * hierarchy the new property is to be inserted. So to add a new field name
 193  
      * to a table it is not enough to say just</p><p>
 194  
      *
 195  
      * <pre>
 196  
      * config.addProperty("tables.table.fields.field.name", "newField");
 197  
      * </pre>
 198  
      *
 199  
      * </p><p>The statement given above contains some ambiguity. For instance
 200  
      * it is not clear, to which table the new field should be added. If this
 201  
      * method finds such an ambiguity, it is resolved by following the last
 202  
      * valid path. Here this would be the last table. The same is true for the
 203  
      * <code>field</code>; because there are multiple fields and no explicit
 204  
      * index is provided, a new <code>name</code> property would be added to
 205  
      * the last field - which is propably not what was desired.</p><p>To make
 206  
      * things clear explicit indices should be provided whenever possible. In
 207  
      * the example above the exact table could be specified by providing an
 208  
      * index for the <code>table</code> element as in
 209  
      * <code>tables.table(1).fields</code>. By specifying an index it can
 210  
      * also be expressed that at a given position in the configuration tree a
 211  
      * new branch should be added. In the example above we did not want to add
 212  
      * an additional <code>name</code> element to the last field of the table,
 213  
      * but we want a complete new <code>field</code> element. This can be
 214  
      * achieved by specifying an invalid index (like -1) after the element where
 215  
      * a new branch should be created. Given this our example would run:</p>
 216  
      * <p>
 217  
      *
 218  
      * <pre>
 219  
      * config.addProperty("tables.table(1).fields.field(-1).name", "newField");
 220  
      * </pre>
 221  
      *
 222  
      * </p><p>With this notation it is possible to add new branches
 223  
      * everywhere. We could for instance create a new <code>table</code>
 224  
      * element by specifying</p><p>
 225  
      *
 226  
      * <pre>
 227  
      * config.addProperty("tables.table(-1).fields.field.name", "newField2");
 228  
      * </pre>
 229  
      *
 230  
      * </p><p>(Note that because after the <code>table</code> element a new
 231  
      * branch is created indices in following elements are not relevant; the
 232  
      * branch is new so there cannot be any ambiguities.)</p>
 233  
      *
 234  
      * @param key the key of the new property
 235  
      * @param obj the value of the new property
 236  
      */
 237  
     protected void addPropertyDirect(String key, Object obj)
 238  
     {
 239  
         ConfigurationKey.KeyIterator it = new ConfigurationKey(key).iterator();
 240  
         Node parent = fetchAddNode(it, getRoot());
 241  
 
 242  
         Node child = createNode(it.currentKey(true));
 243  
         child.setValue(obj);
 244  
         parent.addChild(child);
 245  
     }
 246  
 
 247  
     /**
 248  
      * Adds a collection of nodes at the specified position of the configuration
 249  
      * tree. This method works similar to <code>addProperty()</code>, but
 250  
      * instead of a single property a whole collection of nodes can be added -
 251  
      * and thus complete configuration sub trees. E.g. with this method it is
 252  
      * possible to add parts of another <code>HierarchicalConfiguration</code>
 253  
      * object to this object.
 254  
      *
 255  
      * @param key the key where the nodes are to be added; can be <b>null </b>,
 256  
      * then they are added to the root node
 257  
      * @param nodes a collection with the <code>Node</code> objects to be
 258  
      * added
 259  
      */
 260  
     public void addNodes(String key, Collection nodes)
 261  
     {
 262  
         if (nodes == null || nodes.isEmpty())
 263  
         {
 264  
             return;
 265  
         }
 266  
 
 267  
         Node parent;
 268  
         if (StringUtils.isEmpty(key))
 269  
         {
 270  
             parent = getRoot();
 271  
         }
 272  
         else
 273  
         {
 274  
             ConfigurationKey.KeyIterator kit = new ConfigurationKey(key).iterator();
 275  
             parent = fetchAddNode(kit, getRoot());
 276  
 
 277  
             // fetchAddNode() does not really fetch the last component,
 278  
             // but one before. So we must perform an additional step.
 279  
             ConfigurationKey keyNew = new ConfigurationKey(kit.currentKey(true));
 280  
             keyNew.append(NEW_KEY);
 281  
             parent = fetchAddNode(keyNew.iterator(), parent);
 282  
         }
 283  
 
 284  
         for (Iterator it = nodes.iterator(); it.hasNext();)
 285  
         {
 286  
             parent.addChild((Node) it.next());
 287  
         }
 288  
     }
 289  
 
 290  
     /**
 291  
      * Checks if this configuration is empty. Empty means that there are no keys
 292  
      * with any values, though there can be some (empty) nodes.
 293  
      *
 294  
      * @return a flag if this configuration is empty
 295  
      */
 296  
     public boolean isEmpty()
 297  
     {
 298  
         return !nodeDefined(getRoot());
 299  
     }
 300  
 
 301  
     /**
 302  
      * Creates a new <code>Configuration</code> object containing all keys
 303  
      * that start with the specified prefix. This implementation will return a
 304  
      * <code>HierarchicalConfiguration</code> object so that the structure of
 305  
      * the keys will be saved.
 306  
      *
 307  
      * @param prefix the prefix of the keys for the subset
 308  
      * @return a new configuration object representing the selected subset
 309  
      */
 310  
     public Configuration subset(String prefix)
 311  
     {
 312  
         Collection nodes = fetchNodeList(prefix);
 313  
         if (nodes.isEmpty())
 314  
         {
 315  
             return new HierarchicalConfiguration();
 316  
         }
 317  
 
 318  
         HierarchicalConfiguration result = new HierarchicalConfiguration();
 319  
         CloneVisitor visitor = new CloneVisitor();
 320  
 
 321  
         for (Iterator it = nodes.iterator(); it.hasNext();)
 322  
         {
 323  
             Node nd = (Node) it.next();
 324  
             nd.visit(visitor, null);
 325  
 
 326  
             List children = visitor.getClone().getChildren();
 327  
             if (children.size() > 0)
 328  
             {
 329  
                 for (int i = 0; i < children.size(); i++)
 330  
                 {
 331  
                     result.getRoot().addChild((Node) children.get(i));
 332  
                 }
 333  
             }
 334  
         }
 335  
 
 336  
         return (result.isEmpty()) ? new HierarchicalConfiguration() : result;
 337  
     }
 338  
 
 339  
     /**
 340  
      * Checks if the specified key is contained in this configuration. Note that
 341  
      * for this configuration the term &quot;contained&quot; means that the key
 342  
      * has an associated value. If there is a node for this key that has no
 343  
      * value but children (either defined or undefined), this method will still
 344  
      * return <b>false </b>.
 345  
      *
 346  
      * @param key the key to be chekced
 347  
      * @return a flag if this key is contained in this configuration
 348  
      */
 349  
     public boolean containsKey(String key)
 350  
     {
 351  
         return getProperty(key) != null;
 352  
     }
 353  
 
 354  
     /**
 355  
      * Sets the value of the specified property.
 356  
      *
 357  
      * @param key the key of the property to set
 358  
      * @param value the new value of this property
 359  
      */
 360  
     public void setProperty(String key, Object value)
 361  
     {
 362  
         Iterator itNodes = fetchNodeList(key).iterator();
 363  
         Iterator itValues = PropertyConverter.toIterator(value, getDelimiter());
 364  
         while (itNodes.hasNext() && itValues.hasNext())
 365  
         {
 366  
             ((Node) itNodes.next()).setValue(itValues.next());
 367  
         }
 368  
 
 369  
         // Add additional nodes if necessary
 370  
         while (itValues.hasNext())
 371  
         {
 372  
             addPropertyDirect(key, itValues.next());
 373  
         }
 374  
 
 375  
         // Remove remaining nodes
 376  
         while (itNodes.hasNext())
 377  
         {
 378  
             clearNode((Node) itNodes.next());
 379  
         }
 380  
     }
 381  
 
 382  
     /**
 383  
      * Removes all values of the property with the given name and of keys that
 384  
      * start with this name. So if there is a property with the key
 385  
      * &quot;foo&quot; and a property with the key &quot;foo.bar&quot;, a call
 386  
      * of <code>clearTree("foo")</code> would remove both properties.
 387  
      *
 388  
      * @param key the key of the property to be removed
 389  
      */
 390  
     public void clearTree(String key)
 391  
     {
 392  
         List nodes = fetchNodeList(key);
 393  
 
 394  
         for (Iterator it = nodes.iterator(); it.hasNext();)
 395  
         {
 396  
             removeNode((Node) it.next());
 397  
         }
 398  
     }
 399  
 
 400  
     /**
 401  
      * Removes the property with the given key. Properties with names that start
 402  
      * with the given key (i.e. properties below the specified key in the
 403  
      * hierarchy) won't be affected.
 404  
      *
 405  
      * @param key the key of the property to be removed
 406  
      */
 407  
     public void clearProperty(String key)
 408  
     {
 409  
         List nodes = fetchNodeList(key);
 410  
 
 411  
         for (Iterator it = nodes.iterator(); it.hasNext();)
 412  
         {
 413  
             clearNode((Node) it.next());
 414  
         }
 415  
     }
 416  
 
 417  
     /**
 418  
      * Returns an iterator with all keys defined in this configuration.
 419  
      * Note that the keys returned by this method will not contain any
 420  
      * indices. This means that some structure will be lost.</p>
 421  
      *
 422  
      * @return an iterator with the defined keys in this configuration
 423  
      */
 424  
     public Iterator getKeys()
 425  
     {
 426  
         DefinedKeysVisitor visitor = new DefinedKeysVisitor();
 427  
         getRoot().visit(visitor, new ConfigurationKey());
 428  
 
 429  
         return visitor.getKeyList().iterator();
 430  
     }
 431  
 
 432  
     /**
 433  
      * Returns an iterator with all keys defined in this configuration that
 434  
      * start with the given prefix. The returned keys will not contain any
 435  
      * indices.
 436  
      *
 437  
      * @param prefix the prefix of the keys to start with
 438  
      * @return an iterator with the found keys
 439  
      */
 440  
     public Iterator getKeys(String prefix)
 441  
     {
 442  
         DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
 443  
         List nodes = fetchNodeList(prefix);
 444  
         ConfigurationKey key = new ConfigurationKey();
 445  
 
 446  
         for (Iterator itNodes = nodes.iterator(); itNodes.hasNext();)
 447  
         {
 448  
             Node node = (Node) itNodes.next();
 449  
             for (Iterator it = node.getChildren().iterator(); it.hasNext();)
 450  
             {
 451  
                 ((Node) it.next()).visit(visitor, key);
 452  
             }
 453  
         }
 454  
 
 455  
         return visitor.getKeyList().iterator();
 456  
     }
 457  
 
 458  
     /**
 459  
      * Returns the maximum defined index for the given key. This is useful if
 460  
      * there are multiple values for this key. They can then be addressed
 461  
      * separately by specifying indices from 0 to the return value of this
 462  
      * method.
 463  
      *
 464  
      * @param key the key to be checked
 465  
      * @return the maximum defined index for this key
 466  
      */
 467  
     public int getMaxIndex(String key)
 468  
     {
 469  
         return fetchNodeList(key).size() - 1;
 470  
     }
 471  
 
 472  
     /**
 473  
      * Creates a copy of this object. This new configuration object will contain
 474  
      * copies of all nodes in the same structure.
 475  
      *
 476  
      * @return the copy
 477  
      * @since 1.2
 478  
      */
 479  
     public Object clone()
 480  
     {
 481  
         try
 482  
         {
 483  
             HierarchicalConfiguration copy = (HierarchicalConfiguration) super
 484  
                     .clone();
 485  
 
 486  
             // clone the nodes, too
 487  
             CloneVisitor v = new CloneVisitor();
 488  
             getRoot().visit(v, null);
 489  
             copy.setRoot(v.getClone());
 490  
 
 491  
             return copy;
 492  
         }
 493  
         catch (CloneNotSupportedException cex)
 494  
         {
 495  
             // should not happen
 496  
             throw new ConfigurationRuntimeException(cex);
 497  
         }
 498  
     }
 499  
 
 500  
     /**
 501  
      * Helper method for fetching a list of all nodes that are addressed by the
 502  
      * specified key.
 503  
      *
 504  
      * @param key the key
 505  
      * @return a list with all affected nodes (never <b>null </b>)
 506  
      */
 507  
     protected List fetchNodeList(String key)
 508  
     {
 509  
         List nodes = new LinkedList();
 510  
         findPropertyNodes(new ConfigurationKey(key).iterator(), getRoot(), nodes);
 511  
         return nodes;
 512  
     }
 513  
 
 514  
     /**
 515  
      * Recursive helper method for fetching a property. This method processes
 516  
      * all facets of a configuration key, traverses the tree of properties and
 517  
      * fetches the the nodes of all matching properties.
 518  
      *
 519  
      * @param keyPart the configuration key iterator
 520  
      * @param node the actual node
 521  
      * @param nodes here the found nodes are stored
 522  
      */
 523  
     protected void findPropertyNodes(ConfigurationKey.KeyIterator keyPart, Node node, Collection nodes)
 524  
     {
 525  
         if (!keyPart.hasNext())
 526  
         {
 527  
             nodes.add(node);
 528  
         }
 529  
         else
 530  
         {
 531  
             String key = keyPart.nextKey(true);
 532  
             List children = node.getChildren(key);
 533  
             if (keyPart.hasIndex())
 534  
             {
 535  
                 if (keyPart.getIndex() < children.size() && keyPart.getIndex() >= 0)
 536  
                 {
 537  
                     findPropertyNodes((ConfigurationKey.KeyIterator) keyPart.clone(), (Node) children.get(keyPart
 538  
                             .getIndex()), nodes);
 539  
                 }
 540  
             }
 541  
             else
 542  
             {
 543  
                 for (Iterator it = children.iterator(); it.hasNext();)
 544  
                 {
 545  
                     findPropertyNodes((ConfigurationKey.KeyIterator) keyPart.clone(), (Node) it.next(), nodes);
 546  
                 }
 547  
             }
 548  
         }
 549  
     }
 550  
 
 551  
     /**
 552  
      * Checks if the specified node is defined.
 553  
      *
 554  
      * @param node the node to be checked
 555  
      * @return a flag if this node is defined
 556  
      */
 557  
     protected boolean nodeDefined(Node node)
 558  
     {
 559  
         DefinedVisitor visitor = new DefinedVisitor();
 560  
         node.visit(visitor, null);
 561  
         return visitor.isDefined();
 562  
     }
 563  
 
 564  
     /**
 565  
      * Removes the specified node from this configuration. This method ensures
 566  
      * that parent nodes that become undefined by this operation are also
 567  
      * removed.
 568  
      *
 569  
      * @param node the node to be removed
 570  
      */
 571  
     protected void removeNode(Node node)
 572  
     {
 573  
         Node parent = node.getParent();
 574  
         if (parent != null)
 575  
         {
 576  
             parent.remove(node);
 577  
             if (!nodeDefined(parent))
 578  
             {
 579  
                 removeNode(parent);
 580  
             }
 581  
         }
 582  
     }
 583  
 
 584  
     /**
 585  
      * Clears the value of the specified node. If the node becomes undefined by
 586  
      * this operation, it is removed from the hierarchy.
 587  
      *
 588  
      * @param node the node to be cleard
 589  
      */
 590  
     protected void clearNode(Node node)
 591  
     {
 592  
         node.setValue(null);
 593  
         if (!nodeDefined(node))
 594  
         {
 595  
             removeNode(node);
 596  
         }
 597  
     }
 598  
 
 599  
     /**
 600  
      * Returns a reference to the parent node of an add operation. Nodes for new
 601  
      * properties can be added as children of this node. If the path for the
 602  
      * specified key does not exist so far, it is created now.
 603  
      *
 604  
      * @param keyIt the iterator for the key of the new property
 605  
      * @param startNode the node to start the search with
 606  
      * @return the parent node for the add operation
 607  
      */
 608  
     protected Node fetchAddNode(ConfigurationKey.KeyIterator keyIt, Node startNode)
 609  
     {
 610  
         if (!keyIt.hasNext())
 611  
         {
 612  
             throw new IllegalArgumentException("Key must be defined!");
 613  
         }
 614  
 
 615  
         return createAddPath(keyIt, findLastPathNode(keyIt, startNode));
 616  
     }
 617  
 
 618  
     /**
 619  
      * Finds the last existing node for an add operation. This method traverses
 620  
      * the configuration tree along the specified key. The last existing node on
 621  
      * this path is returned.
 622  
      *
 623  
      * @param keyIt the key iterator
 624  
      * @param node the actual node
 625  
      * @return the last existing node on the given path
 626  
      */
 627  
     protected Node findLastPathNode(ConfigurationKey.KeyIterator keyIt, Node node)
 628  
     {
 629  
         String keyPart = keyIt.nextKey(true);
 630  
 
 631  
         if (keyIt.hasNext())
 632  
         {
 633  
             List list = node.getChildren(keyPart);
 634  
             int idx = (keyIt.hasIndex()) ? keyIt.getIndex() : list.size() - 1;
 635  
             if (idx < 0 || idx >= list.size())
 636  
             {
 637  
                 return node;
 638  
             }
 639  
             else
 640  
             {
 641  
                 return findLastPathNode(keyIt, (Node) list.get(idx));
 642  
             }
 643  
         }
 644  
 
 645  
         else
 646  
         {
 647  
             return node;
 648  
         }
 649  
     }
 650  
 
 651  
     /**
 652  
      * Creates the missing nodes for adding a new property. This method ensures
 653  
      * that there are corresponding nodes for all components of the specified
 654  
      * configuration key.
 655  
      *
 656  
      * @param keyIt the key iterator
 657  
      * @param root the base node of the path to be created
 658  
      * @return the last node of the path
 659  
      */
 660  
     protected Node createAddPath(ConfigurationKey.KeyIterator keyIt, Node root)
 661  
     {
 662  
         if (keyIt.hasNext())
 663  
         {
 664  
             Node child = createNode(keyIt.currentKey(true));
 665  
             root.addChild(child);
 666  
             keyIt.next();
 667  
             return createAddPath(keyIt, child);
 668  
         }
 669  
         else
 670  
         {
 671  
             return root;
 672  
         }
 673  
     }
 674  
 
 675  
     /**
 676  
      * Creates a new <code>Node</code> object with the specified name. This
 677  
      * method can be overloaded in derived classes if a specific node type is
 678  
      * needed. This base implementation always returns a new object of the
 679  
      * <code>Node</code> class.
 680  
      *
 681  
      * @param name the name of the new node
 682  
      * @return the new node
 683  
      */
 684  
     protected Node createNode(String name)
 685  
     {
 686  
         return new Node(name);
 687  
     }
 688  
 
 689  
     /**
 690  
      * A data class for storing (hierarchical) property information. A property
 691  
      * can have a value and an arbitrary number of child properties.
 692  
      *
 693  
      */
 694  
     public static class Node implements Serializable, Cloneable
 695  
     {
 696  
         /** Stores a reference to this node's parent. */
 697  
         private Node parent;
 698  
 
 699  
         /** Stores the name of this node. */
 700  
         private String name;
 701  
 
 702  
         /** Stores the value of this node. */
 703  
         private Object value;
 704  
 
 705  
         /** Stores a reference to an object this node is associated with. */
 706  
         private Object reference;
 707  
 
 708  
         /** Stores the children of this node. */
 709  
         private LinkedMap children; // Explict type here or we
 710  
 
 711  
         // will get a findbugs error
 712  
         // because Map doesn't imply
 713  
         // Serializable
 714  
 
 715  
         /**
 716  
          * Creates a new instance of <code>Node</code>.
 717  
          */
 718  
         public Node()
 719  
         {
 720  
             this(null);
 721  
         }
 722  
 
 723  
         /**
 724  
          * Creates a new instance of <code>Node</code> and sets the name.
 725  
          *
 726  
          * @param name the node's name
 727  
          */
 728  
         public Node(String name)
 729  
         {
 730  
             setName(name);
 731  
         }
 732  
 
 733  
         /**
 734  
          * Creates a new instance of <code>Node</code> and sets the name and the value.
 735  
          *
 736  
          * @param name the node's name
 737  
          * @param value the value
 738  
          */
 739  
         public Node(String name, Object value)
 740  
         {
 741  
             setName(name);
 742  
             setValue(value);
 743  
         }
 744  
 
 745  
         /**
 746  
          * Returns the name of this node.
 747  
          *
 748  
          * @return the node name
 749  
          */
 750  
         public String getName()
 751  
         {
 752  
             return name;
 753  
         }
 754  
 
 755  
         /**
 756  
          * Returns the value of this node.
 757  
          *
 758  
          * @return the node value (may be <b>null </b>)
 759  
          */
 760  
         public Object getValue()
 761  
         {
 762  
             return value;
 763  
         }
 764  
 
 765  
         /**
 766  
          * Returns the parent of this node.
 767  
          *
 768  
          * @return this node's parent (can be <b>null </b>)
 769  
          */
 770  
         public Node getParent()
 771  
         {
 772  
             return parent;
 773  
         }
 774  
 
 775  
         /**
 776  
          * Sets the name of this node.
 777  
          *
 778  
          * @param string the node name
 779  
          */
 780  
         public void setName(String string)
 781  
         {
 782  
             name = string;
 783  
         }
 784  
 
 785  
         /**
 786  
          * Sets the value of this node.
 787  
          *
 788  
          * @param object the node value
 789  
          */
 790  
         public void setValue(Object object)
 791  
         {
 792  
             value = object;
 793  
         }
 794  
 
 795  
         /**
 796  
          * Sets the parent of this node.
 797  
          *
 798  
          * @param node the parent node
 799  
          */
 800  
         public void setParent(Node node)
 801  
         {
 802  
             parent = node;
 803  
         }
 804  
 
 805  
         /**
 806  
          * Returns the reference object for this node.
 807  
          *
 808  
          * @return the reference object
 809  
          */
 810  
         public Object getReference()
 811  
         {
 812  
             return reference;
 813  
         }
 814  
 
 815  
         /**
 816  
          * Sets the reference object for this node. A node can be associated
 817  
          * with a reference object whose concrete meaning is determined by a sub
 818  
          * class of <code>HierarchicalConfiguration</code>. In an XML
 819  
          * configuration e.g. this reference could be an element in a
 820  
          * corresponding XML document. The reference is used by the
 821  
          * <code>BuilderVisitor</code> class when the configuration is stored.
 822  
          *
 823  
          * @param ref the reference object
 824  
          */
 825  
         public void setReference(Object ref)
 826  
         {
 827  
             reference = ref;
 828  
         }
 829  
 
 830  
         /**
 831  
          * Adds the specified child object to this node. Note that there can be
 832  
          * multiple children with the same name.
 833  
          *
 834  
          * @param child the child to be added
 835  
          */
 836  
         public void addChild(Node child)
 837  
         {
 838  
             if (children == null)
 839  
             {
 840  
                 children = new LinkedMap();
 841  
             }
 842  
 
 843  
             List c = (List) children.get(child.getName());
 844  
             if (c == null)
 845  
             {
 846  
                 c = new ArrayList();
 847  
                 children.put(child.getName(), c);
 848  
             }
 849  
 
 850  
             c.add(child);
 851  
             child.setParent(this);
 852  
         }
 853  
 
 854  
         /**
 855  
          * Returns a list with the child nodes of this node.
 856  
          *
 857  
          * @return a list with the children (can be empty, but never <b>null
 858  
          * </b>)
 859  
          */
 860  
         public List getChildren()
 861  
         {
 862  
             List result = new ArrayList();
 863  
 
 864  
             if (children != null)
 865  
             {
 866  
                 for (Iterator it = children.values().iterator(); it.hasNext();)
 867  
                 {
 868  
                     result.addAll((Collection) it.next());
 869  
                 }
 870  
             }
 871  
 
 872  
             return result;
 873  
         }
 874  
 
 875  
         /**
 876  
          * Returns a list with this node's children with the given name.
 877  
          *
 878  
          * @param name the name of the children
 879  
          * @return a list with all chidren with this name; may be empty, but
 880  
          * never <b>null </b>
 881  
          */
 882  
         public List getChildren(String name)
 883  
         {
 884  
             if (name == null || children == class="keyword">null)
 885  
             {
 886  
                 return getChildren();
 887  
             }
 888  
 
 889  
             List list = new ArrayList();
 890  
             List c = (List) children.get(name);
 891  
             if (c != null)
 892  
             {
 893  
                 list.addAll(c);
 894  
             }
 895  
 
 896  
             return list;
 897  
         }
 898  
 
 899  
         /**
 900  
          * Returns a flag whether this node has child elements.
 901  
          *
 902  
          * @return <b>true</b> if there a child node, <b>false</b> otherwise
 903  
          */
 904  
         public boolean hasChildren()
 905  
         {
 906  
             if (children != null)
 907  
             {
 908  
                 for (Iterator it = children.values().iterator(); it.hasNext();)
 909  
                 {
 910  
                     Collection nodes = (Collection) it.next();
 911  
                     if (!nodes.isEmpty())
 912  
                     {
 913  
                         return true;
 914  
                     }
 915  
                 }
 916  
             }
 917  
 
 918  
             return false;
 919  
         }
 920  
 
 921  
         /**
 922  
          * Removes the specified child from this node.
 923  
          *
 924  
          * @param child the child node to be removed
 925  
          * @return a flag if the child could be found
 926  
          */
 927  
         public boolean remove(Node child)
 928  
         {
 929  
             if (children == null)
 930  
             {
 931  
                 return false;
 932  
             }
 933  
 
 934  
             List c = (List) children.get(child.getName());
 935  
             if (c == null)
 936  
             {
 937  
                 return false;
 938  
             }
 939  
 
 940  
             else
 941  
             {
 942  
                 if (c.remove(child))
 943  
                 {
 944  
                     child.removeReference();
 945  
                     if (c.isEmpty())
 946  
                     {
 947  
                         children.remove(child.getName());
 948  
                     }
 949  
                     return true;
 950  
                 }
 951  
                 else
 952  
                 {
 953  
                     return false;
 954  
                 }
 955  
             }
 956  
         }
 957  
 
 958  
         /**
 959  
          * Removes all children with the given name.
 960  
          *
 961  
          * @param name the name of the children to be removed
 962  
          * @return a flag if children with this name existed
 963  
          */
 964  
         public boolean remove(String name)
 965  
         {
 966  
             if (children == null)
 967  
             {
 968  
                 return false;
 969  
             }
 970  
 
 971  
             List nodes = (List) children.remove(name);
 972  
             if (nodes != null)
 973  
             {
 974  
                 nodesRemoved(nodes);
 975  
                 return true;
 976  
             }
 977  
             else
 978  
             {
 979  
                 return false;
 980  
             }
 981  
         }
 982  
 
 983  
         /**
 984  
          * Removes all children of this node.
 985  
          */
 986  
         public void removeChildren()
 987  
         {
 988  
             if (children != null)
 989  
             {
 990  
                 Iterator it = children.values().iterator();
 991  
                 children = null;
 992  
                 while (it.hasNext())
 993  
                 {
 994  
                     nodesRemoved((Collection) it.next());
 995  
                 }
 996  
             }
 997  
         }
 998  
 
 999  
         /**
 1000  
          * A generic method for traversing this node and all of its children.
 1001  
          * This method sends the passed in visitor to this node and all of its
 1002  
          * children.
 1003  
          *
 1004  
          * @param visitor the visitor
 1005  
          * @param key here a configuration key with the name of the root node of
 1006  
          * the iteration can be passed; if this key is not <b>null </b>, the
 1007  
          * full pathes to the visited nodes are builded and passed to the
 1008  
          * visitor's <code>visit()</code> methods
 1009  
          */
 1010  
         public void visit(NodeVisitor visitor, ConfigurationKey key)
 1011  
         {
 1012  
             int length = 0;
 1013  
             if (key != null)
 1014  
             {
 1015  
                 length = key.length();
 1016  
                 if (getName() != null)
 1017  
                 {
 1018  
                     key.append(StringUtils.replace(getName(), String
 1019  
                             .valueOf(ConfigurationKey.PROPERTY_DELIMITER),
 1020  
                             ConfigurationKey.ESCAPED_DELIMITER));
 1021  
                 }
 1022  
             }
 1023  
 
 1024  
             visitor.visitBeforeChildren(this, key);
 1025  
 
 1026  
             if (children != null)
 1027  
             {
 1028  
                 for (Iterator it = children.values().iterator(); it.hasNext() && !visitor.terminate();)
 1029  
                 {
 1030  
                     Collection col = (Collection) it.next();
 1031  
                     for (Iterator it2 = col.iterator(); it2.hasNext() && !visitor.terminate();)
 1032  
                     {
 1033  
                         ((Node) it2.next()).visit(visitor, key);
 1034  
                     }
 1035  
                 }
 1036  
             }
 1037  
 
 1038  
             if (key != null)
 1039  
             {
 1040  
                 key.setLength(length);
 1041  
             }
 1042  
             visitor.visitAfterChildren(this, key);
 1043  
         }
 1044  
 
 1045  
         /**
 1046  
          * Creates a copy of this object. This is not a deep copy, the children
 1047  
          * are not cloned.
 1048  
          *
 1049  
          * @return a copy of this object
 1050  
          */
 1051  
         public Object clone()
 1052  
         {
 1053  
             try
 1054  
             {
 1055  
                 Node copy = (Node) super.clone();
 1056  
                 copy.children = null;
 1057  
                 return copy;
 1058  
             }
 1059  
             catch (CloneNotSupportedException cex)
 1060  
             {
 1061  
                 return null; // should not happen
 1062  
             }
 1063  
         }
 1064  
 
 1065  
         /**
 1066  
          * Deals with the reference when a node is removed. This method is
 1067  
          * called for each removed child node. It can be overloaded in sub
 1068  
          * classes, for which the reference has a concrete meaning and remove
 1069  
          * operations need some update actions. This default implementation is
 1070  
          * empty.
 1071  
          */
 1072  
         protected void removeReference()
 1073  
         {
 1074  
         }
 1075  
 
 1076  
         /**
 1077  
          * Helper method for calling <code>removeReference()</code> on a list
 1078  
          * of removed nodes. Used by methods that can remove multiple child
 1079  
          * nodes in one step.
 1080  
          *
 1081  
          * @param nodes collection with the nodes to be removed
 1082  
          */
 1083  
         private void nodesRemoved(Collection nodes)
 1084  
         {
 1085  
             for (Iterator it = nodes.iterator(); it.hasNext();)
 1086  
             {
 1087  
                 ((Node) it.next()).removeReference();
 1088  
             }
 1089  
         }
 1090  
     }
 1091  
 
 1092  
     /**
 1093  
      * <p>Definition of a visitor class for traversing a node and all of its
 1094  
      * children.</p><p>This class defines the interface of a visitor for
 1095  
      * <code>Node</code> objects and provides a default implementation. The
 1096  
      * method <code>visit()</code> of <code>Node</code> implements a generic
 1097  
      * iteration algorithm based on the <em>Visitor</em> pattern. By providing
 1098  
      * different implementations of visitors it is possible to collect different
 1099  
      * data during the iteration process.</p>
 1100  
      *
 1101  
      */
 1102  1833
     public static class NodeVisitor
 1103  
     {
 1104  
         /**
 1105  
          * Visits the specified node. This method is called during iteration for
 1106  
          * each node before its children have been visited.
 1107  
          *
 1108  
          * @param node the actual node
 1109  
          * @param key the key of this node (may be <b>null </b>)
 1110  
          */
 1111  
         public void visitBeforeChildren(Node node, ConfigurationKey key)
 1112  
         {
 1113  84
         }
 1114  
 
 1115  
         /**
 1116  
          * Visits the specified node after its children have been processed.
 1117  
          * This gives a visitor the opportunity of collecting additional data
 1118  
          * after the child nodes have been visited.
 1119  
          *
 1120  
          * @param node the node to be visited
 1121  
          * @param key the key of this node (may be <b>null </b>)
 1122  
          */
 1123  
         public void visitAfterChildren(Node node, ConfigurationKey key)
 1124  
         {
 1125  7425
         }
 1126  
 
 1127  
         /**
 1128  
          * Returns a flag that indicates if iteration should be stopped. This
 1129  
          * method is called after each visited node. It can be useful for
 1130  
          * visitors that search a specific node. If this node is found, the
 1131  
          * whole process can be stopped. This base implementation always returns
 1132  
          * <b>false </b>.
 1133  
          *
 1134  
          * @return a flag if iteration should be stopped
 1135  
          */
 1136  
         public boolean terminate()
 1137  
         {
 1138  8361
             return false;
 1139  
         }
 1140  
     }
 1141  
 
 1142  
     /**
 1143  
      * A specialized visitor that checks if a node is defined.
 1144  
      * &quot;Defined&quot; in this terms means that the node or at least one of
 1145  
      * its sub nodes is associated with a value.
 1146  
      *
 1147  
      */
 1148  
     static class DefinedVisitor extends NodeVisitor
 1149  
     {
 1150  
         /** Stores the defined flag. */
 1151  
         private boolean defined;
 1152  
 
 1153  
         /**
 1154  
          * Checks if iteration should be stopped. This can be done if the first
 1155  
          * defined node is found.
 1156  
          *
 1157  
          * @return a flag if iteration should be stopped
 1158  
          */
 1159  
         public boolean terminate()
 1160  
         {
 1161  
             return isDefined();
 1162  
         }
 1163  
 
 1164  
         /**
 1165  
          * Visits the node. Checks if a value is defined.
 1166  
          *
 1167  
          * @param node the actual node
 1168  
          * @param key the key of this node
 1169  
          */
 1170  
         public void visitBeforeChildren(Node node, ConfigurationKey key)
 1171  
         {
 1172  
             defined = node.getValue() != null;
 1173  
         }
 1174  
 
 1175  
         /**
 1176  
          * Returns the defined flag.
 1177  
          *
 1178  
          * @return the defined flag
 1179  
          */
 1180  
         public boolean isDefined()
 1181  
         {
 1182  
             return defined;
 1183  
         }
 1184  
     }
 1185  
 
 1186  
     /**
 1187  
      * A specialized visitor that fills a list with keys that are defined in a
 1188  
      * node hierarchy.
 1189  
      *
 1190  
      */
 1191  
     static class DefinedKeysVisitor extends NodeVisitor
 1192  
     {
 1193  
         /** Stores the list to be filled. */
 1194  
         private Set keyList;
 1195  
 
 1196  
         /** Stores a prefix for the keys. */
 1197  
         private String prefix;
 1198  
 
 1199  
         /**
 1200  
          * Default constructor.
 1201  
          */
 1202  
         public DefinedKeysVisitor()
 1203  
         {
 1204  
             keyList = new ListOrderedSet();
 1205  
         }
 1206  
 
 1207  
         /**
 1208  
          * Creates a new <code>DefinedKeysVisitor</code> instance and sets the
 1209  
          * prefix for the keys to fetch.
 1210  
          *
 1211  
          * @param prefix the prefix
 1212  
          */
 1213  
         public DefinedKeysVisitor(String prefix)
 1214  
         {
 1215  
             this();
 1216  
             this.prefix = prefix;
 1217  
         }
 1218  
 
 1219  
         /**
 1220  
          * Returns the list with all defined keys.
 1221  
          *
 1222  
          * @return the list with the defined keys
 1223  
          */
 1224  
         public Set getKeyList()
 1225  
         {
 1226  
             return keyList;
 1227  
         }
 1228  
 
 1229  
         /**
 1230  
          * Visits the specified node. If this node has a value, its key is added
 1231  
          * to the internal list.
 1232  
          *
 1233  
          * @param node the node to be visited
 1234  
          * @param key the key of this node
 1235  
          */
 1236  
         public void visitBeforeChildren(Node node, ConfigurationKey key)
 1237  
         {
 1238  
             if (node.getValue() != null && key != class="keyword">null)
 1239  
             {
 1240  
                 addKey(key);
 1241  
             }
 1242  
         }
 1243  
 
 1244  
         /**
 1245  
          * Adds the specified key to the internal list.
 1246  
          *
 1247  
          * @param key the key to add
 1248  
          */
 1249  
         protected void addKey(ConfigurationKey key)
 1250  
         {
 1251  
             if (prefix == null)
 1252  
             {
 1253  
                 keyList.add(key.toString());
 1254  
             }
 1255  
             else
 1256  
             {
 1257  
                 StringBuffer buf = new StringBuffer(prefix);
 1258  
                 if (!key.isAttributeKey())
 1259  
                 {
 1260  
                     buf.append(ConfigurationKey.PROPERTY_DELIMITER);
 1261  
                 }
 1262  
                 buf.append(key);
 1263  
                 keyList.add(buf.toString());
 1264  
             }
 1265  
         }
 1266  
     }
 1267  
 
 1268  
     /**
 1269  
      * A specialized visitor that is able to create a deep copy of a node
 1270  
      * hierarchy.
 1271  
      *
 1272  
      */
 1273  
     static class CloneVisitor extends NodeVisitor
 1274  
     {
 1275  
         /** A stack with the actual object to be copied. */
 1276  
         private Stack copyStack;
 1277  
 
 1278  
         /** Stores the result of the clone process. */
 1279  
         private Node result;
 1280  
 
 1281  
         /**
 1282  
          * Creates a new instance of <code>CloneVisitor</code>.
 1283  
          */
 1284  
         public CloneVisitor()
 1285  
         {
 1286  
             copyStack = new Stack();
 1287  
         }
 1288  
 
 1289  
         /**
 1290  
          * Visits the specified node after its children have been processed.
 1291  
          *
 1292  
          * @param node the node
 1293  
          * @param key the key of this node
 1294  
          */
 1295  
         public void visitAfterChildren(Node node, ConfigurationKey key)
 1296  
         {
 1297  
             Node copy = (Node) copyStack.pop();
 1298  
             if (copyStack.isEmpty())
 1299  
             {
 1300  
                 result = copy;
 1301  
             }
 1302  
         }
 1303  
 
 1304  
         /**
 1305  
          * Visits and copies the specified node.
 1306  
          *
 1307  
          * @param node the node
 1308  
          * @param key the key of this node
 1309  
          */
 1310  
         public void visitBeforeChildren(Node node, ConfigurationKey key)
 1311  
         {
 1312  
             Node copy = (Node) node.clone();
 1313  
 
 1314  
             if (!copyStack.isEmpty())
 1315  
             {
 1316  
                 ((Node) copyStack.peek()).addChild(copy);
 1317  
             }
 1318  
 
 1319  
             copyStack.push(copy);
 1320  
         }
 1321  
 
 1322  
         /**
 1323  
          * Returns the result of the clone process. This is the root node of the
 1324  
          * cloned node hierarchy.
 1325  
          *
 1326  
          * @return the cloned root node
 1327  
          */
 1328  
         public Node getClone()
 1329  
         {
 1330  
             return result;
 1331  
         }
 1332  
     }
 1333  
 
 1334  
     /**
 1335  
      * A specialized visitor base class that can be used for storing the tree of
 1336  
      * configuration nodes. The basic idea is that each node can be associated
 1337  
      * with a reference object. This reference object has a concrete meaning in
 1338  
      * a derived class, e.g. an entry in a JNDI context or an XML element. When
 1339  
      * the configuration tree is set up, the <code>load()</code> method is
 1340  
      * responsible for setting the reference objects. When the configuration
 1341  
      * tree is later modified, new nodes do not have a defined reference object.
 1342  
      * This visitor class processes all nodes and finds the ones without a
 1343  
      * defined reference object. For those nodes the <code>insert()</code>
 1344  
      * method is called, which must be defined in concrete sub classes. This
 1345  
      * method can perform all steps to integrate the new node into the original
 1346  
      * structure.
 1347  
      *
 1348  
      */
 1349  
     protected abstract static class BuilderVisitor extends NodeVisitor
 1350  
     {
 1351  
         /**
 1352  
          * Visits the specified node before its children have been traversed.
 1353  
          *
 1354  
          * @param node the node to visit
 1355  
          * @param key the current key
 1356  
          */
 1357  
         public void visitBeforeChildren(Node node, ConfigurationKey key)
 1358  
         {
 1359  
             Iterator children = node.getChildren().iterator();
 1360  
             Node sibling1 = null;
 1361  
             Node nd = null;
 1362  
 
 1363  
             while (children.hasNext())
 1364  
             {
 1365  
                 // find the next new node
 1366  
                 do
 1367  
                 {
 1368  
                     sibling1 = nd;
 1369  
                     nd = (Node) children.next();
 1370  
                 } while (nd.getReference() != null && children.hasNext());
 1371  
 
 1372  
                 if (nd.getReference() == null)
 1373  
                 {
 1374  
                     // find all following new nodes
 1375  
                     List newNodes = new LinkedList();
 1376  
                     newNodes.add(nd);
 1377  
                     while (children.hasNext())
 1378  
                     {
 1379  
                         nd = (Node) children.next();
 1380  
                         if (nd.getReference() == null)
 1381  
                         {
 1382  
                             newNodes.add(nd);
 1383  
                         }
 1384  
                         else
 1385  
                         {
 1386  
                             break;
 1387  
                         }
 1388  
                     }
 1389  
 
 1390  
                     // Insert all new nodes
 1391  
                     Node sibling2 = (nd.getReference() == null) ? class="keyword">null : nd;
 1392  
                     for (Iterator it = newNodes.iterator(); it.hasNext();)
 1393  
                     {
 1394  
                         Node insertNode = (Node) it.next();
 1395  
                         if (insertNode.getReference() == null)
 1396  
                         {
 1397  
                             Object ref = insert(insertNode, node, sibling1, sibling2);
 1398  
                             if (ref != null)
 1399  
                             {
 1400  
                                 insertNode.setReference(ref);
 1401  
                             }
 1402  
                             sibling1 = insertNode;
 1403  
                         }
 1404  
                     }
 1405  
                 }
 1406  
             }
 1407  
         }
 1408  
 
 1409  
         /**
 1410  
          * Inserts a new node into the structure constructed by this builder.
 1411  
          * This method is called for each node that has been added to the
 1412  
          * configuration tree after the configuration has been loaded from its
 1413  
          * source. These new nodes have to be inserted into the original
 1414  
          * structure. The passed in nodes define the position of the node to be
 1415  
          * inserted: its parent and the siblings between to insert. The return
 1416  
          * value is interpreted as the new reference of the affected
 1417  
          * <code>Node</code> object; if it is not <b>null </b>, it is passed
 1418  
          * to the node's <code>setReference()</code> method.
 1419  
          *
 1420  
          * @param newNode the node to be inserted
 1421  
          * @param parent the parent node
 1422  
          * @param sibling1 the sibling after which the node is to be inserted;
 1423  
          * can be <b>null </b> if the new node is going to be the first child
 1424  
          * node
 1425  
          * @param sibling2 the sibling before which the node is to be inserted;
 1426  
          * can be <b>null </b> if the new node is going to be the last child
 1427  
          * node
 1428  
          * @return the reference object for the node to be inserted
 1429  
          */
 1430  
         protected abstract Object insert(Node newNode, Node parent, Node sibling1, Node sibling2);
 1431  
     }
 1432  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.