    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.util.ArrayList;
    21  import java.util.Collection;
    22  import java.util.HashSet;
    23  import java.util.Iterator;
    24  import java.util.List;
    25  import java.util.Set;
    26
    27  import junit.framework.TestCase;
    28
    29  import org.apache.commons.collections.CollectionUtils;
    30  import org.apache.commons.configuration.HierarchicalConfiguration.Node;
    31  import org.apache.commons.configuration.event.ConfigurationEvent;
    32  import org.apache.commons.configuration.event.ConfigurationListener;
    33  import org.apache.commons.configuration.tree.ConfigurationNode;
    34  import org.apache.commons.configuration.tree.DefaultConfigurationNode;
    35  import org.apache.commons.configuration.tree.DefaultExpressionEngine;
    36  import org.apache.commons.configuration.tree.ExpressionEngine;
    37
    38  /***
    39   * Test class for HierarchicalConfiguration.
    40   *
    41   * @version $Id: TestHierarchicalConfiguration.java 722238 2008-12-01 21:28:31Z oheger $
    42   */
    43  public class TestHierarchicalConfiguration extends TestCase
    44  {
    45      private static String[] tables = { "users", "documents" };
    46
    47      private static String[][] fields =
    48      {
    49          { "uid", "uname", "firstName", "lastName", "email" },
    50          { "docid", "name", "creationDate", "authorID", "version" }
    51      };
    52
    53      private HierarchicalConfiguration config;
    54
    55      protected void setUp() throws Exception
    56      {
    57          /***
    58           * Initialize the configuration with the following structure:
    59           *
    60           * tables
    61           *      table
    62           *         name
    63           *         fields
    64           *             field
    65           *                 name
    66           *             field
    67           *                 name
    68           */
    69          config = new HierarchicalConfiguration();
    70          HierarchicalConfiguration.Node nodeTables = createNode("tables", null);
    71          for(int i = 0; i < tables.length; i++)
    72          {
    73              HierarchicalConfiguration.Node nodeTable = createNode("table", null);
    74              nodeTables.addChild(nodeTable);
    75              HierarchicalConfiguration.Node nodeName = createNode("name", tables[i]);
    76              nodeTable.addChild(nodeName);
    77              HierarchicalConfiguration.Node nodeFields = createNode("fields", null);
    78              nodeTable.addChild(nodeFields);
    79
    80              for (int j = 0; j < fields[i].length; j++)
    81              {
    82                  nodeFields.addChild(createFieldNode(fields[i][j]));
    83              }
    84          }
    85
    86          config.getRoot().addChild(nodeTables);
    87      }
    88
    89      public void testSetRoot()
    90      {
    91          try
    92          {
    93              config.setRoot(null);
    94              fail("Could set null root node!");
    95          }
    96          catch(IllegalArgumentException iex)
    97          {
    98              //ok
    99          }
    100
    101         config.setRoot(new HierarchicalConfiguration.Node("test"));
    102         assertTrue(config.isEmpty());
    103     }
    104
    105     public void testSetRootNode()
    106     {
    107         config.setRootNode(new DefaultConfigurationNode("testNode"));
    108         assertNotSame("Same root node", config.getRootNode(), config.getRoot());
    109         assertEquals("Wrong name of root node", "testNode", config.getRoot().getName());
    110
    111         config.setRootNode(new HierarchicalConfiguration.Node("test"));
    112         assertSame("Wrong root node", config.getRootNode(), config.getRoot());
    113     }
    114
    115     public void testSetRootNodeNull()
    116     {
    117         try
    118         {
    119             config.setRootNode(null);
    120             fail("Could set null root node!");
    121         }
    122         catch(IllegalArgumentException iex)
    123         {
    124             //ok
    125         }
    126     }
    127
    128     public void testIsEmpty()
    129     {
    130         assertFalse(config.isEmpty());
    131         HierarchicalConfiguration conf2 = new HierarchicalConfiguration();
    132         assertTrue(conf2.isEmpty());
    133         HierarchicalConfiguration.Node child1 = new HierarchicalConfiguration.Node("child1");
    134         HierarchicalConfiguration.Node child2 = new HierarchicalConfiguration.Node("child2");
    135         child1.addChild(child2);
    136         conf2.getRoot().addChild(child1);
    137         assertTrue(conf2.isEmpty());
    138     }
    139
    140     public void testGetProperty()
    141     {
    142         assertNull(config.getProperty("tables.table.resultset"));
    143         assertNull(config.getProperty("tables.table.fields.field"));
    144
    145         Object prop = config.getProperty("tables.table(0).fields.field.name");
    146         assertNotNull(prop);
    147         assertTrue(prop instanceof Collection);
    148         assertEquals(5, ((Collection) prop).size());
    149
    150         prop = config.getProperty("tables.table.fields.field.name");
    151         assertNotNull(prop);
    152         assertTrue(prop instanceof Collection);
    153         assertEquals(10, ((Collection) prop).size());
    154
    155         prop = config.getProperty("tables.table.fields.field(3).name");
    156         assertNotNull(prop);
    157         assertTrue(prop instanceof Collection);
    158         assertEquals(2, ((Collection) prop).size());
    159
    160         prop = config.getProperty("tables.table(1).fields.field(2).name");
    161         assertNotNull(prop);
    162         assertEquals("creationDate", prop.toString());
    163     }
    164
    165     public void testSetProperty()
    166     {
    167         config.setProperty("tables.table(0).name", "resources");
    168         assertEquals("resources", config.getString("tables.table(0).name"));
    169         config.setProperty("tables.table.name", "tab1,tab2");
    170         assertEquals("tab1", config.getString("tables.table(0).name"));
    171         assertEquals("tab2", config.getString("tables.table(1).name"));
    172
    173         config.setProperty("test.items.item", new int[] { 2, 4, 8, 16 });
    174         assertEquals(3, config.getMaxIndex("test.items.item"));
    175         assertEquals(8, config.getInt("test.items.item(2)"));
    176         config.setProperty("test.items.item(2)", new Integer(6));
    177         assertEquals(6, config.getInt("test.items.item(2)"));
    178         config.setProperty("test.items.item(2)", new int[] { 7, 9, 11 });
    179         assertEquals(5, config.getMaxIndex("test.items.item"));
    180
    181         config.setProperty("test", Boolean.TRUE);
    182         config.setProperty("test.items", "01/01/05");
    183         assertEquals(5, config.getMaxIndex("test.items.item"));
    184         assertTrue(config.getBoolean("test"));
    185         assertEquals("01/01/05", config.getProperty("test.items"));
    186
    187         config.setProperty("test.items.item", new Integer(42));
    188         assertEquals(0, config.getMaxIndex("test.items.item"));
    189         assertEquals(42, config.getInt("test.items.item"));
    190     }
    191
    192     public void testClearProperty()
    193     {
    194         config.clearProperty("tables.table(0).fields.field(0).name");
    195         assertEquals("uname", config.getProperty("tables.table(0).fields.field(0).name"));
    196         config.clearProperty("tables.table(0).name");
    197         assertFalse(config.containsKey("tables.table(0).name"));
    198         assertEquals("firstName", config.getProperty("tables.table(0).fields.field(1).name"));
    199         assertEquals("documents", config.getProperty("tables.table.name"));
    200         config.clearProperty("tables.table");
    201         assertEquals("documents", config.getProperty("tables.table.name"));
    202
    203         config.addProperty("test", "first");
    204         config.addProperty("test.level", "second");
    205         config.clearProperty("test");
    206         assertEquals("second", config.getString("test.level"));
    207         assertFalse(config.containsKey("test"));
    208     }
    209
    210     public void testClearTree()
    211     {
    212         Object prop = config.getProperty("tables.table(0).fields.field.name");
    213         assertNotNull(prop);
    214         config.clearTree("tables.table(0).fields.field(3)");
    215         prop = config.getProperty("tables.table(0).fields.field.name");
    216         assertNotNull(prop);
    217         assertTrue(prop instanceof Collection);
    218         assertEquals(4, ((Collection) prop).size());
    219
    220         config.clearTree("tables.table(0).fields");
    221         assertNull(config.getProperty("tables.table(0).fields.field.name"));
    222         prop = config.getProperty("tables.table.fields.field.name");
    223         assertNotNull(prop);
    224         assertTrue(prop instanceof Collection);
    225         assertEquals(5, ((Collection) prop).size());
    226
    227         config.clearTree("tables.table(1)");
    228         assertNull(config.getProperty("tables.table.fields.field.name"));
    229     }
    230
    231     /***
    232      * Tests removing more complex node structures.
    233      */
    234     public void testClearTreeComplex()
    235     {
    236         final int count = 5;
    237         // create the structure
    238         for (int idx = 0; idx < count; idx++)
    239         {
    240             config.addProperty("indexList.index(-1)[@default]", Boolean.FALSE);
    241             config.addProperty("indexList.index[@name]", "test" + idx);
    242             config.addProperty("indexList.index.dir", "testDir" + idx);
    243         }
    244         assertEquals("Wrong number of nodes", count - 1, config
    245                 .getMaxIndex("indexList.index[@name]"));
    246
    247         // Remove a sub tree
    248         boolean found = false;
    249         for (int idx = 0; true; idx++)
    250         {
    251             String name = config.getString("indexList.index(" + idx
    252                     + ")[@name]");
    253             if (name == null)
    254             {
    255                 break;
    256             }
    257             if ("test3".equals(name))
    258             {
    259                 assertEquals("Wrong dir", "testDir3", config
    260                         .getString("indexList.index(" + idx + ").dir"));
    261                 config.clearTree("indexList.index(" + idx + ")");
    262                 found = true;
    263             }
    264         }
    265         assertTrue("Key to remove not found", found);
    266         assertEquals("Wrong number of nodes after remove", count - 2, config
    267                 .getMaxIndex("indexList.index[@name]"));
    268         assertEquals("Wrong number of dir nodes after remove", count - 2,
    269                 config.getMaxIndex("indexList.index.dir"));
    270
    271         // Verify
    272         for (int idx = 0; true; idx++)
    273         {
    274             String name = config.getString("indexList.index(" + idx
    275                     + ")[@name]");
    276             if (name == null)
    277             {
    278                 break;
    279             }
    280             if ("test3".equals(name))
    281             {
    282                 fail("Key was not removed!");
    283             }
    284         }
    285     }
    286
    287     /***
    288      * Tests the clearTree() method on a hierarchical structure of nodes. This
    289      * is a test case for CONFIGURATION-293.
    290      */
    291     public void testClearTreeHierarchy()
    292     {
    293         config.addProperty("a.b.c", "c");
    294         config.addProperty("a.b.c.d", "d");
    295         config.addProperty("a.b.c.d.e", "e");
    296         config.clearTree("a.b.c");
    297         assertFalse("Property not removed", config.containsKey("a.b.c"));
    298         assertFalse("Sub property not removed", config.containsKey("a.b.c.d"));
    299     }
    300
    301     public void testContainsKey()
    302     {
    303         assertTrue(config.containsKey("tables.table(0).name"));
    304         assertTrue(config.containsKey("tables.table(1).name"));
    305         assertFalse(config.containsKey("tables.table(2).name"));
    306
    307         assertTrue(config.containsKey("tables.table(0).fields.field.name"));
    308         assertFalse(config.containsKey("tables.table(0).fields.field"));
    309         config.clearTree("tables.table(0).fields");
    310         assertFalse(config.containsKey("tables.table(0).fields.field.name"));
    311
    312         assertTrue(config.containsKey("tables.table.fields.field.name"));
    313     }
    314
    315     public void testGetKeys()
    316     {
    317         List keys = new ArrayList();
    318         for (Iterator it = config.getKeys(); it.hasNext();)
    319         {
    320             keys.add(it.next());
    321         }
    322
    323         assertEquals(2, keys.size());
    324         assertTrue(keys.contains("tables.table.name"));
    325         assertTrue(keys.contains("tables.table.fields.field.name"));
    326
    327         // test the order of the keys returned
    328         config.addProperty("order.key1", "value1");
    329         config.addProperty("order.key2", "value2");
    330         config.addProperty("order.key3", "value3");
    331
    332         Iterator it = config.getKeys("order");
    333         assertEquals("1st key", "order.key1", it.next());
    334         assertEquals("2nd key", "order.key2", it.next());
    335         assertEquals("3rd key", "order.key3", it.next());
    336     }
    337
    338     public void testGetKeysString()
    339     {
    340         // add some more properties to make it more interesting
    341         config.addProperty("tables.table(0).fields.field(1).type", "VARCHAR");
    342         config.addProperty("tables.table(0)[@type]", "system");
    343         config.addProperty("tables.table(0).size", "42");
    344         config.addProperty("tables.table(0).fields.field(0).size", "128");
    345         config.addProperty("connections.connection.param.url", "url1");
    346         config.addProperty("connections.connection.param.user", "me");
    347         config.addProperty("connections.connection.param.pwd", "secret");
    348         config.addProperty("connections.connection(-1).param.url", "url2");
    349         config.addProperty("connections.connection(1).param.user", "guest");
    350
    351         checkKeys("tables.table(1)", new String[] { "name", "fields.field.name" });
    352         checkKeys("tables.table(0)",
    353                 new String[] { "name", "fields.field.name", "tables.table(0)[@type]", "size", "fields.field.type", "fields.field.size" });
    354         checkKeys("connections.connection(0).param",
    355                 new String[] {"url", "user", "pwd" });
    356         checkKeys("connections.connection(1).param",
    357                 new String[] {"url", "user" });
    358     }
    359
    360     /***
    361      * Tests getKeys() with a prefix when the prefix matches exactly a key.
    362      */
    363     public void testGetKeysWithKeyAsPrefix()
    364     {
    365         config.addProperty("order.key1", "value1");
    366         config.addProperty("order.key2", "value2");
    367         Iterator it = config.getKeys("order.key1");
    368         assertTrue("no key found", it.hasNext());
    369         assertEquals("1st key", "order.key1", it.next());
    370         assertFalse("more keys than expected", it.hasNext());
    371     }
    372
    373     /***
    374      * Tests getKeys() with a prefix when the prefix matches exactly a key, and
    375      * there are multiple keys starting with this prefix.
    376      */
    377     public void testGetKeysWithKeyAsPrefixMultiple()
    378     {
    379         config.addProperty("order.key1", "value1");
    380         config.addProperty("order.key1.test", "value2");
    381         config.addProperty("order.key1.test.complex", "value2");
    382         Iterator it = config.getKeys("order.key1");
    383         assertEquals("Wrong key 1", "order.key1", it.next());
    384         assertEquals("Wrong key 2", "order.key1.test", it.next());
    385         assertEquals("Wrong key 3", "order.key1.test.complex", it.next());
    386         assertFalse("More keys than expected", it.hasNext());
    387     }
    388
    389     public void testAddProperty()
    390     {
    391         config.addProperty("tables.table(0).fields.field(-1).name", "phone");
    392         Object prop = config.getProperty("tables.table(0).fields.field.name");
    393         assertNotNull(prop);
    394         assertTrue(prop instanceof Collection);
    395         assertEquals(6, ((Collection) prop).size());
    396
    397         config.addProperty("tables.table(0).fields.field.name", "fax");
    398         prop = config.getProperty("tables.table.fields.field(5).name");
    399         assertNotNull(prop);
    400         assertTrue(prop instanceof List);
    401         List list = (List) prop;
    402         assertEquals("phone", list.get(0));
    403         assertEquals("fax", list.get(1));
    404
    405         config.addProperty("tables.table(-1).name", "config");
    406         prop = config.getProperty("tables.table.name");
    407         assertNotNull(prop);
    408         assertTrue(prop instanceof Collection);
    409         assertEquals(3, ((Collection) prop).size());
    410         config.addProperty("tables.table(2).fields.field(0).name", "cid");
    411         config.addProperty("tables.table(2).fields.field(-1).name",
    412         "confName");
    413         prop = config.getProperty("tables.table(2).fields.field.name");
    414         assertNotNull(prop);
    415         assertTrue(prop instanceof Collection);
    416         assertEquals(2, ((Collection) prop).size());
    417         assertEquals("confName",
    418         config.getProperty("tables.table(2).fields.field(1).name"));
    419
    420         config.addProperty("connection.user", "scott");
    421         config.addProperty("connection.passwd", "tiger");
    422         assertEquals("tiger", config.getProperty("connection.passwd"));
    423
    424         ConfigurationKey key = new ConfigurationKey();
    425         key.append("tables").append("table").appendIndex(0);
    426         key.appendAttribute("tableType");
    427         config.addProperty(key.toString(), "system");
    428         assertEquals("system", config.getProperty(key.toString()));
    429
    430         try
    431         {
    432             config.addProperty(".", "InvalidKey");
    433             fail("Could add invalid key!");
    434         }
    435         catch(IllegalArgumentException iex)
    436         {
    437             //ok
    438         }
    439     }
    440
    441     public void testGetMaxIndex()
    442     {
    443         assertEquals(4, config.getMaxIndex("tables.table(0).fields.field"));
    444         assertEquals(4, config.getMaxIndex("tables.table(1).fields.field"));
    445         assertEquals(1, config.getMaxIndex("tables.table"));
    446         assertEquals(1, config.getMaxIndex("tables.table.name"));
    447         assertEquals(0, config.getMaxIndex("tables.table(0).name"));
    448         assertEquals(0, config.getMaxIndex("tables.table(1).fields.field(1)"));
    449         assertEquals(-1, config.getMaxIndex("tables.table(2).fields"));
    450
    451         int maxIdx = config.getMaxIndex("tables.table(0).fields.field.name");
    452         for(int i = 0; i <= maxIdx; i++)
    453         {
    454             ConfigurationKey key = new ConfigurationKey("tables.table(0).fields");
    455             key.append("field").appendIndex(i).append("name");
    456             assertNotNull(config.getProperty(key.toString()));
    457         }
    458     }
    459
    460     public void testSubset()
    461     {
    462         // test the subset on the first table
    463         Configuration subset = config.subset("tables.table(0)");
    464         assertEquals(tables[0], subset.getProperty("name"));
    465
    466         Object prop = subset.getProperty("fields.field.name");
    467         assertNotNull(prop);
    468         assertTrue(prop instanceof Collection);
    469         assertEquals(5, ((Collection) prop).size());
    470
    471         for (int i = 0; i < fields[0].length; i++)
    472         {
    473             ConfigurationKey key = new ConfigurationKey();
    474             key.append("fields").append("field").appendIndex(i);
    475             key.append("name");
    476             assertEquals(fields[0][i], subset.getProperty(key.toString()));
    477         }
    478
    479         // test the subset on the second table
    480         assertTrue("subset is not empty", config.subset("tables.table(2)").isEmpty());
    481
    482         // test the subset on the fields
    483         subset = config.subset("tables.table.fields.field");
    484         prop = subset.getProperty("name");
    485         assertTrue("prop is not a collection", prop instanceof Collection);
    486         assertEquals(10, ((Collection) prop).size());
    487
    488         assertEquals(fields[0][0], subset.getProperty("name(0)"));
    489
    490         // test the subset on the field names
    491         subset = config.subset("tables.table.fields.field.name");
    492         assertTrue("subset is not empty", subset.isEmpty());
    493     }
    494
    495     /***
    496      * Tests the subset() method when the specified node has a value. This value
    497      * must be available in the subset, too. Related to CONFIGURATION-295.
    498      */
    499     public void testSubsetNodeWithValue()
    500     {
    501         config.setProperty("tables.table(0).fields", "My fields");
    502         Configuration subset = config.subset("tables.table(0).fields");
    503         assertEquals("Wrong field name", fields[0][0], subset
    504                 .getString("field(0).name"));
    505         assertEquals("Wrong value of root", "My fields", subset.getString(""));
    506     }
    507
    508     /***
    509      * Tests the subset() method when the specified key selects multiple keys.
    510      * The resulting root node should have a value only if exactly one of the
    511      * selected nodes has a value. Related to CONFIGURATION-295.
    512      */
    513     public void testSubsetMultipleNodesWithValues()
    514     {
    515         config.setProperty("tables.table(0).fields", "My fields");
    516         Configuration subset = config.subset("tables.table.fields");
    517         assertEquals("Wrong value of root", "My fields", subset.getString(""));
    518         config.setProperty("tables.table(1).fields", "My other fields");
    519         subset = config.subset("tables.table.fields");
    520         assertNull("Root value is not null though there are multiple values",
    521                 subset.getString(""));
    522     }
    523
    524     /***
    525      * Tests the configurationAt() method to obtain a configuration for a sub
    526      * tree.
    527      */
    528     public void testConfigurationAt()
    529     {
    530         HierarchicalConfiguration subConfig = config
    531                 .configurationAt("tables.table(1)");
    532         assertEquals("Wrong table name", tables[1], subConfig.getString("name"));
    533         List lstFlds = subConfig.getList("fields.field.name");
    534         assertEquals("Wrong number of fields", fields[1].length, lstFlds.size());
    535         for (int i = 0; i < fields[1].length; i++)
    536         {
    537             assertEquals("Wrong field at position " + i, fields[1][i], lstFlds
    538                     .get(i));
    539         }
    540
    541         subConfig.setProperty("name", "testTable");
    542         assertEquals("Change not visible in parent", "testTable", config
    543                 .getString("tables.table(1).name"));
    544         config.setProperty("tables.table(1).fields.field(2).name", "testField");
    545         assertEquals("Change not visible in sub config", "testField", subConfig
    546                 .getString("fields.field(2).name"));
    547     }
    548
    549     /***
    550      * Tests the configurationAt() method when the passed in key does not exist.
    551      */
    552     public void testConfigurationAtUnknownSubTree()
    553     {
    554         try
    555         {
    556             config.configurationAt("non.existing.key");
    557             fail("Could obtain sub config for unknown key!");
    558         }
    559         catch (IllegalArgumentException iex)
    560         {
    561             // ok
    562         }
    563     }
    564
    565     /***
    566      * Tests the configurationAt() method when the passed in key selects
    567      * multiple nodes. This should cause an exception.
    568      */
    569     public void testConfigurationAtMultipleNodes()
    570     {
    571         try
    572         {
    573             config.configurationAt("tables.table.name");
    574             fail("Could create sub config with non unique key!");
    575         }
    576         catch (IllegalArgumentException iex)
    577         {
    578             // ok
    579         }
    580     }
    581
    582     /***
    583      * Tests the configurationsAt() method.
    584      */
    585     public void testConfigurationsAt()
    586     {
    587         List lstFlds = config.configurationsAt("tables.table(1).fields.field");
    588         assertEquals("Wrong size of fields", fields[1].length, lstFlds.size());
    589         for (int i = 0; i < fields[1].length; i++)
    590         {
    591             HierarchicalConfiguration sub = (HierarchicalConfiguration) lstFlds
    592                     .get(i);
    593             assertEquals("Wrong field at position " + i, fields[1][i], sub
    594                     .getString("name"));
    595         }
    596     }
    597
    598     /***
    599      * Tests the configurationsAt() method when the passed in key does not
    600      * select any sub nodes.
    601      */
    602     public void testConfigurationsAtEmpty()
    603     {
    604         assertTrue("List is not empty", config.configurationsAt("unknown.key")
    605                 .isEmpty());
    606     }
    607
    608     public void testClone()
    609     {
    610         Configuration copy = (Configuration) config.clone();
    611         assertTrue(copy instanceof HierarchicalConfiguration);
    612         checkContent(copy);
    613     }
    614
    615     /***
    616      * Tests whether registered event handlers are handled correctly when a
    617      * configuration is cloned. They should not be registered at the clone.
    618      */
    619     public void testCloneWithEventListeners()
    620     {
    621         config.addConfigurationListener(new ConfigurationListener()
    622         {
    623             public void configurationChanged(ConfigurationEvent event)
    624             {
    625                 // just a dummy
    626             }
    627         });
    628         HierarchicalConfiguration copy = (HierarchicalConfiguration) config
    629                 .clone();
    630         assertTrue("Event listener registered at clone", copy
    631                 .getConfigurationListeners().isEmpty());
    632     }
    633
    634     public void testAddNodes()
    635     {
    636         Collection nodes = new ArrayList();
    637         nodes.add(createFieldNode("birthDate"));
    638         nodes.add(createFieldNode("lastLogin"));
    639         nodes.add(createFieldNode("language"));
    640         config.addNodes("tables.table(0).fields", nodes);
    641         assertEquals(7, config.getMaxIndex("tables.table(0).fields.field"));
    642         assertEquals("birthDate", config.getString("tables.table(0).fields.field(5).name"));
    643         assertEquals("lastLogin", config.getString("tables.table(0).fields.field(6).name"));
    644         assertEquals("language", config.getString("tables.table(0).fields.field(7).name"));
    645     }
    646
    647     /***
    648      * Tests the addNodes() method when the provided key does not exist. In
    649      * this case, a new node (or even a complete new branch) will be created.
    650      */
    651     public void testAddNodesForNonExistingKey()
    652     {
    653         Collection nodes = new ArrayList();
    654         nodes.add(createNode("usr", "scott"));
    655         Node nd = createNode("pwd", "tiger");
    656         nd.setAttribute(true);
    657         nodes.add(nd);
    658         config.addNodes("database.connection.settings", nodes);
    659
    660         assertEquals("Usr node not found", "scott", config.getString("database.connection.settings.usr"));
    661         assertEquals("Pwd node not found", "tiger", config.getString("database.connection.settings[@pwd]"));
    662     }
    663
    664     /***
    665      * Tests the addNodes() method when the new nodes should be added to an
    666      * attribute node. This is not allowed.
    667      */
    668     public void testAddNodesWithAttributeKey()
    669     {
    670         Collection nodes = new ArrayList();
    671         nodes.add(createNode("testNode", "yes"));
    672         try
    673         {
    674             config.addNodes("database.connection[@settings]", nodes);
    675             fail("Could add nodes to an attribute node!");
    676         }
    677         catch(IllegalArgumentException iex)
    678         {
    679             //ok
    680         }
    681     }
    682
    683     /***
    684      * Tests copying nodes from one configuration to another one.
    685      */
    686     public void testAddNodesCopy()
    687     {
    688         HierarchicalConfiguration configDest = new HierarchicalConfiguration();
    689         configDest.addProperty("test", "TEST");
    690         Collection nodes = config.getRootNode().getChildren();
    691         assertEquals("Wrong number of children", 1, nodes.size());
    692         configDest.addNodes("newNodes", nodes);
    693         for (int i = 0; i < tables.length; i++)
    694         {
    695             String keyTab = "newNodes.tables.table(" + i + ").";
    696             assertEquals("Table " + i + " not found", tables[i], configDest
    697                     .getString(keyTab + "name"));
    698             for (int j = 0; j < fields[i].length; j++)
    699             {
    700                 assertEquals("Invalid field " + j + " in table " + i,
    701                         fields[i][j], configDest.getString(keyTab
    702                                 + "fields.field(" + j + ").name"));
    703             }
    704         }
    705     }
    706
    707     /***
    708      * Tests adding an attribute node with the addNodes() method.
    709      */
    710     public void testAddNodesAttributeNode()
    711     {
    712         Collection nodes = new ArrayList();
    713         ConfigurationNode nd = createNode("length", "10");
    714         nd.setAttribute(true);
    715         nodes.add(nd);
    716         config.addNodes("tables.table(0).fields.field(1)", nodes);
    717         assertEquals("Attribute was not added", "10", config
    718                 .getString("tables.table(0).fields.field(1)[@length]"));
    719     }
    720
    721     /***
    722      * Tests removing children from a configuration node.
    723      */
    724     public void testNodeRemove()
    725     {
    726         HierarchicalConfiguration.Node node = new HierarchicalConfiguration.Node(
    727                 "parent", "test");
    728         assertFalse(node.hasChildren());
    729         node.removeChildren(); // should have no effect
    730         assertFalse(node.remove("child"));
    731
    732         node.addChild(createNode("test", "test"));
    733         assertTrue(node.hasChildren());
    734         assertTrue(node.remove("test"));
    735         assertFalse(node.hasChildren());
    736
    737         for (int i = 0; i < 10; i++)
    738         {
    739             node.addChild(createNode("child" + i, "test" + i));
    740         }
    741         assertTrue(node.hasChildren());
    742         assertFalse(node.remove("child"));
    743         assertTrue(node.remove("child2"));
    744         assertTrue(node.getChildren("child2").isEmpty());
    745
    746         HierarchicalConfiguration.Node child = createNode("child0", "testChild");
    747         assertFalse(node.remove(child));
    748         node.addChild(child);
    749         assertTrue(node.remove(child));
    750         assertEquals(1, node.getChildren("child0").size());
    751         assertEquals("test0", ((HierarchicalConfiguration.Node) node
    752                 .getChildren("child0").get(0)).getValue());
    753
    754         assertTrue(node.remove("child0"));
    755         assertFalse(node.remove(child));
    756
    757         node.removeChildren();
    758         assertTrue(node.getChildren().isEmpty());
    759         assertFalse(node.remove(child));
    760     }
    761
    762     /***
    763      * Tests the visitor mechanism.
    764      */
    765     public void testNodeVisitor()
    766     {
    767         CountVisitor v = new CountVisitor();
    768         config.getRoot().visit(v, null);
    769         assertEquals(28, v.beforeCount);
    770         assertEquals(v.beforeCount, v.afterCount);
    771     }
    772
    773     /***
    774      * Tests setting a custom expression engine, which uses a slightly different
    775      * syntax.
    776      */
    777     public void testSetExpressionEngine()
    778     {
    779         config.setExpressionEngine(null);
    780         assertNotNull("Expression engine is null", config.getExpressionEngine());
    781         assertSame("Default engine is not used", HierarchicalConfiguration
    782                 .getDefaultExpressionEngine(), config.getExpressionEngine());
    783
    784         config.setExpressionEngine(createAlternativeExpressionEngine());
    785         checkAlternativeSyntax();
    786     }
    787
    788     /***
    789      * Tests setting the default expression engine. This should impact all
    790      * configuration instances that do not have their own engine.
    791      */
    792     public void testSetDefaultExpressionEngine()
    793     {
    794         ExpressionEngine engineOld = HierarchicalConfiguration.getDefaultExpressionEngine();
    795         HierarchicalConfiguration
    796                 .setDefaultExpressionEngine(createAlternativeExpressionEngine());
    797         checkAlternativeSyntax();
    798         HierarchicalConfiguration.setDefaultExpressionEngine(engineOld);
    799     }
    800
    801     /***
    802      * Tests setting the default expression engine to null. This should not be
    803      * allowed.
    804      */
    805     public void testSetDefaultExpressionEngineNull()
    806     {
    807         try
    808         {
    809             HierarchicalConfiguration.setDefaultExpressionEngine(null);
    810             fail("Could set default expression engine to null!");
    811         }
    812         catch (IllegalArgumentException iex)
    813         {
    814             // ok
    815         }
    816     }
    817
    818 	/***
    819      * Tests the copy constructor.
    820      */
    821 	public void testInitCopy()
    822 	{
    823 		HierarchicalConfiguration copy = new HierarchicalConfiguration(config);
    824 		checkContent(copy);
    825 	}
    826
    827 	/***
    828      * Tests whether the nodes of a copied configuration are independent from
    829      * the source configuration.
    830      */
    831 	public void testInitCopyUpdate()
    832 	{
    833 		HierarchicalConfiguration copy = new HierarchicalConfiguration(config);
    834 		config.setProperty("tables.table(0).name", "NewTable");
    835 		checkContent(copy);
    836 	}
    837
    838     /***
    839      * Tests interpolation facilities.
    840      */
    841     public void testInterpolation()
    842     {
    843         config.addProperty("base.dir", "/home/foo");
    844         config.addProperty("test.absolute.dir.dir1", "${base.dir}/path1");
    845         config.addProperty("test.absolute.dir.dir2", "${base.dir}/path2");
    846         config.addProperty("test.absolute.dir.dir3", "${base.dir}/path3");
    847
    848         Configuration sub = config.subset("test.absolute.dir");
    849         for (int i = 1; i < 4; i++)
    850         {
    851             assertEquals("Wrong interpolation in parent", "/home/foo/path" + i,
    852                     config.getString("test.absolute.dir.dir" + i));
    853             assertEquals("Wrong interpolation in subnode",
    854                     "/home/foo/path" + i, sub.getString("dir" + i));
    855         }
    856     }
    857
    858     /***
    859      * Basic interpolation tests.
    860      */
    861     public void testInterpolationBasic()
    862     {
    863         InterpolationTestHelper.testInterpolation(config);
    864     }
    865
    866     /***
    867      * Tests multiple levels of interpolation.
    868      */
    869     public void testInterpolationMultipleLevels()
    870     {
    871         InterpolationTestHelper.testMultipleInterpolation(config);
    872     }
    873
    874     /***
    875      * Tests an invalid interpolation that causes an endless loop.
    876      */
    877     public void testInterpolationLoop()
    878     {
    879         InterpolationTestHelper.testInterpolationLoop(config);
    880     }
    881
    882     /***
    883      * Tests interpolation with a subset.
    884      */
    885     public void testInterpolationSubset()
    886     {
    887         InterpolationTestHelper.testInterpolationSubset(config);
    888     }
    889
    890     /***
    891      * Tests interpolation of a variable, which cannot be resolved.
    892      */
    893     public void testInterpolationUnknownProperty()
    894     {
    895         InterpolationTestHelper.testInterpolationUnknownProperty(config);
    896     }
    897
    898     /***
    899      * Tests interpolation with system properties.
    900      */
    901     public void testInterpolationSysProperties()
    902     {
    903         InterpolationTestHelper.testInterpolationSystemProperties(config);
    904     }
    905
    906     /***
    907      * Tests interpolation with constant values.
    908      */
    909     public void testInterpolationConstants()
    910     {
    911         InterpolationTestHelper.testInterpolationConstants(config);
    912     }
    913
    914     /***
    915      * Tests escaping variables.
    916      */
    917     public void testInterpolationEscaped()
    918     {
    919         InterpolationTestHelper.testInterpolationEscaped(config);
    920     }
    921
    922     /***
    923      * Tests manipulating the interpolator.
    924      */
    925     public void testInterpolator()
    926     {
    927         InterpolationTestHelper.testGetInterpolator(config);
    928     }
    929
    930     /***
    931      * Tests obtaining a configuration with all variables substituted.
    932      */
    933     public void testInterpolatedConfiguration()
    934     {
    935         HierarchicalConfiguration c = (HierarchicalConfiguration) InterpolationTestHelper
    936                 .testInterpolatedConfiguration(config);
    937
    938         // tests whether the hierarchical structure has been maintained
    939         config = c;
    940         testGetProperty();
    941     }
    942
    943 	/***
    944      * Tests the copy constructor when a null reference is passed.
    945      */
    946 	public void testInitCopyNull()
    947 	{
    948 		HierarchicalConfiguration copy = new HierarchicalConfiguration(null);
    949 		assertTrue("Configuration not empty", copy.isEmpty());
    950 	}
    951
    952 	/***
    953      * Tests the parents of nodes when setRootNode() is involved. This is
    954      * related to CONFIGURATION-334.
    955      */
    956     public void testNodeParentsAfterSetRootNode()
    957     {
    958         DefaultConfigurationNode root = new DefaultConfigurationNode();
    959         DefaultConfigurationNode child1 = new DefaultConfigurationNode(
    960                 "child1", "test1");
    961         root.addChild(child1);
    962         config.setRootNode(root);
    963         config.addProperty("child2", "test2");
    964         List nodes = config.getExpressionEngine().query(config.getRootNode(),
    965                 "child2");
    966         assertEquals("Wrong number of result nodes", 1, nodes.size());
    967         ConfigurationNode child2 = (ConfigurationNode) nodes.get(0);
    968         assertEquals("Different parent nodes", child1.getParentNode(), child2
    969                 .getParentNode());
    970     }
    971
    972     /***
    973      * Tests calling getRoot() after a root node was set using setRootNode() and
    974      * further child nodes have been added. The newly add child nodes should be
    975      * present in the root node returned.
    976      */
    977     public void testGetRootAfterSetRootNode()
    978     {
    979         DefaultConfigurationNode root = new DefaultConfigurationNode();
    980         DefaultConfigurationNode child1 = new DefaultConfigurationNode(
    981                 "child1", "test1");
    982         root.addChild(child1);
    983         config.setRootNode(root);
    984         config.addProperty("child2", "test2");
    985         ConfigurationNode oldRoot = config.getRoot();
    986         assertEquals("Wrong number of children", 2, oldRoot.getChildrenCount());
    987     }
    988
    989 	/***
    990      * Helper method for testing the getKeys(String) method.
    991      *
    992      * @param prefix the key to pass into getKeys()
    993      * @param expected the expected result
    994      */
    995     private void checkKeys(String prefix, String[] expected)
    996     {
    997         Set values = new HashSet();
    998         for(int i = 0; i < expected.length; i++)
    999         {
    1000             values.add((expected[i].startsWith(prefix)) ? expected[i] :  prefix + "." + expected[i]);
    1001         }
    1002
    1003         Iterator itKeys = config.getKeys(prefix);
    1004         while(itKeys.hasNext())
    1005         {
    1006             String key = (String) itKeys.next();
    1007             if(!values.contains(key))
    1008             {
    1009                 fail("Found unexpected key: " + key);
    1010             }
    1011             else
    1012             {
    1013                 values.remove(key);
    1014             }
    1015         }
    1016
    1017         assertTrue("Remaining keys " + values, values.isEmpty());
    1018     }
    1019
    1020     /***
    1021      * Helper method for checking keys using an alternative syntax.
    1022      */
    1023     private void checkAlternativeSyntax()
    1024     {
    1025         assertNull(config.getProperty("tables/table/resultset"));
    1026         assertNull(config.getProperty("tables/table/fields/field"));
    1027
    1028         Object prop = config.getProperty("tables/table[0]/fields/field/name");
    1029         assertNotNull(prop);
    1030         assertTrue(prop instanceof Collection);
    1031         assertEquals(5, ((Collection) prop).size());
    1032
    1033         prop = config.getProperty("tables/table/fields/field/name");
    1034         assertNotNull(prop);
    1035         assertTrue(prop instanceof Collection);
    1036         assertEquals(10, ((Collection) prop).size());
    1037
    1038         prop = config.getProperty("tables/table/fields/field[3]/name");
    1039         assertNotNull(prop);
    1040         assertTrue(prop instanceof Collection);
    1041         assertEquals(2, ((Collection) prop).size());
    1042
    1043         prop = config.getProperty("tables/table[1]/fields/field[2]/name");
    1044         assertNotNull(prop);
    1045         assertEquals("creationDate", prop.toString());
    1046
    1047         Set keys = new HashSet();
    1048         CollectionUtils.addAll(keys, config.getKeys());
    1049         assertEquals("Wrong number of defined keys", 2, keys.size());
    1050         assertTrue("Key not found", keys.contains("tables/table/name"));
    1051         assertTrue("Key not found", keys
    1052                 .contains("tables/table/fields/field/name"));
    1053     }
    1054
    1055 	/***
    1056      * Checks the content of the passed in configuration object. Used by some
    1057      * tests that copy a configuration.
    1058      *
    1059      * @param c the configuration to check
    1060      */
    1061 	private void checkContent(Configuration c)
    1062 	{
    1063 		for (int i = 0; i < tables.length; i++)
    1064 		{
    1065 			assertEquals(tables[i], c.getString("tables.table(" + i + ").name"));
    1066 			for (int j = 0; j < fields[i].length; j++)
    1067 			{
    1068 				assertEquals(fields[i][j], c.getString("tables.table(" + i
    1069 						+ ").fields.field(" + j + ").name"));
    1070 			}
    1071 		}
    1072 	}
    1073
    1074 	private ExpressionEngine createAlternativeExpressionEngine()
    1075 	{
    1076         DefaultExpressionEngine engine = new DefaultExpressionEngine();
    1077         engine.setPropertyDelimiter("/");
    1078         engine.setIndexStart("[");
    1079         engine.setIndexEnd("]");
    1080         return engine;
    1081     }
    1082
    1083     /***
    1084      * Helper method for creating a field node with its children.
    1085      *
    1086      * @param name the name of the field
    1087      * @return the field node
    1088      */
    1089     private static HierarchicalConfiguration.Node createFieldNode(String name)
    1090     {
    1091         HierarchicalConfiguration.Node fld = createNode("field", null);
    1092         fld.addChild(createNode("name", name));
    1093         return fld;
    1094     }
    1095
    1096     /***
    1097      * Helper method for creating a configuration node.
    1098      * @param name the node's name
    1099      * @param value the node's value
    1100      * @return the new node
    1101      */
    1102     private static HierarchicalConfiguration.Node createNode(String name, Object value)
    1103     {
    1104         HierarchicalConfiguration.Node node = new HierarchicalConfiguration.Node(name);
    1105         node.setValue(value);
    1106         return node;
    1107     }
    1108
    1109     /***
    1110      * A test visitor implementation for checking whether all visitor methods
    1111      * are correctly called.
    1112      */
    1113     static class CountVisitor extends HierarchicalConfiguration.NodeVisitor
    1114     {
    1115         public int beforeCount;
    1116
    1117         public int afterCount;
    1118
    1119         public void visitAfterChildren(Node node, ConfigurationKey key)
    1120         {
    1121             super.visitAfterChildren(node, key);
    1122             afterCount++;
    1123         }
    1124
    1125         public void visitBeforeChildren(Node node, ConfigurationKey key)
    1126         {
    1127             super.visitBeforeChildren(node, key);
    1128             beforeCount++;
    1129         }
    1130     }
    1131 }