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.Iterator;
30  import java.util.List;
31  
32  import javax.xml.parsers.DocumentBuilder;
33  import javax.xml.parsers.DocumentBuilderFactory;
34  
35  import org.apache.commons.configuration.reloading.FileAlwaysReloadingStrategy;
36  import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
37  import org.apache.commons.configuration.reloading.InvariantReloadingStrategy;
38  import org.apache.commons.configuration.tree.ConfigurationNode;
39  import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
40  import org.xml.sax.SAXException;
41  import org.xml.sax.SAXParseException;
42  import org.xml.sax.helpers.DefaultHandler;
43  
44  import junit.framework.TestCase;
45  
46  /***
47   * test for loading and saving xml properties files
48   *
49   * @version $Id: TestXMLConfiguration.java 513498 2007-03-01 21:15:07Z oheger $
50   */
51  public class TestXMLConfiguration extends TestCase
52  {
53      /*** Constant for the used encoding.*/
54      static final String ENCODING = "ISO-8859-1";
55  
56      /*** Constant for the test system ID.*/
57      static final String SYSTEM_ID = "properties.dtd";
58  
59      /*** Constant for the test public ID.*/
60      static final String PUBLIC_ID = "-//Commons Configuration//DTD Test Configuration 1.3//EN";
61  
62      /*** Constant for the DOCTYPE declaration.*/
63      static final String DOCTYPE_DECL = " PUBLIC \"" + PUBLIC_ID + "\" \"" + SYSTEM_ID + "\">";
64  
65      /*** Constant for the DOCTYPE prefix.*/
66      static final String DOCTYPE = "<!DOCTYPE ";
67  
68      /*** Constant for the transformer factory property.*/
69      static final String PROP_FACTORY = "javax.xml.transform.TransformerFactory";
70  
71      /*** The File that we test with */
72      private String testProperties = new File("conf/test.xml").getAbsolutePath();
73      private String testProperties2 = new File("conf/testDigesterConfigurationInclude1.xml").getAbsolutePath();
74      private String testBasePath = new File("conf").getAbsolutePath();
75      private File testSaveConf = new File("target/testsave.xml");
76  
77      private XMLConfiguration conf;
78  
79      protected void setUp() throws Exception
80      {
81          conf = new XMLConfiguration();
82          conf.setFile(new File(testProperties));
83          conf.load();
84          removeTestFile();
85      }
86  
87      public void testGetProperty()
88      {
89          assertEquals("value", conf.getProperty("element"));
90      }
91  
92      public void testGetCommentedProperty()
93      {
94          assertEquals("", conf.getProperty("test.comment"));
95      }
96  
97      public void testGetPropertyWithXMLEntity()
98      {
99          assertEquals("1<2", conf.getProperty("test.entity"));
100     }
101 
102     public void testClearProperty() throws ConfigurationException, IOException
103     {
104         // test non-existent element
105         String key = "clearly";
106         conf.clearProperty(key);
107         assertNull(key, conf.getProperty(key));
108         assertNull(key, conf.getProperty(key));
109 
110         // test single element
111         conf.load();
112         key = "clear.element";
113         conf.clearProperty(key);
114         assertNull(key, conf.getProperty(key));
115         assertNull(key, conf.getProperty(key));
116 
117         // test single element with attribute
118         conf.load();
119         key = "clear.element2";
120         conf.clearProperty(key);
121         assertNull(key, conf.getProperty(key));
122         assertNull(key, conf.getProperty(key));
123         key = "clear.element2[@id]";
124         assertNotNull(key, conf.getProperty(key));
125         assertNotNull(key, conf.getProperty(key));
126 
127         // test non-text/cdata element
128         conf.load();
129         key = "clear.comment";
130         conf.clearProperty(key);
131         assertNull(key, conf.getProperty(key));
132         assertNull(key, conf.getProperty(key));
133 
134         // test cdata element
135         conf.load();
136         key = "clear.cdata";
137         conf.clearProperty(key);
138         assertNull(key, conf.getProperty(key));
139         assertNull(key, conf.getProperty(key));
140 
141         // test multiple sibling elements
142         conf.load();
143         key = "clear.list.item";
144         conf.clearProperty(key);
145         assertNull(key, conf.getProperty(key));
146         assertNull(key, conf.getProperty(key));
147         key = "clear.list.item[@id]";
148         assertNotNull(key, conf.getProperty(key));
149         assertNotNull(key, conf.getProperty(key));
150 
151         // test multiple, disjoined elements
152         conf.load();
153         key = "list.item";
154         conf.clearProperty(key);
155         assertNull(key, conf.getProperty(key));
156         assertNull(key, conf.getProperty(key));
157     }
158 
159     public void testgetProperty() {
160         // test non-leaf element
161         Object property = conf.getProperty("clear");
162         assertNull(property);
163 
164         // test non-existent element
165         property = conf.getProperty("e");
166         assertNull(property);
167 
168         // test non-existent element
169         property = conf.getProperty("element3[@n]");
170         assertNull(property);
171 
172         // test single element
173         property = conf.getProperty("element");
174         assertNotNull(property);
175         assertTrue(property instanceof String);
176         assertEquals("value", property);
177 
178         // test single attribute
179         property = conf.getProperty("element3[@name]");
180         assertNotNull(property);
181         assertTrue(property instanceof String);
182         assertEquals("foo", property);
183 
184         // test non-text/cdata element
185         property = conf.getProperty("test.comment");
186         assertEquals("", property);
187 
188         // test cdata element
189         property = conf.getProperty("test.cdata");
190         assertNotNull(property);
191         assertTrue(property instanceof String);
192         assertEquals("<cdata value>", property);
193 
194         // test multiple sibling elements
195         property = conf.getProperty("list.sublist.item");
196         assertNotNull(property);
197         assertTrue(property instanceof List);
198         List list = (List)property;
199         assertEquals(2, list.size());
200         assertEquals("five", list.get(0));
201         assertEquals("six", list.get(1));
202 
203         // test multiple, disjoined elements
204         property = conf.getProperty("list.item");
205         assertNotNull(property);
206         assertTrue(property instanceof List);
207         list = (List)property;
208         assertEquals(4, list.size());
209         assertEquals("one", list.get(0));
210         assertEquals("two", list.get(1));
211         assertEquals("three", list.get(2));
212         assertEquals("four", list.get(3));
213 
214         // test multiple, disjoined attributes
215         property = conf.getProperty("list.item[@name]");
216         assertNotNull(property);
217         assertTrue(property instanceof List);
218         list = (List)property;
219         assertEquals(2, list.size());
220         assertEquals("one", list.get(0));
221         assertEquals("three", list.get(1));
222     }
223 
224     public void testGetAttribute()
225     {
226         assertEquals("element3[@name]", "foo", conf.getProperty("element3[@name]"));
227     }
228 
229     public void testClearAttribute() throws Exception
230     {
231         // test non-existent attribute
232         String key = "clear[@id]";
233         conf.clearProperty(key);
234         assertNull(key, conf.getProperty(key));
235         assertNull(key, conf.getProperty(key));
236 
237         // test single attribute
238         conf.load();
239         key = "clear.element2[@id]";
240         conf.clearProperty(key);
241         assertNull(key, conf.getProperty(key));
242         assertNull(key, conf.getProperty(key));
243         key = "clear.element2";
244         assertNotNull(key, conf.getProperty(key));
245         assertNotNull(key, conf.getProperty(key));
246 
247         // test multiple, disjoined attributes
248         conf.load();
249         key = "clear.list.item[@id]";
250         conf.clearProperty(key);
251         assertNull(key, conf.getProperty(key));
252         assertNull(key, conf.getProperty(key));
253         key = "clear.list.item";
254         assertNotNull(key, conf.getProperty(key));
255         assertNotNull(key, conf.getProperty(key));
256     }
257 
258     public void testSetAttribute()
259     {
260         // replace an existing attribute
261         conf.setProperty("element3[@name]", "bar");
262         assertEquals("element3[@name]", "bar", conf.getProperty("element3[@name]"));
263 
264         // set a new attribute
265         conf.setProperty("foo[@bar]", "value");
266         assertEquals("foo[@bar]", "value", conf.getProperty("foo[@bar]"));
267         
268         conf.setProperty("name1","value1");
269         assertEquals("value1",conf.getProperty("name1"));
270     }
271 
272     public void testAddAttribute()
273     {
274         conf.addProperty("element3[@name]", "bar");
275 
276         List list = conf.getList("element3[@name]");
277         assertNotNull("null list", list);
278         assertTrue("'foo' element missing", list.contains("foo"));
279         assertTrue("'bar' element missing", list.contains("bar"));
280         assertEquals("list size", 2, list.size());
281     }
282 
283     public void testAddObjectAttribute()
284     {
285         conf.addProperty("test.boolean[@value]", Boolean.TRUE);
286         assertTrue("test.boolean[@value]", conf.getBoolean("test.boolean[@value]"));
287     }
288 
289     public void testAddList()
290     {
291         conf.addProperty("test.array", "value1");
292         conf.addProperty("test.array", "value2");
293 
294         List list = conf.getList("test.array");
295         assertNotNull("null list", list);
296         assertTrue("'value1' element missing", list.contains("value1"));
297         assertTrue("'value2' element missing", list.contains("value2"));
298         assertEquals("list size", 2, list.size());
299     }
300 
301     public void testGetComplexProperty()
302     {
303         assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
304     }
305 
306     public void testSettingFileNames()
307     {
308         conf = new XMLConfiguration();
309         conf.setFileName(testProperties);
310         assertEquals(testProperties.toString(), conf.getFileName());
311 
312         conf.setBasePath(testBasePath);
313         conf.setFileName("hello.xml");
314         assertEquals("hello.xml", conf.getFileName());
315         assertEquals(testBasePath.toString(), conf.getBasePath());
316         assertEquals(new File(testBasePath, "hello.xml"), conf.getFile());
317 
318         conf.setBasePath(testBasePath);
319         conf.setFileName("subdir/hello.xml");
320         assertEquals("subdir/hello.xml", conf.getFileName());
321         assertEquals(testBasePath.toString(), conf.getBasePath());
322         assertEquals(new File(testBasePath, "subdir/hello.xml"), conf.getFile());
323     }
324 
325     public void testLoad() throws Exception
326     {
327         conf = new XMLConfiguration();
328         conf.setFileName(testProperties);
329         conf.load();
330 
331         assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
332     }
333 
334     public void testLoadWithBasePath() throws Exception
335     {
336         conf = new XMLConfiguration();
337 
338         conf.setFileName("test.xml");
339         conf.setBasePath(testBasePath);
340         conf.load();
341 
342         assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
343     }
344 
345     /***
346      * Tests constructing an XMLConfiguration from a non existing file and
347      * later saving to this file.
348      */
349     public void testLoadAndSaveFromFile() throws Exception
350     {
351         // If the file does not exist, an empty config is created
352         conf = new XMLConfiguration(testSaveConf);
353         assertTrue(conf.isEmpty());
354         conf.addProperty("test", "yes");
355         conf.save();
356         
357         conf = new XMLConfiguration(testSaveConf);
358         assertEquals("yes", conf.getString("test"));
359     }
360     
361     /***
362      * Tests loading a configuration from a URL.
363      */
364     public void testLoadFromURL() throws Exception
365     {
366         URL url = new File(testProperties).toURL();
367         conf = new XMLConfiguration(url);
368         assertEquals("value", conf.getProperty("element"));
369         assertEquals(url, conf.getURL());
370     }
371     
372     /***
373      * Tests loading from a stream.
374      */
375     public void testLoadFromStream() throws Exception
376     {
377         String xml = "<?xml version=\"1.0\"?><config><test>1</test></config>";
378         conf = new XMLConfiguration();
379         conf.load(new ByteArrayInputStream(xml.getBytes()));
380         assertEquals(1, conf.getInt("test"));
381         
382         conf = new XMLConfiguration();
383         conf.load(new ByteArrayInputStream(xml.getBytes()), "UTF8");
384         assertEquals(1, conf.getInt("test"));
385     }
386     
387     /***
388      * Tests loading a non well formed XML from a string.
389      */
390     public void testLoadInvalidXML() throws Exception
391     {
392         String xml = "<?xml version=\"1.0\"?><config><test>1</rest></config>";
393         conf = new XMLConfiguration();
394         try
395         {
396             conf.load(new StringReader(xml));
397             fail("Could load invalid XML!");
398         }
399         catch(ConfigurationException cex)
400         {
401             //ok
402         }
403     }
404 
405     public void testSetProperty() throws Exception
406     {
407         conf.setProperty("element.string", "hello");
408 
409         assertEquals("'element.string'", "hello", conf.getString("element.string"));
410         assertEquals("XML value of element.string", "hello", conf.getProperty("element.string"));
411     }
412 
413     public void testAddProperty()
414     {
415         // add a property to a non initialized xml configuration
416         XMLConfiguration config = new XMLConfiguration();
417         config.addProperty("test.string", "hello");
418 
419         assertEquals("'test.string'", "hello", config.getString("test.string"));
420     }
421 
422     public void testAddObjectProperty()
423     {
424         // add a non string property
425         conf.addProperty("test.boolean", Boolean.TRUE);
426         assertTrue("'test.boolean'", conf.getBoolean("test.boolean"));
427     }
428 
429     public void testSave() throws Exception
430     {
431         // add an array of strings to the configuration
432         conf.addProperty("string", "value1");
433         for (int i = 1; i < 5; i++)
434         {
435             conf.addProperty("test.array", "value" + i);
436         }
437 
438         // add an array of strings in an attribute
439         for (int i = 1; i < 5; i++)
440         {
441            conf.addProperty("test.attribute[@array]", "value" + i);
442         }
443         
444         // add comma delimited lists with escaped delimiters
445         conf.addProperty("split.list5", "a//,b//,c");
446         conf.setProperty("element3", "value//,value1//,value2");
447         conf.setProperty("element3[@name]", "foo//,bar");
448 
449         // save the configuration
450         conf.save(testSaveConf.getAbsolutePath());
451 
452         // read the configuration and compare the properties
453         XMLConfiguration checkConfig = new XMLConfiguration();
454         checkConfig.setFileName(testSaveConf.getAbsolutePath());
455         checkSavedConfig(checkConfig);
456     }
457     
458     /***
459      * Tests saving to a URL.
460      */
461     public void testSaveToURL() throws Exception
462     {
463         conf.save(testSaveConf.toURL());
464         XMLConfiguration checkConfig = new XMLConfiguration();
465         checkConfig.setFile(testSaveConf);
466         checkSavedConfig(checkConfig);
467     }
468     
469     /***
470      * Tests saving to a stream.
471      */
472     public void testSaveToStream() throws Exception
473     {
474         assertNull(conf.getEncoding());
475         conf.setEncoding("UTF8");
476         FileOutputStream out = null;
477         try
478         {
479             out = new FileOutputStream(testSaveConf);
480             conf.save(out);
481         }
482         finally
483         {
484             if(out != null)
485             {
486                 out.close();
487             }
488         }
489         
490         XMLConfiguration checkConfig = new XMLConfiguration();
491         checkConfig.setFile(testSaveConf);
492         checkSavedConfig(checkConfig);
493         
494         try
495         {
496             out = new FileOutputStream(testSaveConf);
497             conf.save(out, "UTF8");
498         }
499         finally
500         {
501             if(out != null)
502             {
503                 out.close();
504             }
505         }
506         
507         checkConfig.clear();
508         checkSavedConfig(checkConfig);
509     }
510 
511     public void testAutoSave() throws Exception
512     {
513         conf.setFile(new File("target/testsave.xml"));
514         assertFalse(conf.isAutoSave());
515         conf.setAutoSave(true);
516         assertTrue(conf.isAutoSave());
517         conf.setProperty("autosave", "ok");
518 
519         // reload the configuration
520         XMLConfiguration conf2 = new XMLConfiguration(conf.getFile());
521         assertEquals("'autosave' property", "ok", conf2.getString("autosave"));
522         
523         conf.clearTree("clear");
524         conf2 = new XMLConfiguration(conf.getFile());
525         Configuration sub = conf2.subset("clear");
526         assertTrue(sub.isEmpty());
527     }
528     
529     /***
530      * Tests if a second file can be appended to a first.
531      */
532     public void testAppend() throws Exception
533     {
534         conf = new XMLConfiguration();
535         conf.setFileName(testProperties);
536         conf.load();
537         conf.load(testProperties2);
538         assertEquals("value", conf.getString("element"));
539         assertEquals("tasks", conf.getString("table.name"));
540         
541         conf.save(testSaveConf);
542         conf = new XMLConfiguration(testSaveConf);
543         assertEquals("value", conf.getString("element"));
544         assertEquals("tasks", conf.getString("table.name"));
545         assertEquals("application", conf.getString("table[@tableType]"));
546     }
547     
548     /***
549      * Tests saving attributes (related to issue 34442).
550      */
551     public void testSaveAttributes() throws Exception
552     {
553         conf.clear();
554         conf.load();
555         conf.save(testSaveConf);
556         conf = new XMLConfiguration();
557         conf.load(testSaveConf);
558         assertEquals("foo", conf.getString("element3[@name]"));
559     }
560 
561     /***
562      * Tests collaboration between XMLConfiguration and a reloading strategy.
563      */
564     public void testReloading() throws Exception
565     {
566         assertNotNull(conf.getReloadingStrategy());
567         assertTrue(conf.getReloadingStrategy() instanceof InvariantReloadingStrategy);
568         PrintWriter out = null;
569 
570         try
571         {
572             out = new PrintWriter(new FileWriter(testSaveConf));
573             out.println("<?xml version=\"1.0\"?><config><test>1</test></config>");
574             out.close();
575             out = null;
576             conf.setFile(testSaveConf);
577             FileAlwaysReloadingStrategy strategy = new FileAlwaysReloadingStrategy();
578             strategy.setRefreshDelay(100);
579             conf.setReloadingStrategy(strategy);
580             assertEquals(strategy, conf.getReloadingStrategy());
581             assertEquals("Wrong file monitored", testSaveConf.getAbsolutePath(),
582                     strategy.getMonitoredFile().getAbsolutePath());
583             conf.load();
584             assertEquals(1, conf.getInt("test"));
585 
586             out = new PrintWriter(new FileWriter(testSaveConf));
587             out.println("<?xml version=\"1.0\"?><config><test>2</test></config>");
588             out.close();
589             out = null;
590 
591             int value = conf.getInt("test");
592             assertEquals("No reloading performed", 2, value);
593         }
594         finally
595         {
596             if (out != null)
597             {
598                 out.close();
599             }
600         }
601     }
602 
603     /***
604      * Tests access to tag names with delimiter characters.
605      */
606     public void testComplexNames()
607     {
608         assertEquals("Name with dot", conf.getString("complexNames.my..elem"));
609         assertEquals("Another dot", conf.getString("complexNames.my..elem.sub..elem"));
610     }
611 
612     /***
613      * Tests setting a custom document builder.
614      */
615     public void testCustomDocBuilder() throws Exception
616     {
617         // Load an invalid XML file with the default (non validating)
618         // doc builder. This should work...
619         conf = new XMLConfiguration();
620         conf.load(new File("conf/testValidateInvalid.xml"));
621         assertEquals("customers", conf.getString("table.name"));
622         assertFalse(conf.containsKey("table.fields.field(1).type"));
623 
624         // Now create a validating doc builder and set it.
625         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
626         factory.setValidating(true);
627         DocumentBuilder builder = factory.newDocumentBuilder();
628         builder.setErrorHandler(new DefaultHandler() {
629             public void error(SAXParseException ex) throws SAXException
630             {
631                 throw ex;
632             }
633         });
634         conf = new XMLConfiguration();
635         conf.setDocumentBuilder(builder);
636         try
637         {
638             conf.load(new File("conf/testValidateInvalid.xml"));
639             fail("Could load invalid file with validating set to true!");
640         }
641         catch(ConfigurationException ex)
642         {
643             //ok
644         }
645 
646         // Try to load a valid document with a validating builder
647         conf = new XMLConfiguration();
648         conf.setDocumentBuilder(builder);
649         conf.load(new File("conf/testValidateValid.xml"));
650         assertTrue(conf.containsKey("table.fields.field(1).type"));
651     }
652 
653     /***
654      * Tests the clone() method.
655      */
656     public void testClone()
657     {
658         Configuration c = (Configuration) conf.clone();
659         assertTrue(c instanceof XMLConfiguration);
660         XMLConfiguration copy = (XMLConfiguration) c;
661         assertNotNull(conf.getDocument());
662         assertNull(copy.getDocument());
663         assertNotNull(conf.getFileName());
664         assertNull(copy.getFileName());
665 
666         copy.setProperty("element3", "clonedValue");
667         assertEquals("value", conf.getString("element3"));
668         conf.setProperty("element3[@name]", "originalFoo");
669         assertEquals("foo", copy.getString("element3[@name]"));
670     }
671 
672     /***
673      * Tests saving a configuration after cloning to ensure that the clone and
674      * the original are completely detachted.
675      */
676     public void testCloneWithSave() throws ConfigurationException
677     {
678         XMLConfiguration c = (XMLConfiguration) conf.clone();
679         c.addProperty("test.newProperty", Boolean.TRUE);
680         conf.addProperty("test.orgProperty", Boolean.TRUE);
681         c.save(testSaveConf);
682         XMLConfiguration c2 = new XMLConfiguration(testSaveConf);
683         assertTrue("New property after clone() was not saved", c2
684                 .getBoolean("test.newProperty"));
685         assertFalse("Property of original config was saved", c2
686                 .containsKey("test.orgProperty"));
687     }
688 
689     /***
690      * Tests the subset() method. There was a bug that calling subset() had
691      * undesired side effects.
692      */
693     public void testSubset() throws ConfigurationException
694     {
695         conf = new XMLConfiguration();
696         conf.load(new File("conf/testHierarchicalXMLConfiguration.xml"));
697         conf.subset("tables.table(0)");
698         conf.save(testSaveConf);
699 
700         conf = new XMLConfiguration(testSaveConf);
701         assertEquals("users", conf.getString("tables.table(0).name"));
702     }
703 
704     /***
705      * Tests string properties with list delimiters and escaped delimiters.
706      */
707     public void testSplitLists()
708     {
709         assertEquals("a", conf.getString("split.list3[@values]"));
710         assertEquals(2, conf.getMaxIndex("split.list3[@values]"));
711         assertEquals("a,b,c", conf.getString("split.list4[@values]"));
712         assertEquals("a", conf.getString("split.list1"));
713         assertEquals(2, conf.getMaxIndex("split.list1"));
714         assertEquals("a,b,c", conf.getString("split.list2"));
715     }
716 
717     /***
718      * Tests string properties with list delimiters when delimiter parsing
719      * is disabled
720      */
721     public void testDelimiterParsingDisabled() throws ConfigurationException {
722         XMLConfiguration conf2 = new XMLConfiguration();
723         conf2.setDelimiterParsingDisabled(true);
724         conf2.setFile(new File(testProperties));
725         conf2.load();
726 
727         assertEquals("a,b,c", conf2.getString("split.list3[@values]"));
728         assertEquals(0, conf2.getMaxIndex("split.list3[@values]"));
729         assertEquals("a//,b//,c", conf2.getString("split.list4[@values]"));
730         assertEquals("a,b,c", conf2.getString("split.list1"));
731         assertEquals(0, conf2.getMaxIndex("split.list1"));
732         assertEquals("a//,b//,c", conf2.getString("split.list2"));
733     }
734 
735     /***
736      * Tests whether a DTD can be accessed.
737      */
738     public void testDtd() throws ConfigurationException
739     {
740         conf = new XMLConfiguration("testDtd.xml");
741         assertEquals("value1", conf.getString("entry(0)"));
742         assertEquals("test2", conf.getString("entry(1)[@key]"));
743     }
744 
745     /***
746      * Tests DTD validation using the setValidating() method.
747      */
748     public void testValidating() throws ConfigurationException
749     {
750         File nonValidFile = new File("conf/testValidateInvalid.xml");
751         conf = new XMLConfiguration();
752         assertFalse(conf.isValidating());
753         
754         // Load a non valid XML document. Should work for isValidating() == false
755         conf.load(nonValidFile);
756         assertEquals("customers", conf.getString("table.name"));
757         assertFalse(conf.containsKey("table.fields.field(1).type"));
758         
759         // Now set the validating flag to true
760         conf.setValidating(true);
761         try
762         {
763             conf.load(nonValidFile);
764             fail("Validation was not performed!");
765         }
766         catch(ConfigurationException cex)
767         {
768             //ok
769         }
770     }
771     
772     /***
773      * Tests handling of empty elements.
774      */
775     public void testEmptyElements() throws ConfigurationException
776     {
777         assertTrue(conf.containsKey("empty"));
778         assertEquals("", conf.getString("empty"));
779         conf.addProperty("empty2", "");
780         conf.setProperty("empty", "no more empty");
781         conf.save(testSaveConf);
782         
783         conf = new XMLConfiguration(testSaveConf);
784         assertEquals("no more empty", conf.getString("empty"));
785         assertEquals("", conf.getProperty("empty2"));
786     }
787     
788     /***
789      * Tests whether the encoding is correctly detected by the XML parser. This
790      * is done by loading an XML file with the encoding "UTF-16". If this
791      * encoding is not detected correctly, an exception will be thrown that
792      * "Content is not allowed in prolog". This test case is related to issue
793      * 34204.
794      */
795     public void testLoadWithEncoding() throws ConfigurationException
796     {
797         File file = new File("conf/testEncoding.xml");
798         conf = new XMLConfiguration();
799         conf.load(file);
800         assertEquals("test3_yoge", conf.getString("yoge"));
801     }
802     
803     /***
804      * Tests whether the encoding is written to the generated XML file.
805      */
806     public void testSaveWithEncoding() throws ConfigurationException
807     {
808         conf = new XMLConfiguration();
809         conf.setProperty("test", "a value");
810         conf.setEncoding(ENCODING);
811 
812         StringWriter out = new StringWriter();
813         conf.save(out);
814         assertTrue("Encoding was not written to file", out.toString().indexOf(
815                 "encoding=\"" + ENCODING + "\"") >= 0);
816     }
817     
818     /***
819      * Tests whether a default encoding is used if no specific encoding is set.
820      * According to the XSLT specification (http://www.w3.org/TR/xslt#output)
821      * this should be either UTF-8 or UTF-16.
822      */
823     public void testSaveWithNullEncoding() throws ConfigurationException
824     {
825         conf = new XMLConfiguration();
826         conf.setProperty("testNoEncoding", "yes");
827         conf.setEncoding(null);
828 
829         StringWriter out = new StringWriter();
830         conf.save(out);
831         assertTrue("Encoding was written to file", out.toString().indexOf(
832                 "encoding=\"UTF-") >= 0);
833     }
834 
835     /***
836      * Tests whether the DOCTYPE survives a save operation.
837      */
838     public void testSaveWithDoctype() throws ConfigurationException
839     {
840         String content = "<?xml  version=\"1.0\"?>"
841                 + DOCTYPE
842                 + "properties"
843                 + DOCTYPE_DECL
844                 + "<properties version=\"1.0\"><entry key=\"test\">value</entry></properties>";
845         StringReader in = new StringReader(content);
846         conf = new XMLConfiguration();
847         conf.setFileName("conf/testDtd.xml");
848         conf.load();
849         conf.clear();
850         conf.load(in);
851 
852         assertEquals("Wrong public ID", PUBLIC_ID, conf.getPublicID());
853         assertEquals("Wrong system ID", SYSTEM_ID, conf.getSystemID());
854         StringWriter out = new StringWriter();
855         conf.save(out);
856         System.out.println(out.toString());
857         assertTrue("Did not find DOCTYPE", out.toString().indexOf(DOCTYPE) >= 0);
858     }
859 
860     /***
861      * Tests setting public and system IDs for the D'OCTYPE and then saving the
862      * configuration. This should generate a DOCTYPE declaration.
863      */
864     public void testSaveWithDoctypeIDs() throws ConfigurationException
865     {
866         assertNull("A public ID was found", conf.getPublicID());
867         assertNull("A system ID was found", conf.getSystemID());
868         conf.setPublicID(PUBLIC_ID);
869         conf.setSystemID(SYSTEM_ID);
870         StringWriter out = new StringWriter();
871         conf.save(out);
872         assertTrue("Did not find DOCTYPE", out.toString().indexOf(
873                 DOCTYPE + "testconfig" + DOCTYPE_DECL) >= 0);
874     }
875 
876     /***
877      * Tests saving a configuration when an invalid transformer factory is
878      * specified. In this case the error thrown by the TransformerFactory class
879      * should be caught and re-thrown as a ConfigurationException.
880      */
881     public void testSaveWithInvalidTransformerFactory()
882     {
883         System.setProperty(PROP_FACTORY, "an.invalid.Class");
884         try
885         {
886             conf.save(testSaveConf);
887             fail("Could save with invalid TransformerFactory!");
888         }
889         catch (ConfigurationException cex)
890         {
891             // ok
892         }
893         finally
894         {
895             System.getProperties().remove(PROP_FACTORY);
896         }
897     }
898 
899     /***
900      * Tests if reloads are recognized by subset().
901      */
902     public void testSubsetWithReload() throws ConfigurationException
903     {
904         XMLConfiguration c = setUpReloadTest();
905         Configuration sub = c.subset("test");
906         assertEquals("New value not read", "newValue", sub.getString("entity"));
907     }
908 
909     /***
910      * Tests if reloads are recognized by configurationAt().
911      */
912     public void testConfigurationAtWithReload() throws ConfigurationException
913     {
914         XMLConfiguration c = setUpReloadTest();
915         HierarchicalConfiguration sub = c.configurationAt("test(0)");
916         assertEquals("New value not read", "newValue", sub.getString("entity"));
917     }
918 
919     /***
920      * Tests if reloads are recognized by configurationsAt().
921      */
922     public void testConfigurationsAtWithReload() throws ConfigurationException
923     {
924         XMLConfiguration c = setUpReloadTest();
925         List configs = c.configurationsAt("test");
926         assertEquals("New value not read", "newValue",
927                 ((HierarchicalConfiguration) configs.get(0))
928                         .getString("entity"));
929     }
930 
931     /***
932      * Tests accessing properties when the XPATH expression engine is set.
933      */
934     public void testXPathExpressionEngine()
935     {
936         conf.setExpressionEngine(new XPathExpressionEngine());
937         assertEquals("Wrong attribute value", "foo\"bar", conf
938                 .getString("test[1]/entity/@name"));
939         conf.clear();
940         assertNull(conf.getString("test[1]/entity/@name"));
941     }
942     
943     /***
944      * Tests the copy constructor.
945      */
946     public void testInitCopy() throws ConfigurationException
947     {
948     	XMLConfiguration copy = new XMLConfiguration(conf);
949         assertEquals("value", copy.getProperty("element"));
950         assertNull("Document was copied, too", copy.getDocument());
951         ConfigurationNode root = copy.getRootNode();
952         for(Iterator it = root.getChildren().iterator(); it.hasNext();)
953         {
954         	ConfigurationNode node = (ConfigurationNode) it.next();
955         	assertNull("Reference was not cleared", node.getReference());
956         }
957 
958         removeTestFile();
959         copy.setFile(testSaveConf);
960         copy.save();
961         copy.clear();
962         checkSavedConfig(copy);
963     }
964 
965     /***
966      * Prepares a configuration object for testing a reload operation.
967      *
968      * @return the initialized configuration
969      * @throws ConfigurationException if an error occurs
970      */
971     private XMLConfiguration setUpReloadTest() throws ConfigurationException
972     {
973         removeTestFile();
974         conf.save(testSaveConf);
975         XMLConfiguration c = new XMLConfiguration(testSaveConf);
976         c.setReloadingStrategy(new FileChangedReloadingStrategy()
977         {
978             // Report always a change
979             protected boolean hasChanged()
980             {
981                 return true;
982             }
983         });
984         conf.setProperty("test(0).entity", "newValue");
985         conf.save(testSaveConf);
986         return c;
987     }
988 
989     /***
990      * Removes the test output file if it exists.
991      */
992     private void removeTestFile()
993     {
994         if (testSaveConf.exists())
995         {
996             assertTrue(testSaveConf.delete());
997         }
998     }
999 
1000     /***
1001      * Helper method for checking if a save operation was successful. Loads a
1002      * saved configuration and then tests against a reference configuration.
1003      * @param checkConfig the configuration to check
1004      * @throws ConfigurationException if an error occurs
1005      */
1006     private void checkSavedConfig(FileConfiguration checkConfig) throws ConfigurationException
1007     {
1008         checkConfig.load();
1009 
1010         for (Iterator i = conf.getKeys(); i.hasNext();)
1011         {
1012             String key = (String) i.next();
1013             assertTrue("The saved configuration doesn't contain the key '" + key + "'", checkConfig.containsKey(key));
1014             assertEquals("Value of the '" + key + "' property", conf.getProperty(key), checkConfig.getProperty(key));
1015         }
1016     }
1017 }