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