Merge branch 'release'
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 1fae428..ed98e60 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -23,6 +23,12 @@
<author email="dev@commons.apache.org">Apache Commons Community</author>
</properties>
<body>
+ <release version="2.10.2" date="YYYY-MM-DD" description="Minor release with new features and updated dependencies; requires Java 8 or above.">
+ <!-- FIX -->
+ <action type="fix" dev="ggregory" due-to="Gary Gregory">Fail-fast with a NullPointerException if DataConfiguration.DataConfiguration(Configuration) is called with null.</action>
+ <action type="fix" dev="ggregory" due-to="Gary Gregory">Fail-fast with a NullPointerException if XMLPropertiesConfiguration.XMLPropertiesConfiguration(Element) is called with null.</action>
+ <action type="fix" dev="ggregory" due-to="Gary Gregory">Fail-fast with a NullPointerException if a SubsetConfiguration constructor is called with a null Configuration.</action>
+ </release>
<release version="2.10.1" date="2024-03-17" description="Minor release with new features and updated dependencies; requires Java 8 or above.">
<!-- FIX -->
<action type="fix" issue="CONFIGURATION-839" dev="ggregory" due-to="Bob Marinier, Gary Gregory">java.lang.module.FindException: Module servlet.api not found.</action>
diff --git a/src/main/java/org/apache/commons/configuration2/CompositeConfiguration.java b/src/main/java/org/apache/commons/configuration2/CompositeConfiguration.java
index 129aff7..53beae5 100644
--- a/src/main/java/org/apache/commons/configuration2/CompositeConfiguration.java
+++ b/src/main/java/org/apache/commons/configuration2/CompositeConfiguration.java
@@ -60,6 +60,7 @@
* </p>
*/
public class CompositeConfiguration extends AbstractConfiguration implements Cloneable {
+
/** List holding all the configuration */
private List<Configuration> configList = new LinkedList<>();
@@ -89,9 +90,9 @@
* @param inMemoryConfiguration the in memory configuration to use
*/
public CompositeConfiguration(final Configuration inMemoryConfiguration) {
- configList.clear();
+ this.configList.clear();
this.inMemoryConfiguration = inMemoryConfiguration;
- configList.add(inMemoryConfiguration);
+ this.configList.add(inMemoryConfiguration);
}
/**
diff --git a/src/main/java/org/apache/commons/configuration2/DataConfiguration.java b/src/main/java/org/apache/commons/configuration2/DataConfiguration.java
index 574256f..9960971 100644
--- a/src/main/java/org/apache/commons/configuration2/DataConfiguration.java
+++ b/src/main/java/org/apache/commons/configuration2/DataConfiguration.java
@@ -29,6 +29,8 @@
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.function.Supplier;
import org.apache.commons.configuration2.convert.ConversionHandler;
import org.apache.commons.configuration2.convert.DefaultConversionHandler;
@@ -145,8 +147,8 @@
* @param configuration the wrapped configuration
*/
public DataConfiguration(final Configuration configuration) {
- this.configuration = configuration;
- dataConversionHandler = new DataConversionHandler();
+ this.configuration = Objects.requireNonNull(configuration);
+ this.dataConversionHandler = new DataConversionHandler();
}
/**
@@ -195,6 +197,15 @@
return configuration.containsKey(key);
}
+ private <R> R applyTempDateFormat(final String format, final Supplier<R> supplier) {
+ TEMP_DATE_FORMAT.set(format);
+ try {
+ return supplier.get();
+ } finally {
+ TEMP_DATE_FORMAT.remove();
+ }
+ }
+
@Override
protected void clearPropertyDirect(final String key) {
configuration.clearProperty(key);
@@ -918,12 +929,7 @@
* @throws ConversionException is thrown if the key maps to an object that is not a Date.
*/
public Date getDate(final String key, final Date defaultValue, final String format) {
- TEMP_DATE_FORMAT.set(format);
- try {
- return get(Date.class, key, defaultValue);
- } finally {
- TEMP_DATE_FORMAT.remove();
- }
+ return applyTempDateFormat(format, () -> get(Date.class, key, defaultValue));
}
public List<Date> getDateList(final String key) {
@@ -972,12 +978,7 @@
* @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
*/
public List<Date> getDateList(final String key, final List<Date> defaultValue, final String format) {
- TEMP_DATE_FORMAT.set(format);
- try {
- return getList(Date.class, key, defaultValue);
- } finally {
- TEMP_DATE_FORMAT.remove();
- }
+ return applyTempDateFormat(format, () -> getList(Date.class, key, defaultValue));
}
/**
@@ -1036,12 +1037,7 @@
* @throws ConversionException is thrown if the key maps to an object that is not a list of Dates.
*/
public Date[] getDateArray(final String key, final Date[] defaultValue, final String format) {
- TEMP_DATE_FORMAT.set(format);
- try {
- return get(Date[].class, key, defaultValue);
- } finally {
- TEMP_DATE_FORMAT.remove();
- }
+ return applyTempDateFormat(format, () -> get(Date[].class, key, defaultValue));
}
/**
@@ -1106,12 +1102,7 @@
* @throws ConversionException is thrown if the key maps to an object that is not a Calendar.
*/
public Calendar getCalendar(final String key, final Calendar defaultValue, final String format) {
- TEMP_DATE_FORMAT.set(format);
- try {
- return get(Calendar.class, key, defaultValue);
- } finally {
- TEMP_DATE_FORMAT.remove();
- }
+ return applyTempDateFormat(format, () -> get(Calendar.class, key, defaultValue));
}
/**
@@ -1171,12 +1162,7 @@
* @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
*/
public List<Calendar> getCalendarList(final String key, final List<Calendar> defaultValue, final String format) {
- TEMP_DATE_FORMAT.set(format);
- try {
- return getList(Calendar.class, key, defaultValue);
- } finally {
- TEMP_DATE_FORMAT.remove();
- }
+ return applyTempDateFormat(format, () -> getList(Calendar.class, key, defaultValue));
}
/**
@@ -1238,12 +1224,7 @@
* @throws ConversionException is thrown if the key maps to an object that is not a list of Calendars.
*/
public Calendar[] getCalendarArray(final String key, final Calendar[] defaultValue, final String format) {
- TEMP_DATE_FORMAT.set(format);
- try {
- return get(Calendar[].class, key, defaultValue);
- } finally {
- TEMP_DATE_FORMAT.remove();
- }
+ return applyTempDateFormat(format, () -> get(Calendar[].class, key, defaultValue));
}
/**
diff --git a/src/main/java/org/apache/commons/configuration2/MapConfiguration.java b/src/main/java/org/apache/commons/configuration2/MapConfiguration.java
index c42591e..4319bdf 100644
--- a/src/main/java/org/apache/commons/configuration2/MapConfiguration.java
+++ b/src/main/java/org/apache/commons/configuration2/MapConfiguration.java
@@ -100,7 +100,7 @@
* @since 1.8
*/
public MapConfiguration(final Properties props) {
- map = convertPropertiesToMap(props);
+ map = toMap(props);
}
/**
@@ -220,7 +220,7 @@
* @return a newly created map with all string keys of the properties
*/
@SuppressWarnings("unchecked")
- private static Map<String, Object> convertPropertiesToMap(final Properties props) {
+ private static Map<String, Object> toMap(final Properties props) {
@SuppressWarnings("rawtypes")
final Map map = props;
return map;
diff --git a/src/main/java/org/apache/commons/configuration2/SubsetConfiguration.java b/src/main/java/org/apache/commons/configuration2/SubsetConfiguration.java
index fd87053..5bc63db 100644
--- a/src/main/java/org/apache/commons/configuration2/SubsetConfiguration.java
+++ b/src/main/java/org/apache/commons/configuration2/SubsetConfiguration.java
@@ -18,6 +18,7 @@
package org.apache.commons.configuration2;
import java.util.Iterator;
+import java.util.Objects;
import org.apache.commons.configuration2.convert.ListDelimiterHandler;
import org.apache.commons.lang3.StringUtils;
@@ -59,14 +60,10 @@
* @param parent The parent configuration (must not be <b>null</b>)
* @param prefix The prefix used to select the properties
* @param delimiter The prefix delimiter
- * @throws IllegalArgumentException if the parent configuration is <b>null</b>
+ * @throws NullPointerException if the parent configuration is <b>null</b>
*/
public SubsetConfiguration(final Configuration parent, final String prefix, final String delimiter) {
- if (parent == null) {
- throw new IllegalArgumentException("Parent configuration must not be null!");
- }
-
- this.parent = parent;
+ this.parent = Objects.requireNonNull(parent, "parent");
this.prefix = prefix;
this.delimiter = delimiter;
initInterpolator();
diff --git a/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java b/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
index 645ee6c..953dd54 100644
--- a/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
+++ b/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
@@ -23,6 +23,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
@@ -91,14 +92,14 @@
}
/**
- * Creates and loads the xml properties from the specified DOM node.
+ * Creates and loads the XML properties from the specified DOM node.
*
- * @param element The DOM element
- * @throws ConfigurationException Error while loading the properties file
+ * @param element The non-null DOM element.
+ * @throws ConfigurationException Error while loading the Element.
* @since 2.0
*/
public XMLPropertiesConfiguration(final Element element) throws ConfigurationException {
- this.load(element);
+ load(Objects.requireNonNull(element, "element"));
}
/**
diff --git a/src/test/java/org/apache/commons/configuration2/NonCloneableConfiguration.java b/src/test/java/org/apache/commons/configuration2/NonCloneableConfiguration.java
index 33e6118..b8899a6 100644
--- a/src/test/java/org/apache/commons/configuration2/NonCloneableConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/NonCloneableConfiguration.java
@@ -23,15 +23,18 @@
* for testing implementations of clone() methods. It does not make much sense otherwise; all methods are just dummies.
*/
public class NonCloneableConfiguration extends AbstractConfiguration {
+
/**
* Dummy implementation of this method.
*/
@Override
protected void addPropertyDirect(final String key, final Object value) {
+ // noop
}
@Override
protected void clearPropertyDirect(final String key) {
+ // noop
}
/**
diff --git a/src/test/java/org/apache/commons/configuration2/TestDataConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestDataConfiguration.java
index c3d3a84..4c53bcf 100644
--- a/src/test/java/org/apache/commons/configuration2/TestDataConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestDataConfiguration.java
@@ -1977,9 +1977,12 @@
final DataConfiguration conf = new DataConfiguration(baseconf);
assertTrue(conf.isEmpty());
-
baseconf.setProperty("foo", "bar");
-
assertFalse(conf.isEmpty());
}
+
+ @Test
+ public void testNullConfiguration() {
+ assertThrows(NullPointerException.class, () -> new DataConfiguration(null));
+ }
}
diff --git a/src/test/java/org/apache/commons/configuration2/TestSubsetConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestSubsetConfiguration.java
index 4ba2456..c3a6ba1 100644
--- a/src/test/java/org/apache/commons/configuration2/TestSubsetConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestSubsetConfiguration.java
@@ -60,6 +60,14 @@
assertFalse(config.isEmpty());
}
+ /**
+ * Tries to create an instance without a parent configuration.
+ */
+ @Test
+ public void testConstructNullParent() {
+ assertThrows(NullPointerException.class, () -> new SubsetConfiguration(null, ""));
+ }
+
@Test
public void testGetChildKey() {
final Configuration conf = new BaseConfiguration();
@@ -170,14 +178,6 @@
assertFalse(subset.containsKey("ng.key2"));
}
- /**
- * Tries to create an instance without a parent configuration.
- */
- @Test
- public void testInitNoParent() {
- assertThrows(IllegalArgumentException.class, () -> new SubsetConfiguration(null, ""));
- }
-
@Test
public void testInterpolationForKeysOfTheParent() {
final BaseConfiguration config = new BaseConfiguration();
@@ -243,6 +243,63 @@
assertTrue(keys.isEmpty());
}
+ @Test
+ public void testPrefixDelimiter(){
+ final BaseConfiguration config = new BaseConfiguration();
+ config.setProperty("part1.part2@test.key1", "value1");
+ config.setProperty("part1.part2", "value2");
+ config.setProperty("part3.part4@testing.key2", "value3");
+
+ final SubsetConfiguration subset = new SubsetConfiguration(config, "part1.part2", "@");
+ // Check subset properties
+ assertEquals("value1", subset.getString("test.key1"));
+ assertEquals("value2", subset.getString(""));
+ assertNull(subset.getString("testing.key2"));
+
+ // Check for empty subset configuration and iterator
+ assertEquals(2, subset.size());
+ assertFalse(subset.isEmpty());
+ assertTrue(subset.getKeys().hasNext());
+ }
+
+ @Test
+ public void testPrefixDelimiterNegativeTest(){
+ final BaseConfiguration config = new BaseConfiguration();
+ config.setProperty("part1.part2@test.key1", "value1");
+ config.setProperty("part3.part4@testing.key2", "value2");
+
+ final SubsetConfiguration subset = new SubsetConfiguration(config, "part1.part2", "@") {
+ // Anonymous inner class declaration to override SubsetConfiguration.getKeysInternal() - Call
+ // ImutableConfiguration.getKeys(String) on the parent configuration of the SubsetConfiguration in order to
+ // not consequently pass the prefix delimiter
+ @Override
+ protected Iterator<String> getKeysInternal() {
+ Class<?> subsetIteratorClass;
+ try {
+ subsetIteratorClass = Class
+ .forName("org.apache.commons.configuration2.SubsetConfiguration$SubsetIterator");
+ final Constructor<?> ctor = subsetIteratorClass.getDeclaredConstructor(SubsetConfiguration.class,
+ Iterator.class);
+ ctor.setAccessible(true);
+
+ return (Iterator<String>) ctor.newInstance(this, parent.getKeys("part1.part2"));
+ } catch (final Exception ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ };
+
+ // Check subset properties - contains one property
+ assertEquals("value1", subset.getString("test.key1"));
+ assertNull(subset.getString("testing.key2"));
+
+ // Check for empty subset configuration and iterator - even if the SubsetConfiguration contains properties, like
+ // checked previously its states that it is empty
+ assertEquals(0, subset.size());
+ assertTrue(subset.isEmpty());
+ assertFalse(subset.getKeys().hasNext());
+ }
+
/**
* Tests whether the list delimiter handler is also set for the parent configuration.
*/
@@ -308,61 +365,4 @@
subset.setThrowExceptionOnMissing(true);
assertThrows(NoSuchElementException.class, () -> config.getString("foo"));
}
-
- @Test
- public void testPrefixDelimiter(){
- final BaseConfiguration config = new BaseConfiguration();
- config.setProperty("part1.part2@test.key1", "value1");
- config.setProperty("part1.part2", "value2");
- config.setProperty("part3.part4@testing.key2", "value3");
-
- final SubsetConfiguration subset = new SubsetConfiguration(config, "part1.part2", "@");
- // Check subset properties
- assertEquals("value1", subset.getString("test.key1"));
- assertEquals("value2", subset.getString(""));
- assertNull(subset.getString("testing.key2"));
-
- // Check for empty subset configuration and iterator
- assertEquals(2, subset.size());
- assertFalse(subset.isEmpty());
- assertTrue(subset.getKeys().hasNext());
- }
-
- @Test
- public void testPrefixDelimiterNegativeTest(){
- final BaseConfiguration config = new BaseConfiguration();
- config.setProperty("part1.part2@test.key1", "value1");
- config.setProperty("part3.part4@testing.key2", "value2");
-
- final SubsetConfiguration subset = new SubsetConfiguration(config, "part1.part2", "@") {
- // Anonymous inner class declaration to override SubsetConfiguration.getKeysInternal() - Call
- // ImutableConfiguration.getKeys(String) on the parent configuration of the SubsetConfiguration in order to
- // not consequently pass the prefix delimiter
- @Override
- protected Iterator<String> getKeysInternal() {
- Class<?> subsetIteratorClass;
- try {
- subsetIteratorClass = Class
- .forName("org.apache.commons.configuration2.SubsetConfiguration$SubsetIterator");
- final Constructor<?> ctor = subsetIteratorClass.getDeclaredConstructor(SubsetConfiguration.class,
- Iterator.class);
- ctor.setAccessible(true);
-
- return (Iterator<String>) ctor.newInstance(this, parent.getKeys("part1.part2"));
- } catch (final Exception ex) {
- throw new IllegalArgumentException(ex);
- }
- }
- };
-
- // Check subset properties - contains one property
- assertEquals("value1", subset.getString("test.key1"));
- assertNull(subset.getString("testing.key2"));
-
- // Check for empty subset configuration and iterator - even if the SubsetConfiguration contains properties, like
- // checked previously its states that it is empty
- assertEquals(0, subset.size());
- assertTrue(subset.isEmpty());
- assertFalse(subset.getKeys().hasNext());
- }
}
diff --git a/src/test/java/org/apache/commons/configuration2/TestXMLPropertiesConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestXMLPropertiesConfiguration.java
index 4cbeed0..373ca9f 100644
--- a/src/test/java/org/apache/commons/configuration2/TestXMLPropertiesConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestXMLPropertiesConfiguration.java
@@ -20,6 +20,7 @@
import static org.apache.commons.configuration2.TempDirUtils.newFile;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.File;
import java.net.URL;
@@ -66,6 +67,9 @@
@Test
public void testDOMLoad() throws Exception {
+ // Edge case
+ assertThrows(NullPointerException.class, () -> new XMLPropertiesConfiguration(null));
+ // Normal case
final URL location = ConfigurationAssert.getTestURL(TEST_PROPERTIES_FILE);
final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
final DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();