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