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 2022/07/14 14:34:37 UTC
[commons-configuration] 04/04: Add DefaultConversionHandler#setListDelimiterHandler(ListDelimiterHandler).
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 5de7c48e4d591e638562672fbbe38db5d14e89b3
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Thu Jul 14 10:34:26 2022 -0400
Add DefaultConversionHandler#setListDelimiterHandler(ListDelimiterHandler).
---
src/changes/changes.xml | 3 +
.../convert/DefaultConversionHandler.java | 281 +++++++++++----------
.../convert/TestDefaultConversionHandler.java | 14 +-
3 files changed, 169 insertions(+), 129 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 3e0a1fe8..b53a320a 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,6 +30,9 @@
CombinedConfiguration#getKeys() can throw NoSuchElementException.
</action>
<!-- ADD -->
+ <action issue="CONFIGURATION-799" type="fix" dev="ggregory" due-to="Xinshiyou, Gary Gregory">
+ Add DefaultConversionHandler#setListDelimiterHandler(ListDelimiterHandler).
+ </action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Dependabot">
Bump spotbugs-maven-plugin from 4.7.0.0 to 4.7.1.0 #193.
diff --git a/src/main/java/org/apache/commons/configuration2/convert/DefaultConversionHandler.java b/src/main/java/org/apache/commons/configuration2/convert/DefaultConversionHandler.java
index acd3d1ba..4cfb856f 100644
--- a/src/main/java/org/apache/commons/configuration2/convert/DefaultConversionHandler.java
+++ b/src/main/java/org/apache/commons/configuration2/convert/DefaultConversionHandler.java
@@ -45,6 +45,7 @@ import org.apache.commons.lang3.ClassUtils;
* @since 2.0
*/
public class DefaultConversionHandler implements ConversionHandler {
+
/**
* A default instance of this class. Because an instance of this class can be shared between arbitrary objects it is
* possible to make use of this default instance anywhere.
@@ -54,9 +55,6 @@ public class DefaultConversionHandler implements ConversionHandler {
/** The default format for dates. */
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
- /** A helper object used for extracting values from complex objects. */
- private static final ListDelimiterHandler EXTRACTOR = DisabledListDelimiterHandler.INSTANCE;
-
/**
* Constant for a default {@code ConfigurationInterpolator} to be used if none is provided by the caller.
*/
@@ -67,72 +65,142 @@ public class DefaultConversionHandler implements ConversionHandler {
}
};
+ /** The default {@link ListDelimiterHandler} used for extracting values from complex objects. */
+ static final ListDelimiterHandler LIST_DELIMITER_HANDLER = DisabledListDelimiterHandler.INSTANCE;
+
/** The current date format. */
private volatile String dateFormat;
+ /** The default {@link ListDelimiterHandler} used for extracting values from complex objects. */
+ private volatile ListDelimiterHandler listDelimiterHandler = DisabledListDelimiterHandler.INSTANCE;
+
/**
- * Returns the date format used by this conversion handler.
+ * Obtains a {@code ConfigurationInterpolator}. If the passed in one is not <b>null</b>, it is used. Otherwise, a
+ * default one is returned.
*
- * @return the date format
+ * @param ci the {@code ConfigurationInterpolator} provided by the caller
+ * @return the {@code ConfigurationInterpolator} to be used
*/
- public String getDateFormat() {
- final String fmt = dateFormat;
- return fmt != null ? fmt : DEFAULT_DATE_FORMAT;
+ private static ConfigurationInterpolator fetchInterpolator(final ConfigurationInterpolator ci) {
+ return ci != null ? ci : NULL_INTERPOLATOR;
}
/**
- * Sets the date format to be used by this conversion handler. This format is applied by conversions to {@code Date} or
- * {@code Calendar} objects. The string is passed to the {@code java.text.SimpleDateFormat} class, so it must be
- * compatible with this class. If no date format has been set, a default format is used.
+ * Performs the conversion from the passed in source object to the specified target class. This method is called for
+ * each conversion to be done. The source object has already been passed to the {@link ConfigurationInterpolator}, so
+ * interpolation does not have to be done again. (The passed in {@code ConfigurationInterpolator} may still be necessary
+ * for extracting values from complex objects; it is guaranteed to be non <b>null</b>.) The source object may be a
+ * complex object, e.g. a collection or an array. This base implementation checks whether the source object is complex.
+ * If so, it delegates to {@link #extractConversionValue(Object, Class, ConfigurationInterpolator)} to obtain a single
+ * value. Eventually, {@link #convertValue(Object, Class, ConfigurationInterpolator)} is called with the single value to
+ * be converted.
*
- * @param dateFormat the date format string
- * @see #DEFAULT_DATE_FORMAT
+ * @param <T> the desired target type of the conversion
+ * @param src the source object to be converted
+ * @param targetCls the desired target class
+ * @param ci the {@code ConfigurationInterpolator} (not <b>null</b>)
+ * @return the converted value
+ * @throws ConversionException if conversion is not possible
*/
- public void setDateFormat(final String dateFormat) {
- this.dateFormat = dateFormat;
+ protected <T> T convert(final Object src, final Class<T> targetCls, final ConfigurationInterpolator ci) {
+ final Object conversionSrc = isComplexObject(src) ? extractConversionValue(src, targetCls, ci) : src;
+ return convertValue(ci.interpolate(conversionSrc), targetCls, ci);
}
- @Override
- public <T> T to(final Object src, final Class<T> targetCls, final ConfigurationInterpolator ci) {
- final ConfigurationInterpolator interpolator = fetchInterpolator(ci);
- return convert(interpolator.interpolate(src), targetCls, interpolator);
+ /**
+ * Helper method for converting all values of a source object and storing them in a collection.
+ *
+ * @param <T> the target type of the conversion
+ * @param src the source object
+ * @param elemClass the target class of the conversion
+ * @param ci the {@code ConfigurationInterpolator}
+ * @param dest the collection in which to store the results
+ * @throws ConversionException if a conversion cannot be performed
+ */
+ private <T> void convertToCollection(final Object src, final Class<T> elemClass, final ConfigurationInterpolator ci, final Collection<T> dest) {
+ for (final Object o : extractValues(ci.interpolate(src))) {
+ dest.add(convert(o, elemClass, ci));
+ }
}
/**
- * {@inheritDoc} This implementation extracts all values stored in the passed in source object, converts them to the
- * target type, and adds them to a result array. Arrays of objects and of primitive types are supported. If the source
- * object is <b>null</b>, result is <b>null</b>, too.
+ * Performs a conversion of a single value to the specified target class. The passed in source object is guaranteed to
+ * be a single value, but it can be <b>null</b>. Derived classes that want to extend the available conversions, but are
+ * happy with the handling of complex objects, just need to override this method.
+ *
+ * @param <T> the desired target type of the conversion
+ * @param src the source object (a single value)
+ * @param targetCls the target class of the conversion
+ * @param ci the {@code ConfigurationInterpolator} (not <b>null</b>)
+ * @return the converted value
+ * @throws ConversionException if conversion is not possible
*/
- @Override
- public Object toArray(final Object src, final Class<?> elemClass, final ConfigurationInterpolator ci) {
+ protected <T> T convertValue(final Object src, final Class<T> targetCls, final ConfigurationInterpolator ci) {
if (src == null) {
return null;
}
- if (isEmptyElement(src)) {
- return Array.newInstance(elemClass, 0);
- }
- final ConfigurationInterpolator interpolator = fetchInterpolator(ci);
- return elemClass.isPrimitive() ? toPrimitiveArray(src, elemClass, interpolator) : toObjectArray(src, elemClass, interpolator);
+ // This is a safe cast because PropertyConverter either returns an
+ // object of the correct class or throws an exception.
+ @SuppressWarnings("unchecked")
+ final T result = (T) PropertyConverter.to(targetCls, src, this);
+ return result;
}
/**
- * {@inheritDoc} This implementation extracts all values stored in the passed in source object, converts them to the
- * target type, and adds them to the target collection. The target collection must not be <b>null</b>. If the source
- * object is <b>null</b>, nothing is added to the collection.
+ * Extracts a single value from a complex object. This method is called by {@code convert()} if the source object is
+ * complex. This implementation extracts the first value from the complex object and returns it.
*
- * @throws IllegalArgumentException if the target collection is <b>null</b>
+ * @param container the complex object
+ * @param targetCls the target class of the conversion
+ * @param ci the {@code ConfigurationInterpolator} (not <b>null</b>)
+ * @return the value to be converted (may be <b>null</b> if no values are found)
*/
- @Override
- public <T> void toCollection(final Object src, final Class<T> elemClass, final ConfigurationInterpolator ci, final Collection<T> dest) {
- if (dest == null) {
- throw new IllegalArgumentException("Target collection must not be null!");
- }
+ protected Object extractConversionValue(final Object container, final Class<?> targetCls, final ConfigurationInterpolator ci) {
+ final Collection<?> values = extractValues(container, 1);
+ return values.isEmpty() ? null : ci.interpolate(values.iterator().next());
+ }
- if (src != null && !isEmptyElement(src)) {
- final ConfigurationInterpolator interpolator = fetchInterpolator(ci);
- convertToCollection(src, elemClass, interpolator, dest);
- }
+ /**
+ * Extracts all values contained in the given source object and returns them as a flat collection.
+ *
+ * @param source the source object (may be a single value or a complex object)
+ * @return a collection with all extracted values
+ */
+ protected Collection<?> extractValues(final Object source) {
+ return extractValues(source, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Extracts a maximum number of values contained in the given source object and returns them as flat collection. This
+ * method is useful if the caller only needs a subset of values, e.g. only the first one.
+ *
+ * @param source the source object (may be a single value or a complex object)
+ * @param limit the number of elements to extract
+ * @return a collection with all extracted values
+ */
+ protected Collection<?> extractValues(final Object source, final int limit) {
+ return listDelimiterHandler.flatten(source, limit);
+ }
+
+ /**
+ * Returns the date format used by this conversion handler.
+ *
+ * @return the date format
+ */
+ public String getDateFormat() {
+ final String fmt = dateFormat;
+ return fmt != null ? fmt : DEFAULT_DATE_FORMAT;
+ }
+
+ /**
+ * Gets the {@link ListDelimiterHandler} used for extracting values from complex objects.
+ *
+ * @return the {@link ListDelimiterHandler} used for extracting values from complex objects, never null.
+ * @since 2.9.0
+ */
+ public ListDelimiterHandler getListDelimiterHandler() {
+ return listDelimiterHandler;
}
/**
@@ -166,85 +234,69 @@ public class DefaultConversionHandler implements ConversionHandler {
}
/**
- * Performs the conversion from the passed in source object to the specified target class. This method is called for
- * each conversion to be done. The source object has already been passed to the {@link ConfigurationInterpolator}, so
- * interpolation does not have to be done again. (The passed in {@code ConfigurationInterpolator} may still be necessary
- * for extracting values from complex objects; it is guaranteed to be non <b>null</b>.) The source object may be a
- * complex object, e.g. a collection or an array. This base implementation checks whether the source object is complex.
- * If so, it delegates to {@link #extractConversionValue(Object, Class, ConfigurationInterpolator)} to obtain a single
- * value. Eventually, {@link #convertValue(Object, Class, ConfigurationInterpolator)} is called with the single value to
- * be converted.
+ * Sets the date format to be used by this conversion handler. This format is applied by conversions to {@code Date} or
+ * {@code Calendar} objects. The string is passed to the {@code java.text.SimpleDateFormat} class, so it must be
+ * compatible with this class. If no date format has been set, a default format is used.
*
- * @param <T> the desired target type of the conversion
- * @param src the source object to be converted
- * @param targetCls the desired target class
- * @param ci the {@code ConfigurationInterpolator} (not <b>null</b>)
- * @return the converted value
- * @throws ConversionException if conversion is not possible
+ * @param dateFormat the date format string
+ * @see #DEFAULT_DATE_FORMAT
*/
- protected <T> T convert(final Object src, final Class<T> targetCls, final ConfigurationInterpolator ci) {
- final Object conversionSrc = isComplexObject(src) ? extractConversionValue(src, targetCls, ci) : src;
- return convertValue(ci.interpolate(conversionSrc), targetCls, ci);
+ public void setDateFormat(final String dateFormat) {
+ this.dateFormat = dateFormat;
}
/**
- * Extracts a maximum number of values contained in the given source object and returns them as flat collection. This
- * method is useful if the caller only needs a subset of values, e.g. only the first one.
+ * Sets the {@link ListDelimiterHandler} used for extracting values from complex objects.
*
- * @param source the source object (may be a single value or a complex object)
- * @param limit the number of elements to extract
- * @return a collection with all extracted values
+ * @param listDelimiterHandler the {@link ListDelimiterHandler} used for extracting values from complex objects. Setting
+ * the value to null resets the value to its default.
+ * @since 2.9.0
*/
- protected Collection<?> extractValues(final Object source, final int limit) {
- return EXTRACTOR.flatten(source, limit);
+ public void setListDelimiterHandler(final ListDelimiterHandler listDelimiterHandler) {
+ this.listDelimiterHandler = listDelimiterHandler != null ? listDelimiterHandler : LIST_DELIMITER_HANDLER;
}
- /**
- * Extracts all values contained in the given source object and returns them as a flat collection.
- *
- * @param source the source object (may be a single value or a complex object)
- * @return a collection with all extracted values
- */
- protected Collection<?> extractValues(final Object source) {
- return extractValues(source, Integer.MAX_VALUE);
+ @Override
+ public <T> T to(final Object src, final Class<T> targetCls, final ConfigurationInterpolator ci) {
+ final ConfigurationInterpolator interpolator = fetchInterpolator(ci);
+ return convert(interpolator.interpolate(src), targetCls, interpolator);
}
/**
- * Extracts a single value from a complex object. This method is called by {@code convert()} if the source object is
- * complex. This implementation extracts the first value from the complex object and returns it.
- *
- * @param container the complex object
- * @param targetCls the target class of the conversion
- * @param ci the {@code ConfigurationInterpolator} (not <b>null</b>)
- * @return the value to be converted (may be <b>null</b> if no values are found)
+ * {@inheritDoc} This implementation extracts all values stored in the passed in source object, converts them to the
+ * target type, and adds them to a result array. Arrays of objects and of primitive types are supported. If the source
+ * object is <b>null</b>, result is <b>null</b>, too.
*/
- protected Object extractConversionValue(final Object container, final Class<?> targetCls, final ConfigurationInterpolator ci) {
- final Collection<?> values = extractValues(container, 1);
- return values.isEmpty() ? null : ci.interpolate(values.iterator().next());
+ @Override
+ public Object toArray(final Object src, final Class<?> elemClass, final ConfigurationInterpolator ci) {
+ if (src == null) {
+ return null;
+ }
+ if (isEmptyElement(src)) {
+ return Array.newInstance(elemClass, 0);
+ }
+
+ final ConfigurationInterpolator interpolator = fetchInterpolator(ci);
+ return elemClass.isPrimitive() ? toPrimitiveArray(src, elemClass, interpolator) : toObjectArray(src, elemClass, interpolator);
}
/**
- * Performs a conversion of a single value to the specified target class. The passed in source object is guaranteed to
- * be a single value, but it can be <b>null</b>. Derived classes that want to extend the available conversions, but are
- * happy with the handling of complex objects, just need to override this method.
+ * {@inheritDoc} This implementation extracts all values stored in the passed in source object, converts them to the
+ * target type, and adds them to the target collection. The target collection must not be <b>null</b>. If the source
+ * object is <b>null</b>, nothing is added to the collection.
*
- * @param <T> the desired target type of the conversion
- * @param src the source object (a single value)
- * @param targetCls the target class of the conversion
- * @param ci the {@code ConfigurationInterpolator} (not <b>null</b>)
- * @return the converted value
- * @throws ConversionException if conversion is not possible
+ * @throws IllegalArgumentException if the target collection is <b>null</b>
*/
- protected <T> T convertValue(final Object src, final Class<T> targetCls, final ConfigurationInterpolator ci) {
- if (src == null) {
- return null;
+ @Override
+ public <T> void toCollection(final Object src, final Class<T> elemClass, final ConfigurationInterpolator ci, final Collection<T> dest) {
+ if (dest == null) {
+ throw new IllegalArgumentException("Target collection must not be null!");
}
- // This is a safe cast because PropertyConverter either returns an
- // object of the correct class or throws an exception.
- @SuppressWarnings("unchecked")
- final T result = (T) PropertyConverter.to(targetCls, src, this);
- return result;
+ if (src != null && !isEmptyElement(src)) {
+ final ConfigurationInterpolator interpolator = fetchInterpolator(ci);
+ convertToCollection(src, elemClass, interpolator, dest);
+ }
}
/**
@@ -304,31 +356,4 @@ public class DefaultConversionHandler implements ConversionHandler {
}
return array;
}
-
- /**
- * Helper method for converting all values of a source object and storing them in a collection.
- *
- * @param <T> the target type of the conversion
- * @param src the source object
- * @param elemClass the target class of the conversion
- * @param ci the {@code ConfigurationInterpolator}
- * @param dest the collection in which to store the results
- * @throws ConversionException if a conversion cannot be performed
- */
- private <T> void convertToCollection(final Object src, final Class<T> elemClass, final ConfigurationInterpolator ci, final Collection<T> dest) {
- for (final Object o : extractValues(ci.interpolate(src))) {
- dest.add(convert(o, elemClass, ci));
- }
- }
-
- /**
- * Obtains a {@code ConfigurationInterpolator}. If the passed in one is not <b>null</b>, it is used. Otherwise, a
- * default one is returned.
- *
- * @param ci the {@code ConfigurationInterpolator} provided by the caller
- * @return the {@code ConfigurationInterpolator} to be used
- */
- private static ConfigurationInterpolator fetchInterpolator(final ConfigurationInterpolator ci) {
- return ci != null ? ci : NULL_INTERPOLATOR;
- }
}
diff --git a/src/test/java/org/apache/commons/configuration2/convert/TestDefaultConversionHandler.java b/src/test/java/org/apache/commons/configuration2/convert/TestDefaultConversionHandler.java
index 05d79161..ce751c83 100644
--- a/src/test/java/org/apache/commons/configuration2/convert/TestDefaultConversionHandler.java
+++ b/src/test/java/org/apache/commons/configuration2/convert/TestDefaultConversionHandler.java
@@ -76,7 +76,7 @@ public class TestDefaultConversionHandler {
}
@Before
- public void setUp() throws Exception {
+ public void setUp() {
handler = new DefaultConversionHandler();
}
@@ -88,6 +88,18 @@ public class TestDefaultConversionHandler {
assertEquals("Wrong date format", DefaultConversionHandler.DEFAULT_DATE_FORMAT, handler.getDateFormat());
}
+ @Test
+ public synchronized void testListDelimiterHandler() {
+ assertEquals(DefaultConversionHandler.LIST_DELIMITER_HANDLER, handler.getListDelimiterHandler());
+ handler.setListDelimiterHandler(null);
+ assertEquals(DefaultConversionHandler.LIST_DELIMITER_HANDLER, handler.getListDelimiterHandler());
+ final LegacyListDelimiterHandler legacyListDelimiterHandler = new LegacyListDelimiterHandler(',');
+ handler.setListDelimiterHandler(legacyListDelimiterHandler);
+ assertEquals(legacyListDelimiterHandler, handler.getListDelimiterHandler());
+ handler.setListDelimiterHandler(null);
+ assertEquals(DefaultConversionHandler.LIST_DELIMITER_HANDLER, handler.getListDelimiterHandler());
+ }
+
/**
* Tests whether the date format can be changed.
*/