commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject svn commit: r366201 - in /jakarta/commons/proper/configuration/trunk/src: java/org/apache/commons/configuration/ java/org/apache/commons/configuration/tree/ test/org/apache/commons/configuration/
Date Thu, 05 Jan 2006 15:34:29 GMT
Author: oheger
Date: Thu Jan  5 07:34:20 2006
New Revision: 366201

URL: http://svn.apache.org/viewcvs?rev=366201&view=rev
Log:
Incorporated new expression engine classes into HierarchicalConfiguration; this class now
supports pluggable expression engines.

Added:
    jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ConfigurationNodeVisitorAdapter.java
  (with props)
Modified:
    jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java
    jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestHierarchicalConfiguration.java

Modified: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java?rev=366201&r1=366200&r2=366201&view=diff
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java
(original)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalConfiguration.java
Thu Jan  5 07:34:20 2006
@@ -27,7 +27,12 @@
 
 import org.apache.commons.collections.set.ListOrderedSet;
 import org.apache.commons.collections.iterators.SingletonIterator;
+import org.apache.commons.configuration.tree.ConfigurationNode;
+import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
 import org.apache.commons.configuration.tree.DefaultConfigurationNode;
+import org.apache.commons.configuration.tree.DefaultExpressionEngine;
+import org.apache.commons.configuration.tree.ExpressionEngine;
+import org.apache.commons.configuration.tree.NodeAddData;
 import org.apache.commons.lang.StringUtils;
 
 /**
@@ -95,12 +100,15 @@
  */
 public class HierarchicalConfiguration extends AbstractConfiguration implements Serializable,
Cloneable
 {
-    /** Constant for a new dummy key. */
-    private static final String NEW_KEY = "newKey";
+    /** Stores the default expression engine to be used for new objects.*/
+    private static ExpressionEngine defaultExpressionEngine = new DefaultExpressionEngine();
 
     /** Stores the root node of this configuration. */
     private Node root = new Node();
 
+    /** Stores the expression engine for this instance.*/
+    private ExpressionEngine expressionEngine;
+
     /**
      * Returns the root node of this hierarchical configuration.
      *
@@ -126,8 +134,66 @@
     }
 
     /**
-     * Fetches the specified property. Performs a recursive lookup in the tree
-     * with the configuration properties.
+     * Returns the default expression engine.
+     *
+     * @return the default expression engine
+     * @since 1.3
+     */
+    public static ExpressionEngine getDefaultExpressionEngine()
+    {
+        return defaultExpressionEngine;
+    }
+
+    /**
+     * Sets the default expression engine. This expression engine will be used
+     * if no specific engine was set for an instance. It is shared between all
+     * hierarchical configuration instances. So modifying its properties will
+     * impact all instances, for which no specific engine is set.
+     *
+     * @param engine the new default expression engine
+     * @since 1.3
+     */
+    public static void setDefaultExpressionEngine(ExpressionEngine engine)
+    {
+        if (engine == null)
+        {
+            throw new IllegalArgumentException(
+                    "Default expression engine must not be null!");
+        }
+        defaultExpressionEngine = engine;
+    }
+
+    /**
+     * Returns the expression engine used by this configuration. This method
+     * will never return <b>null</b>; if no specific expression engine was set,
+     * the default expression engine will be returned.
+     *
+     * @return the current expression engine
+     * @since 1.3
+     */
+    public ExpressionEngine getExpressionEngine()
+    {
+        return (expressionEngine != null) ? expressionEngine
+                : getDefaultExpressionEngine();
+    }
+
+    /**
+     * Sets the expression engine to be used by this configuration. All property
+     * keys this configuration has to deal with will be interpreted by this
+     * engine.
+     *
+     * @param expressionEngine the new expression engine; can be <b>null</b>,
+     * then the default expression engine will be used
+     * @since 1.3
+     */
+    public void setExpressionEngine(ExpressionEngine expressionEngine)
+    {
+        this.expressionEngine = expressionEngine;
+    }
+
+    /**
+     * Fetches the specified property. This task is delegated to the associated
+     * expression engine.
      *
      * @param key the key to be looked up
      * @return the found value
@@ -164,92 +230,18 @@
     }
 
     /**
-     * <p>Adds the property with the specified key.</p><p>To be able to
deal
-     * with the structure supported by this configuration implementation the
-     * passed in key is of importance, especially the indices it might contain.
-     * The following example should clearify this: Suppose the actual
-     * configuration contains the following elements:</p><p>
-     *
-     * <pre>
-     * tables
-     *    +-- table
-     *            +-- name = user
-     *            +-- fields
-     *                    +-- field
-     *                            +-- name = uid
-     *                    +-- field
-     *                            +-- name = firstName
-     *                    ...
-     *    +-- table
-     *            +-- name = documents
-     *            +-- fields
-     *                   ...
-     * </pre>
-     *
-     * </p><p>In this example a database structure is defined, e.g. all fields
-     * of the first table could be accessed using the key
-     * <code>tables.table(0).fields.field.name</code>. If now properties are
-     * to be added, it must be exactly specified at which position in the
-     * hierarchy the new property is to be inserted. So to add a new field name
-     * to a table it is not enough to say just</p><p>
-     *
-     * <pre>
-     * config.addProperty("tables.table.fields.field.name", "newField");
-     * </pre>
-     *
-     * </p><p>The statement given above contains some ambiguity. For instance
-     * it is not clear, to which table the new field should be added. If this
-     * method finds such an ambiguity, it is resolved by following the last
-     * valid path. Here this would be the last table. The same is true for the
-     * <code>field</code>; because there are multiple fields and no explicit
-     * index is provided, a new <code>name</code> property would be added to
-     * the last field - which is propably not what was desired.</p><p>To make
-     * things clear explicit indices should be provided whenever possible. In
-     * the example above the exact table could be specified by providing an
-     * index for the <code>table</code> element as in
-     * <code>tables.table(1).fields</code>. By specifying an index it can
-     * also be expressed that at a given position in the configuration tree a
-     * new branch should be added. In the example above we did not want to add
-     * an additional <code>name</code> element to the last field of the table,
-     * but we want a complete new <code>field</code> element. This can be
-     * achieved by specifying an invalid index (like -1) after the element where
-     * a new branch should be created. Given this our example would run:</p>
-     * <p>
-     *
-     * <pre>
-     * config.addProperty("tables.table(1).fields.field(-1).name", "newField");
-     * </pre>
-     *
-     * </p><p>With this notation it is possible to add new branches
-     * everywhere. We could for instance create a new <code>table</code>
-     * element by specifying</p><p>
-     *
-     * <pre>
-     * config.addProperty("tables.table(-1).fields.field.name", "newField2");
-     * </pre>
-     *
-     * </p><p>(Note that because after the <code>table</code> element
a new
-     * branch is created indices in following elements are not relevant; the
-     * branch is new so there cannot be any ambiguities.)</p>
+     * Adds the property with the specified key. This task will be delegated to
+     * the associated <code>ExpressionEngine</code>, so the passed in key
+     * must match the requirements of this implementation.
      *
      * @param key the key of the new property
      * @param obj the value of the new property
      */
     protected void addPropertyDirect(String key, Object obj)
     {
-        ConfigurationKey.KeyIterator it = new ConfigurationKey(key).iterator();
-        Node parent = fetchAddNode(it, getRoot());
-
-        Node child = createNode(it.currentKey(false));
-        child.setValue(obj);
-        if (it.isAttribute())
-        {
-            parent.addAttribute(child);
-        }
-        else
-        {
-            parent.addChild(child);
-        }
+        NodeAddData data = getExpressionEngine().prepareAdd(getRoot(), key);
+        ConfigurationNode node = processNodeAddData(data);
+        node.setValue(obj);
     }
 
     /**
@@ -258,7 +250,9 @@
      * instead of a single property a whole collection of nodes can be added -
      * and thus complete configuration sub trees. E.g. with this method it is
      * possible to add parts of another <code>HierarchicalConfiguration</code>
-     * object to this object.
+     * object to this object. If the passed in key refers to an existing and
+     * unique node, the new nodes are added to this node. Otherwise a new node
+     * will be created at the specified position in the hierarchy.
      *
      * @param key the key where the nodes are to be added; can be <b>null </b>,
      * then they are added to the root node
@@ -272,26 +266,36 @@
             return;
         }
 
-        Node parent;
-        if (StringUtils.isEmpty(key))
+        ConfigurationNode parent;
+        List target = fetchNodeList(key);
+        if (target.size() == 1)
         {
-            parent = getRoot();
+            // existing unique key
+            parent = (ConfigurationNode) target.get(0);
         }
         else
         {
-            ConfigurationKey.KeyIterator kit = new ConfigurationKey(key).iterator();
-            parent = fetchAddNode(kit, getRoot());
-
-            // fetchAddNode() does not really fetch the last component,
-            // but one before. So we must perform an additional step.
-            ConfigurationKey keyNew = new ConfigurationKey(kit.currentKey(true));
-            keyNew.append(NEW_KEY);
-            parent = fetchAddNode(keyNew.iterator(), parent);
+            // otherwise perform an add operation
+            parent = processNodeAddData(getExpressionEngine().prepareAdd(
+                    getRoot(), key));
         }
 
+        if (parent.isAttribute())
+        {
+            throw new IllegalArgumentException(
+                    "Cannot add nodes to an attribute node!");
+        }
         for (Iterator it = nodes.iterator(); it.hasNext();)
         {
-            parent.addChild((Node) it.next());
+            ConfigurationNode child = (ConfigurationNode) it.next();
+            if (child.isAttribute())
+            {
+                parent.addAttribute(child);
+            }
+            else
+            {
+                parent.addChild(child);
+            }
         }
     }
 
@@ -440,7 +444,7 @@
     public Iterator getKeys()
     {
         DefinedKeysVisitor visitor = new DefinedKeysVisitor();
-        getRoot().visit(visitor, new ConfigurationKey());
+        getRoot().visit(visitor);
 
         return visitor.getKeyList().iterator();
     }
@@ -457,18 +461,17 @@
     {
         DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
         List nodes = fetchNodeList(prefix);
-        ConfigurationKey key = new ConfigurationKey();
 
         for (Iterator itNodes = nodes.iterator(); itNodes.hasNext();)
         {
             Node node = (Node) itNodes.next();
             for (Iterator it = node.getChildren().iterator(); it.hasNext();)
             {
-                ((Node) it.next()).visit(visitor, key);
+                ((Node) it.next()).visit(visitor);
             }
             for (Iterator it = node.getAttributes().iterator(); it.hasNext();)
             {
-                ((Node) it.next()).visit(visitor, key);
+                ((Node) it.next()).visit(visitor);
             }
         }
 
@@ -526,9 +529,7 @@
      */
     protected List fetchNodeList(String key)
     {
-        List nodes = new LinkedList();
-        findPropertyNodes(new ConfigurationKey(key).iterator(), getRoot(), nodes);
-        return nodes;
+        return getExpressionEngine().query(getRoot(), key);
     }
 
     /**
@@ -539,33 +540,14 @@
      * @param keyPart the configuration key iterator
      * @param node the actual node
      * @param nodes here the found nodes are stored
+     * @deprecated Property keys are now evaluated by the expression engine
+     * associated with the configuration; this method will no longer be called.
+     * If you want to modify the way properties are looked up, consider
+     * implementing you own <code>ExpressionEngine</code> implementation.
      */
-    protected void findPropertyNodes(ConfigurationKey.KeyIterator keyPart, Node node, Collection
nodes)
+    protected void findPropertyNodes(ConfigurationKey.KeyIterator keyPart,
+            Node node, Collection nodes)
     {
-        if (!keyPart.hasNext())
-        {
-            nodes.add(node);
-        }
-        else
-        {
-            String key = keyPart.nextKey(false);
-            List children = keyPart.isAttribute() ? node.getAttributes(key) : node.getChildren(key);
-            if (keyPart.hasIndex())
-            {
-                if (keyPart.getIndex() < children.size() && keyPart.getIndex()
>= 0)
-                {
-                    findPropertyNodes((ConfigurationKey.KeyIterator) keyPart.clone(), (Node)
children.get(keyPart
-                            .getIndex()), nodes);
-                }
-            }
-            else
-            {
-                for (Iterator it = children.iterator(); it.hasNext();)
-                {
-                    findPropertyNodes((ConfigurationKey.KeyIterator) keyPart.clone(), (Node)
it.next(), nodes);
-                }
-            }
-        }
     }
 
     /**
@@ -624,15 +606,15 @@
      * @param keyIt the iterator for the key of the new property
      * @param startNode the node to start the search with
      * @return the parent node for the add operation
+     * @deprecated Adding new properties is now to a major part delegated to the
+     * <code>ExpressionEngine</code> associated with this configuration instance.
+     * This method will no longer be called. Developers who want to modify the
+     * process of adding new properties should consider implementing their own
+     * expression engine.
      */
     protected Node fetchAddNode(ConfigurationKey.KeyIterator keyIt, Node startNode)
     {
-        if (!keyIt.hasNext())
-        {
-            throw new IllegalArgumentException("Key must be defined!");
-        }
-
-        return createAddPath(keyIt, findLastPathNode(keyIt, startNode));
+        return null;
     }
 
     /**
@@ -643,29 +625,15 @@
      * @param keyIt the key iterator
      * @param node the actual node
      * @return the last existing node on the given path
+     * @deprecated Adding new properties is now to a major part delegated to the
+     * <code>ExpressionEngine</code> associated with this configuration instance.
+     * This method will no longer be called. Developers who want to modify the
+     * process of adding new properties should consider implementing their own
+     * expression engine.
      */
     protected Node findLastPathNode(ConfigurationKey.KeyIterator keyIt, Node node)
     {
-        String keyPart = keyIt.nextKey(false);
-
-        if (keyIt.hasNext())
-        {
-            List list = keyIt.isAttribute() ? node.getAttributes(keyPart) : node.getChildren(keyPart);
-            int idx = (keyIt.hasIndex()) ? keyIt.getIndex() : list.size() - 1;
-            if (idx < 0 || idx >= list.size())
-            {
-                return node;
-            }
-            else
-            {
-                return findLastPathNode(keyIt, (Node) list.get(idx));
-            }
-        }
-
-        else
-        {
-            return node;
-        }
+        return null;
     }
 
     /**
@@ -676,21 +644,15 @@
      * @param keyIt the key iterator
      * @param root the base node of the path to be created
      * @return the last node of the path
+     * @deprecated Adding new properties is now to a major part delegated to the
+     * <code>ExpressionEngine</code> associated with this configuration instance.
+     * This method will no longer be called. Developers who want to modify the
+     * process of adding new properties should consider implementing their own
+     * expression engine.
      */
     protected Node createAddPath(ConfigurationKey.KeyIterator keyIt, Node root)
     {
-        if (keyIt.hasNext())
-        {
-            Node child = createNode(keyIt.currentKey(false));
-            child.setAttribute(keyIt.isAttribute());
-            root.addChild(child);
-            keyIt.next();
-            return createAddPath(keyIt, child);
-        }
-        else
-        {
-            return root;
-        }
+        return null;
     }
 
     /**
@@ -708,6 +670,39 @@
     }
 
     /**
+     * Helper method for processing a node add data object obtained from the
+     * expression engine. This method will create all new nodes.
+     *
+     * @param data the data object
+     * @return the new node
+     * @since 1.3
+     */
+    private ConfigurationNode processNodeAddData(NodeAddData data)
+    {
+        ConfigurationNode node = data.getParent();
+
+        // Create missing nodes on the path
+        for (Iterator it = data.getPathNodes().iterator(); it.hasNext();)
+        {
+            ConfigurationNode child = createNode((String) it.next());
+            node.addChild(child);
+            node = child;
+        }
+
+        // Add new target node
+        ConfigurationNode child = createNode(data.getNewNodeName());
+        if (data.isAttribute())
+        {
+            node.addAttribute(child);
+        }
+        else
+        {
+            node.addChild(child);
+        }
+        return child;
+    }
+
+    /**
      * A data class for storing (hierarchical) property information. A property
      * can have a value and an arbitrary number of child properties. From version 1.3 on
this class
      * is only a thin wrapper over the <code>{@link org.apache.commons.configuration.tree.DefaultConfigurationNode
DefaultconfigurationNode}</code>
@@ -947,15 +942,14 @@
     /**
      * A specialized visitor that fills a list with keys that are defined in a
      * node hierarchy.
-     *
      */
-    static class DefinedKeysVisitor extends NodeVisitor
+    class DefinedKeysVisitor extends ConfigurationNodeVisitorAdapter
     {
         /** Stores the list to be filled. */
         private Set keyList;
 
-        /** Stores a prefix for the keys. */
-        private String prefix;
+        /** A stack with the keys of the already processed nodes. */
+        private Stack parentKeys;
 
         /**
          * Default constructor.
@@ -963,6 +957,7 @@
         public DefinedKeysVisitor()
         {
             keyList = new ListOrderedSet();
+            parentKeys = new Stack();
         }
 
         /**
@@ -974,7 +969,7 @@
         public DefinedKeysVisitor(String prefix)
         {
             this();
-            this.prefix = prefix;
+            parentKeys.push(prefix);
         }
 
         /**
@@ -988,40 +983,31 @@
         }
 
         /**
-         * Visits the specified node. If this node has a value, its key is added
-         * to the internal list.
+         * Visits the node after its children has been processed. Removes this
+         * node's key from the stack.
          *
-         * @param node the node to be visited
-         * @param key the key of this node
+         * @param node the node
          */
-        public void visitBeforeChildren(Node node, ConfigurationKey key)
+        public void visitAfterChildren(ConfigurationNode node)
         {
-            if (node.getValue() != null && key != null)
-            {
-                addKey(key);
-            }
+            parentKeys.pop();
         }
 
         /**
-         * Adds the specified key to the internal list.
+         * Visits the specified node. If this node has a value, its key is added
+         * to the internal list.
          *
-         * @param key the key to add
+         * @param node the node to be visited
          */
-        protected void addKey(ConfigurationKey key)
+        public void visitBeforeChildren(ConfigurationNode node)
         {
-            if (prefix == null)
-            {
-                keyList.add(key.toString());
-            }
-            else
+            String parentKey = parentKeys.isEmpty() ? null
+                    : (String) parentKeys.peek();
+            String key = getExpressionEngine().nodeKey(node, parentKey);
+            parentKeys.push(key);
+            if (node.getValue() != null)
             {
-                StringBuffer buf = new StringBuffer(prefix);
-                if (!key.isAttributeKey())
-                {
-                    buf.append(ConfigurationKey.PROPERTY_DELIMITER);
-                }
-                buf.append(key);
-                keyList.add(buf.toString());
+                keyList.add(key);
             }
         }
     }

Added: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ConfigurationNodeVisitorAdapter.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ConfigurationNodeVisitorAdapter.java?rev=366201&view=auto
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ConfigurationNodeVisitorAdapter.java
(added)
+++ jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ConfigurationNodeVisitorAdapter.java
Thu Jan  5 07:34:20 2006
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2005-2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.configuration.tree;
+
+/**
+ * <p>
+ * A simple adapter class that simplyfies writing custom node visitor
+ * implementations.
+ * </p>
+ * <p>
+ * This class provides dummy implementations for the methods defined in the
+ * <code>ConfigurationNodeVisitor</code> interface. Derived classes only need
+ * to override the methods they really need.
+ * </p>
+ *
+ * @author Oliver Heger
+ */
+public class ConfigurationNodeVisitorAdapter implements
+        ConfigurationNodeVisitor
+{
+    /**
+     * Empty dummy implementation of this interface method.
+     *
+     * @param node the node
+     */
+    public void visitBeforeChildren(ConfigurationNode node)
+    {
+    }
+
+    /**
+     * Empty dummy implementation of this interface method.
+     *
+     * @param node the node
+     */
+    public void visitAfterChildren(ConfigurationNode node)
+    {
+    }
+
+    /**
+     * Dummy implementation of this interface method. Returns always <b>false</b>.
+     *
+     * @return the terminate flag
+     */
+    public boolean terminate()
+    {
+        return false;
+    }
+}

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ConfigurationNodeVisitorAdapter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ConfigurationNodeVisitorAdapter.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/ConfigurationNodeVisitorAdapter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestHierarchicalConfiguration.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestHierarchicalConfiguration.java?rev=366201&r1=366200&r2=366201&view=diff
==============================================================================
--- jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestHierarchicalConfiguration.java
(original)
+++ jakarta/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestHierarchicalConfiguration.java
Thu Jan  5 07:34:20 2006
@@ -1,5 +1,5 @@
 /*
- * Copyright 2001-2005 The Apache Software Foundation.
+ * Copyright 2001-2006 The Apache Software Foundation.
  *
  * Licensed under the Apache License, Version 2.0 (the "License")
  * you may not use this file except in compliance with the License.
@@ -23,7 +23,10 @@
 import java.util.List;
 import java.util.Set;
 
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.configuration.HierarchicalConfiguration.Node;
+import org.apache.commons.configuration.tree.DefaultExpressionEngine;
+import org.apache.commons.configuration.tree.ExpressionEngine;
 
 import junit.framework.TestCase;
 
@@ -387,18 +390,44 @@
         assertEquals("birthDate", config.getString("tables.table(0).fields.field(5).name"));
         assertEquals("lastLogin", config.getString("tables.table(0).fields.field(6).name"));
         assertEquals("language", config.getString("tables.table(0).fields.field(7).name"));
+    }
+
+    /**
+     * Tests the addNodes() method when the provided key does not exist. In
+     * this case, a new node (or even a complete new branch) will be created.
+     */
+    public void testAddNodesForNonExistingKey()
+    {
+        Collection nodes = new ArrayList();
+        nodes.add(createNode("usr", "scott"));
+        Node nd = createNode("pwd", "tiger");
+        nd.setAttribute(true);
+        nodes.add(nd);
+        config.addNodes("database.connection.settings", nodes);
         
+        assertEquals("Usr node not found", "scott", config.getString("database.connection.settings.usr"));
+        assertEquals("Pwd node not found", "tiger", config.getString("database.connection.settings[@pwd]"));
+    }
+    
+    /**
+     * Tests the addNodes() method when the new nodes should be added to an
+     * attribute node. This is not allowed.
+     */
+    public void testAddNodesWithAttributeKey()
+    {
+        Collection nodes = new ArrayList();
+        nodes.add(createNode("testNode", "yes"));
         try
         {
-            config.addNodes(".", nodes);
-            fail("Could use empty key!");
+            config.addNodes("database.connection[@settings]", nodes);
+            fail("Could add nodes to an attribute node!");
         }
         catch(IllegalArgumentException iex)
         {
             //ok
         }
     }
-    
+
     /**
      * Tests removing children from a configuration node.
      */
@@ -452,6 +481,49 @@
     }
     
     /**
+     * Tests setting a custom expression engine, which uses a slightly different
+     * syntax.
+     */
+    public void testSetExpressionEngine()
+    {
+        config.setExpressionEngine(null);
+        assertNotNull("Expression engine is null", config.getExpressionEngine());
+        assertSame("Default engine is not used", HierarchicalConfiguration
+                .getDefaultExpressionEngine(), config.getExpressionEngine());
+
+        config.setExpressionEngine(createAlternativeExpressionEngine());
+        checkAlternativeSyntax();
+    }
+
+    /**
+     * Tests setting the default expression engine. This should impact all
+     * configuration instances that do not have their own engine.
+     */
+    public void testSetDefaultExpressionEngine()
+    {
+        HierarchicalConfiguration
+                .setDefaultExpressionEngine(createAlternativeExpressionEngine());
+        checkAlternativeSyntax();
+    }
+
+    /**
+     * Tests setting the default expression engine to null. This should not be
+     * allowed.
+     */
+    public void testSetDefaultExpressionEngineNull()
+    {
+        try
+        {
+            HierarchicalConfiguration.setDefaultExpressionEngine(null);
+            fail("Could set default expression engine to null!");
+        }
+        catch (IllegalArgumentException iex)
+        {
+            // ok
+        }
+    }
+
+    /**
      * Helper method for testing the getKeys(String) method.
      * @param prefix the key to pass into getKeys()
      * @param expected the expected result
@@ -482,7 +554,52 @@
     }
     
     /**
+     * Helper method for checking keys using an alternative syntax.
+     */
+    private void checkAlternativeSyntax()
+    {
+        assertNull(config.getProperty("tables/table/resultset"));
+        assertNull(config.getProperty("tables/table/fields/field"));
+
+        Object prop = config.getProperty("tables/table[0]/fields/field/name");
+        assertNotNull(prop);
+        assertTrue(prop instanceof Collection);
+        assertEquals(5, ((Collection) prop).size());
+
+        prop = config.getProperty("tables/table/fields/field/name");
+        assertNotNull(prop);
+        assertTrue(prop instanceof Collection);
+        assertEquals(10, ((Collection) prop).size());
+
+        prop = config.getProperty("tables/table/fields/field[3]/name");
+        assertNotNull(prop);
+        assertTrue(prop instanceof Collection);
+        assertEquals(2, ((Collection) prop).size());
+
+        prop = config.getProperty("tables/table[1]/fields/field[2]/name");
+        assertNotNull(prop);
+        assertEquals("creationDate", prop.toString());
+
+        Set keys = new HashSet();
+        CollectionUtils.addAll(keys, config.getKeys());
+        assertEquals("Wrong number of defined keys", 2, keys.size());
+        assertTrue("Key not found", keys.contains("tables/table/name"));
+        assertTrue("Key not found", keys
+                .contains("tables/table/fields/field/name"));
+    }
+
+    private ExpressionEngine createAlternativeExpressionEngine()
+    {
+        DefaultExpressionEngine engine = new DefaultExpressionEngine();
+        engine.setPropertyDelimiter("/");
+        engine.setIndexStart("[");
+        engine.setIndexEnd("]");
+        return engine;
+    }
+    
+    /**
      * Helper method for creating a field node with its children.
+     * 
      * @param name the name of the field
      * @return the field node
      */



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message