You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/11/03 22:43:58 UTC

[commons-configuration] branch master updated: Sort members.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-configuration.git


The following commit(s) were added to refs/heads/master by this push:
     new a76a3a6  Sort members.
a76a3a6 is described below

commit a76a3a65c4d2e3530c69c1fecc6e4fe6f2f221ed
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Nov 3 17:43:54 2019 -0500

    Sort members.
---
 .../configuration2/TestXMLConfiguration.java       | 2106 ++++++++++----------
 1 file changed, 1053 insertions(+), 1053 deletions(-)

diff --git a/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java
index 16681e4..dd8a3a7 100644
--- a/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java
@@ -66,6 +66,28 @@ import org.xml.sax.helpers.DefaultHandler;
  */
 public class TestXMLConfiguration
 {
+    /**
+     * A thread used for testing concurrent access to a builder.
+     */
+    private class ReloadThread extends Thread
+    {
+        private final FileBasedConfigurationBuilder<?> builder;
+
+        ReloadThread(final FileBasedConfigurationBuilder<?> confBulder)
+        {
+            builder = confBulder;
+        }
+
+        @Override
+        public void run()
+        {
+            for (int i = 0; i < LOOP_COUNT; i++)
+            {
+                builder.resetResult();
+            }
+        }
+    }
+
     /** XML Catalog */
     private static final String CATALOG_FILES = ConfigurationAssert
             .getTestFile("catalog.xml").getAbsolutePath();
@@ -88,222 +110,396 @@ public class TestXMLConfiguration
     /** Constant for the transformer factory property.*/
     static final String PROP_FACTORY = "javax.xml.transform.TransformerFactory";
 
+    /** Constant for the number of test threads. */
+    private static final int THREAD_COUNT = 5;
+    /** Constant for the number of loops in tests with multiple threads. */
+    private static final int LOOP_COUNT = 100;
+    /**
+     * Creates a new XMLConfiguration and loads the specified file.
+     *
+     * @param fileName the name of the file to be loaded
+     * @return the newly created configuration instance
+     * @throws ConfigurationException if an error occurs
+     */
+    private static XMLConfiguration createFromFile(final String fileName)
+            throws ConfigurationException
+    {
+        final XMLConfiguration config = new XMLConfiguration();
+        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
+        load(config, fileName);
+        return config;
+    }
+    /**
+     * Helper method for loading the specified configuration file.
+     *
+     * @param config the configuration
+     * @param fileName the name of the file to be loaded
+     * @throws ConfigurationException if an error occurs
+     */
+    private static void load(final XMLConfiguration config, final String fileName)
+            throws ConfigurationException
+    {
+        final FileHandler handler = new FileHandler(config);
+        handler.setFileName(fileName);
+        handler.load();
+    }
     /** Helper object for creating temporary files. */
     @Rule
     public TemporaryFolder folder = new TemporaryFolder();
 
     /** The File that we test with */
     private final String testProperties = ConfigurationAssert.getTestFile("test.xml").getAbsolutePath();
+
     private final String testProperties2 = ConfigurationAssert.getTestFile("testDigesterConfigurationInclude1.xml").getAbsolutePath();
+
     private File testSaveConf;
-    private File testSaveFile;
-    private final String testFile2 = ConfigurationAssert.getTestFile("sample.xml").getAbsolutePath();
 
-    /** Constant for the number of test threads. */
-    private static final int THREAD_COUNT = 5;
+    private File testSaveFile;
 
-    /** Constant for the number of loops in tests with multiple threads. */
-    private static final int LOOP_COUNT = 100;
+    private final String testFile2 = ConfigurationAssert.getTestFile("sample.xml").getAbsolutePath();
 
     private XMLConfiguration conf;
 
-    @Before
-    public void setUp() throws Exception
+    /**
+     * Helper method for testing whether a configuration was correctly saved to
+     * the default output file.
+     *
+     * @return the newly loaded configuration
+     * @throws ConfigurationException if an error occurs
+     */
+    private XMLConfiguration checkSavedConfig() throws ConfigurationException
     {
-        testSaveConf = folder.newFile("testsave.xml");
-        testSaveFile = folder.newFile("testsample2.xml");
-        conf = createFromFile(testProperties);
-        removeTestFile();
+        return checkSavedConfig(testSaveConf);
     }
 
     /**
-     * Helper method for loading the specified configuration file.
+     * Tests whether the saved configuration file matches the original data.
      *
-     * @param config the configuration
-     * @param fileName the name of the file to be loaded
+     * @param saveFile the saved configuration file
+     * @return the newly loaded configuration
      * @throws ConfigurationException if an error occurs
      */
-    private static void load(final XMLConfiguration config, final String fileName)
+    private XMLConfiguration checkSavedConfig(final File saveFile)
             throws ConfigurationException
     {
-        final FileHandler handler = new FileHandler(config);
-        handler.setFileName(fileName);
-        handler.load();
+        final XMLConfiguration config = createFromFile(saveFile.getAbsolutePath());
+        ConfigurationAssert.assertConfigurationEquals(conf, config);
+        return config;
     }
 
     /**
-     * Creates a new XMLConfiguration and loads the specified file.
+     * Helper method for testing saving and loading a configuration when
+     * delimiter parsing is disabled.
      *
-     * @param fileName the name of the file to be loaded
-     * @return the newly created configuration instance
+     * @param key the key to be checked
      * @throws ConfigurationException if an error occurs
      */
-    private static XMLConfiguration createFromFile(final String fileName)
+    private void checkSaveDelimiterParsingDisabled(final String key)
             throws ConfigurationException
     {
-        final XMLConfiguration config = new XMLConfiguration();
-        config.setListDelimiterHandler(new DefaultListDelimiterHandler(','));
-        load(config, fileName);
-        return config;
+        conf.clear();
+        conf.setListDelimiterHandler(new DisabledListDelimiterHandler());
+        load(conf, testProperties);
+        conf.setProperty(key, "C:\\Temp\\,C:\\Data\\");
+        conf.addProperty(key, "a,b,c");
+        saveTestConfig();
+        final XMLConfiguration checkConf = new XMLConfiguration();
+        checkConf.setListDelimiterHandler(conf.getListDelimiterHandler());
+        load(checkConf, testSaveConf.getAbsolutePath());
+        ConfigurationAssert.assertConfigurationEquals(conf, checkConf);
     }
 
-    @Test
-    public void testGetProperty()
+    /**
+     * Creates a validating document builder.
+     * @return the document builder
+     * @throws ParserConfigurationException if an error occurs
+     */
+    private DocumentBuilder createValidatingDocBuilder()
+            throws ParserConfigurationException
     {
-        assertEquals("value", conf.getProperty("element"));
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setValidating(true);
+        final DocumentBuilder builder = factory.newDocumentBuilder();
+        builder.setErrorHandler(new DefaultHandler() {
+            @Override
+            public void error(final SAXParseException ex) throws SAXException
+            {
+                throw ex;
+            }
+        });
+        return builder;
     }
 
-    @Test
-    public void testGetCommentedProperty()
+    /**
+     * Removes the test output file if it exists.
+     */
+    private void removeTestFile()
     {
-        assertEquals("", conf.getProperty("test.comment"));
+        if (testSaveConf.exists())
+        {
+            assertTrue(testSaveConf.delete());
+        }
     }
 
-    @Test
-    public void testGetPropertyWithXMLEntity()
+    /**
+     * Helper method for saving the test configuration to the default output
+     * file.
+     *
+     * @throws ConfigurationException if an error occurs
+     */
+    private void saveTestConfig() throws ConfigurationException
     {
-        assertEquals("1<2", conf.getProperty("test.entity"));
+        final FileHandler handler = new FileHandler(conf);
+        handler.save(testSaveConf);
     }
 
-    @Test
-    public void testClearPropertyNotExisting()
+    @Before
+    public void setUp() throws Exception
     {
-        final String key = "clearly";
-        conf.clearProperty(key);
-        assertNull(key, conf.getProperty(key));
-        assertNull(key, conf.getProperty(key));
+        testSaveConf = folder.newFile("testsave.xml");
+        testSaveFile = folder.newFile("testsample2.xml");
+        conf = createFromFile(testProperties);
+        removeTestFile();
     }
 
     @Test
-    public void testClearPropertySingleElement()
+    public void testAddList()
     {
-        final String key = "clear.element";
-        conf.clearProperty(key);
-        assertNull(key, conf.getProperty(key));
-        assertNull(key, conf.getProperty(key));
+        conf.addProperty("test.array", "value1");
+        conf.addProperty("test.array", "value2");
+
+        final List<Object> list = conf.getList("test.array");
+        assertNotNull("null list", list);
+        assertTrue("'value1' element missing", list.contains("value1"));
+        assertTrue("'value2' element missing", list.contains("value2"));
+        assertEquals("list size", 2, list.size());
     }
 
+    /**
+     * Tests saving a configuration after a node was added. Test for
+     * CONFIGURATION-294.
+     */
     @Test
-    public void testClearPropertySingleElementWithAttribute()
+    public void testAddNodesAndSave() throws ConfigurationException
     {
-        String key = "clear.element2";
-        conf.clearProperty(key);
-        assertNull(key, conf.getProperty(key));
-        assertNull(key, conf.getProperty(key));
-        key = "clear.element2[@id]";
-        assertNotNull(key, conf.getProperty(key));
-        assertNotNull(key, conf.getProperty(key));
+        final ImmutableNode.Builder bldrNode = new ImmutableNode.Builder(1);
+        bldrNode.addChild(NodeStructureHelper.createNode("child", null));
+        bldrNode.addAttribute("attr", "");
+        final ImmutableNode node2 = NodeStructureHelper.createNode("test2", null);
+        conf.addNodes("add.nodes",
+                Arrays.asList(bldrNode.name("test").create(), node2));
+        saveTestConfig();
+        conf.setProperty("add.nodes.test", "true");
+        conf.setProperty("add.nodes.test.child", "yes");
+        conf.setProperty("add.nodes.test[@attr]", "existing");
+        conf.setProperty("add.nodes.test2", "anotherValue");
+        saveTestConfig();
+        final XMLConfiguration c2 = new XMLConfiguration();
+        load(c2, testSaveConf.getAbsolutePath());
+        assertEquals("Value was not saved", "true", c2
+                .getString("add.nodes.test"));
+        assertEquals("Child value was not saved", "yes", c2
+                .getString("add.nodes.test.child"));
+        assertEquals("Attr value was not saved", "existing", c2
+                .getString("add.nodes.test[@attr]"));
+        assertEquals("Node2 not saved", "anotherValue", c2
+                .getString("add.nodes.test2"));
     }
 
+    /**
+     * Tests adding nodes from another configuration.
+     */
     @Test
-    public void testClearPropertyNonText()
+    public void testAddNodesCopy() throws ConfigurationException
     {
-        final String key = "clear.comment";
-        conf.clearProperty(key);
-        assertNull(key, conf.getProperty(key));
-        assertNull(key, conf.getProperty(key));
+        final XMLConfiguration c2 = new XMLConfiguration();
+        load(c2, testProperties2);
+        conf.addNodes("copiedProperties", c2.getModel().getNodeHandler()
+                .getRootNode().getChildren());
+        saveTestConfig();
+        checkSavedConfig();
     }
 
+    /**
+     * Tests whether it is possible to add nodes to a XMLConfiguration through a
+     * SubnodeConfiguration and whether these nodes have the correct type. This
+     * test is related to CONFIGURATION-472.
+     */
     @Test
-    public void testClearPropertyCData()
+    public void testAddNodesToSubnodeConfiguration() throws Exception
     {
-        final String key = "clear.cdata";
-        conf.clearProperty(key);
-        assertNull(key, conf.getProperty(key));
-        assertNull(key, conf.getProperty(key));
+        final HierarchicalConfiguration<ImmutableNode> sub =
+                conf.configurationAt("element2", true);
+        sub.addProperty("newKey", "newvalue");
+        assertEquals("Property not added", "newvalue",
+                conf.getString("element2.newKey"));
     }
 
     @Test
-    public void testClearPropertyMultipleSiblings()
+    public void testAddObjectAttribute()
     {
-        String key = "clear.list.item";
-        conf.clearProperty(key);
-        assertNull(key, conf.getProperty(key));
-        assertNull(key, conf.getProperty(key));
-        key = "clear.list.item[@id]";
-        assertNotNull(key, conf.getProperty(key));
-        assertNotNull(key, conf.getProperty(key));
+        conf.addProperty("test.boolean[@value]", Boolean.TRUE);
+        assertTrue("test.boolean[@value]", conf.getBoolean("test.boolean[@value]"));
     }
 
     @Test
-    public void testClearPropertyMultipleDisjoined() throws Exception
+    public void testAddObjectProperty()
     {
-        final String key = "list.item";
-        conf.clearProperty(key);
-        assertNull(key, conf.getProperty(key));
-        assertNull(key, conf.getProperty(key));
+        // add a non string property
+        conf.addProperty("test.boolean", Boolean.TRUE);
+        assertTrue("'test.boolean'", conf.getBoolean("test.boolean"));
     }
 
     @Test
-    public void testgetProperty() {
-        // test non-leaf element
-        Object property = conf.getProperty("clear");
-        assertNull(property);
-
-        // test non-existent element
-        property = conf.getProperty("e");
-        assertNull(property);
+    public void testAddProperty()
+    {
+        // add a property to a non initialized xml configuration
+        final XMLConfiguration config = new XMLConfiguration();
+        config.addProperty("test.string", "hello");
 
-        // test non-existent element
-        property = conf.getProperty("element3[@n]");
-        assertNull(property);
+        assertEquals("'test.string'", "hello", config.getString("test.string"));
+    }
 
-        // test single element
-        property = conf.getProperty("element");
-        assertNotNull(property);
-        assertTrue(property instanceof String);
-        assertEquals("value", property);
+    /**
+     * Tests whether list properties are added correctly if delimiter parsing is
+     * disabled. This test is related to CONFIGURATION-495.
+     */
+    @Test
+    public void testAddPropertyListWithDelimiterParsingDisabled()
+            throws ConfigurationException
+    {
+        conf.clear();
+        final String prop = "delimiterListProp";
+        conf.setListDelimiterHandler(DisabledListDelimiterHandler.INSTANCE);
+        final List<String> list = Arrays.asList("val", "val2", "val3");
+        conf.addProperty(prop, list);
+        saveTestConfig();
+        final XMLConfiguration conf2 = new XMLConfiguration();
+        load(conf2, testSaveConf.getAbsolutePath());
+        assertEquals("Wrong list property", list, conf2.getProperty(prop));
+    }
 
-        // test single attribute
-        property = conf.getProperty("element3[@name]");
-        assertNotNull(property);
-        assertTrue(property instanceof String);
-        assertEquals("foo", property);
+    /**
+     * Tests if a second file can be appended to a first.
+     */
+    @Test
+    public void testAppend() throws Exception
+    {
+        load(conf, testProperties2);
+        assertEquals("value", conf.getString("element"));
+        assertEquals("tasks", conf.getString("table.name"));
 
-        // test non-text/cdata element
-        property = conf.getProperty("test.comment");
-        assertEquals("", property);
+        saveTestConfig();
+        conf = createFromFile(testSaveConf.getAbsolutePath());
+        assertEquals("value", conf.getString("element"));
+        assertEquals("tasks", conf.getString("table.name"));
+        assertEquals("application", conf.getString("table[@tableType]"));
+    }
 
-        // test cdata element
-        property = conf.getProperty("test.cdata");
-        assertNotNull(property);
-        assertTrue(property instanceof String);
-        assertEquals("<cdata value>", property);
+    /**
+     * Tries to create an attribute with multiple values. Only the first value
+     * is taken into account.
+     */
+    @Test
+    public void testAttributeKeyWithMultipleValues()
+            throws ConfigurationException
+    {
+        conf.addProperty("errorTest[@multiAttr]", Arrays.asList("v1", "v2"));
+        saveTestConfig();
+        final XMLConfiguration checkConfig = new XMLConfiguration();
+        load(checkConfig, testSaveConf.getAbsolutePath());
+        assertEquals("Wrong attribute value", "v1",
+                checkConfig.getString("errorTest[@multiAttr]"));
+    }
 
-        // test multiple sibling elements
-        property = conf.getProperty("list.sublist.item");
-        assertNotNull(property);
-        assertTrue(property instanceof List);
-        List<?> list = (List<?>) property;
-        assertEquals(2, list.size());
-        assertEquals("five", list.get(0));
-        assertEquals("six", list.get(1));
+    /**
+     * Tests whether the addNodes() method triggers an auto save.
+     */
+    @Test
+    public void testAutoSaveAddNodes() throws ConfigurationException
+    {
+        final FileBasedConfigurationBuilder<XMLConfiguration> builder =
+                new FileBasedConfigurationBuilder<>(
+                        XMLConfiguration.class);
+        builder.configure(new FileBasedBuilderParametersImpl()
+                .setFileName(testProperties));
+        conf = builder.getConfiguration();
+        builder.getFileHandler().setFile(testSaveConf);
+        builder.setAutoSave(true);
+        final ImmutableNode node = NodeStructureHelper.createNode(
+                "addNodesTest", Boolean.TRUE);
+        final Collection<ImmutableNode> nodes = new ArrayList<>(1);
+        nodes.add(node);
+        conf.addNodes("test.autosave", nodes);
+        final XMLConfiguration c2 = new XMLConfiguration();
+        load(c2, testSaveConf.getAbsolutePath());
+        assertTrue("Added nodes are not saved", c2
+                .getBoolean("test.autosave.addNodesTest"));
+    }
 
-        // test multiple, disjoined elements
-        property = conf.getProperty("list.item");
-        assertNotNull(property);
-        assertTrue(property instanceof List);
-        list = (List<?>) property;
-        assertEquals(4, list.size());
-        assertEquals("one", list.get(0));
-        assertEquals("two", list.get(1));
-        assertEquals("three", list.get(2));
-        assertEquals("four", list.get(3));
+    /**
+     * Tests whether the auto save mechanism is triggered by changes at a
+     * subnode configuration.
+     */
+    @Test
+    public void testAutoSaveWithSubnodeConfig() throws ConfigurationException
+    {
+        final FileBasedConfigurationBuilder<XMLConfiguration> builder =
+                new FileBasedConfigurationBuilder<>(
+                        XMLConfiguration.class);
+        builder.configure(new FileBasedBuilderParametersImpl()
+                .setFileName(testProperties));
+        conf = builder.getConfiguration();
+        builder.getFileHandler().setFile(testSaveConf);
+        builder.setAutoSave(true);
+        final String newValue = "I am autosaved";
+        final Configuration sub = conf.configurationAt("element2.subelement", true);
+        sub.setProperty("subsubelement", newValue);
+        assertEquals("Change not visible to parent", newValue,
+                conf.getString("element2.subelement.subsubelement"));
+        final XMLConfiguration conf2 = new XMLConfiguration();
+        load(conf2, testSaveConf.getAbsolutePath());
+        assertEquals("Change was not saved", newValue,
+                conf2.getString("element2.subelement.subsubelement"));
+    }
 
-        // test multiple, disjoined attributes
-        property = conf.getProperty("list.item[@name]");
-        assertNotNull(property);
-        assertTrue(property instanceof List);
-        list = (List<?>) property;
-        assertEquals(2, list.size());
-        assertEquals("one", list.get(0));
-        assertEquals("three", list.get(1));
+    /**
+     * Tests whether a subnode configuration created from another subnode
+     * configuration of a XMLConfiguration can trigger the auto save mechanism.
+     */
+    @Test
+    public void testAutoSaveWithSubSubnodeConfig() throws ConfigurationException
+    {
+        final FileBasedConfigurationBuilder<XMLConfiguration> builder =
+                new FileBasedConfigurationBuilder<>(
+                        XMLConfiguration.class);
+        builder.configure(new FileBasedBuilderParametersImpl()
+                .setFileName(testProperties));
+        conf = builder.getConfiguration();
+        builder.getFileHandler().setFile(testSaveConf);
+        builder.setAutoSave(true);
+        final String newValue = "I am autosaved";
+        final HierarchicalConfiguration<?> sub1 = conf.configurationAt("element2", true);
+        final HierarchicalConfiguration<?> sub2 = sub1.configurationAt("subelement", true);
+        sub2.setProperty("subsubelement", newValue);
+        assertEquals("Change not visible to parent", newValue, conf
+                .getString("element2.subelement.subsubelement"));
+        final XMLConfiguration conf2 = new XMLConfiguration();
+        load(conf2, testSaveConf.getAbsolutePath());
+        assertEquals("Change was not saved", newValue, conf2
+                .getString("element2.subelement.subsubelement"));
     }
 
     @Test
-    public void testGetAttribute()
+    public void testClearAttributeMultipleDisjoined() throws Exception
     {
-        assertEquals("element3[@name]", "foo", conf.getProperty("element3[@name]"));
+        String key = "clear.list.item[@id]";
+        conf.clearProperty(key);
+        assertNull(key, conf.getProperty(key));
+        assertNull(key, conf.getProperty(key));
+        key = "clear.list.item";
+        assertNotNull(key, conf.getProperty(key));
+        assertNotNull(key, conf.getProperty(key));
     }
 
     @Test
@@ -328,330 +524,222 @@ public class TestXMLConfiguration
     }
 
     @Test
-    public void testClearAttributeMultipleDisjoined() throws Exception
+    public void testClearPropertyCData()
     {
-        String key = "clear.list.item[@id]";
+        final String key = "clear.cdata";
         conf.clearProperty(key);
         assertNull(key, conf.getProperty(key));
         assertNull(key, conf.getProperty(key));
-        key = "clear.list.item";
-        assertNotNull(key, conf.getProperty(key));
-        assertNotNull(key, conf.getProperty(key));
     }
 
     @Test
-    public void testSetAttribute()
+    public void testClearPropertyMultipleDisjoined() throws Exception
     {
-        // replace an existing attribute
-        conf.setProperty("element3[@name]", "bar");
-        assertEquals("element3[@name]", "bar", conf.getProperty("element3[@name]"));
-
-        // set a new attribute
-        conf.setProperty("foo[@bar]", "value");
-        assertEquals("foo[@bar]", "value", conf.getProperty("foo[@bar]"));
-
-        conf.setProperty("name1", "value1");
-        assertEquals("value1", conf.getProperty("name1"));
+        final String key = "list.item";
+        conf.clearProperty(key);
+        assertNull(key, conf.getProperty(key));
+        assertNull(key, conf.getProperty(key));
     }
 
-    /**
-     * Tests whether an attribute value can be overridden.
-     */
     @Test
-    public void testOverrideAttribute()
+    public void testClearPropertyMultipleSiblings()
     {
-        conf.addProperty("element3[@name]", "bar");
-
-        final List<Object> list = conf.getList("element3[@name]");
-        assertNotNull("null list", list);
-        assertTrue("'bar' element missing", list.contains("bar"));
-        assertEquals("list size", 1, list.size());
+        String key = "clear.list.item";
+        conf.clearProperty(key);
+        assertNull(key, conf.getProperty(key));
+        assertNull(key, conf.getProperty(key));
+        key = "clear.list.item[@id]";
+        assertNotNull(key, conf.getProperty(key));
+        assertNotNull(key, conf.getProperty(key));
     }
 
     @Test
-    public void testAddObjectAttribute()
+    public void testClearPropertyNonText()
     {
-        conf.addProperty("test.boolean[@value]", Boolean.TRUE);
-        assertTrue("test.boolean[@value]", conf.getBoolean("test.boolean[@value]"));
-    }
-
-    /**
-     * Tests setting an attribute on the root element.
-     */
-    @Test
-    public void testSetRootAttribute() throws ConfigurationException
-    {
-        conf.setProperty("[@test]", "true");
-        assertEquals("Root attribute not set", "true", conf
-                .getString("[@test]"));
-        saveTestConfig();
-        XMLConfiguration checkConf = checkSavedConfig();
-        assertTrue("Attribute not found after save", checkConf
-                .containsKey("[@test]"));
-        checkConf.setProperty("[@test]", "newValue");
-        conf = checkConf;
-        saveTestConfig();
-        checkConf = checkSavedConfig();
-        assertEquals("Attribute not modified after save", "newValue", checkConf
-                .getString("[@test]"));
+        final String key = "clear.comment";
+        conf.clearProperty(key);
+        assertNull(key, conf.getProperty(key));
+        assertNull(key, conf.getProperty(key));
     }
 
     @Test
-    public void testSetRootNamespace() throws ConfigurationException
+    public void testClearPropertyNotExisting()
     {
-        conf.addProperty(  "[@xmlns:foo]",  "http://example.com/" );
-        conf.addProperty(  "foo:bar", "foobar" );
-        assertEquals("Root attribute not set", "http://example.com/", conf
-                .getString("[@xmlns:foo]"));
-        saveTestConfig();
-        final XMLConfiguration checkConf = checkSavedConfig();
-        assertTrue("Attribute not found after save", checkConf
-                .containsKey("[@xmlns:foo]"));
-        checkConf.setProperty("[@xmlns:foo]", "http://example.net/");
+        final String key = "clearly";
+        conf.clearProperty(key);
+        assertNull(key, conf.getProperty(key));
+        assertNull(key, conf.getProperty(key));
     }
 
     @Test
-    public void testAddList()
+    public void testClearPropertySingleElement()
     {
-        conf.addProperty("test.array", "value1");
-        conf.addProperty("test.array", "value2");
-
-        final List<Object> list = conf.getList("test.array");
-        assertNotNull("null list", list);
-        assertTrue("'value1' element missing", list.contains("value1"));
-        assertTrue("'value2' element missing", list.contains("value2"));
-        assertEquals("list size", 2, list.size());
+        final String key = "clear.element";
+        conf.clearProperty(key);
+        assertNull(key, conf.getProperty(key));
+        assertNull(key, conf.getProperty(key));
     }
 
     @Test
-    public void testGetComplexProperty()
+    public void testClearPropertySingleElementWithAttribute()
     {
-        assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
+        String key = "clear.element2";
+        conf.clearProperty(key);
+        assertNull(key, conf.getProperty(key));
+        assertNull(key, conf.getProperty(key));
+        key = "clear.element2[@id]";
+        assertNotNull(key, conf.getProperty(key));
+        assertNotNull(key, conf.getProperty(key));
     }
 
     /**
-     * Tests constructing an XMLConfiguration from a non existing file and later
-     * saving to this file.
+     * Tests removing the text of the root element.
      */
     @Test
-    public void testLoadAndSaveFromFile() throws Exception
+    public void testClearTextRootElement() throws ConfigurationException
     {
-        // If the file does not exist, an empty config is created
-        assertFalse("File exists", testSaveConf.exists());
-        final FileBasedConfigurationBuilder<XMLConfiguration> builder =
-                new FileBasedConfigurationBuilder<>(
-                        XMLConfiguration.class, null, true);
-        builder.configure(new FileBasedBuilderParametersImpl()
-                .setFile(testSaveConf));
-        conf = builder.getConfiguration();
-        assertTrue(conf.isEmpty());
-        conf.addProperty("test", "yes");
-        builder.save();
+        final String xml = "<e a=\"v\">text</e>";
+        conf.clear();
+        final StringReader in = new StringReader(xml);
+        final FileHandler handler = new FileHandler(conf);
+        handler.load(in);
+        assertEquals("Wrong text of root", "text", conf.getString(""));
 
-        final XMLConfiguration checkConfig =
-                createFromFile(testSaveConf.getAbsolutePath());
-        assertEquals("yes", checkConfig.getString("test"));
+        conf.clearProperty("");
+        saveTestConfig();
+        checkSavedConfig();
     }
 
     /**
-     * Tests loading from a stream.
+     * Tests the clone() method.
      */
     @Test
-    public void testLoadFromStream() throws Exception
+    public void testClone()
     {
-        final String xml = "<?xml version=\"1.0\"?><config><test>1</test></config>";
-        conf = new XMLConfiguration();
-        FileHandler handler = new FileHandler(conf);
-        handler.load(new ByteArrayInputStream(xml.getBytes()));
-        assertEquals(1, conf.getInt("test"));
+        final Configuration c = (Configuration) conf.clone();
+        assertTrue(c instanceof XMLConfiguration);
+        final XMLConfiguration copy = (XMLConfiguration) c;
+        assertNotNull(conf.getDocument());
+        assertNull(copy.getDocument());
 
-        conf = new XMLConfiguration();
-        handler = new FileHandler(conf);
-        handler.load(new ByteArrayInputStream(xml.getBytes()), "UTF8");
-        assertEquals(1, conf.getInt("test"));
+        copy.setProperty("element3", "clonedValue");
+        assertEquals("value", conf.getString("element3"));
+        conf.setProperty("element3[@name]", "originalFoo");
+        assertEquals("foo", copy.getString("element3[@name]"));
     }
 
     /**
-     * Tests loading a non well formed XML from a string.
+     * Tests saving a configuration after cloning to ensure that the clone and
+     * the original are completely detached.
      */
-    @Test(expected = ConfigurationException.class)
-    public void testLoadInvalidXML() throws Exception
-    {
-        final String xml = "<?xml version=\"1.0\"?><config><test>1</rest></config>";
-        conf = new XMLConfiguration();
-        final FileHandler handler = new FileHandler(conf);
-        handler.load(new StringReader(xml));
-    }
-
     @Test
-    public void testSetProperty() throws Exception
+    public void testCloneWithSave() throws ConfigurationException
     {
-        conf.setProperty("element.string", "hello");
-
-        assertEquals("'element.string'", "hello", conf.getString("element.string"));
-        assertEquals("XML value of element.string", "hello", conf.getProperty("element.string"));
+        final XMLConfiguration c = (XMLConfiguration) conf.clone();
+        c.addProperty("test.newProperty", Boolean.TRUE);
+        conf.addProperty("test.orgProperty", Boolean.TRUE);
+        new FileHandler(c).save(testSaveConf);
+        final XMLConfiguration c2 = new XMLConfiguration();
+        load(c2, testSaveConf.getAbsolutePath());
+        assertTrue("New property after clone() was not saved", c2
+                .getBoolean("test.newProperty"));
+        assertFalse("Property of original config was saved", c2
+                .containsKey("test.orgProperty"));
     }
 
+    /**
+     * Tests access to tag names with delimiter characters.
+     */
     @Test
-    public void testAddProperty()
+    public void testComplexNames()
     {
-        // add a property to a non initialized xml configuration
-        final XMLConfiguration config = new XMLConfiguration();
-        config.addProperty("test.string", "hello");
-
-        assertEquals("'test.string'", "hello", config.getString("test.string"));
+        assertEquals("Name with dot", conf.getString("complexNames.my..elem"));
+        assertEquals("Another dot", conf.getString("complexNames.my..elem.sub..elem"));
     }
 
     @Test
-    public void testAddObjectProperty()
+    public void testConcurrentGetAndReload() throws ConfigurationException,
+            InterruptedException
     {
-        // add a non string property
-        conf.addProperty("test.boolean", Boolean.TRUE);
-        assertTrue("'test.boolean'", conf.getBoolean("test.boolean"));
-    }
+        final FileBasedConfigurationBuilder<XMLConfiguration> builder =
+                new FileBasedConfigurationBuilder<>(
+                        XMLConfiguration.class);
+        builder.configure(new FileBasedBuilderParametersImpl()
+                .setFileName(testProperties));
+        XMLConfiguration config = builder.getConfiguration();
+        assertTrue("Property not found",
+                config.getProperty("test.short") != null);
 
-    @Test
-    public void testSave() throws Exception
-    {
-        // add an array of strings to the configuration
-        conf.addProperty("string", "value1");
-        for (int i = 1; i < 5; i++)
+        final Thread testThreads[] = new Thread[THREAD_COUNT];
+        for (int i = 0; i < testThreads.length; ++i)
         {
-            conf.addProperty("test.array", "value" + i);
+            testThreads[i] = new ReloadThread(builder);
+            testThreads[i].start();
         }
 
-        // add comma delimited lists with escaped delimiters
-        conf.addProperty("split.list5", "a\\,b\\,c");
-        conf.setProperty("element3", "value\\,value1\\,value2");
-        conf.setProperty("element3[@name]", "foo\\,bar");
-
-        // save the configuration
-        saveTestConfig();
+        for (int i = 0; i < LOOP_COUNT; i++)
+        {
+            config = builder.getConfiguration();
+            assertTrue("Property not found", config.getProperty("test.short") != null);
+        }
 
-        // read the configuration and compare the properties
-        checkSavedConfig();
+        for (final Thread testThread : testThreads) {
+            testThread.join();
+        }
     }
 
     /**
-     * Tests saving to a URL.
+     * Tests the copy constructor for null input.
      */
     @Test
-    public void testSaveToURL() throws Exception
+    public void testCopyNull()
     {
-        final FileHandler handler = new FileHandler(conf);
-        handler.save(testSaveConf.toURI().toURL());
-        checkSavedConfig(testSaveConf);
+        conf = new XMLConfiguration(null);
+        assertTrue("Not empty", conf.isEmpty());
+        assertEquals("Wrong root element name", "configuration",
+                conf.getRootElementName());
     }
 
     /**
-     * Tests saving to a stream.
+     * Tests whether the name of the root element is copied when a configuration
+     * is created using the copy constructor.
      */
     @Test
-    public void testSaveToStream() throws ConfigurationException, IOException
+    public void testCopyRootName() throws ConfigurationException
     {
-        FileOutputStream out = null;
-        final FileHandler handler = new FileHandler(conf);
-        try
-        {
-            out = new FileOutputStream(testSaveConf);
-            handler.save(out, "UTF8");
-        }
-        finally
-        {
-            if(out != null)
-            {
-                out.close();
-            }
-        }
-
-        checkSavedConfig(testSaveConf);
+        final String rootName = "rootElement";
+        final String xml = "<" + rootName + "><test>true</test></" + rootName
+                + ">";
+        conf.clear();
+        new FileHandler(conf).load(new StringReader(xml));
+        XMLConfiguration copy = new XMLConfiguration(conf);
+        assertEquals("Wrong name of root element", rootName, copy
+                .getRootElementName());
+        new FileHandler(copy).save(testSaveConf);
+        copy = new XMLConfiguration();
+        load(copy, testSaveConf.getAbsolutePath());
+        assertEquals("Wrong name of root element after save", rootName, copy
+                .getRootElementName());
     }
 
     /**
-     * Tests whether a configuration can be saved to a stream with a specific encoding.
+     * Tests whether the name of the root element is copied for a configuration
+     * for which not yet a document exists.
      */
     @Test
-    public void testSaveToStreamWithEncoding() throws ConfigurationException, IOException
+    public void testCopyRootNameNoDocument() throws ConfigurationException
     {
-        final FileHandler handler = new FileHandler(conf);
-        handler.setEncoding("UTF8");
-        FileOutputStream out = null;
-        try
-        {
-            out = new FileOutputStream(testSaveConf);
-            handler.save(out);
-        }
-        finally
-        {
-            if(out != null)
-            {
-                out.close();
-            }
-        }
-
-        checkSavedConfig(testSaveConf);
-    }
-
-    /**
-     * Tests if a second file can be appended to a first.
-     */
-    @Test
-    public void testAppend() throws Exception
-    {
-        load(conf, testProperties2);
-        assertEquals("value", conf.getString("element"));
-        assertEquals("tasks", conf.getString("table.name"));
-
-        saveTestConfig();
-        conf = createFromFile(testSaveConf.getAbsolutePath());
-        assertEquals("value", conf.getString("element"));
-        assertEquals("tasks", conf.getString("table.name"));
-        assertEquals("application", conf.getString("table[@tableType]"));
-    }
-
-    /**
-     * Tests saving attributes (related to issue 34442).
-     */
-    @Test
-    public void testSaveAttributes() throws Exception
-    {
-        conf.clear();
-        load(conf, testProperties);
-        saveTestConfig();
+        final String rootName = "rootElement";
         conf = new XMLConfiguration();
-        load(conf, testSaveConf.getAbsolutePath());
-        assertEquals("foo", conf.getString("element3[@name]"));
-    }
-
-    /**
-     * Tests access to tag names with delimiter characters.
-     */
-    @Test
-    public void testComplexNames()
-    {
-        assertEquals("Name with dot", conf.getString("complexNames.my..elem"));
-        assertEquals("Another dot", conf.getString("complexNames.my..elem.sub..elem"));
-    }
-
-    /**
-     * Creates a validating document builder.
-     * @return the document builder
-     * @throws ParserConfigurationException if an error occurs
-     */
-    private DocumentBuilder createValidatingDocBuilder()
-            throws ParserConfigurationException
-    {
-        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-        factory.setValidating(true);
-        final DocumentBuilder builder = factory.newDocumentBuilder();
-        builder.setErrorHandler(new DefaultHandler() {
-            @Override
-            public void error(final SAXParseException ex) throws SAXException
-            {
-                throw ex;
-            }
-        });
-        return builder;
+        conf.setRootElementName(rootName);
+        conf.setProperty("test", Boolean.TRUE);
+        final XMLConfiguration copy = new XMLConfiguration(conf);
+        assertEquals("Wrong name of root element", rootName, copy
+                .getRootElementName());
+        new FileHandler(copy).save(testSaveConf);
+        load(copy, testSaveConf.getAbsolutePath());
+        assertEquals("Wrong name of root element after save", rootName, copy
+                .getRootElementName());
     }
 
     /**
@@ -697,74 +785,6 @@ public class TestXMLConfiguration
     }
 
     /**
-     * Tests the clone() method.
-     */
-    @Test
-    public void testClone()
-    {
-        final Configuration c = (Configuration) conf.clone();
-        assertTrue(c instanceof XMLConfiguration);
-        final XMLConfiguration copy = (XMLConfiguration) c;
-        assertNotNull(conf.getDocument());
-        assertNull(copy.getDocument());
-
-        copy.setProperty("element3", "clonedValue");
-        assertEquals("value", conf.getString("element3"));
-        conf.setProperty("element3[@name]", "originalFoo");
-        assertEquals("foo", copy.getString("element3[@name]"));
-    }
-
-    /**
-     * Tests saving a configuration after cloning to ensure that the clone and
-     * the original are completely detached.
-     */
-    @Test
-    public void testCloneWithSave() throws ConfigurationException
-    {
-        final XMLConfiguration c = (XMLConfiguration) conf.clone();
-        c.addProperty("test.newProperty", Boolean.TRUE);
-        conf.addProperty("test.orgProperty", Boolean.TRUE);
-        new FileHandler(c).save(testSaveConf);
-        final XMLConfiguration c2 = new XMLConfiguration();
-        load(c2, testSaveConf.getAbsolutePath());
-        assertTrue("New property after clone() was not saved", c2
-                .getBoolean("test.newProperty"));
-        assertFalse("Property of original config was saved", c2
-                .containsKey("test.orgProperty"));
-    }
-
-    /**
-     * Tests the subset() method. There was a bug that calling subset() had
-     * undesired side effects.
-     */
-    @Test
-    public void testSubset() throws ConfigurationException
-    {
-        conf = new XMLConfiguration();
-        load(conf, "testHierarchicalXMLConfiguration.xml");
-        conf.subset("tables.table(0)");
-        saveTestConfig();
-
-        conf = new XMLConfiguration();
-        load(conf, "testHierarchicalXMLConfiguration.xml");
-        assertEquals("users", conf.getString("tables.table(0).name"));
-    }
-
-    /**
-     * Tests string properties with list delimiters and escaped delimiters.
-     */
-    @Test
-    public void testSplitLists()
-    {
-        assertEquals("a,b,c", conf.getString("split.list3[@values]"));
-        assertEquals(0, conf.getMaxIndex("split.list3[@values]"));
-        assertEquals("a\\,b\\,c", conf.getString("split.list4[@values]"));
-        assertEquals("a", conf.getString("split.list1"));
-        assertEquals(2, conf.getMaxIndex("split.list1"));
-        assertEquals("a,b,c", conf.getString("split.list2"));
-    }
-
-    /**
      * Tests string properties with list delimiters when delimiter parsing
      * is disabled
      */
@@ -800,52 +820,6 @@ public class TestXMLConfiguration
         assertEquals("a\\,b\\,c", conf2.getString("split/list2"));
     }
 
-     /**
-     * Tests string properties with list delimiters when delimiter parsing
-     * is disabled
-     */
-    @Test
-    public void testSaveWithDelimiterParsingDisabled() throws ConfigurationException {
-        conf = new XMLConfiguration();
-        conf.setExpressionEngine(new XPathExpressionEngine());
-        load(conf, testProperties);
-
-        assertEquals("a,b,c", conf.getString("split/list3/@values"));
-        assertEquals(0, conf.getMaxIndex("split/list3/@values"));
-        assertEquals("a\\,b\\,c", conf.getString("split/list4/@values"));
-        assertEquals("a,b,c", conf.getString("split/list1"));
-        assertEquals(0, conf.getMaxIndex("split/list1"));
-        assertEquals("a\\,b\\,c", conf.getString("split/list2"));
-        // save the configuration
-        saveTestConfig();
-
-        XMLConfiguration config = new XMLConfiguration();
-        //config.setExpressionEngine(new XPathExpressionEngine());
-        load(config, testFile2);
-        config.setProperty("Employee[@attr1]", "3,2,1");
-        assertEquals("3,2,1", config.getString("Employee[@attr1]"));
-        new FileHandler(config).save(testSaveFile);
-        config = new XMLConfiguration();
-        //config.setExpressionEngine(new XPathExpressionEngine());
-        load(config, testSaveFile.getAbsolutePath());
-        config.setProperty("Employee[@attr1]", "1,2,3");
-        assertEquals("1,2,3", config.getString("Employee[@attr1]"));
-        config.setProperty("Employee[@attr2]", "one, two, three");
-        assertEquals("one, two, three", config.getString("Employee[@attr2]"));
-        config.setProperty("Employee.text", "a,b,d");
-        assertEquals("a,b,d", config.getString("Employee.text"));
-        config.setProperty("Employee.Salary", "100,000");
-        assertEquals("100,000", config.getString("Employee.Salary"));
-        new FileHandler(config).save(testSaveFile);
-        final XMLConfiguration checkConfig = new XMLConfiguration();
-        checkConfig.setExpressionEngine(new XPathExpressionEngine());
-        load(checkConfig, testSaveFile.getAbsolutePath());
-        assertEquals("1,2,3", checkConfig.getString("Employee/@attr1"));
-        assertEquals("one, two, three", checkConfig.getString("Employee/@attr2"));
-        assertEquals("a,b,d", checkConfig.getString("Employee/text"));
-        assertEquals("100,000", checkConfig.getString("Employee/Salary"));
-    }
-
     /**
      * Tests whether a DTD can be accessed.
      */
@@ -859,33 +833,24 @@ public class TestXMLConfiguration
     }
 
     /**
-     * Tests DTD validation using the setValidating() method.
+     * Tests whether an attribute can be set to an empty string. This test is
+     * related to CONFIGURATION-446.
      */
     @Test
-    public void testValidating() throws ConfigurationException
-    {
-        final File nonValidFile = ConfigurationAssert.getTestFile("testValidateInvalid.xml");
-        conf = new XMLConfiguration();
-        assertFalse(conf.isValidating());
-
-        // Load a non valid XML document. Should work for isValidating() == false
-        load(conf, nonValidFile.getAbsolutePath());
-        assertEquals("customers", conf.getString("table.name"));
-        assertFalse(conf.containsKey("table.fields.field(1).type"));
-    }
-
-    /**
-     * Tests whether an invalid file is detected when validating is enabled.
-     */
-    @Test(expected = ConfigurationException.class)
-    public void testValidatingInvalidFile() throws ConfigurationException
+    public void testEmptyAttribute() throws ConfigurationException
     {
+        final String key = "element3[@value]";
+        conf.setProperty(key, "");
+        assertTrue("Key not found", conf.containsKey(key));
+        assertEquals("Wrong value", "", conf.getString(key));
+        saveTestConfig();
         conf = new XMLConfiguration();
-        conf.setValidating(true);
-        load(conf, "testValidateInvalid.xml");
+        load(conf, testSaveConf.getAbsolutePath());
+        assertTrue("Key not found after save", conf.containsKey(key));
+        assertEquals("Wrong value after save", "", conf.getString(key));
     }
 
-    /**
+     /**
      * Tests handling of empty elements.
      */
     @Test
@@ -916,170 +881,101 @@ public class TestXMLConfiguration
         assertTrue("Reloaded configuration not empty", conf.isEmpty());
     }
 
-    /**
-     * Tests whether the encoding is correctly detected by the XML parser. This
-     * is done by loading an XML file with the encoding "UTF-16". If this
-     * encoding is not detected correctly, an exception will be thrown that
-     * "Content is not allowed in prolog". This test case is related to issue
-     * 34204.
-     */
     @Test
-    public void testLoadWithEncoding() throws ConfigurationException
+    public void testGetAttribute()
     {
-        conf = new XMLConfiguration();
-        new FileHandler(conf).load(ConfigurationAssert.getTestFile("testEncoding.xml"));
-        assertEquals("test3_yoge", conf.getString("yoge"));
+        assertEquals("element3[@name]", "foo", conf.getProperty("element3[@name]"));
     }
 
-
     @Test
-    public void testLoadWithRootNamespace() throws ConfigurationException
+    public void testGetCommentedProperty()
     {
-        conf = new XMLConfiguration();
-        new FileHandler(conf).load(ConfigurationAssert.getTestFile("testRootNamespace.xml"));
-        assertEquals("http://example.com/", conf.getString("[@xmlns:foo]"));
+        assertEquals("", conf.getProperty("test.comment"));
     }
 
     @Test
-    public void testLoadChildNamespace() throws ConfigurationException
+    public void testGetComplexProperty()
     {
-        conf = new XMLConfiguration();
-        new FileHandler(conf).load(ConfigurationAssert.getTestFile("testChildNamespace.xml"));
-        assertEquals("http://example.com/", conf.getString("foo:bar.[@xmlns:foo]"));
+        assertEquals("I'm complex!", conf.getProperty("element2.subelement.subsubelement"));
     }
 
-    /**
-     * Tests whether the encoding is written to the generated XML file.
-     */
     @Test
-    public void testSaveWithEncoding() throws ConfigurationException
-    {
-        conf = new XMLConfiguration();
-        conf.setProperty("test", "a value");
-        final FileHandler handler = new FileHandler(conf);
-        handler.setEncoding(ENCODING);
+    public void testgetProperty() {
+        // test non-leaf element
+        Object property = conf.getProperty("clear");
+        assertNull(property);
 
-        final StringWriter out = new StringWriter();
-        handler.save(out);
-        assertThat("Encoding was not written to file", out.toString(),
-                containsString("encoding=\"" + ENCODING + "\""));
-    }
+        // test non-existent element
+        property = conf.getProperty("e");
+        assertNull(property);
 
-    @Test
-    public void testSaveWithRootAttributes() throws ConfigurationException
-    {
-        conf.setProperty("[@xmlns:ex]", "http://example.com/");
-        assertEquals("Root attribute not set", "http://example.com/", conf
-                .getString("[@xmlns:ex]"));
-        final FileHandler handler = new FileHandler(conf);
+        // test non-existent element
+        property = conf.getProperty("element3[@n]");
+        assertNull(property);
 
-        final StringWriter out = new StringWriter();
-        handler.save(out);
-        assertThat("Encoding was not written to file", out.toString(),
-                containsString("testconfig xmlns:ex=\"http://example.com/\""));
-    }
+        // test single element
+        property = conf.getProperty("element");
+        assertNotNull(property);
+        assertTrue(property instanceof String);
+        assertEquals("value", property);
 
-    @Test
-    public void testSaveWithRootAttributes_ByHand() throws ConfigurationException
-    {
-        conf = new XMLConfiguration();
-        conf.addProperty(  "[@xmlns:foo]",  "http://example.com/" );
-        assertEquals("Root attribute not set", "http://example.com/", conf
-                .getString("[@xmlns:foo]"));
-        final FileHandler handler = new FileHandler(conf);
+        // test single attribute
+        property = conf.getProperty("element3[@name]");
+        assertNotNull(property);
+        assertTrue(property instanceof String);
+        assertEquals("foo", property);
 
-        final StringWriter out = new StringWriter();
-        handler.save(out);
-        assertThat("Encoding was not written to file", out.toString(),
-                containsString("configuration xmlns:foo=\"http://example.com/\""));
-    }
+        // test non-text/cdata element
+        property = conf.getProperty("test.comment");
+        assertEquals("", property);
 
-    /**
-     * Tests whether a default encoding is used if no specific encoding is set.
-     * According to the XSLT specification (http://www.w3.org/TR/xslt#output)
-     * this should be either UTF-8 or UTF-16.
-     */
-    @Test
-    public void testSaveWithNullEncoding() throws ConfigurationException
-    {
-        conf = new XMLConfiguration();
-        conf.setProperty("testNoEncoding", "yes");
-        final FileHandler handler = new FileHandler(conf);
+        // test cdata element
+        property = conf.getProperty("test.cdata");
+        assertNotNull(property);
+        assertTrue(property instanceof String);
+        assertEquals("<cdata value>", property);
 
-        final StringWriter out = new StringWriter();
-        handler.save(out);
-        assertThat("Encoding was written to file", out.toString(),
-                containsString("encoding=\"UTF-"));
-    }
+        // test multiple sibling elements
+        property = conf.getProperty("list.sublist.item");
+        assertNotNull(property);
+        assertTrue(property instanceof List);
+        List<?> list = (List<?>) property;
+        assertEquals(2, list.size());
+        assertEquals("five", list.get(0));
+        assertEquals("six", list.get(1));
 
-    /**
-     * Tests whether the DOCTYPE survives a save operation.
-     */
-    @Test
-    public void testSaveWithDoctype() throws ConfigurationException
-    {
-        conf = new XMLConfiguration();
-        load(conf, "testDtdPublic.xml");
+        // test multiple, disjoined elements
+        property = conf.getProperty("list.item");
+        assertNotNull(property);
+        assertTrue(property instanceof List);
+        list = (List<?>) property;
+        assertEquals(4, list.size());
+        assertEquals("one", list.get(0));
+        assertEquals("two", list.get(1));
+        assertEquals("three", list.get(2));
+        assertEquals("four", list.get(3));
 
-        assertEquals("Wrong public ID", PUBLIC_ID, conf.getPublicID());
-        assertEquals("Wrong system ID", SYSTEM_ID, conf.getSystemID());
-        final StringWriter out = new StringWriter();
-        new FileHandler(conf).save(out);
-        assertThat("Did not find DOCTYPE", out.toString(),
-                containsString(DOCTYPE));
+        // test multiple, disjoined attributes
+        property = conf.getProperty("list.item[@name]");
+        assertNotNull(property);
+        assertTrue(property instanceof List);
+        list = (List<?>) property;
+        assertEquals(2, list.size());
+        assertEquals("one", list.get(0));
+        assertEquals("three", list.get(1));
     }
 
-    /**
-     * Tests setting public and system IDs for the DOCTYPE and then saving the
-     * configuration. This should generate a DOCTYPE declaration.
-     */
     @Test
-    public void testSaveWithDoctypeIDs() throws ConfigurationException
+    public void testGetProperty()
     {
-        assertNull("A public ID was found", conf.getPublicID());
-        assertNull("A system ID was found", conf.getSystemID());
-        conf.setPublicID(PUBLIC_ID);
-        conf.setSystemID(SYSTEM_ID);
-        final StringWriter out = new StringWriter();
-        new FileHandler(conf).save(out);
-        assertThat("Did not find DOCTYPE", out.toString(), containsString(
-                DOCTYPE + "testconfig" + DOCTYPE_DECL));
+        assertEquals("value", conf.getProperty("element"));
     }
 
-    /**
-     * Tests saving a configuration if an invalid transformer factory is
-     * specified. In this case an error is thrown by the transformer factory.
-     * XMLConfiguration should not catch this error.
-     */
-    @Test
-    public void testSaveWithInvalidTransformerFactory() throws ConfigurationException {
-        System.setProperty(PROP_FACTORY, "an.invalid.Class");
-        try
-        {
-            saveTestConfig();
-            fail("Could save with invalid TransformerFactory!");
-        }
-        catch (final TransformerFactoryConfigurationError cex)
-        {
-            // ok
-        }
-        finally
-        {
-            System.getProperties().remove(PROP_FACTORY);
-        }
-    }
 
-    /**
-     * Tests accessing properties when the XPATH expression engine is set.
-     */
     @Test
-    public void testXPathExpressionEngine()
+    public void testGetPropertyWithXMLEntity()
     {
-        conf.setExpressionEngine(new XPathExpressionEngine());
-        assertEquals("Wrong attribute value", "foo\"bar", conf
-                .getString("test[1]/entity/@name"));
-        conf.clear();
-        assertNull(conf.getString("test[1]/entity/@name"));
+        assertEquals("1<2", conf.getProperty("test.entity"));
     }
 
     /**
@@ -1098,35 +994,6 @@ public class TestXMLConfiguration
     }
 
     /**
-     * Tests setting text of the root element.
-     */
-    @Test
-    public void testSetTextRootElement() throws ConfigurationException
-    {
-        conf.setProperty("", "Root text");
-        saveTestConfig();
-        checkSavedConfig();
-    }
-
-    /**
-     * Tests removing the text of the root element.
-     */
-    @Test
-    public void testClearTextRootElement() throws ConfigurationException
-    {
-        final String xml = "<e a=\"v\">text</e>";
-        conf.clear();
-        final StringReader in = new StringReader(xml);
-        final FileHandler handler = new FileHandler(conf);
-        handler.load(in);
-        assertEquals("Wrong text of root", "text", conf.getString(""));
-
-        conf.clearProperty("");
-        saveTestConfig();
-        checkSavedConfig();
-    }
-
-    /**
      * Tests list nodes with multiple values and attributes.
      */
     @Test
@@ -1184,259 +1051,115 @@ public class TestXMLConfiguration
     }
 
     /**
-     * Tests whether the auto save mechanism is triggered by changes at a
-     * subnode configuration.
+     * Tests constructing an XMLConfiguration from a non existing file and later
+     * saving to this file.
      */
     @Test
-    public void testAutoSaveWithSubnodeConfig() throws ConfigurationException
+    public void testLoadAndSaveFromFile() throws Exception
     {
+        // If the file does not exist, an empty config is created
+        assertFalse("File exists", testSaveConf.exists());
         final FileBasedConfigurationBuilder<XMLConfiguration> builder =
                 new FileBasedConfigurationBuilder<>(
-                        XMLConfiguration.class);
+                        XMLConfiguration.class, null, true);
         builder.configure(new FileBasedBuilderParametersImpl()
-                .setFileName(testProperties));
+                .setFile(testSaveConf));
         conf = builder.getConfiguration();
-        builder.getFileHandler().setFile(testSaveConf);
-        builder.setAutoSave(true);
-        final String newValue = "I am autosaved";
-        final Configuration sub = conf.configurationAt("element2.subelement", true);
-        sub.setProperty("subsubelement", newValue);
-        assertEquals("Change not visible to parent", newValue,
-                conf.getString("element2.subelement.subsubelement"));
-        final XMLConfiguration conf2 = new XMLConfiguration();
-        load(conf2, testSaveConf.getAbsolutePath());
-        assertEquals("Change was not saved", newValue,
-                conf2.getString("element2.subelement.subsubelement"));
+        assertTrue(conf.isEmpty());
+        conf.addProperty("test", "yes");
+        builder.save();
+
+        final XMLConfiguration checkConfig =
+                createFromFile(testSaveConf.getAbsolutePath());
+        assertEquals("yes", checkConfig.getString("test"));
     }
 
-    /**
-     * Tests whether a subnode configuration created from another subnode
-     * configuration of a XMLConfiguration can trigger the auto save mechanism.
-     */
     @Test
-    public void testAutoSaveWithSubSubnodeConfig() throws ConfigurationException
+    public void testLoadChildNamespace() throws ConfigurationException
     {
-        final FileBasedConfigurationBuilder<XMLConfiguration> builder =
-                new FileBasedConfigurationBuilder<>(
-                        XMLConfiguration.class);
-        builder.configure(new FileBasedBuilderParametersImpl()
-                .setFileName(testProperties));
-        conf = builder.getConfiguration();
-        builder.getFileHandler().setFile(testSaveConf);
-        builder.setAutoSave(true);
-        final String newValue = "I am autosaved";
-        final HierarchicalConfiguration<?> sub1 = conf.configurationAt("element2", true);
-        final HierarchicalConfiguration<?> sub2 = sub1.configurationAt("subelement", true);
-        sub2.setProperty("subsubelement", newValue);
-        assertEquals("Change not visible to parent", newValue, conf
-                .getString("element2.subelement.subsubelement"));
-        final XMLConfiguration conf2 = new XMLConfiguration();
-        load(conf2, testSaveConf.getAbsolutePath());
-        assertEquals("Change was not saved", newValue, conf2
-                .getString("element2.subelement.subsubelement"));
+        conf = new XMLConfiguration();
+        new FileHandler(conf).load(ConfigurationAssert.getTestFile("testChildNamespace.xml"));
+        assertEquals("http://example.com/", conf.getString("foo:bar.[@xmlns:foo]"));
     }
 
     /**
-     * Tests saving and loading a configuration when delimiter parsing is
-     * disabled.
+     * Tests loading from a stream.
      */
     @Test
-    public void testSaveDelimiterParsingDisabled()
-            throws ConfigurationException
-    {
-        checkSaveDelimiterParsingDisabled("list.delimiter.test");
-    }
-
-    /**
-     * Helper method for testing saving and loading a configuration when
-     * delimiter parsing is disabled.
-     *
-     * @param key the key to be checked
-     * @throws ConfigurationException if an error occurs
-     */
-    private void checkSaveDelimiterParsingDisabled(final String key)
-            throws ConfigurationException
-    {
-        conf.clear();
-        conf.setListDelimiterHandler(new DisabledListDelimiterHandler());
-        load(conf, testProperties);
-        conf.setProperty(key, "C:\\Temp\\,C:\\Data\\");
-        conf.addProperty(key, "a,b,c");
-        saveTestConfig();
-        final XMLConfiguration checkConf = new XMLConfiguration();
-        checkConf.setListDelimiterHandler(conf.getListDelimiterHandler());
-        load(checkConf, testSaveConf.getAbsolutePath());
-        ConfigurationAssert.assertConfigurationEquals(conf, checkConf);
-    }
-
-    /**
-     * Tests that attribute values are not split.
-     */
-    @Test
-    public void testNoDelimiterParsingInAttrValues() throws ConfigurationException
-    {
-        conf.clear();
-        load(conf, testProperties);
-        final List<Object> expr = conf.getList("expressions[@value]");
-        assertEquals("Wrong list size", 1, expr.size());
-        assertEquals("Wrong element 1", "a || (b && c) | !d", expr.get(0));
-    }
-
-    /**
-     * Tries to create an attribute with multiple values. Only the first value
-     * is taken into account.
-     */
-    @Test
-    public void testAttributeKeyWithMultipleValues()
-            throws ConfigurationException
+    public void testLoadFromStream() throws Exception
     {
-        conf.addProperty("errorTest[@multiAttr]", Arrays.asList("v1", "v2"));
-        saveTestConfig();
-        final XMLConfiguration checkConfig = new XMLConfiguration();
-        load(checkConfig, testSaveConf.getAbsolutePath());
-        assertEquals("Wrong attribute value", "v1",
-                checkConfig.getString("errorTest[@multiAttr]"));
-    }
+        final String xml = "<?xml version=\"1.0\"?><config><test>1</test></config>";
+        conf = new XMLConfiguration();
+        FileHandler handler = new FileHandler(conf);
+        handler.load(new ByteArrayInputStream(xml.getBytes()));
+        assertEquals(1, conf.getInt("test"));
 
-    /**
-     * Tests adding nodes from another configuration.
-     */
-    @Test
-    public void testAddNodesCopy() throws ConfigurationException
-    {
-        final XMLConfiguration c2 = new XMLConfiguration();
-        load(c2, testProperties2);
-        conf.addNodes("copiedProperties", c2.getModel().getNodeHandler()
-                .getRootNode().getChildren());
-        saveTestConfig();
-        checkSavedConfig();
+        conf = new XMLConfiguration();
+        handler = new FileHandler(conf);
+        handler.load(new ByteArrayInputStream(xml.getBytes()), "UTF8");
+        assertEquals(1, conf.getInt("test"));
     }
 
     /**
-     * Tests whether the addNodes() method triggers an auto save.
+     * Tests loading a non well formed XML from a string.
      */
-    @Test
-    public void testAutoSaveAddNodes() throws ConfigurationException
+    @Test(expected = ConfigurationException.class)
+    public void testLoadInvalidXML() throws Exception
     {
-        final FileBasedConfigurationBuilder<XMLConfiguration> builder =
-                new FileBasedConfigurationBuilder<>(
-                        XMLConfiguration.class);
-        builder.configure(new FileBasedBuilderParametersImpl()
-                .setFileName(testProperties));
-        conf = builder.getConfiguration();
-        builder.getFileHandler().setFile(testSaveConf);
-        builder.setAutoSave(true);
-        final ImmutableNode node = NodeStructureHelper.createNode(
-                "addNodesTest", Boolean.TRUE);
-        final Collection<ImmutableNode> nodes = new ArrayList<>(1);
-        nodes.add(node);
-        conf.addNodes("test.autosave", nodes);
-        final XMLConfiguration c2 = new XMLConfiguration();
-        load(c2, testSaveConf.getAbsolutePath());
-        assertTrue("Added nodes are not saved", c2
-                .getBoolean("test.autosave.addNodesTest"));
+        final String xml = "<?xml version=\"1.0\"?><config><test>1</rest></config>";
+        conf = new XMLConfiguration();
+        final FileHandler handler = new FileHandler(conf);
+        handler.load(new StringReader(xml));
     }
 
     /**
-     * Tests saving a configuration after a node was added. Test for
-     * CONFIGURATION-294.
+     * Tests whether the encoding is correctly detected by the XML parser. This
+     * is done by loading an XML file with the encoding "UTF-16". If this
+     * encoding is not detected correctly, an exception will be thrown that
+     * "Content is not allowed in prolog". This test case is related to issue
+     * 34204.
      */
     @Test
-    public void testAddNodesAndSave() throws ConfigurationException
+    public void testLoadWithEncoding() throws ConfigurationException
     {
-        final ImmutableNode.Builder bldrNode = new ImmutableNode.Builder(1);
-        bldrNode.addChild(NodeStructureHelper.createNode("child", null));
-        bldrNode.addAttribute("attr", "");
-        final ImmutableNode node2 = NodeStructureHelper.createNode("test2", null);
-        conf.addNodes("add.nodes",
-                Arrays.asList(bldrNode.name("test").create(), node2));
-        saveTestConfig();
-        conf.setProperty("add.nodes.test", "true");
-        conf.setProperty("add.nodes.test.child", "yes");
-        conf.setProperty("add.nodes.test[@attr]", "existing");
-        conf.setProperty("add.nodes.test2", "anotherValue");
-        saveTestConfig();
-        final XMLConfiguration c2 = new XMLConfiguration();
-        load(c2, testSaveConf.getAbsolutePath());
-        assertEquals("Value was not saved", "true", c2
-                .getString("add.nodes.test"));
-        assertEquals("Child value was not saved", "yes", c2
-                .getString("add.nodes.test.child"));
-        assertEquals("Attr value was not saved", "existing", c2
-                .getString("add.nodes.test[@attr]"));
-        assertEquals("Node2 not saved", "anotherValue", c2
-                .getString("add.nodes.test2"));
+        conf = new XMLConfiguration();
+        new FileHandler(conf).load(ConfigurationAssert.getTestFile("testEncoding.xml"));
+        assertEquals("test3_yoge", conf.getString("yoge"));
     }
 
-    /**
-     * Tests saving a configuration that was created from a hierarchical
-     * configuration. This test exposes bug CONFIGURATION-301.
-     */
     @Test
-    public void testSaveAfterCreateWithCopyConstructor()
-            throws ConfigurationException
+    public void testLoadWithRootNamespace() throws ConfigurationException
     {
-        final HierarchicalConfiguration<ImmutableNode> hc =
-                conf.configurationAt("element2");
-        conf = new XMLConfiguration(hc);
-        saveTestConfig();
-        final XMLConfiguration checkConfig = checkSavedConfig();
-        assertEquals("Wrong name of root element", "element2", checkConfig
-                .getRootElementName());
+        conf = new XMLConfiguration();
+        new FileHandler(conf).load(ConfigurationAssert.getTestFile("testRootNamespace.xml"));
+        assertEquals("http://example.com/", conf.getString("[@xmlns:foo]"));
     }
 
     /**
-     * Tests whether the name of the root element is copied when a configuration
-     * is created using the copy constructor.
+     * Tests that attribute values are not split.
      */
     @Test
-    public void testCopyRootName() throws ConfigurationException
+    public void testNoDelimiterParsingInAttrValues() throws ConfigurationException
     {
-        final String rootName = "rootElement";
-        final String xml = "<" + rootName + "><test>true</test></" + rootName
-                + ">";
         conf.clear();
-        new FileHandler(conf).load(new StringReader(xml));
-        XMLConfiguration copy = new XMLConfiguration(conf);
-        assertEquals("Wrong name of root element", rootName, copy
-                .getRootElementName());
-        new FileHandler(copy).save(testSaveConf);
-        copy = new XMLConfiguration();
-        load(copy, testSaveConf.getAbsolutePath());
-        assertEquals("Wrong name of root element after save", rootName, copy
-                .getRootElementName());
+        load(conf, testProperties);
+        final List<Object> expr = conf.getList("expressions[@value]");
+        assertEquals("Wrong list size", 1, expr.size());
+        assertEquals("Wrong element 1", "a || (b && c) | !d", expr.get(0));
     }
 
     /**
-     * Tests whether the name of the root element is copied for a configuration
-     * for which not yet a document exists.
+     * Tests whether an attribute value can be overridden.
      */
     @Test
-    public void testCopyRootNameNoDocument() throws ConfigurationException
+    public void testOverrideAttribute()
     {
-        final String rootName = "rootElement";
-        conf = new XMLConfiguration();
-        conf.setRootElementName(rootName);
-        conf.setProperty("test", Boolean.TRUE);
-        final XMLConfiguration copy = new XMLConfiguration(conf);
-        assertEquals("Wrong name of root element", rootName, copy
-                .getRootElementName());
-        new FileHandler(copy).save(testSaveConf);
-        load(copy, testSaveConf.getAbsolutePath());
-        assertEquals("Wrong name of root element after save", rootName, copy
-                .getRootElementName());
-    }
+        conf.addProperty("element3[@name]", "bar");
 
-    /**
-     * Tests the copy constructor for null input.
-     */
-    @Test
-    public void testCopyNull()
-    {
-        conf = new XMLConfiguration(null);
-        assertTrue("Not empty", conf.isEmpty());
-        assertEquals("Wrong root element name", "configuration",
-                conf.getRootElementName());
+        final List<Object> list = conf.getList("element3[@name]");
+        assertNotNull("null list", list);
+        assertTrue("'bar' element missing", list.contains("bar"));
+        assertEquals("list size", 1, list.size());
     }
 
     /**
@@ -1451,6 +1174,17 @@ public class TestXMLConfiguration
     }
 
     /**
+     * Tests an xml:space attribute with an invalid value. This will be
+     * interpreted as default.
+     */
+    @Test
+    public void testPreserveSpaceInvalid()
+    {
+        assertEquals("Invalid not trimmed", "Some other text", conf
+                .getString("space.testInvalid"));
+    }
+
+    /**
      * Tests whether the xml:space attribute works directly on the current
      * element. This test is related to CONFIGURATION-555.
      */
@@ -1475,94 +1209,163 @@ public class TestXMLConfiguration
     }
 
     /**
-     * Tests an xml:space attribute with an invalid value. This will be
-     * interpreted as default.
-     */
-    @Test
-    public void testPreserveSpaceInvalid()
-    {
-        assertEquals("Invalid not trimmed", "Some other text", conf
-                .getString("space.testInvalid"));
-    }
-
-    /**
-     * Tests modifying an XML document and saving it with schema validation enabled.
+     * Tests whether the public ID is accessed in a synchronized manner.
      */
     @Test
-    public void testSaveWithValidation() throws Exception
+    public void testPublicIdSynchronized()
     {
-        final CatalogResolver resolver = new CatalogResolver();
-        resolver.setCatalogFiles(CATALOG_FILES);
-        conf = new XMLConfiguration();
-        conf.setEntityResolver(resolver);
-        conf.setSchemaValidation(true);
-        load(conf, testFile2);
-        conf.setProperty("Employee.SSN", "123456789");
         final SynchronizerTestImpl sync = new SynchronizerTestImpl();
         conf.setSynchronizer(sync);
-        conf.validate();
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
-        saveTestConfig();
-        conf = new XMLConfiguration();
-        load(conf, testSaveConf.getAbsolutePath());
-        assertEquals("123456789", conf.getString("Employee.SSN"));
+        conf.setPublicID(PUBLIC_ID);
+        assertEquals("PublicID not set", PUBLIC_ID, conf.getPublicID());
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE, Methods.BEGIN_READ,
+                Methods.END_READ);
     }
 
     /**
-     * Tests modifying an XML document and validating it against the schema.
+     * Tests a direct invocation of the read() method. This is not allowed
+     * because certain initializations have not been done. This test is
+     * related to CONFIGURATION-641.
      */
     @Test
-    public void testSaveWithValidationFailure() throws Exception
+    public void testReadCalledDirectly() throws IOException
     {
-        final CatalogResolver resolver = new CatalogResolver();
-        resolver.setCatalogFiles(CATALOG_FILES);
         conf = new XMLConfiguration();
-        conf.setEntityResolver(resolver);
-        conf.setSchemaValidation(true);
-        load(conf, testFile2);
-        conf.setProperty("Employee.Email", "JohnDoe@apache.org");
+        final String content = "<configuration><test>1</test></configuration>";
+        final ByteArrayInputStream bis = new ByteArrayInputStream(content.getBytes());
         try
         {
-            conf.validate();
-            fail("No validation failure on save");
+            conf.read(bis);
+            fail("No exception thrown!");
         }
-        catch (final Exception e)
+        catch (final ConfigurationException e)
         {
-            final Throwable cause = e.getCause();
-            assertNotNull("No cause for exception on save", cause);
-            assertTrue("Incorrect exception on save", cause instanceof SAXParseException);
+            assertThat(e.getMessage(), containsString("FileHandler"));
         }
     }
 
     @Test
-    public void testConcurrentGetAndReload() throws ConfigurationException,
-            InterruptedException
+    public void testSave() throws Exception
     {
-        final FileBasedConfigurationBuilder<XMLConfiguration> builder =
-                new FileBasedConfigurationBuilder<>(
-                        XMLConfiguration.class);
-        builder.configure(new FileBasedBuilderParametersImpl()
-                .setFileName(testProperties));
-        XMLConfiguration config = builder.getConfiguration();
-        assertTrue("Property not found",
-                config.getProperty("test.short") != null);
-
-        final Thread testThreads[] = new Thread[THREAD_COUNT];
-        for (int i = 0; i < testThreads.length; ++i)
+        // add an array of strings to the configuration
+        conf.addProperty("string", "value1");
+        for (int i = 1; i < 5; i++)
         {
-            testThreads[i] = new ReloadThread(builder);
-            testThreads[i].start();
+            conf.addProperty("test.array", "value" + i);
         }
 
-        for (int i = 0; i < LOOP_COUNT; i++)
+        // add comma delimited lists with escaped delimiters
+        conf.addProperty("split.list5", "a\\,b\\,c");
+        conf.setProperty("element3", "value\\,value1\\,value2");
+        conf.setProperty("element3[@name]", "foo\\,bar");
+
+        // save the configuration
+        saveTestConfig();
+
+        // read the configuration and compare the properties
+        checkSavedConfig();
+    }
+
+    /**
+     * Tests saving a configuration that was created from a hierarchical
+     * configuration. This test exposes bug CONFIGURATION-301.
+     */
+    @Test
+    public void testSaveAfterCreateWithCopyConstructor()
+            throws ConfigurationException
+    {
+        final HierarchicalConfiguration<ImmutableNode> hc =
+                conf.configurationAt("element2");
+        conf = new XMLConfiguration(hc);
+        saveTestConfig();
+        final XMLConfiguration checkConfig = checkSavedConfig();
+        assertEquals("Wrong name of root element", "element2", checkConfig
+                .getRootElementName());
+    }
+
+    /**
+     * Tests saving attributes (related to issue 34442).
+     */
+    @Test
+    public void testSaveAttributes() throws Exception
+    {
+        conf.clear();
+        load(conf, testProperties);
+        saveTestConfig();
+        conf = new XMLConfiguration();
+        load(conf, testSaveConf.getAbsolutePath());
+        assertEquals("foo", conf.getString("element3[@name]"));
+    }
+
+    /**
+     * Tests saving and loading a configuration when delimiter parsing is
+     * disabled.
+     */
+    @Test
+    public void testSaveDelimiterParsingDisabled()
+            throws ConfigurationException
+    {
+        checkSaveDelimiterParsingDisabled("list.delimiter.test");
+    }
+
+    /**
+     * Tests saving to a stream.
+     */
+    @Test
+    public void testSaveToStream() throws ConfigurationException, IOException
+    {
+        FileOutputStream out = null;
+        final FileHandler handler = new FileHandler(conf);
+        try
         {
-            config = builder.getConfiguration();
-            assertTrue("Property not found", config.getProperty("test.short") != null);
+            out = new FileOutputStream(testSaveConf);
+            handler.save(out, "UTF8");
+        }
+        finally
+        {
+            if(out != null)
+            {
+                out.close();
+            }
         }
 
-        for (final Thread testThread : testThreads) {
-            testThread.join();
+        checkSavedConfig(testSaveConf);
+    }
+
+    /**
+     * Tests whether a configuration can be saved to a stream with a specific encoding.
+     */
+    @Test
+    public void testSaveToStreamWithEncoding() throws ConfigurationException, IOException
+    {
+        final FileHandler handler = new FileHandler(conf);
+        handler.setEncoding("UTF8");
+        FileOutputStream out = null;
+        try
+        {
+            out = new FileOutputStream(testSaveConf);
+            handler.save(out);
+        }
+        finally
+        {
+            if(out != null)
+            {
+                out.close();
+            }
         }
+
+        checkSavedConfig(testSaveConf);
+    }
+
+    /**
+     * Tests saving to a URL.
+     */
+    @Test
+    public void testSaveToURL() throws Exception
+    {
+        final FileHandler handler = new FileHandler(conf);
+        handler.save(testSaveConf.toURI().toURL());
+        checkSavedConfig(testSaveConf);
     }
 
     /**
@@ -1588,36 +1391,243 @@ public class TestXMLConfiguration
     }
 
     /**
-     * Tests whether an attribute can be set to an empty string. This test is
-     * related to CONFIGURATION-446.
+     * Tests string properties with list delimiters when delimiter parsing
+     * is disabled
+     */
+    @Test
+    public void testSaveWithDelimiterParsingDisabled() throws ConfigurationException {
+        conf = new XMLConfiguration();
+        conf.setExpressionEngine(new XPathExpressionEngine());
+        load(conf, testProperties);
+
+        assertEquals("a,b,c", conf.getString("split/list3/@values"));
+        assertEquals(0, conf.getMaxIndex("split/list3/@values"));
+        assertEquals("a\\,b\\,c", conf.getString("split/list4/@values"));
+        assertEquals("a,b,c", conf.getString("split/list1"));
+        assertEquals(0, conf.getMaxIndex("split/list1"));
+        assertEquals("a\\,b\\,c", conf.getString("split/list2"));
+        // save the configuration
+        saveTestConfig();
+
+        XMLConfiguration config = new XMLConfiguration();
+        //config.setExpressionEngine(new XPathExpressionEngine());
+        load(config, testFile2);
+        config.setProperty("Employee[@attr1]", "3,2,1");
+        assertEquals("3,2,1", config.getString("Employee[@attr1]"));
+        new FileHandler(config).save(testSaveFile);
+        config = new XMLConfiguration();
+        //config.setExpressionEngine(new XPathExpressionEngine());
+        load(config, testSaveFile.getAbsolutePath());
+        config.setProperty("Employee[@attr1]", "1,2,3");
+        assertEquals("1,2,3", config.getString("Employee[@attr1]"));
+        config.setProperty("Employee[@attr2]", "one, two, three");
+        assertEquals("one, two, three", config.getString("Employee[@attr2]"));
+        config.setProperty("Employee.text", "a,b,d");
+        assertEquals("a,b,d", config.getString("Employee.text"));
+        config.setProperty("Employee.Salary", "100,000");
+        assertEquals("100,000", config.getString("Employee.Salary"));
+        new FileHandler(config).save(testSaveFile);
+        final XMLConfiguration checkConfig = new XMLConfiguration();
+        checkConfig.setExpressionEngine(new XPathExpressionEngine());
+        load(checkConfig, testSaveFile.getAbsolutePath());
+        assertEquals("1,2,3", checkConfig.getString("Employee/@attr1"));
+        assertEquals("one, two, three", checkConfig.getString("Employee/@attr2"));
+        assertEquals("a,b,d", checkConfig.getString("Employee/text"));
+        assertEquals("100,000", checkConfig.getString("Employee/Salary"));
+    }
+
+    /**
+     * Tests whether the DOCTYPE survives a save operation.
+     */
+    @Test
+    public void testSaveWithDoctype() throws ConfigurationException
+    {
+        conf = new XMLConfiguration();
+        load(conf, "testDtdPublic.xml");
+
+        assertEquals("Wrong public ID", PUBLIC_ID, conf.getPublicID());
+        assertEquals("Wrong system ID", SYSTEM_ID, conf.getSystemID());
+        final StringWriter out = new StringWriter();
+        new FileHandler(conf).save(out);
+        assertThat("Did not find DOCTYPE", out.toString(),
+                containsString(DOCTYPE));
+    }
+
+    /**
+     * Tests setting public and system IDs for the DOCTYPE and then saving the
+     * configuration. This should generate a DOCTYPE declaration.
+     */
+    @Test
+    public void testSaveWithDoctypeIDs() throws ConfigurationException
+    {
+        assertNull("A public ID was found", conf.getPublicID());
+        assertNull("A system ID was found", conf.getSystemID());
+        conf.setPublicID(PUBLIC_ID);
+        conf.setSystemID(SYSTEM_ID);
+        final StringWriter out = new StringWriter();
+        new FileHandler(conf).save(out);
+        assertThat("Did not find DOCTYPE", out.toString(), containsString(
+                DOCTYPE + "testconfig" + DOCTYPE_DECL));
+    }
+
+    /**
+     * Tests whether the encoding is written to the generated XML file.
+     */
+    @Test
+    public void testSaveWithEncoding() throws ConfigurationException
+    {
+        conf = new XMLConfiguration();
+        conf.setProperty("test", "a value");
+        final FileHandler handler = new FileHandler(conf);
+        handler.setEncoding(ENCODING);
+
+        final StringWriter out = new StringWriter();
+        handler.save(out);
+        assertThat("Encoding was not written to file", out.toString(),
+                containsString("encoding=\"" + ENCODING + "\""));
+    }
+
+    /**
+     * Tests saving a configuration if an invalid transformer factory is
+     * specified. In this case an error is thrown by the transformer factory.
+     * XMLConfiguration should not catch this error.
+     */
+    @Test
+    public void testSaveWithInvalidTransformerFactory() throws ConfigurationException {
+        System.setProperty(PROP_FACTORY, "an.invalid.Class");
+        try
+        {
+            saveTestConfig();
+            fail("Could save with invalid TransformerFactory!");
+        }
+        catch (final TransformerFactoryConfigurationError cex)
+        {
+            // ok
+        }
+        finally
+        {
+            System.getProperties().remove(PROP_FACTORY);
+        }
+    }
+
+    /**
+     * Tests whether a default encoding is used if no specific encoding is set.
+     * According to the XSLT specification (http://www.w3.org/TR/xslt#output)
+     * this should be either UTF-8 or UTF-16.
+     */
+    @Test
+    public void testSaveWithNullEncoding() throws ConfigurationException
+    {
+        conf = new XMLConfiguration();
+        conf.setProperty("testNoEncoding", "yes");
+        final FileHandler handler = new FileHandler(conf);
+
+        final StringWriter out = new StringWriter();
+        handler.save(out);
+        assertThat("Encoding was written to file", out.toString(),
+                containsString("encoding=\"UTF-"));
+    }
+
+    @Test
+    public void testSaveWithRootAttributes() throws ConfigurationException
+    {
+        conf.setProperty("[@xmlns:ex]", "http://example.com/");
+        assertEquals("Root attribute not set", "http://example.com/", conf
+                .getString("[@xmlns:ex]"));
+        final FileHandler handler = new FileHandler(conf);
+
+        final StringWriter out = new StringWriter();
+        handler.save(out);
+        assertThat("Encoding was not written to file", out.toString(),
+                containsString("testconfig xmlns:ex=\"http://example.com/\""));
+    }
+
+    @Test
+    public void testSaveWithRootAttributes_ByHand() throws ConfigurationException
+    {
+        conf = new XMLConfiguration();
+        conf.addProperty(  "[@xmlns:foo]",  "http://example.com/" );
+        assertEquals("Root attribute not set", "http://example.com/", conf
+                .getString("[@xmlns:foo]"));
+        final FileHandler handler = new FileHandler(conf);
+
+        final StringWriter out = new StringWriter();
+        handler.save(out);
+        assertThat("Encoding was not written to file", out.toString(),
+                containsString("configuration xmlns:foo=\"http://example.com/\""));
+    }
+
+    /**
+     * Tests modifying an XML document and saving it with schema validation enabled.
      */
     @Test
-    public void testEmptyAttribute() throws ConfigurationException
+    public void testSaveWithValidation() throws Exception
     {
-        final String key = "element3[@value]";
-        conf.setProperty(key, "");
-        assertTrue("Key not found", conf.containsKey(key));
-        assertEquals("Wrong value", "", conf.getString(key));
+        final CatalogResolver resolver = new CatalogResolver();
+        resolver.setCatalogFiles(CATALOG_FILES);
+        conf = new XMLConfiguration();
+        conf.setEntityResolver(resolver);
+        conf.setSchemaValidation(true);
+        load(conf, testFile2);
+        conf.setProperty("Employee.SSN", "123456789");
+        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
+        conf.setSynchronizer(sync);
+        conf.validate();
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
         saveTestConfig();
         conf = new XMLConfiguration();
         load(conf, testSaveConf.getAbsolutePath());
-        assertTrue("Key not found after save", conf.containsKey(key));
-        assertEquals("Wrong value after save", "", conf.getString(key));
+        assertEquals("123456789", conf.getString("Employee.SSN"));
     }
 
     /**
-     * Tests whether it is possible to add nodes to a XMLConfiguration through a
-     * SubnodeConfiguration and whether these nodes have the correct type. This
-     * test is related to CONFIGURATION-472.
+     * Tests modifying an XML document and validating it against the schema.
      */
     @Test
-    public void testAddNodesToSubnodeConfiguration() throws Exception
+    public void testSaveWithValidationFailure() throws Exception
     {
-        final HierarchicalConfiguration<ImmutableNode> sub =
-                conf.configurationAt("element2", true);
-        sub.addProperty("newKey", "newvalue");
-        assertEquals("Property not added", "newvalue",
-                conf.getString("element2.newKey"));
+        final CatalogResolver resolver = new CatalogResolver();
+        resolver.setCatalogFiles(CATALOG_FILES);
+        conf = new XMLConfiguration();
+        conf.setEntityResolver(resolver);
+        conf.setSchemaValidation(true);
+        load(conf, testFile2);
+        conf.setProperty("Employee.Email", "JohnDoe@apache.org");
+        try
+        {
+            conf.validate();
+            fail("No validation failure on save");
+        }
+        catch (final Exception e)
+        {
+            final Throwable cause = e.getCause();
+            assertNotNull("No cause for exception on save", cause);
+            assertTrue("Incorrect exception on save", cause instanceof SAXParseException);
+        }
+    }
+
+    @Test
+    public void testSetAttribute()
+    {
+        // replace an existing attribute
+        conf.setProperty("element3[@name]", "bar");
+        assertEquals("element3[@name]", "bar", conf.getProperty("element3[@name]"));
+
+        // set a new attribute
+        conf.setProperty("foo[@bar]", "value");
+        assertEquals("foo[@bar]", "value", conf.getProperty("foo[@bar]"));
+
+        conf.setProperty("name1", "value1");
+        assertEquals("value1", conf.getProperty("name1"));
+    }
+
+    @Test
+    public void testSetProperty() throws Exception
+    {
+        conf.setProperty("element.string", "hello");
+
+        assertEquals("'element.string'", "hello", conf.getString("element.string"));
+        assertEquals("XML value of element.string", "hello", conf.getProperty("element.string"));
     }
 
     /**
@@ -1638,143 +1648,133 @@ public class TestXMLConfiguration
     }
 
     /**
-     * Tests whether list properties are added correctly if delimiter parsing is
-     * disabled. This test is related to CONFIGURATION-495.
+     * Tests setting an attribute on the root element.
      */
     @Test
-    public void testAddPropertyListWithDelimiterParsingDisabled()
-            throws ConfigurationException
+    public void testSetRootAttribute() throws ConfigurationException
     {
-        conf.clear();
-        final String prop = "delimiterListProp";
-        conf.setListDelimiterHandler(DisabledListDelimiterHandler.INSTANCE);
-        final List<String> list = Arrays.asList("val", "val2", "val3");
-        conf.addProperty(prop, list);
+        conf.setProperty("[@test]", "true");
+        assertEquals("Root attribute not set", "true", conf
+                .getString("[@test]"));
         saveTestConfig();
-        final XMLConfiguration conf2 = new XMLConfiguration();
-        load(conf2, testSaveConf.getAbsolutePath());
-        assertEquals("Wrong list property", list, conf2.getProperty(prop));
+        XMLConfiguration checkConf = checkSavedConfig();
+        assertTrue("Attribute not found after save", checkConf
+                .containsKey("[@test]"));
+        checkConf.setProperty("[@test]", "newValue");
+        conf = checkConf;
+        saveTestConfig();
+        checkConf = checkSavedConfig();
+        assertEquals("Attribute not modified after save", "newValue", checkConf
+                .getString("[@test]"));
     }
 
-    /**
-     * Tests whether the system ID is accessed in a synchronized manner.
-     */
     @Test
-    public void testSystemIdSynchronized()
+    public void testSetRootNamespace() throws ConfigurationException
     {
-        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
-        conf.setSynchronizer(sync);
-        conf.setSystemID(SYSTEM_ID);
-        assertEquals("SystemID not set", SYSTEM_ID, conf.getSystemID());
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE, Methods.BEGIN_READ,
-                Methods.END_READ);
+        conf.addProperty(  "[@xmlns:foo]",  "http://example.com/" );
+        conf.addProperty(  "foo:bar", "foobar" );
+        assertEquals("Root attribute not set", "http://example.com/", conf
+                .getString("[@xmlns:foo]"));
+        saveTestConfig();
+        final XMLConfiguration checkConf = checkSavedConfig();
+        assertTrue("Attribute not found after save", checkConf
+                .containsKey("[@xmlns:foo]"));
+        checkConf.setProperty("[@xmlns:foo]", "http://example.net/");
     }
 
     /**
-     * Tests whether the public ID is accessed in a synchronized manner.
+     * Tests setting text of the root element.
      */
     @Test
-    public void testPublicIdSynchronized()
+    public void testSetTextRootElement() throws ConfigurationException
     {
-        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
-        conf.setSynchronizer(sync);
-        conf.setPublicID(PUBLIC_ID);
-        assertEquals("PublicID not set", PUBLIC_ID, conf.getPublicID());
-        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE, Methods.BEGIN_READ,
-                Methods.END_READ);
+        conf.setProperty("", "Root text");
+        saveTestConfig();
+        checkSavedConfig();
     }
 
     /**
-     * Tests a direct invocation of the read() method. This is not allowed
-     * because certain initializations have not been done. This test is
-     * related to CONFIGURATION-641.
+     * Tests string properties with list delimiters and escaped delimiters.
      */
     @Test
-    public void testReadCalledDirectly() throws IOException
+    public void testSplitLists()
     {
-        conf = new XMLConfiguration();
-        final String content = "<configuration><test>1</test></configuration>";
-        final ByteArrayInputStream bis = new ByteArrayInputStream(content.getBytes());
-        try
-        {
-            conf.read(bis);
-            fail("No exception thrown!");
-        }
-        catch (final ConfigurationException e)
-        {
-            assertThat(e.getMessage(), containsString("FileHandler"));
-        }
+        assertEquals("a,b,c", conf.getString("split.list3[@values]"));
+        assertEquals(0, conf.getMaxIndex("split.list3[@values]"));
+        assertEquals("a\\,b\\,c", conf.getString("split.list4[@values]"));
+        assertEquals("a", conf.getString("split.list1"));
+        assertEquals(2, conf.getMaxIndex("split.list1"));
+        assertEquals("a,b,c", conf.getString("split.list2"));
     }
 
     /**
-     * Removes the test output file if it exists.
+     * Tests the subset() method. There was a bug that calling subset() had
+     * undesired side effects.
      */
-    private void removeTestFile()
+    @Test
+    public void testSubset() throws ConfigurationException
     {
-        if (testSaveConf.exists())
-        {
-            assertTrue(testSaveConf.delete());
-        }
+        conf = new XMLConfiguration();
+        load(conf, "testHierarchicalXMLConfiguration.xml");
+        conf.subset("tables.table(0)");
+        saveTestConfig();
+
+        conf = new XMLConfiguration();
+        load(conf, "testHierarchicalXMLConfiguration.xml");
+        assertEquals("users", conf.getString("tables.table(0).name"));
     }
 
     /**
-     * Helper method for saving the test configuration to the default output
-     * file.
-     *
-     * @throws ConfigurationException if an error occurs
+     * Tests whether the system ID is accessed in a synchronized manner.
      */
-    private void saveTestConfig() throws ConfigurationException
+    @Test
+    public void testSystemIdSynchronized()
     {
-        final FileHandler handler = new FileHandler(conf);
-        handler.save(testSaveConf);
+        final SynchronizerTestImpl sync = new SynchronizerTestImpl();
+        conf.setSynchronizer(sync);
+        conf.setSystemID(SYSTEM_ID);
+        assertEquals("SystemID not set", SYSTEM_ID, conf.getSystemID());
+        sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE, Methods.BEGIN_READ,
+                Methods.END_READ);
     }
 
     /**
-     * Tests whether the saved configuration file matches the original data.
-     *
-     * @param saveFile the saved configuration file
-     * @return the newly loaded configuration
-     * @throws ConfigurationException if an error occurs
+     * Tests DTD validation using the setValidating() method.
      */
-    private XMLConfiguration checkSavedConfig(final File saveFile)
-            throws ConfigurationException
+    @Test
+    public void testValidating() throws ConfigurationException
     {
-        final XMLConfiguration config = createFromFile(saveFile.getAbsolutePath());
-        ConfigurationAssert.assertConfigurationEquals(conf, config);
-        return config;
+        final File nonValidFile = ConfigurationAssert.getTestFile("testValidateInvalid.xml");
+        conf = new XMLConfiguration();
+        assertFalse(conf.isValidating());
+
+        // Load a non valid XML document. Should work for isValidating() == false
+        load(conf, nonValidFile.getAbsolutePath());
+        assertEquals("customers", conf.getString("table.name"));
+        assertFalse(conf.containsKey("table.fields.field(1).type"));
     }
 
     /**
-     * Helper method for testing whether a configuration was correctly saved to
-     * the default output file.
-     *
-     * @return the newly loaded configuration
-     * @throws ConfigurationException if an error occurs
+     * Tests whether an invalid file is detected when validating is enabled.
      */
-    private XMLConfiguration checkSavedConfig() throws ConfigurationException
+    @Test(expected = ConfigurationException.class)
+    public void testValidatingInvalidFile() throws ConfigurationException
     {
-        return checkSavedConfig(testSaveConf);
+        conf = new XMLConfiguration();
+        conf.setValidating(true);
+        load(conf, "testValidateInvalid.xml");
     }
 
     /**
-     * A thread used for testing concurrent access to a builder.
+     * Tests accessing properties when the XPATH expression engine is set.
      */
-    private class ReloadThread extends Thread
+    @Test
+    public void testXPathExpressionEngine()
     {
-        private final FileBasedConfigurationBuilder<?> builder;
-
-        ReloadThread(final FileBasedConfigurationBuilder<?> confBulder)
-        {
-            builder = confBulder;
-        }
-
-        @Override
-        public void run()
-        {
-            for (int i = 0; i < LOOP_COUNT; i++)
-            {
-                builder.resetResult();
-            }
-        }
+        conf.setExpressionEngine(new XPathExpressionEngine());
+        assertEquals("Wrong attribute value", "foo\"bar", conf
+                .getString("test[1]/entity/@name"));
+        conf.clear();
+        assertNull(conf.getString("test[1]/entity/@name"));
     }
 }