Skip to content

Commit 570d20b

Browse files
committed
feat: updated existing converters
1 parent a31a210 commit 570d20b

12 files changed

+367
-139
lines changed

src/main/java/org/apache/commons/beanutils2/ConvertUtilsBean.java

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.nio.file.Path;
2727
import java.sql.Timestamp;
2828
import java.time.Duration;
29+
import java.time.Instant;
2930
import java.time.LocalDate;
3031
import java.time.LocalDateTime;
3132
import java.time.LocalTime;
@@ -57,6 +58,7 @@
5758
import org.apache.commons.beanutils2.converters.EnumConverter;
5859
import org.apache.commons.beanutils2.converters.FileConverter;
5960
import org.apache.commons.beanutils2.converters.FloatConverter;
61+
import org.apache.commons.beanutils2.converters.InstantConverter;
6062
import org.apache.commons.beanutils2.converters.IntegerConverter;
6163
import org.apache.commons.beanutils2.converters.LocalDateConverter;
6264
import org.apache.commons.beanutils2.converters.LocalDateTimeConverter;
@@ -112,6 +114,7 @@
112114
* <li>java.sql.Date (no default value)</li>
113115
* <li>java.sql.Time (no default value)</li>
114116
* <li>java.sql.Timestamp (no default value)</li>
117+
* <li>java.time.Instant (no default value)</li>
115118
* <li>java.time.LocalDate (no default value)</li>
116119
* <li>java.time.LocalDateTime (no default value)</li>
117120
* <li>java.time.LocalTime (no default value)</li>
@@ -545,6 +548,7 @@ private void registerArrays(final boolean throwException, final int defaultArray
545548
registerArrayConverter(java.util.Date.class, new DateConverter(), throwException, defaultArraySize);
546549
registerArrayConverter(Calendar.class, new CalendarConverter(), throwException, defaultArraySize);
547550
registerArrayConverter(File.class, new FileConverter(), throwException, defaultArraySize);
551+
registerArrayConverter(Instant.class, new InstantConverter(), throwException, defaultArraySize);
548552
registerArrayConverter(Path.class, new PathConverter(), throwException, defaultArraySize);
549553
registerArrayConverter(java.sql.Date.class, new SqlDateConverter(), throwException, defaultArraySize);
550554
registerArrayConverter(java.sql.Time.class, new SqlTimeConverter(), throwException, defaultArraySize);
@@ -578,6 +582,7 @@ private void registerArrays(final boolean throwException, final int defaultArray
578582
* <li>{@link java.util.Date.class} - {@link DateConverter}</li>
579583
* <li>{@link java.util.Calendar.class} - {@link CalendarConverter}</li>
580584
* <li>{@code File.class} - {@link FileConverter}</li>
585+
* <li>{@code Instant.class} - {@link InstantConverter}</li>
581586
* <li>{@code Path.class} - {@link PathConverter}</li>
582587
* <li>{@link java.sql.Date.class} - {@link SqlDateConverter}</li>
583588
* <li>{@link java.sql.Time.class} - {@link SqlTimeConverter}</li>
@@ -610,6 +615,7 @@ private void registerOther(final boolean throwException) {
610615
register(java.util.Date.class, throwException ? new DateConverter() : new DateConverter(null));
611616
register(Calendar.class, throwException ? new CalendarConverter() : new CalendarConverter(null));
612617
register(File.class, throwException ? new FileConverter() : new FileConverter(null));
618+
register(Instant.class, throwException ? new InstantConverter() : new InstantConverter(null));
613619
register(Path.class, throwException ? new PathConverter() : new PathConverter(null));
614620
register(java.sql.Date.class, throwException ? new SqlDateConverter() : new SqlDateConverter(null));
615621
register(java.sql.Time.class, throwException ? new SqlTimeConverter() : new SqlTimeConverter(null));

src/main/java/org/apache/commons/beanutils2/converters/CharacterConverter.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,19 @@
2222
* <p>
2323
* Can be configured to either return a <i>default value</i> or throw a
2424
* {@code ConversionException} if a conversion error occurs.
25+
* </p>
26+
*
27+
* <p>
28+
* Also accepts hexadecimal {@link String strings} if values are prefixed with {@link #HEX_PREFIX}.
29+
* </p>
2530
*
2631
* @since 1.3
2732
*/
2833
public final class CharacterConverter extends AbstractConverter<Character> {
2934

35+
/** Determines if an input is a hexadecimal {@link String}. */
36+
private static final String HEX_PREFIX = "0x";
37+
3038
/**
3139
* Constructs a <b>java.lang.Character</b> <i>Converter</i> that throws
3240
* a {@code ConversionException} if an error occurs.
@@ -84,7 +92,19 @@ protected String convertToString(final Object value) {
8492
@Override
8593
protected <T> T convertToType(final Class<T> type, final Object value) throws Exception {
8694
if (Character.class.equals(type) || Character.TYPE.equals(type)) {
87-
return type.cast(Character.valueOf(value.toString().charAt(0)));
95+
final String stringValue = toString(value);
96+
97+
if (stringValue.isEmpty()) {
98+
throw new IllegalArgumentException("Value must not be empty");
99+
}
100+
101+
if (stringValue.length() > 2 && stringValue.substring(0, 2).equalsIgnoreCase(HEX_PREFIX)) {
102+
final String substring = stringValue.substring(HEX_PREFIX.length());
103+
final int hex = Integer.parseInt(substring, 16);
104+
return type.cast((char) hex);
105+
}
106+
107+
return type.cast(stringValue.charAt(0));
88108
}
89109

90110
throw conversionException(type, value);

src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java

+22
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.time.OffsetDateTime;
2626
import java.time.ZoneId;
2727
import java.time.ZonedDateTime;
28+
import java.time.format.DateTimeParseException;
2829
import java.time.temporal.TemporalAccessor;
2930
import java.util.Calendar;
3031
import java.util.Date;
@@ -152,6 +153,8 @@ protected String convertToString(final Object value) {
152153
} else if (value instanceof TemporalAccessor) {
153154
// Backstop for other TemporalAccessor implementations.
154155
date = Date.from(Instant.from(((TemporalAccessor) value)));
156+
} else if (value instanceof Instant) {
157+
date = Date.from((Instant) value);
155158
}
156159

157160
String result = null;
@@ -188,6 +191,7 @@ protected String convertToString(final Object value) {
188191
* <li>{@link java.time.LocalDate}</li>
189192
* <li>{@link java.time.LocalDateTime}</li>
190193
* <li>{@link java.time.OffsetDateTime}</li>
194+
* <li>{@link java.time.Instant}</li>
191195
* <li>{@link java.time.ZonedDateTime}</li>
192196
* <li>{@link java.sql.Date}</li>
193197
* <li>{@link java.sql.Time}</li>
@@ -269,6 +273,11 @@ protected <T> T convertToType(final Class<T> targetType, final Object value) thr
269273
return toDate(targetType, date.toInstant().toEpochMilli());
270274
}
271275

276+
if (value instanceof Instant) {
277+
final Instant date = (Instant) value;
278+
return toDate(targetType, date.toEpochMilli());
279+
}
280+
272281
// Convert all other types to String & handle
273282
final String stringValue = toTrim(value);
274283
if (stringValue.isEmpty()) {
@@ -587,6 +596,10 @@ private <T> T toDate(final Class<T> type, final long value) {
587596
return type.cast(offsetDateTime);
588597
}
589598

599+
if (type.equals(Instant.class)) {
600+
return type.cast(Instant.ofEpochMilli(value));
601+
}
602+
590603
// java.util.Calendar
591604
if (type.equals(Calendar.class)) {
592605
Calendar calendar = null;
@@ -620,6 +633,7 @@ private <T> T toDate(final Class<T> type, final long value) {
620633
* <li>{@link java.sql.Date}</li>
621634
* <li>{@link java.sql.Time}</li>
622635
* <li>{@link java.sql.Timestamp}</li>
636+
* <li>{@link java.time.Instant}</li>
623637
* </ul>
624638
* <p>
625639
* <strong>N.B.</strong> No default String conversion
@@ -663,6 +677,14 @@ private <T> T toDate(final Class<T> type, final String value) {
663677
}
664678
}
665679

680+
if (type.equals(Instant.class)) {
681+
try {
682+
return type.cast(Instant.parse(value));
683+
} catch (final DateTimeParseException ex) {
684+
throw new ConversionException("String must be in ISO-8601 format to create a java.time.Instant");
685+
}
686+
}
687+
666688
final String msg = toString(getClass()) + " does not support default String to '"
667689
+ toString(type) + "' conversion.";
668690
if (log().isWarnEnabled()) {

src/main/java/org/apache/commons/beanutils2/converters/DurationConverter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public DurationConverter(final Duration defaultValue) {
6363
@Override
6464
protected <T> T convertToType(final Class<T> type, final Object value) throws Throwable {
6565
if (Duration.class.equals(type)) {
66-
return type.cast(Duration.parse((String.valueOf(value))));
66+
return type.cast(Duration.parse(toString(value)));
6767
}
6868

6969
throw conversionException(type, value);

src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java

+37-7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
*/
1717
package org.apache.commons.beanutils2.converters;
1818

19+
import java.util.regex.Matcher;
20+
import java.util.regex.Pattern;
21+
1922
/**
2023
* {@link org.apache.commons.beanutils2.Converter} implementation that handles conversion
2124
* to and from <b>java.lang.Enum</b> objects.
@@ -30,6 +33,11 @@
3033
*/
3134
public final class EnumConverter<E extends Enum<E>> extends AbstractConverter<Enum<E>> {
3235

36+
/** Matches if a given input is an enum string. */
37+
private static final Pattern ENUM_PATTERN = Pattern.compile(
38+
"((?:[a-z\\d.]+)*)\\.([A-Za-z\\d]+)[#.]([A-Z\\d_]+)"
39+
);
40+
3341
/**
3442
* Constructs a <b>java.lang.Enum</b> <i>Converter</i> that throws
3543
* a {@code ConversionException} if an error occurs.
@@ -63,15 +71,37 @@ public EnumConverter(final Enum<E> defaultValue) {
6371
@Override
6472
protected <R> R convertToType(final Class<R> type, final Object value) throws Throwable {
6573
if (Enum.class.isAssignableFrom(type)) {
66-
final String enumValue = String.valueOf(value);
67-
final R[] constants = type.getEnumConstants();
68-
if (constants == null) {
69-
throw conversionException(type, value);
74+
final String stringValue = toString(value);
75+
76+
try {
77+
return type.cast((Enum) Enum.valueOf((Class) type, stringValue));
78+
} catch (IllegalArgumentException ex) {
79+
// Continue to check fully qualified name.
80+
}
81+
82+
Matcher matcher = ENUM_PATTERN.matcher(stringValue);
83+
84+
if (!matcher.matches()) {
85+
throw new IllegalArgumentException(
86+
"Value doesn't follow Enum naming convention, expecting input like: java.time.DayOfWeek.MONDAY");
7087
}
71-
for (final R candidate : constants) {
72-
if (((Enum)candidate).name().equalsIgnoreCase(enumValue)) {
73-
return candidate;
88+
89+
String className = matcher.group(1) + "." + matcher.group(2);
90+
91+
try {
92+
Class classForName = Class.forName(className);
93+
94+
if (!classForName.isEnum()) {
95+
throw new IllegalArgumentException("Value isn't an enumerated type.");
7496
}
97+
98+
if (!type.isAssignableFrom(classForName)) {
99+
throw new IllegalArgumentException("Class is not the required type.");
100+
}
101+
102+
return type.cast((Enum) Enum.valueOf(classForName, matcher.group(3)));
103+
} catch (ClassNotFoundException ex) {
104+
throw new IllegalArgumentException("Class \"" + className + "\" doesn't exist.", ex);
75105
}
76106
}
77107

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.beanutils2.converters;
18+
19+
import java.time.Instant;
20+
21+
/**
22+
* {@link org.apache.commons.beanutils2.Converter} implementation that handles conversion
23+
* to and from {@link Instant} objects.
24+
*
25+
* <p>
26+
* Can be configured to either return a <i>default value</i> or throw a
27+
* {@code ConversionException} if a conversion error occurs.
28+
* </p>
29+
*
30+
* @since 2.0
31+
* @see Instant
32+
*/
33+
public final class InstantConverter extends DateTimeConverter<Instant> {
34+
35+
/**
36+
* Constructs a {@link Instant} <i>Converter</i> that throws a {@code ConversionException} if an
37+
* error occurs.
38+
*/
39+
public InstantConverter() {
40+
super();
41+
}
42+
43+
/**
44+
* Constructs a {@link Instant} <i>Converter</i> that returns a default value if an error occurs.
45+
*
46+
* @param defaultValue The default value to be returned if the value to be converted is missing or an error occurs
47+
* converting the value.
48+
*/
49+
public InstantConverter(final Instant defaultValue) {
50+
super(defaultValue);
51+
}
52+
53+
/**
54+
* Gets the default type this {@code Converter} handles.
55+
*
56+
* @return The default type this {@code Converter} handles.
57+
*/
58+
@Override
59+
protected Class<Instant> getDefaultType() {
60+
return Instant.class;
61+
}
62+
}

src/main/java/org/apache/commons/beanutils2/converters/PeriodConverter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public PeriodConverter(final Period defaultValue) {
6363
@Override
6464
protected <T> T convertToType(final Class<T> type, final Object value) throws Throwable {
6565
if (Period.class.equals(type)) {
66-
return type.cast(Period.parse((String.valueOf(value))));
66+
return type.cast(Period.parse(toString(value)));
6767
}
6868

6969
throw conversionException(type, value);

0 commit comments

Comments
 (0)