View Javadoc

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