001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.configuration;
019    
020    import static org.junit.Assert.assertEquals;
021    import static org.junit.Assert.assertFalse;
022    import static org.junit.Assert.assertNotNull;
023    import static org.junit.Assert.assertNotSame;
024    import static org.junit.Assert.assertNull;
025    import static org.junit.Assert.assertSame;
026    import static org.junit.Assert.assertTrue;
027    import static org.junit.Assert.fail;
028    
029    import java.util.ArrayList;
030    import java.util.Collection;
031    import java.util.HashSet;
032    import java.util.Iterator;
033    import java.util.List;
034    import java.util.Set;
035    
036    import org.apache.commons.collections.CollectionUtils;
037    import org.apache.commons.configuration.HierarchicalConfiguration.Node;
038    import org.apache.commons.configuration.event.ConfigurationEvent;
039    import org.apache.commons.configuration.event.ConfigurationListener;
040    import org.apache.commons.configuration.tree.ConfigurationNode;
041    import org.apache.commons.configuration.tree.DefaultConfigurationKey;
042    import org.apache.commons.configuration.tree.DefaultConfigurationNode;
043    import org.apache.commons.configuration.tree.DefaultExpressionEngine;
044    import org.apache.commons.configuration.tree.ExpressionEngine;
045    import org.junit.Before;
046    import org.junit.Test;
047    
048    /**
049     * Test class for HierarchicalConfiguration.
050     *
051     * @version $Id: TestHierarchicalConfiguration.java 1231749 2012-01-15 20:48:56Z oheger $
052     */
053    public class TestHierarchicalConfiguration
054    {
055        private static String[] tables = { "users", "documents" };
056    
057        private static String[][] fields =
058        {
059            { "uid", "uname", "firstName", "lastName", "email" },
060            { "docid", "name", "creationDate", "authorID", "version" }
061        };
062    
063        private HierarchicalConfiguration config;
064    
065        @Before
066        public void setUp() throws Exception
067        {
068            /**
069             * Initialize the configuration with the following structure:
070             *
071             * tables
072             *      table
073             *         name
074             *         fields
075             *             field
076             *                 name
077             *             field
078             *                 name
079             */
080            config = new HierarchicalConfiguration();
081            HierarchicalConfiguration.Node nodeTables = createNode("tables", null);
082            for(int i = 0; i < tables.length; i++)
083            {
084                HierarchicalConfiguration.Node nodeTable = createNode("table", null);
085                nodeTables.addChild(nodeTable);
086                HierarchicalConfiguration.Node nodeName = createNode("name", tables[i]);
087                nodeTable.addChild(nodeName);
088                HierarchicalConfiguration.Node nodeFields = createNode("fields", null);
089                nodeTable.addChild(nodeFields);
090    
091                for (int j = 0; j < fields[i].length; j++)
092                {
093                    nodeFields.addChild(createFieldNode(fields[i][j]));
094                }
095            }
096    
097            config.getRoot().addChild(nodeTables);
098        }
099    
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    }