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