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

commit 20d6a5dbf68a9b4835d7dc20bbde053449fc651f
Author: Gary Gregory <[email protected]>
AuthorDate: Thu Apr 2 08:08:34 2026 -0400

    Add XMLConfiguration.read(Element)
    
    Add ConfigurationException.ConfigurationException(Throwable, String,
    Object...)
---
 src/changes/changes.xml                            |  2 ++
 .../configuration2/PropertiesConfiguration.java    |  2 +-
 .../commons/configuration2/XMLConfiguration.java   | 39 ++++++++++++++++------
 .../configuration2/XMLPropertiesConfiguration.java |  3 --
 .../combined/CombinedConfigurationBuilder.java     |  4 +--
 .../configuration2/ex/ConfigurationException.java  | 16 ++++++++-
 .../configuration2/io/DefaultFileSystem.java       |  6 ++--
 .../commons/configuration2/io/FileHandler.java     | 12 ++-----
 .../commons/configuration2/io/VFSFileSystem.java   |  8 ++---
 .../plist/XMLPropertyListConfiguration.java        |  3 --
 .../configuration2/TestXMLConfiguration.java       | 35 +++++++++++++++----
 11 files changed, 88 insertions(+), 42 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 9e4eee234..34dd097b0 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -27,7 +27,9 @@
       <!-- FIX -->
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix Apache RAT 
plugin console warnings.</action>
       <!-- ADD -->
+      <action type="fix" dev="ggregory" due-to="Gary Gregory">Add 
XMLConfiguration.read(Element).</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Add 
ConfigurationException.ConfigurationException(String, Object...).</action>
+      <action type="fix" dev="ggregory" due-to="Gary Gregory">Add 
ConfigurationException.ConfigurationException(Throwable, String, 
Object...).</action>
       <!-- UPDATE -->
       <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
org.apache.commons:commons-parent from 92 to 97.</action>
       <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
org.apache.commons:commons-text from 1.14.0 to 1.15.0.</action>
diff --git 
a/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java 
b/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java
index 19f0aab20..68a39e42b 100644
--- 
a/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java
+++ 
b/src/main/java/org/apache/commons/configuration2/PropertiesConfiguration.java
@@ -1405,7 +1405,7 @@ public class PropertiesConfiguration extends 
BaseConfiguration implements FileBa
         }
 
         if (url == null) {
-            getIncludeListener().accept(new ConfigurationException("Cannot 
resolve include file " + fileName, new FileNotFoundException(fileName)));
+            getIncludeListener().accept(new ConfigurationException(new 
FileNotFoundException(fileName), "Cannot resolve include file %s", fileName));
         } else {
             final FileHandler fh = new FileHandler(this);
             fh.setFileLocator(locator);
diff --git 
a/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java 
b/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java
index 60ae716ca..3f621c061 100644
--- a/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java
+++ b/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java
@@ -476,10 +476,9 @@ public class XMLConfiguration extends 
BaseHierarchicalConfiguration implements F
      * @param element the current XML element
      * @return a map with all attribute values extracted for the current node
      */
-    private static Map<String, String> processAttributes(final Element 
element) {
+    private static Map<String, String> processAttributes(final Node element) {
         final NamedNodeMap attributes = element.getAttributes();
         final Map<String, String> attrmap = new HashMap<>();
-
         for (int i = 0; i < attributes.getLength(); ++i) {
             final Node w3cNode = attributes.item(i);
             if (w3cNode instanceof Attr) {
@@ -487,7 +486,6 @@ public class XMLConfiguration extends 
BaseHierarchicalConfiguration implements F
                 attrmap.put(attr.getName(), attr.getValue());
             }
         }
-
         return attrmap;
     }
 
@@ -503,7 +501,6 @@ public class XMLConfiguration extends 
BaseHierarchicalConfiguration implements F
      */
     private static boolean shouldTrim(final Element element, final boolean 
currentTrim) {
         final Attr attr = element.getAttributeNode(ATTR_SPACE);
-
         if (attr == null) {
             return currentTrim;
         }
@@ -850,21 +847,23 @@ public class XMLConfiguration extends 
BaseHierarchicalConfiguration implements F
     /**
      * Initializes this configuration from an XML document.
      *
-     * @param docHelper the helper object with the document to be parsed
-     * @param elemRefs a flag whether references to the XML elements should be 
set
+     * @param docHelper the helper object with the document to be parsed.
+     * @param elemRefs a flag whether references to the XML elements should be 
set.
      */
     private void initProperties(final XMLDocumentHelper docHelper, final 
boolean elemRefs) {
-        final Document document = docHelper.getDocument();
         setPublicID(docHelper.getSourcePublicID());
         setSystemID(docHelper.getSourceSystemID());
+        initProperties(docHelper, elemRefs, 
docHelper.getDocument().getDocumentElement());
+    }
 
+    private void initProperties(final XMLDocumentHelper docHelper, final 
boolean elemRefs, final Element element) {
         final ImmutableNode.Builder rootBuilder = new ImmutableNode.Builder();
         final MutableObject<String> rootValue = new MutableObject<>();
         final Map<ImmutableNode, Object> elemRefMap = elemRefs ? new 
HashMap<>() : null;
-        final Map<String, String> attributes = constructHierarchy(rootBuilder, 
rootValue, document.getDocumentElement(), elemRefMap, true, 0);
+        final Map<String, String> attributes = constructHierarchy(rootBuilder, 
rootValue, element, elemRefMap, true, 0);
         attributes.remove(ATTR_SPACE_INTERNAL);
         final ImmutableNode top = 
rootBuilder.value(rootValue.getValue()).addAttributes(attributes).create();
-        getSubConfigurationParentModel().mergeRoot(top, 
document.getDocumentElement().getTagName(), elemRefMap, elemRefs ? docHelper : 
null, this);
+        getSubConfigurationParentModel().mergeRoot(top, element.getTagName(), 
elemRefMap, elemRefs ? docHelper : null, this);
     }
 
     /**
@@ -934,13 +933,33 @@ public class XMLConfiguration extends 
BaseHierarchicalConfiguration implements F
             final Document oldDocument = getDocument();
             initProperties(XMLDocumentHelper.forSourceDocument(newDocument), 
oldDocument == null);
         } catch (final SAXParseException spe) {
-            throw new ConfigurationException("Error parsing " + 
source.getSystemId(), spe);
+            throw new ConfigurationException(spe, "Error parsing system ID 
%s", source.getSystemId());
         } catch (final Exception e) {
             getLogger().debug("Unable to load the configuration: " + e);
             throw new ConfigurationException("Unable to load the 
configuration", e);
         }
     }
 
+    /**
+     * Loads the configuration from the given XML DOM Element.
+     * <p>
+     * This method can be used to initialize the configuration from an XML 
element in a document that has already been parsed. This is especially useful 
if the
+     * configuration is only a part of a larger XML document.
+     * </p>
+     *
+     * @param element the input element.
+     * @throws ConfigurationException if an error occurs.
+     * @since 2.14.0
+     */
+    public void read(final Element element) throws ConfigurationException {
+        try {
+            initProperties(getDocumentHelper(), getDocument() == null, 
element);
+        } catch (final Exception e) {
+            getLogger().debug("Unable to load the configuration: " + e);
+            throw new ConfigurationException(e, "Unable to load the 
configuration %s", element);
+        }
+    }
+
     /**
      * Loads the configuration from the given input stream. This is analogous 
to {@link #read(Reader)}, but data is read
      * from a stream. Note that this method will be called most time when 
reading an XML configuration source. By reading
diff --git 
a/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
 
b/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
index e3ff6a88c..daa7b22af 100644
--- 
a/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
+++ 
b/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
@@ -224,10 +224,8 @@ public class XMLPropertiesConfiguration extends 
BaseConfiguration implements Fil
         final SAXParserFactory factory = SAXParserFactory.newInstance();
         factory.setNamespaceAware(false);
         factory.setValidating(true);
-
         try {
             final SAXParser parser = factory.newSAXParser();
-
             final XMLReader xmlReader = parser.getXMLReader();
             xmlReader.setEntityResolver((publicId, systemId) -> new 
InputSource(getClass().getClassLoader().getResourceAsStream("properties.dtd")));
             xmlReader.setContentHandler(new XMLPropertiesHandler());
@@ -235,7 +233,6 @@ public class XMLPropertiesConfiguration extends 
BaseConfiguration implements Fil
         } catch (final Exception e) {
             throw new ConfigurationException("Unable to parse the 
configuration file", e);
         }
-
         // todo: support included properties ?
     }
 
diff --git 
a/src/main/java/org/apache/commons/configuration2/builder/combined/CombinedConfigurationBuilder.java
 
b/src/main/java/org/apache/commons/configuration2/builder/combined/CombinedConfigurationBuilder.java
index bd1b9b90a..fb924edfb 100644
--- 
a/src/main/java/org/apache/commons/configuration2/builder/combined/CombinedConfigurationBuilder.java
+++ 
b/src/main/java/org/apache/commons/configuration2/builder/combined/CombinedConfigurationBuilder.java
@@ -1143,8 +1143,8 @@ public class CombinedConfigurationBuilder extends 
BasicConfigurationBuilder<Comb
         if (fileName != null) {
             try {
                 SystemConfiguration.setSystemProperties(basePath, fileName);
-            } catch (final Exception ex) {
-                throw new ConfigurationException("Error setting system 
properties from " + fileName, ex);
+            } catch (final Exception e) {
+                throw new ConfigurationException(e, "Error setting system 
properties from %s (basePath = %s)", fileName, basePath);
             }
         }
     }
diff --git 
a/src/main/java/org/apache/commons/configuration2/ex/ConfigurationException.java
 
b/src/main/java/org/apache/commons/configuration2/ex/ConfigurationException.java
index b6fae7284..617b915c0 100644
--- 
a/src/main/java/org/apache/commons/configuration2/ex/ConfigurationException.java
+++ 
b/src/main/java/org/apache/commons/configuration2/ex/ConfigurationException.java
@@ -57,7 +57,8 @@ public class ConfigurationException extends Exception {
      * Constructs a new {@code ConfigurationException} with specified detail 
message and nested {@code Throwable}.
      *
      * @param message the error message
-     * @param cause the exception or error that caused this exception to be 
thrown
+     * @param cause   the cause (which is saved for later retrieval by the 
{@link #getCause()} method). (A {@code null} value is permitted, and indicates 
that
+     *                the cause is nonexistent or unknown.)
      */
     public ConfigurationException(final String message, final Throwable cause) 
{
         super(message, cause);
@@ -71,4 +72,17 @@ public class ConfigurationException extends Exception {
     public ConfigurationException(final Throwable cause) {
         super(cause);
     }
+
+    /**
+     * Constructs a new {@code ConfigurationException} with specified detail 
message.
+     *
+     * @param format the error message for for {@link String#format(String, 
Object...)}.
+     * @param params the error parameters for for {@link String#format(String, 
Object...)}.
+     * @param cause  the cause (which is saved for later retrieval by the 
{@link #getCause()} method). (A {@code null} value is permitted, and indicates 
that
+     *               the cause is nonexistent or unknown.)
+     * @since 2.14.0
+     */
+    public ConfigurationException(final Throwable cause, final String format, 
Object... params) {
+        super(String.format(format, params), cause);
+    }
 }
diff --git 
a/src/main/java/org/apache/commons/configuration2/io/DefaultFileSystem.java 
b/src/main/java/org/apache/commons/configuration2/io/DefaultFileSystem.java
index bbfe4125b..06f27891c 100644
--- a/src/main/java/org/apache/commons/configuration2/io/DefaultFileSystem.java
+++ b/src/main/java/org/apache/commons/configuration2/io/DefaultFileSystem.java
@@ -153,7 +153,7 @@ public class DefaultFileSystem extends FileSystem {
         try {
             return urlConnectionOptions == null ? url.openStream() : 
urlConnectionOptions.openConnection(url).getInputStream();
         } catch (final Exception e) {
-            throw new ConfigurationException("Unable to load the configuration 
from the URL " + url, e);
+            throw new ConfigurationException(e, "Unable to load the 
configuration from the URL %s", url);
         }
     }
 
@@ -164,7 +164,7 @@ public class DefaultFileSystem extends FileSystem {
             createPath(file);
             return new FileOutputStream(file);
         } catch (final FileNotFoundException e) {
-            throw new ConfigurationException("Unable to save to file " + file, 
e);
+            throw new ConfigurationException(e, "Unable to save to file %s", 
file);
         }
     }
 
@@ -196,7 +196,7 @@ public class DefaultFileSystem extends FileSystem {
             }
             return out;
         } catch (final IOException e) {
-            throw new ConfigurationException("Could not save to URL " + url, 
e);
+            throw new ConfigurationException(e, "Could not save to URL %s", 
url);
         }
     }
 
diff --git 
a/src/main/java/org/apache/commons/configuration2/io/FileHandler.java 
b/src/main/java/org/apache/commons/configuration2/io/FileHandler.java
index 970a35abc..45575b147 100644
--- a/src/main/java/org/apache/commons/configuration2/io/FileHandler.java
+++ b/src/main/java/org/apache/commons/configuration2/io/FileHandler.java
@@ -699,7 +699,7 @@ public class FileHandler {
         } catch (final ConfigurationException e) {
             throw e;
         } catch (final Exception e) {
-            throw new ConfigurationException("Unable to load the configuration 
from the URL " + url, e);
+            throw new ConfigurationException(e, "Unable to load the 
configuration from the URL ", url);
         } finally {
             closeSilent(in);
         }
@@ -771,19 +771,16 @@ public class FileHandler {
      */
     private void loadFromTransformedStream(final InputStream in, final String 
encoding) throws ConfigurationException {
         Reader reader = null;
-
         if (encoding != null) {
             try {
                 reader = new InputStreamReader(in, encoding);
             } catch (final UnsupportedEncodingException e) {
-                throw new ConfigurationException("The requested encoding is 
not supported, try the default encoding.", e);
+                throw new ConfigurationException(e, "The requested encoding %s 
is not supported, try the default encoding.", encoding);
             }
         }
-
         if (reader == null) {
             reader = new InputStreamReader(in);
         }
-
         loadFromReader(reader);
     }
 
@@ -1031,19 +1028,16 @@ public class FileHandler {
         try {
             injectFileLocator(url);
             Writer writer = null;
-
             if (encoding != null) {
                 try {
                     writer = new OutputStreamWriter(out, encoding);
                 } catch (final UnsupportedEncodingException e) {
-                    throw new ConfigurationException("The requested encoding 
is not supported, try the default encoding.", e);
+                    throw new ConfigurationException(e, "The requested 
encoding %s is not supported, try the default encoding.", encoding);
                 }
             }
-
             if (writer == null) {
                 writer = new OutputStreamWriter(out);
             }
-
             saveToWriter(writer);
         } finally {
             syncSupport.unlock(LockMode.WRITE);
diff --git 
a/src/main/java/org/apache/commons/configuration2/io/VFSFileSystem.java 
b/src/main/java/org/apache/commons/configuration2/io/VFSFileSystem.java
index 061e6b524..2a13459ae 100644
--- a/src/main/java/org/apache/commons/configuration2/io/VFSFileSystem.java
+++ b/src/main/java/org/apache/commons/configuration2/io/VFSFileSystem.java
@@ -110,8 +110,8 @@ public class VFSFileSystem extends DefaultFileSystem {
                 throw new ConfigurationException("Cannot access content of 
%s", file.getName().getFriendlyURI());
             }
             return content.getInputStream();
-        } catch (final FileSystemException fse) {
-            throw new ConfigurationException("Unable to access " + 
url.toString(), fse);
+        } catch (final FileSystemException e) {
+            throw new ConfigurationException(e, "Unable to access %s", url);
         }
     }
 
@@ -173,8 +173,8 @@ public class VFSFileSystem extends DefaultFileSystem {
                 throw new ConfigurationException("Cannot access content of 
%s", url);
             }
             return content.getOutputStream();
-        } catch (final FileSystemException fse) {
-            throw new ConfigurationException("Unable to access " + url, fse);
+        } catch (final FileSystemException e) {
+            throw new ConfigurationException(e, "Unable to access ", url);
         }
     }
 
diff --git 
a/src/main/java/org/apache/commons/configuration2/plist/XMLPropertyListConfiguration.java
 
b/src/main/java/org/apache/commons/configuration2/plist/XMLPropertyListConfiguration.java
index c7621d472..1eb539e39 100644
--- 
a/src/main/java/org/apache/commons/configuration2/plist/XMLPropertyListConfiguration.java
+++ 
b/src/main/java/org/apache/commons/configuration2/plist/XMLPropertyListConfiguration.java
@@ -648,18 +648,15 @@ public class XMLPropertyListConfiguration extends 
BaseHierarchicalConfiguration
     public void read(final Reader in) throws ConfigurationException {
         // set up the DTD validation
         final EntityResolver resolver = (publicId, systemId) -> new 
InputSource(getClass().getClassLoader().getResourceAsStream("PropertyList-1.0.dtd"));
-
         // parse the file
         final XMLPropertyListHandler handler = new XMLPropertyListHandler();
         try {
             final SAXParserFactory factory = SAXParserFactory.newInstance();
             factory.setValidating(true);
-
             final SAXParser parser = factory.newSAXParser();
             parser.getXMLReader().setEntityResolver(resolver);
             parser.getXMLReader().setContentHandler(handler);
             parser.getXMLReader().parse(new InputSource(in));
-
             getNodeModel().mergeRoot(handler.getResultBuilder().createNode(), 
null, null, null, this);
         } catch (final Exception e) {
             throw new ConfigurationException("Unable to parse the 
configuration file", e);
diff --git 
a/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java 
b/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java
index e5d179c66..c903574bc 100644
--- a/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java
@@ -68,6 +68,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
@@ -177,6 +178,18 @@ public class TestXMLConfiguration {
 
     private XMLConfiguration conf;
 
+    private Element buildDomElementFixture() throws SAXException, IOException, 
ParserConfigurationException {
+        return (Element) buildDomNodeFixture();
+    }
+
+    private Node buildDomNodeFixture() throws SAXException, IOException, 
ParserConfigurationException {
+        final String content = "<configuration><test 
attr=\"x\">1</test></configuration>";
+        final Node document = 
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new 
ByteArrayInputStream(content.getBytes()));
+        final Node node = document.getFirstChild().getFirstChild(); // <test>
+        assertEquals("test", node.getNodeName()); // sanity check
+        return node;
+    }
+
     /**
      * Helper method for testing whether a configuration was correctly saved 
to the default output file.
      *
@@ -1080,14 +1093,24 @@ public class TestXMLConfiguration {
      * Tests how to read from a DOM Node.
      */
     @Test
-    void testReadDomNode() throws Exception {
+    void testReadDomElementManually() throws Exception {
          conf = new XMLConfiguration();
-         final String content = "<configuration><test 
attr=\"x\">1</test></configuration>";
-         final Node document = 
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new 
ByteArrayInputStream(content.getBytes()));
-         final Node node = document.getFirstChild().getFirstChild(); // <test>
+         final Element domElement = buildDomElementFixture();
          conf.initFileLocator(FileLocatorUtils.fileLocator().create());
-         // Read from a DOM Node
-         conf.read(new ByteArrayInputStream(nodeToByteArray(node)));
+         // Read from a DOM Element
+         conf.read(new ByteArrayInputStream(nodeToByteArray(domElement)));
+         assertEquals("x", conf.getString("[@attr]"));
+    }
+
+    /**
+     * Tests how to read from a DOM Node.
+     */
+    @Test
+    void testReadDomElement() throws Exception {
+         conf = new XMLConfiguration();
+         final Element domElement = buildDomElementFixture();
+         // Read from a DOM Element
+         conf.read(domElement);
          assertEquals("x", conf.getString("[@attr]"));
     }
 

Reply via email to