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.io.ByteArrayInputStream;
21  import java.io.File;
22  import java.io.FileOutputStream;
23  import java.io.FileWriter;
24  import java.io.IOException;
25  import java.io.PrintWriter;
26  import java.io.StringReader;
27  import java.io.StringWriter;
28  import java.net.URL;
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.Iterator;
32  import java.util.List;
33  
34  import javax.xml.parsers.DocumentBuilder;
35  import javax.xml.parsers.DocumentBuilderFactory;
36  
37  import junit.framework.TestCase;
38  
39  import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy;
40  import org.apache.commons.configuration.reloading.InvariantReloadingStrategy;
41  import org.apache.commons.configuration.tree.ConfigurationNode;
42  import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
43  import org.xml.sax.SAXException;
44  import org.xml.sax.SAXParseException;
45  import org.xml.sax.helpers.DefaultHandler;
46  
47  /***
48   * test for loading and saving xml properties files
49   *
50   * @version $Id: TestXMLConfiguration.java 721895 2008-11-30 21:08:42Z oheger $
51   */
52  public class TestXMLConfiguration extends TestCase
53  {
54      /*** Constant for the used encoding.*/
55      static final String ENCODING = "ISO-8859-1";
56  
57      /*** Constant for the test system ID.*/
58      static final String SYSTEM_ID = "properties.dtd";
59  
60      /*** Constant for the test public ID.*/
61      static final String PUBLIC_ID = "-//Commons Configuration//DTD Test Configuration 1.3//EN";
62  
63      /*** Constant for the DOCTYPE declaration.*/
64      static final String DOCTYPE_DECL = " PUBLIC \"" + PUBLIC_ID + "\" \"" + SYSTEM_ID + "\">";
65  
66      /*** Constant for the DOCTYPE prefix.*/
67      static final String DOCTYPE = "<!DOCTYPE ";
68  
69      /*** Constant for the transformer factory property.*/
70      static final String PROP_FACTORY = "javax.xml.transform.TransformerFactory";
71  
72      /*** The File that we test with */
73      private String testProperties = new File("conf/test.xml").getAbsolutePath();
74      private String testProperties2 = new File("conf/testDigesterConfigurationInclude1.xml").getAbsolutePath();
75      private String testBasePath = new File("conf").getAbsolutePath();
76      private File testSaveConf = new File("target/testsave.xml");
77  
78      private XMLConfiguration conf;
79  
80      protected void setUp() throws Exception
81      {
82          conf = new XMLConfiguration();
83          conf.setFile(new File(testProperties));
84          conf.load();
85          removeTestFile();
86      }
87  
88      public void testGetProperty()
89      {
90          assertEquals("value", conf.getProperty("element"));
91      }
92  
93      public void testGetCommentedProperty()
94      {
95          assertEquals("", conf.getProperty("test.comment"));
96      }
97  
98      public void testGetPropertyWithXMLEntity()
99      {
100         assertEquals("1<2", conf.getProperty("test.entity"));
101     }
102 
103     public void testClearProperty() throws ConfigurationException, IOException
104     {
105         // test non-existent element
106         String key = "clearly";
107         conf.clearProperty(key);
108         assertNull(key, conf.getProperty(key));
109         assertNull(key, conf.getProperty(key));
110 
111         // test single element
112         conf.load();
113         key = "clear.element";
114         conf.clearProperty(key);
115         assertNull(key, conf.getProperty(key));
116         assertNull(key, conf.getProperty(key));
117 
118         // test single element with attribute
119         conf.load();
120         key = "clear.element2";
121         conf.clearProperty(key);
122         assertNull(key, conf.getProperty(key));
123         assertNull(key, conf.getProperty(key));
124         key = "clear.element2[@id]";
125         assertNotNull(key, conf.getProperty(key));
126         assertNotNull(key, conf.getProperty(key));
127 
128         // test non-text/cdata element
129         conf.load();
130         key = "clear.comment";
131         conf.clearProperty(key);
132         assertNull(key, conf.getProperty(key));
133         assertNull(key, conf.getProperty(key));
134 
135         // test cdata element
136         conf.load();
137         key = "clear.cdata";
138         conf.clearProperty(key);
139         assertNull(key, conf.getProperty(key));
140         assertNull(key, conf.getProperty(key));
141 
142         // test multiple sibling elements
143         conf.load();
144         key = "clear.list.item";
145         conf.clearProperty(key);
146         assertNull(key, conf.getProperty(key));
147         assertNull(key, conf.getProperty(key));
148         key = "clear.list.item[@id]";
149         assertNotNull(key, conf.getProperty(key));
150         assertNotNull(key, conf.getProperty(key));
151 
152         // test multiple, disjoined elements
153         conf.load();
154         key = "list.item";
155         conf.clearProperty(key);
156         assertNull(key, conf.getProperty(key));
157         assertNull(key, conf.getProperty(key));
158     }
159 
160     public void testgetProperty() {
161         // test non-leaf element
162         Object property = conf.getProperty("clear");
163         assertNull(property);
164 
165         // test non-existent element
166         property = conf.getProperty("e");
167         assertNull(property);
168 
169         // test non-existent element
170         property = conf.getProperty("element3[@n]");
171         assertNull(property);
172 
173         // test single element
174         property = conf.getProperty("element");
175         assertNotNull(property);
176         assertTrue(property instanceof String);
177         assertEquals("value", property);
178 
179         // test single attribute
180         property = conf.getProperty("element3[@name]");
181         assertNotNull(property);
182         assertTrue(property instanceof String);
183         assertEquals("foo", property);
184 
185         // test non-text/cdata element
186         property = conf.getProperty("test.comment");
187         assertEquals("", property);
188 
189         // test cdata element
190         property = conf.getProperty("test.cdata");
191         assertNotNull(property);
192         assertTrue(property instanceof String);
193         assertEquals("<cdata value>", property);
194 
195         // test multiple sibling elements
196         property = conf.getProperty("list.sublist.item");
197         assertNotNull(property);
198         assertTrue(property instanceof List);
199         List list = (List)property;
200         assertEquals(2, list.size());
201         assertEquals("five", list.get(0));
202         assertEquals("six", list.get(1));
203 
204         // test multiple, disjoined elements
205         property = conf.getProperty("list.item");
206         assertNotNull(property);
207         assertTrue(property instanceof List);
208         list = (List)property;
209         assertEquals(4, list.size());
210         assertEquals("one", list.get(0));
211         assertEquals("two", list.get(1));
212         assertEquals("three", list.get(2));
213         assertEquals("four", list.get(3));
214 
215         // test multiple, disjoined attributes
216         property = conf.getProperty("list.item[@name]");
217         assertNotNull(property);
218         assertTrue(property instanceof List);
219         list = (List)property;
220         assertEquals(2, list.size());
221         assertEquals("one", list.get(0));
222         assertEquals("three", list.get(1));
223     }
224 
225     public void testGetAttribute()
226     {
227         assertEquals("element3[@name]", "foo", conf.getProperty("element3[@name]"));
228     }
229 
230     public void testClearAttribute() throws Exception
231     {
232         // test non-existent attribute
233         String key = "clear[@id]";
234         conf.clearProperty(key);
235         assertNull(key, conf.getProperty(key));
236         assertNull(key, conf.getProperty(key));
237 
238         // test single attribute
239         conf.load();
240         key = "clear.element2[@id]";
241         conf.clearProperty(key);
242         assertNull(key, conf.getProperty(key));
243         assertNull(key, conf.getProperty(key));
244         key = "clear.element2";
245         assertNotNull(key, conf.getProperty(key));
246         assertNotNull(key, conf.getProperty(key));
247 
248         // test multiple, disjoined attributes
249         conf.load();
250         key = "clear.list.item[@id]";
251         conf.clearProperty(key);
252         assertNull(key, conf.getProperty(key));
253         assertNull(key, conf.getProperty(key));
254         key = "clear.list.item";
255         assertNotNull(key, conf.getProperty(key));
256         assertNotNull(key, conf.getProperty(key));
257     }
258 
259     public void testSetAttribute()
260     {
261         // replace an existing attribute
262         conf.setProperty("element3[@name]", "bar");
263         assertEquals("element3[@name]", "bar", conf.getProperty("element3[@name]"));
264 
265         // set a new attribute
266         conf.setProperty("foo[@bar]", "value");
267         assertEquals("foo[@bar]", "value", conf.getProperty("foo[@bar]"));
268 
269         conf.setProperty("name1","value1");
270         assertEquals("value1",conf.getProperty("name1"));
271     }
272 
273     public void testAddAttribute()
274     {
275         conf.addProperty("element3[@name]", "bar");
276 
277         List list = conf.getList("element3[@name]");
278         assertNotNull("null list", list);
279         assertTrue("'foo' element missing", list.contains("foo"));
280         assertTrue("'bar' element missing", list.contains("bar"));
281         assertEquals("list size", 2, list.size());
282     }
283 
284     public void testAddObjectAttribute()
285     {
286         conf.addProperty("test.boolean[@value]", Boolean.TRUE);
287         assertTrue("test.boolean[@value]", conf.getBoolean("test.boolean[@value]"));
288     }
289 
290     /***
291      * Tests setting an attribute on the root element.
292      */
293     public void testSetRootAttribute() throws ConfigurationException
294     {
295         conf.setProperty("[@test]", "true");
296         assertEquals("Root attribute not set", "true", conf
297                 .getString("[@test]"));
298         conf.save(testSaveConf);
299         XMLConfiguration checkConf = new XMLConfiguration();
300         checkConf.setFile(testSaveConf);
301         checkSavedConfig(checkConf);
302         assertTrue("Attribute not found after save", checkConf
303                 .containsKey("[@test]"));
304         checkConf.setProperty("[@test]", "newValue");
305         checkConf.save();
306         conf = checkConf;
307         checkConf = new XMLConfiguration();
308         checkConf.setFile(testSaveConf);
309         checkSavedConfig(checkConf);
310         assertEquals("Attribute not modified after save", "newValue", checkConf
311                 .getString("[@test]"));
312     }
313 
314     /***
315      * Tests whether the configuration's root node is initialized with a
316      * reference to the corresponding XML element.
317      */
318     public void testGetRootReference()
319     {
320         assertNotNull("Root node has no reference", conf.getRootNode()
321                 .getReference());
322     }
323 
324     public void testAddList()
325     {
326         conf.addProperty("test.array", "value1");
327         conf.addProperty("test.array", "value2");
328 
329         List list = conf.getList("test.array");
330         assertNotNull("null list", list);
331         assertTrue("'value1' element missing", list.contains("value1"));
332         assertTrue("'value2' element missing", list.contains("value2"));
333         assertEquals("list size", 2, list.size());
334     }
335 
336     public void testGetComplexProperty()
337     {
338         assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
339     }
340 
341     public void testSettingFileNames()
342     {
343         conf = new XMLConfiguration();
344         conf.setFileName(testProperties);
345         assertEquals(testProperties.toString(), conf.getFileName());
346 
347         conf.setBasePath(testBasePath);
348         conf.setFileName("hello.xml");
349         assertEquals("hello.xml", conf.getFileName());
350         assertEquals(testBasePath.toString(), conf.getBasePath());
351         assertEquals(new File(testBasePath, "hello.xml"), conf.getFile());
352 
353         conf.setBasePath(testBasePath);
354         conf.setFileName("subdir/hello.xml");
355         assertEquals("subdir/hello.xml", conf.getFileName());
356         assertEquals(testBasePath.toString(), conf.getBasePath());
357         assertEquals(new File(testBasePath, "subdir/hello.xml"), conf.getFile());
358     }
359 
360     public void testLoad() throws Exception
361     {
362         conf = new XMLConfiguration();
363         conf.setFileName(testProperties);
364         conf.load();
365 
366         assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
367     }
368 
369     public void testLoadWithBasePath() throws Exception
370     {
371         conf = new XMLConfiguration();
372 
373         conf.setFileName("test.xml");
374         conf.setBasePath(testBasePath);
375         conf.load();
376 
377         assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
378     }
379 
380     /***
381      * Tests constructing an XMLConfiguration from a non existing file and
382      * later saving to this file.
383      */
384     public void testLoadAndSaveFromFile() throws Exception
385     {
386         // If the file does not exist, an empty config is created
387         conf = new XMLConfiguration(testSaveConf);
388         assertTrue(conf.isEmpty());
389         conf.addProperty("test", "yes");
390         conf.save();
391 
392         conf = new XMLConfiguration(testSaveConf);
393         assertEquals("yes", conf.getString("test"));
394     }
395 
396     /***
397      * Tests loading a configuration from a URL.
398      */
399     public void testLoadFromURL() throws Exception
400     {
401         URL url = new File(testProperties).toURL();
402         conf = new XMLConfiguration(url);
403         assertEquals("value", conf.getProperty("element"));
404         assertEquals(url, conf.getURL());
405     }
406 
407     /***
408      * Tests loading from a stream.
409      */
410     public void testLoadFromStream() throws Exception
411     {
412         String xml = "<?xml version=\"1.0\"?><config><test>1</test></config>";
413         conf = new XMLConfiguration();
414         conf.load(new ByteArrayInputStream(xml.getBytes()));
415         assertEquals(1, conf.getInt("test"));
416 
417         conf = new XMLConfiguration();
418         conf.load(new ByteArrayInputStream(xml.getBytes()), "UTF8");
419         assertEquals(1, conf.getInt("test"));
420     }
421 
422     /***
423      * Tests loading a non well formed XML from a string.
424      */
425     public void testLoadInvalidXML() throws Exception
426     {
427         String xml = "<?xml version=\"1.0\"?><config><test>1</rest></config>";
428         conf = new XMLConfiguration();
429         try
430         {
431             conf.load(new StringReader(xml));
432             fail("Could load invalid XML!");
433         }
434         catch(ConfigurationException cex)
435         {
436             //ok
437         }
438     }
439 
440     public void testSetProperty() throws Exception
441     {
442         conf.setProperty("element.string", "hello");
443 
444         assertEquals("'element.string'", "hello", conf.getString("element.string"));
445         assertEquals("XML value of element.string", "hello", conf.getProperty("element.string"));
446     }
447 
448     public void testAddProperty()
449     {
450         // add a property to a non initialized xml configuration
451         XMLConfiguration config = new XMLConfiguration();
452         config.addProperty("test.string", "hello");
453 
454         assertEquals("'test.string'", "hello", config.getString("test.string"));
455     }
456 
457     public void testAddObjectProperty()
458     {
459         // add a non string property
460         conf.addProperty("test.boolean", Boolean.TRUE);
461         assertTrue("'test.boolean'", conf.getBoolean("test.boolean"));
462     }
463 
464     public void testSave() throws Exception
465     {
466         // add an array of strings to the configuration
467         conf.addProperty("string", "value1");
468         for (int i = 1; i < 5; i++)
469         {
470             conf.addProperty("test.array", "value" + i);
471         }
472 
473         // add an array of strings in an attribute
474         for (int i = 1; i < 5; i++)
475         {
476            conf.addProperty("test.attribute[@array]", "value" + i);
477         }
478 
479         // add comma delimited lists with escaped delimiters
480         conf.addProperty("split.list5", "a//,b//,c");
481         conf.setProperty("element3", "value//,value1//,value2");
482         conf.setProperty("element3[@name]", "foo//,bar");
483 
484         // save the configuration
485         conf.save(testSaveConf.getAbsolutePath());
486 
487         // read the configuration and compare the properties
488         XMLConfiguration checkConfig = new XMLConfiguration();
489         checkConfig.setFileName(testSaveConf.getAbsolutePath());
490         checkSavedConfig(checkConfig);
491     }
492 
493     /***
494      * Tests saving to a URL.
495      */
496     public void testSaveToURL() throws Exception
497     {
498         conf.save(testSaveConf.toURL());
499         XMLConfiguration checkConfig = new XMLConfiguration();
500         checkConfig.setFile(testSaveConf);
501         checkSavedConfig(checkConfig);
502     }
503 
504     /***
505      * Tests saving to a stream.
506      */
507     public void testSaveToStream() throws Exception
508     {
509         assertNull(conf.getEncoding());
510         conf.setEncoding("UTF8");
511         FileOutputStream out = null;
512         try
513         {
514             out = new FileOutputStream(testSaveConf);
515             conf.save(out);
516         }
517         finally
518         {
519             if(out != null)
520             {
521                 out.close();
522             }
523         }
524 
525         XMLConfiguration checkConfig = new XMLConfiguration();
526         checkConfig.setFile(testSaveConf);
527         checkSavedConfig(checkConfig);
528 
529         try
530         {
531             out = new FileOutputStream(testSaveConf);
532             conf.save(out, "UTF8");
533         }
534         finally
535         {
536             if(out != null)
537             {
538                 out.close();
539             }
540         }
541 
542         checkConfig.clear();
543         checkSavedConfig(checkConfig);
544     }
545 
546     public void testAutoSave() throws Exception
547     {
548         conf.setFile(testSaveConf);
549         assertFalse(conf.isAutoSave());
550         conf.setAutoSave(true);
551         assertTrue(conf.isAutoSave());
552         conf.setProperty("autosave", "ok");
553 
554         // reload the configuration
555         XMLConfiguration conf2 = new XMLConfiguration(conf.getFile());
556         assertEquals("'autosave' property", "ok", conf2.getString("autosave"));
557 
558         conf.clearTree("clear");
559         conf2 = new XMLConfiguration(conf.getFile());
560         Configuration sub = conf2.subset("clear");
561         assertTrue(sub.isEmpty());
562     }
563 
564     /***
565      * Tests if a second file can be appended to a first.
566      */
567     public void testAppend() throws Exception
568     {
569         conf = new XMLConfiguration();
570         conf.setFileName(testProperties);
571         conf.load();
572         conf.load(testProperties2);
573         assertEquals("value", conf.getString("element"));
574         assertEquals("tasks", conf.getString("table.name"));
575 
576         conf.save(testSaveConf);
577         conf = new XMLConfiguration(testSaveConf);
578         assertEquals("value", conf.getString("element"));
579         assertEquals("tasks", conf.getString("table.name"));
580         assertEquals("application", conf.getString("table[@tableType]"));
581     }
582 
583     /***
584      * Tests saving attributes (related to issue 34442).
585      */
586     public void testSaveAttributes() throws Exception
587     {
588         conf.clear();
589         conf.load();
590         conf.save(testSaveConf);
591         conf = new XMLConfiguration();
592         conf.load(testSaveConf);
593         assertEquals("foo", conf.getString("element3[@name]"));
594     }
595 
596     /***
597      * Tests collaboration between XMLConfiguration and a reloading strategy.
598      */
599     public void testReloading() throws Exception
600     {
601         assertNotNull(conf.getReloadingStrategy());
602         assertTrue(conf.getReloadingStrategy() instanceof InvariantReloadingStrategy);
603         PrintWriter out = null;
604 
605         try
606         {
607             out = new PrintWriter(new FileWriter(testSaveConf));
608             out.println("<?xml version=\"1.0\"?><config><test>1</test></config>");
609             out.close();
610             out = null;
611             conf.setFile(testSaveConf);
612             FileAlwaysReloadingStrategy strategy = new FileAlwaysReloadingStrategy();
613             strategy.setRefreshDelay(100);
614             conf.setReloadingStrategy(strategy);
615             assertEquals(strategy, conf.getReloadingStrategy());
616             assertEquals("Wrong file monitored", testSaveConf.getAbsolutePath(),
617                     strategy.getMonitoredFile().getAbsolutePath());
618             conf.load();
619             assertEquals(1, conf.getInt("test"));
620 
621             out = new PrintWriter(new FileWriter(testSaveConf));
622             out.println("<?xml version=\"1.0\"?><config><test>2</test></config>");
623             out.close();
624             out = null;
625 
626             int value = conf.getInt("test");
627             assertEquals("No reloading performed", 2, value);
628         }
629         finally
630         {
631             if (out != null)
632             {
633                 out.close();
634             }
635         }
636     }
637 
638     /***
639      * Tests access to tag names with delimiter characters.
640      */
641     public void testComplexNames()
642     {
643         assertEquals("Name with dot", conf.getString("complexNames.my..elem"));
644         assertEquals("Another dot", conf.getString("complexNames.my..elem.sub..elem"));
645     }
646 
647     /***
648      * Tests setting a custom document builder.
649      */
650     public void testCustomDocBuilder() throws Exception
651     {
652         // Load an invalid XML file with the default (non validating)
653         // doc builder. This should work...
654         conf = new XMLConfiguration();
655         conf.load(new File("conf/testValidateInvalid.xml"));
656         assertEquals("customers", conf.getString("table.name"));
657         assertFalse(conf.containsKey("table.fields.field(1).type"));
658 
659         // Now create a validating doc builder and set it.
660         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
661         factory.setValidating(true);
662         DocumentBuilder builder = factory.newDocumentBuilder();
663         builder.setErrorHandler(new DefaultHandler() {
664             public void error(SAXParseException ex) throws SAXException
665             {
666                 throw ex;
667             }
668         });
669         conf = new XMLConfiguration();
670         conf.setDocumentBuilder(builder);
671         try
672         {
673             conf.load(new File("conf/testValidateInvalid.xml"));
674             fail("Could load invalid file with validating set to true!");
675         }
676         catch(ConfigurationException ex)
677         {
678             //ok
679         }
680 
681         // Try to load a valid document with a validating builder
682         conf = new XMLConfiguration();
683         conf.setDocumentBuilder(builder);
684         conf.load(new File("conf/testValidateValid.xml"));
685         assertTrue(conf.containsKey("table.fields.field(1).type"));
686     }
687 
688     /***
689      * Tests the clone() method.
690      */
691     public void testClone()
692     {
693         Configuration c = (Configuration) conf.clone();
694         assertTrue(c instanceof XMLConfiguration);
695         XMLConfiguration copy = (XMLConfiguration) c;
696         assertNotNull(conf.getDocument());
697         assertNull(copy.getDocument());
698         assertNotNull(conf.getFileName());
699         assertNull(copy.getFileName());
700 
701         copy.setProperty("element3", "clonedValue");
702         assertEquals("value", conf.getString("element3"));
703         conf.setProperty("element3[@name]", "originalFoo");
704         assertEquals("foo", copy.getString("element3[@name]"));
705     }
706 
707     /***
708      * Tests saving a configuration after cloning to ensure that the clone and
709      * the original are completely detachted.
710      */
711     public void testCloneWithSave() throws ConfigurationException
712     {
713         XMLConfiguration c = (XMLConfiguration) conf.clone();
714         c.addProperty("test.newProperty", Boolean.TRUE);
715         conf.addProperty("test.orgProperty", Boolean.TRUE);
716         c.save(testSaveConf);
717         XMLConfiguration c2 = new XMLConfiguration(testSaveConf);
718         assertTrue("New property after clone() was not saved", c2
719                 .getBoolean("test.newProperty"));
720         assertFalse("Property of original config was saved", c2
721                 .containsKey("test.orgProperty"));
722     }
723 
724     /***
725      * Tests the subset() method. There was a bug that calling subset() had
726      * undesired side effects.
727      */
728     public void testSubset() throws ConfigurationException
729     {
730         conf = new XMLConfiguration();
731         conf.load(new File("conf/testHierarchicalXMLConfiguration.xml"));
732         conf.subset("tables.table(0)");
733         conf.save(testSaveConf);
734 
735         conf = new XMLConfiguration(testSaveConf);
736         assertEquals("users", conf.getString("tables.table(0).name"));
737     }
738 
739     /***
740      * Tests string properties with list delimiters and escaped delimiters.
741      */
742     public void testSplitLists()
743     {
744         assertEquals("a", conf.getString("split.list3[@values]"));
745         assertEquals(2, conf.getMaxIndex("split.list3[@values]"));
746         assertEquals("a,b,c", conf.getString("split.list4[@values]"));
747         assertEquals("a", conf.getString("split.list1"));
748         assertEquals(2, conf.getMaxIndex("split.list1"));
749         assertEquals("a,b,c", conf.getString("split.list2"));
750     }
751 
752     /***
753      * Tests string properties with list delimiters when delimiter parsing
754      * is disabled
755      */
756     public void testDelimiterParsingDisabled() throws ConfigurationException {
757         XMLConfiguration conf2 = new XMLConfiguration();
758         conf2.setDelimiterParsingDisabled(true);
759         conf2.setFile(new File(testProperties));
760         conf2.load();
761 
762         assertEquals("a,b,c", conf2.getString("split.list3[@values]"));
763         assertEquals(0, conf2.getMaxIndex("split.list3[@values]"));
764         assertEquals("a//,b//,c", conf2.getString("split.list4[@values]"));
765         assertEquals("a,b,c", conf2.getString("split.list1"));
766         assertEquals(0, conf2.getMaxIndex("split.list1"));
767         assertEquals("a//,b//,c", conf2.getString("split.list2"));
768     }
769 
770     /***
771      * Tests whether a DTD can be accessed.
772      */
773     public void testDtd() throws ConfigurationException
774     {
775         conf = new XMLConfiguration("testDtd.xml");
776         assertEquals("value1", conf.getString("entry(0)"));
777         assertEquals("test2", conf.getString("entry(1)[@key]"));
778     }
779 
780     /***
781      * Tests DTD validation using the setValidating() method.
782      */
783     public void testValidating() throws ConfigurationException
784     {
785         File nonValidFile = new File("conf/testValidateInvalid.xml");
786         conf = new XMLConfiguration();
787         assertFalse(conf.isValidating());
788 
789         // Load a non valid XML document. Should work for isValidating() == false
790         conf.load(nonValidFile);
791         assertEquals("customers", conf.getString("table.name"));
792         assertFalse(conf.containsKey("table.fields.field(1).type"));
793 
794         // Now set the validating flag to true
795         conf.setValidating(true);
796         try
797         {
798             conf.load(nonValidFile);
799             fail("Validation was not performed!");
800         }
801         catch(ConfigurationException cex)
802         {
803             //ok
804         }
805     }
806 
807     /***
808      * Tests handling of empty elements.
809      */
810     public void testEmptyElements() throws ConfigurationException
811     {
812         assertTrue(conf.containsKey("empty"));
813         assertEquals("", conf.getString("empty"));
814         conf.addProperty("empty2", "");
815         conf.setProperty("empty", "no more empty");
816         conf.save(testSaveConf);
817 
818         conf = new XMLConfiguration(testSaveConf);
819         assertEquals("no more empty", conf.getString("empty"));
820         assertEquals("", conf.getProperty("empty2"));
821     }
822 
823     /***
824      * Tests whether the encoding is correctly detected by the XML parser. This
825      * is done by loading an XML file with the encoding "UTF-16". If this
826      * encoding is not detected correctly, an exception will be thrown that
827      * "Content is not allowed in prolog". This test case is related to issue
828      * 34204.
829      */
830     public void testLoadWithEncoding() throws ConfigurationException
831     {
832         File file = new File("conf/testEncoding.xml");
833         conf = new XMLConfiguration();
834         conf.load(file);
835         assertEquals("test3_yoge", conf.getString("yoge"));
836     }
837 
838     /***
839      * Tests whether the encoding is written to the generated XML file.
840      */
841     public void testSaveWithEncoding() throws ConfigurationException
842     {
843         conf = new XMLConfiguration();
844         conf.setProperty("test", "a value");
845         conf.setEncoding(ENCODING);
846 
847         StringWriter out = new StringWriter();
848         conf.save(out);
849         assertTrue("Encoding was not written to file", out.toString().indexOf(
850                 "encoding=\"" + ENCODING + "\"") >= 0);
851     }
852 
853     /***
854      * Tests whether a default encoding is used if no specific encoding is set.
855      * According to the XSLT specification (http://www.w3.org/TR/xslt#output)
856      * this should be either UTF-8 or UTF-16.
857      */
858     public void testSaveWithNullEncoding() throws ConfigurationException
859     {
860         conf = new XMLConfiguration();
861         conf.setProperty("testNoEncoding", "yes");
862         conf.setEncoding(null);
863 
864         StringWriter out = new StringWriter();
865         conf.save(out);
866         assertTrue("Encoding was written to file", out.toString().indexOf(
867                 "encoding=\"UTF-") >= 0);
868     }
869 
870     /***
871      * Tests whether the DOCTYPE survives a save operation.
872      */
873     public void testSaveWithDoctype() throws ConfigurationException
874     {
875         String content = "<?xml  version=\"1.0\"?>"
876                 + DOCTYPE
877                 + "properties"
878                 + DOCTYPE_DECL
879                 + "<properties version=\"1.0\"><entry key=\"test\">value</entry></properties>";
880         StringReader in = new StringReader(content);
881         conf = new XMLConfiguration();
882         conf.setFileName("conf/testDtd.xml");
883         conf.load();
884         conf.clear();
885         conf.load(in);
886 
887         assertEquals("Wrong public ID", PUBLIC_ID, conf.getPublicID());
888         assertEquals("Wrong system ID", SYSTEM_ID, conf.getSystemID());
889         StringWriter out = new StringWriter();
890         conf.save(out);
891         System.out.println(out.toString());
892         assertTrue("Did not find DOCTYPE", out.toString().indexOf(DOCTYPE) >= 0);
893     }
894 
895     /***
896      * Tests setting public and system IDs for the D'OCTYPE and then saving the
897      * configuration. This should generate a DOCTYPE declaration.
898      */
899     public void testSaveWithDoctypeIDs() throws ConfigurationException
900     {
901         assertNull("A public ID was found", conf.getPublicID());
902         assertNull("A system ID was found", conf.getSystemID());
903         conf.setPublicID(PUBLIC_ID);
904         conf.setSystemID(SYSTEM_ID);
905         StringWriter out = new StringWriter();
906         conf.save(out);
907         assertTrue("Did not find DOCTYPE", out.toString().indexOf(
908                 DOCTYPE + "testconfig" + DOCTYPE_DECL) >= 0);
909     }
910 
911     /***
912      * Tests saving a configuration when an invalid transformer factory is
913      * specified. In this case the error thrown by the TransformerFactory class
914      * should be caught and re-thrown as a ConfigurationException.
915      */
916     public void testSaveWithInvalidTransformerFactory()
917     {
918         System.setProperty(PROP_FACTORY, "an.invalid.Class");
919         try
920         {
921             conf.save(testSaveConf);
922             fail("Could save with invalid TransformerFactory!");
923         }
924         catch (ConfigurationException cex)
925         {
926             // ok
927         }
928         finally
929         {
930             System.getProperties().remove(PROP_FACTORY);
931         }
932     }
933 
934     /***
935      * Tests if reloads are recognized by subset().
936      */
937     public void testSubsetWithReload() throws ConfigurationException
938     {
939         XMLConfiguration c = setUpReloadTest();
940         Configuration sub = c.subset("test");
941         assertEquals("New value not read", "newValue", sub.getString("entity"));
942     }
943 
944     /***
945      * Tests if reloads are recognized by configurationAt().
946      */
947     public void testConfigurationAtWithReload() throws ConfigurationException
948     {
949         XMLConfiguration c = setUpReloadTest();
950         HierarchicalConfiguration sub = c.configurationAt("test(0)");
951         assertEquals("New value not read", "newValue", sub.getString("entity"));
952     }
953 
954     /***
955      * Tests if reloads are recognized by configurationsAt().
956      */
957     public void testConfigurationsAtWithReload() throws ConfigurationException
958     {
959         XMLConfiguration c = setUpReloadTest();
960         List configs = c.configurationsAt("test");
961         assertEquals("New value not read", "newValue",
962                 ((HierarchicalConfiguration) configs.get(0))
963                         .getString("entity"));
964     }
965 
966     /***
967      * Tests whether reloads are recognized when querying the configuration's
968      * keys.
969      */
970     public void testGetKeysWithReload() throws ConfigurationException
971     {
972         XMLConfiguration c = setUpReloadTest();
973         conf.addProperty("aNewKey", "aNewValue");
974         conf.save(testSaveConf);
975         boolean found = false;
976         for (Iterator it = c.getKeys(); it.hasNext();)
977         {
978             if ("aNewKey".equals(it.next()))
979             {
980                 found = true;
981             }
982         }
983         assertTrue("Reload not performed", found);
984     }
985 
986     /***
987      * Tests accessing properties when the XPATH expression engine is set.
988      */
989     public void testXPathExpressionEngine()
990     {
991         conf.setExpressionEngine(new XPathExpressionEngine());
992         assertEquals("Wrong attribute value", "foo\"bar", conf
993                 .getString("test[1]/entity/@name"));
994         conf.clear();
995         assertNull(conf.getString("test[1]/entity/@name"));
996     }
997 
998     /***
999      * Tests the copy constructor.
1000      */
1001     public void testInitCopy() throws ConfigurationException
1002     {
1003     	XMLConfiguration copy = new XMLConfiguration(conf);
1004         assertEquals("value", copy.getProperty("element"));
1005         assertNull("Document was copied, too", copy.getDocument());
1006         ConfigurationNode root = copy.getRootNode();
1007         for(Iterator it = root.getChildren().iterator(); it.hasNext();)
1008         {
1009         	ConfigurationNode node = (ConfigurationNode) it.next();
1010         	assertNull("Reference was not cleared", node.getReference());
1011         }
1012 
1013         removeTestFile();
1014         copy.setFile(testSaveConf);
1015         copy.save();
1016         copy.clear();
1017         checkSavedConfig(copy);
1018     }
1019 
1020     /***
1021      * Tests setting text of the root element.
1022      */
1023     public void testSetTextRootElement() throws ConfigurationException
1024     {
1025         conf.setProperty("", "Root text");
1026         conf.save(testSaveConf);
1027         XMLConfiguration copy = new XMLConfiguration();
1028         copy.setFile(testSaveConf);
1029         checkSavedConfig(copy);
1030     }
1031 
1032     /***
1033      * Tests removing the text of the root element.
1034      */
1035     public void testClearTextRootElement() throws ConfigurationException
1036     {
1037         final String xml = "<e a=\"v\">text</e>";
1038         conf.clear();
1039         StringReader in = new StringReader(xml);
1040         conf.load(in);
1041         assertEquals("Wrong text of root", "text", conf.getString(""));
1042 
1043         conf.clearProperty("");
1044         conf.save(testSaveConf);
1045         XMLConfiguration copy = new XMLConfiguration();
1046         copy.setFile(testSaveConf);
1047         checkSavedConfig(copy);
1048     }
1049 
1050     /***
1051      * Tests list nodes with multiple values and attributes.
1052      */
1053     public void testListWithAttributes()
1054     {
1055         assertEquals("Wrong number of <a> elements", 6, conf.getList(
1056                 "attrList.a").size());
1057         assertEquals("Wrong value of first element", "ABC", conf
1058                 .getString("attrList.a(0)"));
1059         assertEquals("Wrong value of first name attribute", "x", conf
1060                 .getString("attrList.a(0)[@name]"));
1061         assertEquals("Wrong number of name attributes", 5, conf.getList(
1062                 "attrList.a[@name]").size());
1063     }
1064 
1065     /***
1066      * Tests a list node with attributes that has multiple values separated by
1067      * the list delimiter. In this scenario the attribute should be added to the
1068      * node with the first value.
1069      */
1070     public void testListWithAttributesMultiValue()
1071     {
1072         assertEquals("Wrong value of 2nd element", "1", conf
1073                 .getString("attrList.a(1)"));
1074         assertEquals("Wrong value of 2nd name attribute", "y", conf
1075                 .getString("attrList.a(1)[@name]"));
1076         for (int i = 2; i <= 3; i++)
1077         {
1078             assertEquals("Wrong value of element " + (i + 1), i, conf
1079                     .getInt("attrList.a(" + i + ")"));
1080             assertFalse("element " + (i + 1) + " has attribute", conf
1081                     .containsKey("attrList.a(2)[@name]"));
1082         }
1083     }
1084 
1085     /***
1086      * Tests a list node with a multi-value attribute and multiple values. All
1087      * attribute values should be assigned to the node with the first value.
1088      */
1089     public void testListWithMultiAttributesMultiValue()
1090     {
1091         for (int i = 1; i <= 2; i++)
1092         {
1093             assertEquals("Wrong value of multi-valued node", "value" + i, conf
1094                     .getString("attrList.a(" + (i + 3) + ")"));
1095         }
1096         List attrs = conf.getList("attrList.a(4)[@name]");
1097         final String attrVal = "uvw";
1098         assertEquals("Wrong number of name attributes", attrVal.length(), attrs
1099                 .size());
1100         for (int i = 0; i < attrVal.length(); i++)
1101         {
1102             assertEquals("Wrong value for attribute " + i, String
1103                     .valueOf(attrVal.charAt(i)), attrs.get(i));
1104         }
1105         assertEquals("Wrong value of test attribute", "yes", conf
1106                 .getString("attrList.a(4)[@test]"));
1107         assertFalse("Name attribute for 2nd value", conf
1108                 .containsKey("attrList.a(5)[@name]"));
1109         assertFalse("Test attribute for 2nd value", conf
1110                 .containsKey("attrList.a(5)[@test]"));
1111     }
1112 
1113     /***
1114      * Tests whether the auto save mechanism is triggered by changes at a
1115      * subnode configuration.
1116      */
1117     public void testAutoSaveWithSubnodeConfig() throws ConfigurationException
1118     {
1119         final String newValue = "I am autosaved";
1120         conf.setFile(testSaveConf);
1121         conf.setAutoSave(true);
1122         Configuration sub = conf.configurationAt("element2.subelement");
1123         sub.setProperty("subsubelement", newValue);
1124         assertEquals("Change not visible to parent", newValue, conf
1125                 .getString("element2.subelement.subsubelement"));
1126         XMLConfiguration conf2 = new XMLConfiguration(testSaveConf);
1127         assertEquals("Change was not saved", newValue, conf2
1128                 .getString("element2.subelement.subsubelement"));
1129     }
1130 
1131     /***
1132      * Tests whether a subnode configuration created from another subnode
1133      * configuration of a XMLConfiguration can trigger the auto save mechanism.
1134      */
1135     public void testAutoSaveWithSubSubnodeConfig() throws ConfigurationException
1136     {
1137         final String newValue = "I am autosaved";
1138         conf.setFile(testSaveConf);
1139         conf.setAutoSave(true);
1140         SubnodeConfiguration sub1 = conf.configurationAt("element2");
1141         SubnodeConfiguration sub2 = sub1.configurationAt("subelement");
1142         sub2.setProperty("subsubelement", newValue);
1143         assertEquals("Change not visible to parent", newValue, conf
1144                 .getString("element2.subelement.subsubelement"));
1145         XMLConfiguration conf2 = new XMLConfiguration(testSaveConf);
1146         assertEquals("Change was not saved", newValue, conf2
1147                 .getString("element2.subelement.subsubelement"));
1148     }
1149 
1150     /***
1151      * Tests saving and loading a configuration when delimiter parsing is
1152      * disabled.
1153      */
1154     public void testSaveDelimiterParsingDisabled()
1155             throws ConfigurationException
1156     {
1157         checkSaveDelimiterParsingDisabled("list.delimiter.test");
1158     }
1159 
1160     /***
1161      * Tests saving and loading a configuration when delimiter parsing is
1162      * disabled and attributes are involved.
1163      */
1164     public void testSaveDelimiterParsingDisabledAttrs()
1165             throws ConfigurationException
1166     {
1167         checkSaveDelimiterParsingDisabled("list.delimiter.test[@attr]");
1168     }
1169 
1170     /***
1171      * Helper method for testing saving and loading a configuration when
1172      * delimiter parsing is disabled.
1173      *
1174      * @param key the key to be checked
1175      * @throws ConfigurationException if an error occurs
1176      */
1177     private void checkSaveDelimiterParsingDisabled(String key)
1178             throws ConfigurationException
1179     {
1180         conf.clear();
1181         conf.setDelimiterParsingDisabled(true);
1182         conf.load();
1183         conf.setProperty(key, "C://Temp//,C://Data//");
1184         conf.addProperty(key, "a,b,c");
1185         conf.save(testSaveConf);
1186         XMLConfiguration checkConf = new XMLConfiguration();
1187         checkConf.setDelimiterParsingDisabled(true);
1188         checkConf.setFile(testSaveConf);
1189         checkSavedConfig(checkConf);
1190     }
1191 
1192     /***
1193      * Tests multiple attribute values in delimiter parsing disabled mode.
1194      */
1195     public void testDelimiterParsingDisabledMultiAttrValues() throws ConfigurationException
1196     {
1197         conf.clear();
1198         conf.setDelimiterParsingDisabled(true);
1199         conf.load();
1200         List expr = conf.getList("expressions[@value]");
1201         assertEquals("Wrong list size", 2, expr.size());
1202         assertEquals("Wrong element 1", "a || (b && c)", expr.get(0));
1203         assertEquals("Wrong element 2", "!d", expr.get(1));
1204     }
1205 
1206     /***
1207      * Tests using multiple attribute values, which are partly escaped when
1208      * delimiter parsing is not disabled.
1209      */
1210     public void testMultipleAttrValuesEscaped() throws ConfigurationException
1211     {
1212         conf.addProperty("test.dir[@name]", "C://Temp//");
1213         conf.addProperty("test.dir[@name]", "C://Data//");
1214         conf.save(testSaveConf);
1215         XMLConfiguration checkConf = new XMLConfiguration();
1216         checkConf.setFile(testSaveConf);
1217         checkSavedConfig(checkConf);
1218     }
1219 
1220     /***
1221      * Tests a combination of auto save = true and an associated reloading
1222      * strategy.
1223      */
1224     public void testAutoSaveWithReloadingStrategy() throws ConfigurationException
1225     {
1226         conf.setFile(testSaveConf);
1227         conf.save();
1228         conf.setReloadingStrategy(new FileAlwaysReloadingStrategy());
1229         conf.setAutoSave(true);
1230         assertEquals("Value not found", "value", conf.getProperty("element"));
1231     }
1232 
1233     /***
1234      * Tests adding nodes from another configuration.
1235      */
1236     public void testAddNodesCopy() throws ConfigurationException
1237     {
1238         XMLConfiguration c2 = new XMLConfiguration(testProperties2);
1239         conf.addNodes("copiedProperties", c2.getRootNode().getChildren());
1240         conf.save(testSaveConf);
1241         XMLConfiguration checkConf = new XMLConfiguration();
1242         checkConf.setFile(testSaveConf);
1243         checkSavedConfig(checkConf);
1244     }
1245 
1246     /***
1247      * Tests whether the addNodes() method triggers an auto save.
1248      */
1249     public void testAutoSaveAddNodes() throws ConfigurationException
1250     {
1251         conf.setFile(testSaveConf);
1252         conf.setAutoSave(true);
1253         HierarchicalConfiguration.Node node = new HierarchicalConfiguration.Node(
1254                 "addNodesTest", Boolean.TRUE);
1255         Collection nodes = new ArrayList(1);
1256         nodes.add(node);
1257         conf.addNodes("test.autosave", nodes);
1258         XMLConfiguration c2 = new XMLConfiguration(testSaveConf);
1259         assertTrue("Added nodes are not saved", c2
1260                 .getBoolean("test.autosave.addNodesTest"));
1261     }
1262 
1263     /***
1264      * Tests saving a configuration after a node was added. Test for
1265      * CONFIGURATION-294.
1266      */
1267     public void testAddNodesAndSave() throws ConfigurationException
1268     {
1269         ConfigurationNode node = new HierarchicalConfiguration.Node("test");
1270         ConfigurationNode child = new HierarchicalConfiguration.Node("child");
1271         node.addChild(child);
1272         ConfigurationNode attr = new HierarchicalConfiguration.Node("attr");
1273         node.addAttribute(attr);
1274         ConfigurationNode node2 = conf.createNode("test2");
1275         Collection nodes = new ArrayList(2);
1276         nodes.add(node);
1277         nodes.add(node2);
1278         conf.addNodes("add.nodes", nodes);
1279         conf.setFile(testSaveConf);
1280         conf.save();
1281         conf.setProperty("add.nodes.test", "true");
1282         conf.setProperty("add.nodes.test.child", "yes");
1283         conf.setProperty("add.nodes.test[@attr]", "existing");
1284         conf.setProperty("add.nodes.test2", "anotherValue");
1285         conf.save();
1286         XMLConfiguration c2 = new XMLConfiguration(testSaveConf);
1287         assertEquals("Value was not saved", "true", c2
1288                 .getString("add.nodes.test"));
1289         assertEquals("Child value was not saved", "yes", c2
1290                 .getString("add.nodes.test.child"));
1291         assertEquals("Attr value was not saved", "existing", c2
1292                 .getString("add.nodes.test[@attr]"));
1293         assertEquals("Node2 not saved", "anotherValue", c2
1294                 .getString("add.nodes.test2"));
1295     }
1296 
1297     /***
1298      * Tests registering the publicId of a DTD.
1299      */
1300     public void testRegisterEntityId() throws ConfigurationException,
1301             IOException
1302     {
1303         File dtdFile = new File("conf/properties.dtd");
1304         final String publicId = "http://commons.apache.org/test/properties.dtd";
1305         conf = new XMLConfiguration("testDtd.xml");
1306         conf.setPublicID(publicId);
1307         conf.save(testSaveConf);
1308         XMLConfiguration checkConfig = new XMLConfiguration();
1309         checkConfig.setFile(testSaveConf);
1310         checkConfig.registerEntityId(publicId, dtdFile.toURL());
1311         checkConfig.setValidating(true);
1312         checkSavedConfig(checkConfig);
1313     }
1314 
1315     /***
1316      * Tries to register a null public ID. This should cause an exception.
1317      */
1318     public void testRegisterEntityIdNull() throws IOException
1319     {
1320         try
1321         {
1322             conf.registerEntityId(null, new URL("http://commons.apache.org"));
1323             fail("Could register null public ID!");
1324         }
1325         catch (IllegalArgumentException iex)
1326         {
1327             // ok
1328         }
1329     }
1330 
1331     /***
1332      * Tests saving a configuration that was created from a hierarchical
1333      * configuration. This test exposes bug CONFIGURATION-301.
1334      */
1335     public void testSaveAfterCreateWithCopyConstructor()
1336             throws ConfigurationException
1337     {
1338         HierarchicalConfiguration hc = conf.configurationAt("element2");
1339         conf = new XMLConfiguration(hc);
1340         conf.save(testSaveConf);
1341         XMLConfiguration checkConfig = new XMLConfiguration();
1342         checkConfig.setFile(testSaveConf);
1343         checkSavedConfig(checkConfig);
1344         assertEquals("Wrong name of root element", "element2", checkConfig
1345                 .getRootElementName());
1346     }
1347 
1348     /***
1349      * Tests whether the name of the root element is copied when a configuration
1350      * is created using the copy constructor.
1351      */
1352     public void testCopyRootName() throws ConfigurationException
1353     {
1354         final String rootName = "rootElement";
1355         final String xml = "<" + rootName + "><test>true</test></" + rootName
1356                 + ">";
1357         conf.clear();
1358         conf.load(new StringReader(xml));
1359         XMLConfiguration copy = new XMLConfiguration(conf);
1360         assertEquals("Wrong name of root element", rootName, copy
1361                 .getRootElementName());
1362         copy.save(testSaveConf);
1363         copy = new XMLConfiguration(testSaveConf);
1364         assertEquals("Wrong name of root element after save", rootName, copy
1365                 .getRootElementName());
1366     }
1367 
1368     /***
1369      * Tests whether the name of the root element is copied for a configuration
1370      * for which not yet a document exists.
1371      */
1372     public void testCopyRootNameNoDocument() throws ConfigurationException
1373     {
1374         final String rootName = "rootElement";
1375         conf = new XMLConfiguration();
1376         conf.setRootElementName(rootName);
1377         conf.setProperty("test", Boolean.TRUE);
1378         XMLConfiguration copy = new XMLConfiguration(conf);
1379         assertEquals("Wrong name of root element", rootName, copy
1380                 .getRootElementName());
1381         copy.save(testSaveConf);
1382         copy = new XMLConfiguration(testSaveConf);
1383         assertEquals("Wrong name of root element after save", rootName, copy
1384                 .getRootElementName());
1385     }
1386 
1387     /***
1388      * Tests adding an attribute node using the addNodes() method.
1389      */
1390     public void testAddNodesAttributeNode()
1391     {
1392         conf.addProperty("testAddNodes.property[@name]", "prop1");
1393         conf.addProperty("testAddNodes.property(0).value", "value1");
1394         conf.addProperty("testAddNodes.property(-1)[@name]", "prop2");
1395         conf.addProperty("testAddNodes.property(1).value", "value2");
1396         Collection nodes = new ArrayList();
1397         nodes.add(new HierarchicalConfiguration.Node("property"));
1398         conf.addNodes("testAddNodes", nodes);
1399         nodes.clear();
1400         ConfigurationNode nd = new HierarchicalConfiguration.Node("name",
1401                 "prop3");
1402         nd.setAttribute(true);
1403         nodes.add(nd);
1404         conf.addNodes("testAddNodes.property(2)", nodes);
1405         assertEquals("Attribute not added", "prop3", conf
1406                 .getString("testAddNodes.property(2)[@name]"));
1407     }
1408 
1409     /***
1410      * Tests whether spaces are preserved when the xml:space attribute is set.
1411      */
1412     public void testPreserveSpace()
1413     {
1414         assertEquals("Wrong value of blanc", " ", conf.getString("space.blanc"));
1415         assertEquals("Wrong value of stars", " * * ", conf
1416                 .getString("space.stars"));
1417     }
1418 
1419     /***
1420      * Tests whether the xml:space attribute can be overridden in nested
1421      * elements.
1422      */
1423     public void testPreserveSpaceOverride()
1424     {
1425         assertEquals("Not trimmed", "Some text", conf
1426                 .getString("space.description"));
1427     }
1428 
1429     /***
1430      * Tests an xml:space attribute with an invalid value. This will be
1431      * interpreted as default.
1432      */
1433     public void testPreserveSpaceInvalid()
1434     {
1435         assertEquals("Invalid not trimmed", "Some other text", conf
1436                 .getString("space.testInvalid"));
1437     }
1438 
1439     /***
1440      * Tests whether attribute splitting can be disabled.
1441      */
1442     public void testAttributeSplittingDisabled() throws ConfigurationException
1443     {
1444         List values = conf.getList("expressions[@value2]");
1445         assertEquals("Wrong number of attribute values", 2, values.size());
1446         assertEquals("Wrong value 1", "a", values.get(0));
1447         assertEquals("Wrong value 2", "b|c", values.get(1));
1448         XMLConfiguration conf2 = new XMLConfiguration();
1449         conf2.setAttributeSplittingDisabled(true);
1450         conf2.setFile(conf.getFile());
1451         conf2.load();
1452         assertEquals("Attribute was split", "a,b|c", conf2
1453                 .getString("expressions[@value2]"));
1454     }
1455 
1456     /***
1457      * Tests disabling both delimiter parsing and attribute splitting.
1458      */
1459     public void testAttributeSplittingAndDelimiterParsingDisabled()
1460             throws ConfigurationException
1461     {
1462         conf.clear();
1463         conf.setDelimiterParsingDisabled(true);
1464         conf.load();
1465         List values = conf.getList("expressions[@value2]");
1466         assertEquals("Wrong number of attribute values", 2, values.size());
1467         assertEquals("Wrong value 1", "a,b", values.get(0));
1468         assertEquals("Wrong value 2", "c", values.get(1));
1469         XMLConfiguration conf2 = new XMLConfiguration();
1470         conf2.setAttributeSplittingDisabled(true);
1471         conf2.setDelimiterParsingDisabled(true);
1472         conf2.setFile(conf.getFile());
1473         conf2.load();
1474         assertEquals("Attribute was split", "a,b|c", conf2
1475                 .getString("expressions[@value2]"));
1476     }
1477 
1478     /***
1479      * Prepares a configuration object for testing a reload operation.
1480      *
1481      * @return the initialized configuration
1482      * @throws ConfigurationException if an error occurs
1483      */
1484     private XMLConfiguration setUpReloadTest() throws ConfigurationException
1485     {
1486         removeTestFile();
1487         conf.save(testSaveConf);
1488         XMLConfiguration c = new XMLConfiguration(testSaveConf);
1489         c.setReloadingStrategy(new FileAlwaysReloadingStrategy());
1490         conf.setProperty("test(0).entity", "newValue");
1491         conf.save(testSaveConf);
1492         return c;
1493     }
1494 
1495     /***
1496      * Removes the test output file if it exists.
1497      */
1498     private void removeTestFile()
1499     {
1500         if (testSaveConf.exists())
1501         {
1502             assertTrue(testSaveConf.delete());
1503         }
1504     }
1505 
1506     /***
1507      * Helper method for checking if a save operation was successful. Loads a
1508      * saved configuration and then tests against a reference configuration.
1509      * @param checkConfig the configuration to check
1510      * @throws ConfigurationException if an error occurs
1511      */
1512     private void checkSavedConfig(FileConfiguration checkConfig) throws ConfigurationException
1513     {
1514         checkConfig.load();
1515         ConfigurationAssert.assertEquals(conf, checkConfig);
1516     }
1517 }