Coverage report

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

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

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