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]")); }
