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