type, final long value) {
* {@link java.sql.Date}
* {@link java.sql.Time}
* {@link java.sql.Timestamp}
+ * {@link java.time.Instant}
*
*
* N.B. No default String conversion mechanism is provided for {@link java.util.Date} and {@link java.util.Calendar} type.
@@ -624,6 +638,14 @@ private T toDate(final Class type, final String value) {
}
}
+ if (type.equals(Instant.class)) {
+ try {
+ return type.cast(Instant.parse(value));
+ } catch (final DateTimeParseException ex) {
+ throw new ConversionException("String must be in ISO-8601 format to create a java.time.Instant");
+ }
+ }
+
final String msg = toString(getClass()) + " does not support default String to '" + toString(type) + "' conversion.";
if (log().isWarnEnabled()) {
log().warn(" " + msg);
diff --git a/src/main/java/org/apache/commons/beanutils2/converters/DurationConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/DurationConverter.java
index 2943386ab..fd03cd8cf 100644
--- a/src/main/java/org/apache/commons/beanutils2/converters/DurationConverter.java
+++ b/src/main/java/org/apache/commons/beanutils2/converters/DurationConverter.java
@@ -59,7 +59,7 @@ public DurationConverter(final Duration defaultValue) {
@Override
protected T convertToType(final Class type, final Object value) throws Throwable {
if (Duration.class.equals(type)) {
- return type.cast(Duration.parse(String.valueOf(value)));
+ return type.cast(Duration.parse(toString(value)));
}
throw conversionException(type, value);
diff --git a/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java
index fb0378d36..7eae2c583 100644
--- a/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java
+++ b/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java
@@ -59,15 +59,39 @@ public EnumConverter(final Enum defaultValue) {
@Override
protected R convertToType(final Class type, final Object value) throws Throwable {
if (Enum.class.isAssignableFrom(type)) {
- final String enumValue = String.valueOf(value);
- final R[] constants = type.getEnumConstants();
- if (constants == null) {
- throw conversionException(type, value);
+ final String stringValue = toString(value);
+
+ try {
+ return type.cast((Enum) Enum.valueOf((Class) type, stringValue));
+ } catch (IllegalArgumentException ex) {
+ // Continue to check fully qualified name.
}
- for (final R candidate : constants) {
- if (((Enum) candidate).name().equalsIgnoreCase(enumValue)) {
- return candidate;
+
+ final int lastHash = stringValue.lastIndexOf('#');
+ final int lastDot = stringValue.lastIndexOf('.');
+
+ if (lastDot == -1 && lastHash == -1) {
+ throw new IllegalArgumentException(
+ "Expected fully qualified name for Enum constant like: java.time.DayOfWeek.MONDAY");
+ }
+
+ final String enumValue = stringValue.substring(Math.max(lastHash, lastDot) + 1);
+ final String className = stringValue.substring(0, stringValue.length() - enumValue.length() - 1);
+
+ try {
+ Class classForName = Class.forName(className);
+
+ if (!classForName.isEnum()) {
+ throw new IllegalArgumentException("Value isn't an enumerated type.");
}
+
+ if (!type.isAssignableFrom(classForName)) {
+ throw new IllegalArgumentException("Class is not the required type.");
+ }
+
+ return type.cast((Enum) Enum.valueOf(classForName, enumValue));
+ } catch (ClassNotFoundException ex) {
+ throw new IllegalArgumentException("Class \"" + className + "\" doesn't exist.", ex);
}
}
diff --git a/src/main/java/org/apache/commons/beanutils2/converters/InstantConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/InstantConverter.java
new file mode 100644
index 000000000..60f7105ce
--- /dev/null
+++ b/src/main/java/org/apache/commons/beanutils2/converters/InstantConverter.java
@@ -0,0 +1,57 @@
+/*
+ * 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.beanutils2.converters;
+
+import java.time.Instant;
+
+/**
+ * {@link org.apache.commons.beanutils2.Converter} implementation that handles conversion to and from {@link Instant} objects.
+ *
+ * Can be configured to either return a default value or throw a {@code ConversionException} if a conversion error occurs.
+ *
+ *
+ * @since 2.0
+ * @see Instant
+ */
+public final class InstantConverter extends DateTimeConverter {
+
+ /**
+ * Constructs a {@link Instant} Converter that throws a {@code ConversionException} if an error occurs.
+ */
+ public InstantConverter() {
+ super();
+ }
+
+ /**
+ * Constructs a {@link Instant} Converter that returns a default value if an error occurs.
+ *
+ * @param defaultValue The default value to be returned if the value to be converted is missing or an error occurs converting the value.
+ */
+ public InstantConverter(final Instant defaultValue) {
+ super(defaultValue);
+ }
+
+ /**
+ * Gets the default type this {@code Converter} handles.
+ *
+ * @return Default type this {@code Converter} handles.
+ */
+ @Override
+ protected Class getDefaultType() {
+ return Instant.class;
+ }
+}
diff --git a/src/main/java/org/apache/commons/beanutils2/converters/PeriodConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/PeriodConverter.java
index 3946bf0c9..8b663890f 100644
--- a/src/main/java/org/apache/commons/beanutils2/converters/PeriodConverter.java
+++ b/src/main/java/org/apache/commons/beanutils2/converters/PeriodConverter.java
@@ -59,7 +59,7 @@ public PeriodConverter(final Period defaultValue) {
@Override
protected T convertToType(final Class type, final Object value) throws Throwable {
if (Period.class.equals(type)) {
- return type.cast(Period.parse(String.valueOf(value)));
+ return type.cast(Period.parse(toString(value)));
}
throw conversionException(type, value);
diff --git a/src/test/java/org/apache/commons/beanutils2/converters/CharacterConverterTest.java b/src/test/java/org/apache/commons/beanutils2/converters/CharacterConverterTest.java
index 800260ab6..e21b16467 100644
--- a/src/test/java/org/apache/commons/beanutils2/converters/CharacterConverterTest.java
+++ b/src/test/java/org/apache/commons/beanutils2/converters/CharacterConverterTest.java
@@ -30,14 +30,16 @@
*/
class CharacterConverterTest {
- /** Sets Up */
+ private Converter converter;
+
@BeforeEach
public void setUp() throws Exception {
+ converter = new CharacterConverter();
}
- /** Tear Down */
@AfterEach
public void tearDown() throws Exception {
+ converter = null;
}
/**
@@ -45,7 +47,6 @@ public void tearDown() throws Exception {
*/
@Test
void testConvertToChar() {
- final Converter converter = new CharacterConverter();
assertEquals(Character.valueOf('F'), converter.convert(Character.TYPE, "FOO"), "Wrong result");
}
@@ -54,7 +55,6 @@ void testConvertToChar() {
*/
@Test
void testConvertToCharacter() {
- final Converter converter = new CharacterConverter();
assertEquals(Character.valueOf('N'), converter.convert(Character.class, Character.valueOf('N')), "Character Test");
assertEquals(Character.valueOf('F'), converter.convert(Character.class, "FOO"), "String Test");
assertEquals(Character.valueOf('3'), converter.convert(Character.class, Integer.valueOf(321)), "Integer Test");
@@ -65,7 +65,6 @@ void testConvertToCharacter() {
*/
@Test
void testConvertToCharacterNullNoDefault() {
- final Converter converter = new CharacterConverter();
assertThrows(ConversionException.class, () -> converter.convert(Character.class, null));
}
@@ -75,8 +74,6 @@ void testConvertToCharacterNullNoDefault() {
@Test
@SuppressWarnings("unchecked") // testing raw conversion
void testConvertToString() {
-
- final Converter converter = new CharacterConverter();
@SuppressWarnings("rawtypes")
final Converter raw = converter;
@@ -90,10 +87,7 @@ void testConvertToString() {
* Tries a conversion to an unsupported type.
*/
@Test
- @SuppressWarnings("unchecked") // tests failure so allow mismatch
void testConvertToUnsupportedType() {
- @SuppressWarnings("rawtypes") // tests failure so allow mismatch
- final Converter converter = new CharacterConverter();
assertThrows(ConversionException.class, () -> converter.convert(Integer.class, "Test"));
}
diff --git a/src/test/java/org/apache/commons/beanutils2/converters/EnumConverterTest.java b/src/test/java/org/apache/commons/beanutils2/converters/EnumConverterTest.java
index 3af788146..dde2dc7b6 100644
--- a/src/test/java/org/apache/commons/beanutils2/converters/EnumConverterTest.java
+++ b/src/test/java/org/apache/commons/beanutils2/converters/EnumConverterTest.java
@@ -20,6 +20,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import java.time.DayOfWeek;
+import java.util.concurrent.TimeUnit;
+
import org.apache.commons.beanutils2.ConversionException;
import org.apache.commons.beanutils2.Converter;
import org.junit.jupiter.api.AfterEach;
@@ -79,4 +82,38 @@ void testSimpleConversion() throws Exception {
void testUnsupportedType() {
assertThrows(ConversionException.class, () -> converter.convert(Integer.class, "http://www.apache.org"));
}
+
+ @Test
+ void testConvertTimeUnit() {
+ final TimeUnit expected = TimeUnit.NANOSECONDS;
+ final Enum actual = converter.convert(Enum.class, "java.util.concurrent.TimeUnit.NANOSECONDS");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ void testConvertDayOfWeek() {
+ final DayOfWeek expected = DayOfWeek.MONDAY;
+ final DayOfWeek actual = converter.convert(DayOfWeek.class, "java.time.DayOfWeek#MONDAY");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ void testConvertMismatchingEnumType() {
+ assertThrows(ConversionException.class, () -> converter.convert(TimeUnit.class, "java.time.DayOfWeek#MONDAY"));
+ }
+
+ @Test
+ void testBrokenNamingConvention() {
+ assertThrows(ConversionException.class, () -> converter.convert(Enum.class, "JAVA-TIME-DAYOFWEEK#MONDAY"));
+ }
+
+ @Test
+ void testNonEnumClasses() {
+ assertThrows(ConversionException.class, () -> converter.convert(Enum.class, "java.lang.String#MONDAY"));
+ }
+
+ @Test
+ void testNonExistingClasses() {
+ assertThrows(ConversionException.class, () -> converter.convert(Enum.class, "java.lang.does.not.exist#MONDAY"));
+ }
}
diff --git a/src/test/java/org/apache/commons/beanutils2/converters/InstantConverterTest.java b/src/test/java/org/apache/commons/beanutils2/converters/InstantConverterTest.java
new file mode 100644
index 000000000..fd1b54cee
--- /dev/null
+++ b/src/test/java/org/apache/commons/beanutils2/converters/InstantConverterTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.beanutils2.converters;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.time.Instant;
+import java.time.Period;
+
+import org.apache.commons.beanutils2.ConversionException;
+import org.apache.commons.beanutils2.Converter;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test Case for the {@link InstantConverter} class.
+*/
+class InstantConverterTest {
+
+ private Converter converter;
+
+ protected Class> getExpectedType() {
+ return Period.class;
+ }
+
+ protected Converter makeConverter() {
+ return new InstantConverter();
+ }
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ converter = makeConverter();
+ }
+
+ @AfterEach
+ public void tearDown() throws Exception {
+ converter = null;
+ }
+
+ @Test
+ void testConvertingMilliseconds() {
+ final Instant expected = Instant.ofEpochMilli(1596500083605L);
+ final Instant actual = converter.convert(Instant.class, 1596500083605L);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ void testConvertingInstantString() {
+ final Instant expected = Instant.ofEpochMilli(1196676930000L);
+ final Instant actual = converter.convert(Instant.class, "2007-12-03T10:15:30.00Z");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ void testText() {
+ assertThrows(ConversionException.class, () -> converter.convert(Instant.class, "Hello, world!"));
+ }
+
+ @Test
+ void testLocalizedNumber() {
+ assertThrows(ConversionException.class, () -> converter.convert(Instant.class, "200,000,000,000"));
+ }
+}