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