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 }