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