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