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 01bae76 Add ImmutableConfiguration.getDuration() methods. 01bae76 is described below commit 01bae769673fbe8a2cbbf0591f4b455f7ca76d7b Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Wed Aug 25 14:35:30 2021 -0400 Add ImmutableConfiguration.getDuration() methods. --- src/changes/changes.xml | 3 + .../configuration2/AbstractConfiguration.java | 11 + .../configuration2/ImmutableConfiguration.java | 35 ++ .../configuration2/convert/PropertyConverter.java | 30 +- .../configuration2/TestBaseConfiguration.java | 24 ++ .../TestDefaultImmutableConfiguration.java | 369 +++++++++++++++++++++ 6 files changed, 470 insertions(+), 2 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 239be52..707f719 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -75,6 +75,9 @@ <action type="add" dev="ggregory" issue="CONFIGURATION-789" due-to="Gary Gregory"> Add ImmutableConfiguration.getEnum() methods. </action> + <action type="add" dev="ggregory" issue="CONFIGURATION-789" due-to="Gary Gregory"> + Add ImmutableConfiguration.getDuration() methods. + </action> <!-- UPDATES --> <action type="update" dev="ggregory" issue="CONFIGURATION-787" due-to="Gary Gregory"> Update from Apache Commons Lang 3.9 to 3.12.0. diff --git a/src/main/java/org/apache/commons/configuration2/AbstractConfiguration.java b/src/main/java/org/apache/commons/configuration2/AbstractConfiguration.java index 3c0e526..3cde89c 100644 --- a/src/main/java/org/apache/commons/configuration2/AbstractConfiguration.java +++ b/src/main/java/org/apache/commons/configuration2/AbstractConfiguration.java @@ -19,6 +19,7 @@ package org.apache.commons.configuration2; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -987,6 +988,16 @@ public abstract class AbstractConfiguration extends BaseEventSource implements C } @Override + public Duration getDuration(final String key) { + return checkNonNullValue(key, convert(Duration.class, key, null, true)); + } + + @Override + public Duration getDuration(final String key, final Duration defaultValue) { + return convert(Duration.class, key, defaultValue, false); + } + + @Override public float getFloat(final String key) { final Float f = convert(Float.class, key, null, true); return checkNonNullValue(key, f).floatValue(); diff --git a/src/main/java/org/apache/commons/configuration2/ImmutableConfiguration.java b/src/main/java/org/apache/commons/configuration2/ImmutableConfiguration.java index 1ba0052..96da9a1 100644 --- a/src/main/java/org/apache/commons/configuration2/ImmutableConfiguration.java +++ b/src/main/java/org/apache/commons/configuration2/ImmutableConfiguration.java @@ -18,11 +18,14 @@ package org.apache.commons.configuration2; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.Duration; import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; import java.util.Properties; +import org.apache.commons.configuration2.convert.PropertyConverter; import org.apache.commons.configuration2.ex.ConversionException; /** @@ -309,6 +312,38 @@ public interface ImmutableConfiguration { Double getDouble(String key, Double defaultValue); /** + * Gets a {@link Duration} associated with the given configuration key. + * + * @param key The configuration key. + * @return The associated Duration if key is found and has valid format, default value otherwise. + * @throws org.apache.commons.configuration2.ex.ConversionException is thrown if the key maps to an object that is not a + * Duration. + * @since 2.8.0 + */ + default Duration getDuration(final String key) { + final String string = getString(key); + if (string == null) { + throw new NoSuchElementException(key); + } + return PropertyConverter.toDuration(string); + } + + /** + * Gets a {@link Duration} associated with the given configuration key. + * + * @param key The configuration key. + * @param defaultValue The default value. + * @return The associated Duration if key is found and has valid format, default value otherwise. + * @throws org.apache.commons.configuration2.ex.ConversionException is thrown if the key maps to an object that is not a + * Duration. + * @since 2.8.0 + */ + default Duration getDuration(final String key, final Duration defaultValue) { + Object value = getProperty(key); + return value == null ? defaultValue : PropertyConverter.toDuration(value); + } + + /** * Gets the value of a string property that is stored in encoded form in this configuration using a default * {@code ConfigurationDecoder}. This method works like the method with the same name, but it uses a default * {@code ConfigurationDecoder} associated with this configuration. It depends on a specific implementation how this diff --git a/src/main/java/org/apache/commons/configuration2/convert/PropertyConverter.java b/src/main/java/org/apache/commons/configuration2/convert/PropertyConverter.java index b3e53c2..b018193 100644 --- a/src/main/java/org/apache/commons/configuration2/convert/PropertyConverter.java +++ b/src/main/java/org/apache/commons/configuration2/convert/PropertyConverter.java @@ -33,6 +33,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.format.DateTimeParseException; import java.util.Calendar; import java.util.Date; import java.util.Locale; @@ -46,9 +48,9 @@ import org.apache.commons.lang3.StringUtils; /** * A utility class to convert the configuration properties into any type. * - * @since 1.1 + * @since 2.8.0 */ -final class PropertyConverter { +public final class PropertyConverter { /** Constant for the prefix of hex numbers. */ private static final String HEX_PREFIX = "0x"; @@ -150,6 +152,8 @@ final class PropertyConverter { return toInternetAddress(value); } else if (InetAddress.class.isAssignableFrom(cls)) { return toInetAddress(value); + } else if (Duration.class.equals(cls)) { + return toDuration(value); } throw new ConversionException("The value '" + value + "' (" + value.getClass() + ")" + " can't be converted to a " + cls.getName() + " object"); @@ -286,6 +290,28 @@ final class PropertyConverter { } /** + * Convert the specified object into a Duration. + * + * @param value the value to convert + * @return the converted value + * @throws ConversionException thrown if the value cannot be converted to a Duration + * @since 2.8.0 + */ + public static Duration toDuration(final Object value) throws ConversionException { + if (value instanceof Duration) { + return (Duration) value; + } + if (value instanceof CharSequence) { + try { + return Duration.parse((CharSequence) value); + } catch (DateTimeParseException e) { + throw new ConversionException("Could not convert " + value + " to Duration", e); + } + } + throw new ConversionException("The value " + value + " can't be converted to a Duration"); + } + + /** * Convert the specified object into a BigInteger. * * @param value the value to convert diff --git a/src/test/java/org/apache/commons/configuration2/TestBaseConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestBaseConfiguration.java index 8e98d25..12eb7c0 100644 --- a/src/test/java/org/apache/commons/configuration2/TestBaseConfiguration.java +++ b/src/test/java/org/apache/commons/configuration2/TestBaseConfiguration.java @@ -26,6 +26,7 @@ import static org.junit.Assert.fail; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -308,6 +309,29 @@ public class TestBaseConfiguration { } @Test + public void testGetDuration() { + final Duration d = Duration.ofSeconds(1); + config.setProperty("durationD", d.toString()); + final Duration oneD = Duration.ofSeconds(1); + final Duration twoD = Duration.ofSeconds(2); + assertEquals("This returns 1(Duration)", oneD, config.getDuration("durationD")); + assertEquals("This returns 1(Duration)", oneD, config.getDuration("durationD", twoD)); + assertEquals("This returns 2(default Duration)", twoD, config.getDuration("numberNotInConfig", twoD)); + assertEquals("This returns 1(Duration)", oneD, config.getDuration("durationD", twoD)); + } + + @Test(expected = ConversionException.class) + public void testGetDurationIncompatibleType() { + config.setProperty("test.empty", ""); + config.getDuration("test.empty"); + } + + @Test(expected = NoSuchElementException.class) + public void testGetDurationUnknown() { + config.getDuration("numberNotInConfig"); + } + + @Test public void testGetEnum() { config.setProperty("testEnum", EnumFixture.SMALLTALK.name()); config.setProperty("testBadEnum", "This is not an enum value."); diff --git a/src/test/java/org/apache/commons/configuration2/TestDefaultImmutableConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestDefaultImmutableConfiguration.java new file mode 100644 index 0000000..d0b97f3 --- /dev/null +++ b/src/test/java/org/apache/commons/configuration2/TestDefaultImmutableConfiguration.java @@ -0,0 +1,369 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.configuration2; + +import static org.junit.Assert.assertEquals; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.Duration; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Properties; + +import org.apache.commons.configuration2.ex.ConversionException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests {@link ImmutableConfiguration} default methods. + */ +public class TestDefaultImmutableConfiguration { + + /** Tests default methods. This class MUST NOT override the default methods! */ + private class MapImmutableConfiguration implements ImmutableConfiguration { + + Map<String, Object> map = new HashMap<>(); + + @Override + public boolean containsKey(final String key) { + // Super is not a default method. + return false; + } + + @Override + public <T> T get(final Class<T> cls, final String key) { + // Super is not a default method. + return null; + } + + @Override + public <T> T get(final Class<T> cls, final String key, final T defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public Object getArray(final Class<?> cls, final String key) { + // Super is not a default method. + return null; + } + + @Override + public Object getArray(final Class<?> cls, final String key, final Object defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public BigDecimal getBigDecimal(final String key) { + // Super is not a default method. + return null; + } + + @Override + public BigDecimal getBigDecimal(final String key, final BigDecimal defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public BigInteger getBigInteger(final String key) { + // Super is not a default method. + return null; + } + + @Override + public BigInteger getBigInteger(final String key, final BigInteger defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public boolean getBoolean(final String key) { + // Super is not a default method. + return false; + } + + @Override + public boolean getBoolean(final String key, final boolean defaultValue) { + // Super is not a default method. + return false; + } + + @Override + public Boolean getBoolean(final String key, final Boolean defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public byte getByte(final String key) { + // Super is not a default method. + return 0; + } + + @Override + public byte getByte(final String key, final byte defaultValue) { + // Super is not a default method. + return 0; + } + + @Override + public Byte getByte(final String key, final Byte defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public <T> Collection<T> getCollection(final Class<T> cls, final String key, final Collection<T> target) { + // Super is not a default method. + return null; + } + + @Override + public <T> Collection<T> getCollection(final Class<T> cls, final String key, final Collection<T> target, final Collection<T> defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public double getDouble(final String key) { + // Super is not a default method. + return 0; + } + + @Override + public double getDouble(final String key, final double defaultValue) { + // Super is not a default method. + return 0; + } + + @Override + public Double getDouble(final String key, final Double defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public String getEncodedString(final String key) { + // Super is not a default method. + return null; + } + + @Override + public String getEncodedString(final String key, final ConfigurationDecoder decoder) { + // Super is not a default method. + return null; + } + + @Override + public float getFloat(final String key) { + // Super is not a default method. + return 0; + } + + @Override + public float getFloat(final String key, final float defaultValue) { + // Super is not a default method. + return 0; + } + + @Override + public Float getFloat(final String key, final Float defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public int getInt(final String key) { + // Super is not a default method. + return 0; + } + + @Override + public int getInt(final String key, final int defaultValue) { + // Super is not a default method. + return 0; + } + + @Override + public Integer getInteger(final String key, final Integer defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public Iterator<String> getKeys() { + // Super is not a default method. + return null; + } + + @Override + public Iterator<String> getKeys(final String prefix) { + // Super is not a default method. + return null; + } + + @Override + public <T> List<T> getList(final Class<T> cls, final String key) { + // Super is not a default method. + return null; + } + + @Override + public <T> List<T> getList(final Class<T> cls, final String key, final List<T> defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public List<Object> getList(final String key) { + // Super is not a default method. + return null; + } + + @Override + public List<Object> getList(final String key, final List<?> defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public long getLong(final String key) { + // Super is not a default method. + return 0; + } + + @Override + public long getLong(final String key, final long defaultValue) { + // Super is not a default method. + return 0; + } + + @Override + public Long getLong(final String key, final Long defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public Properties getProperties(final String key) { + // Super is not a default method. + return null; + } + + @Override + public Object getProperty(final String key) { + // Super is not a default method. + return map.get(key); + } + + @Override + public short getShort(final String key) { + // Super is not a default method. + return 0; + } + + @Override + public short getShort(final String key, final short defaultValue) { + // Super is not a default method. + return 0; + } + + @Override + public Short getShort(final String key, final Short defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public String getString(final String key) { + return Objects.toString(map.get(key), null); + } + + @Override + public String getString(final String key, final String defaultValue) { + // Super is not a default method. + return null; + } + + @Override + public String[] getStringArray(final String key) { + // Super is not a default method. + return null; + } + + @Override + public ImmutableConfiguration immutableSubset(final String prefix) { + // Super is not a default method. + return null; + } + + @Override + public boolean isEmpty() { + // Super is not a default method. + return false; + } + + @Override + public int size() { + // Super is not a default method. + return 0; + } + + } + + private final MapImmutableConfiguration config = new MapImmutableConfiguration(); + + @Before + @After + public void clearMap() { + config.map.clear(); + } + + @Test + public void testGetDuration() { + final Duration d = Duration.ofSeconds(1); + config.map.put("durationD", d.toString()); + final Duration oneD = Duration.ofSeconds(1); + final Duration twoD = Duration.ofSeconds(2); + assertEquals("This returns 1(Duration)", oneD, config.getDuration("durationD")); + assertEquals("This returns 1(Duration)", oneD, config.getDuration("durationD", twoD)); + assertEquals("This returns 2(default Duration)", twoD, config.getDuration("numberNotInConfig", twoD)); + assertEquals("This returns 1(Duration)", oneD, config.getDuration("durationD", twoD)); + } + + @Test(expected = ConversionException.class) + public void testGetDurationIncompatibleType() { + config.map.put("test.empty", ""); + config.getDuration("test.empty"); + } + + @Test(expected = NoSuchElementException.class) + public void testGetDurationUnknown() { + config.getDuration("numberNotInConfig"); + } + +}