Skip to content

Commit 90b179a

Browse files
committed
feat: updated existing converters
1 parent d121047 commit 90b179a

12 files changed

+376
-234
lines changed

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

+5
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.DurationConverter;
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;
@@ -488,6 +490,7 @@ private void registerStandard(final boolean throwException, final boolean defaul
488490
* <li>{@code java.util.Date.class} - {@link DateConverter}</li>
489491
* <li>{@code java.util.Calendar.class} - {@link CalendarConverter}</li>
490492
* <li>{@code File.class} - {@link FileConverter}</li>
493+
* <li>{@code Instant.class} - {@link InstantConverter}</li>
491494
* <li>{@code Path.class} - {@link PathConverter}</li>
492495
* <li>{@code java.sql.Date.class} - {@link SqlDateConverter}</li>
493496
* <li>{@code java.sql.Time.class} - {@link SqlTimeConverter}</li>
@@ -520,6 +523,7 @@ private void registerOther(final boolean throwException) {
520523
register(java.util.Date.class, throwException ? new DateConverter() : new DateConverter(null));
521524
register(Calendar.class, throwException ? new CalendarConverter() : new CalendarConverter(null));
522525
register(File.class, throwException ? new FileConverter() : new FileConverter(null));
526+
register(Instant.class, throwException ? new InstantConverter() : new InstantConverter(null));
523527
register(Path.class, throwException ? new PathConverter() : new PathConverter(null));
524528
register(java.sql.Date.class, throwException ? new SqlDateConverter() : new SqlDateConverter(null));
525529
register(java.sql.Time.class, throwException ? new SqlTimeConverter() : new SqlTimeConverter(null));
@@ -586,6 +590,7 @@ private void registerArrays(final boolean throwException, final int defaultArray
586590
registerArrayConverter(java.util.Date.class, new DateConverter(), throwException, defaultArraySize);
587591
registerArrayConverter(Calendar.class, new DateConverter(), throwException, defaultArraySize);
588592
registerArrayConverter(File.class, new FileConverter(), throwException, defaultArraySize);
593+
registerArrayConverter(Instant.class, new InstantConverter(), throwException, defaultArraySize);
589594
registerArrayConverter(Path.class, new PathConverter(), throwException, defaultArraySize);
590595
registerArrayConverter(java.sql.Date.class, new SqlDateConverter(), throwException, defaultArraySize);
591596
registerArrayConverter(java.sql.Time.class, new SqlTimeConverter(), throwException, defaultArraySize);

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

+24-1
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,21 @@
2323
* Can be configured to either return a <i>default value</i> or throw a
2424
* {@code ConversionException} if a conversion error occurs.
2525
*
26+
* <p>
27+
* This converter accept hexadecimal {@link String}s if obtaining a
28+
* character from it's numeric value is desired.
29+
*
30+
* Intended for cases where there are concerns regarding the environment,
31+
* such as system/file encodings between clients, applications, and servers.
32+
* </p>
33+
*
2634
* @since 1.3
2735
*/
2836
public final class CharacterConverter extends AbstractConverter {
2937

38+
/** Determines if an input is a hexadecimal {@link String}. */
39+
private static final String HEX_PREFIX = "0x";
40+
3041
/**
3142
* Constructs a <b>java.lang.Character</b> <i>Converter</i> that throws
3243
* a {@code ConversionException} if an error occurs.
@@ -83,7 +94,19 @@ protected String convertToString(final Object value) {
8394
@Override
8495
protected <T> T convertToType(final Class<T> type, final Object value) throws Exception {
8596
if (Character.class.equals(type) || Character.TYPE.equals(type)) {
86-
return type.cast(Character.valueOf(value.toString().charAt(0)));
97+
final String stringValue = toString(value);
98+
99+
if (stringValue.isEmpty()) {
100+
throw new IllegalArgumentException("Value must not be empty");
101+
}
102+
103+
if (stringValue.length() > 2 && stringValue.substring(0, 2).equalsIgnoreCase(HEX_PREFIX)) {
104+
final String substring = stringValue.substring(HEX_PREFIX.length());
105+
final int hex = Integer.parseInt(substring, 16);
106+
return type.cast((char) hex);
107+
}
108+
109+
return type.cast(stringValue.charAt(0));
87110
}
88111

89112
throw conversionException(type, value);

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

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

19+
import org.apache.commons.beanutils2.ConversionException;
20+
1921
import java.text.DateFormat;
2022
import java.text.ParsePosition;
2123
import java.text.SimpleDateFormat;
@@ -25,13 +27,12 @@
2527
import java.time.OffsetDateTime;
2628
import java.time.ZoneId;
2729
import java.time.ZonedDateTime;
30+
import java.time.format.DateTimeParseException;
2831
import java.util.Calendar;
2932
import java.util.Date;
3033
import java.util.Locale;
3134
import java.util.TimeZone;
3235

33-
import org.apache.commons.beanutils2.ConversionException;
34-
3536
/**
3637
* {@link org.apache.commons.beanutils2.Converter} implementation
3738
* that handles conversion to and from <b>date/time</b> objects.
@@ -44,6 +45,7 @@
4445
* <li>{@code java.time.LocalDate}</li>
4546
* <li>{@code java.time.LocalDateTime}</li>
4647
* <li>{@code java.time.OffsetDateTime}</li>
48+
* <li>{@code java.time.Instant}</li>
4749
* <li>{@code java.time.ZonedDateTime}</li>
4850
* <li>{@code java.sql.Date}</li>
4951
* <li>{@code java.sql.Time}</li>
@@ -228,19 +230,21 @@ protected String convertToString(final Object value) throws Throwable {
228230

229231
Date date = null;
230232
if (value instanceof Date) {
231-
date = (Date)value;
233+
date = (Date) value;
232234
} else if (value instanceof Calendar) {
233-
date = ((Calendar)value).getTime();
235+
date = ((Calendar) value).getTime();
234236
} else if (value instanceof Long) {
235-
date = new Date(((Long)value).longValue());
237+
date = new Date(((Long) value).longValue());
236238
} else if (value instanceof LocalDateTime) {
237-
date = java.sql.Timestamp.valueOf(((LocalDateTime)value));
239+
date = java.sql.Timestamp.valueOf(((LocalDateTime) value));
238240
} else if (value instanceof LocalDate) {
239-
date = java.sql.Date.valueOf(((LocalDate)value));
241+
date = java.sql.Date.valueOf(((LocalDate) value));
240242
} else if (value instanceof ZonedDateTime) {
241-
date = Date.from(((ZonedDateTime)value).toInstant());
243+
date = Date.from(((ZonedDateTime) value).toInstant());
242244
} else if (value instanceof OffsetDateTime) {
243-
date = Date.from(((OffsetDateTime)value).toInstant());
245+
date = Date.from(((OffsetDateTime) value).toInstant());
246+
} else if (value instanceof Instant) {
247+
date = Date.from(((Instant) value));
244248
}
245249

246250
String result = null;
@@ -260,7 +264,7 @@ protected String convertToString(final Object value) throws Throwable {
260264
result = value.toString();
261265
if (log().isDebugEnabled()) {
262266
log().debug(" Converted to String using toString() '" + result + "'");
263-
}
267+
}
264268
}
265269
return result;
266270
}
@@ -277,6 +281,7 @@ protected String convertToString(final Object value) throws Throwable {
277281
* <li>{@code java.time.LocalDate}</li>
278282
* <li>{@code java.time.LocalDateTime}</li>
279283
* <li>{@code java.time.OffsetDateTime}</li>
284+
* <li>{@code java.time.Instant}</li>
280285
* <li>{@code java.time.ZonedDateTime}</li>
281286
* <li>{@code java.sql.Date}</li>
282287
* <li>{@code java.sql.Time}</li>
@@ -320,46 +325,51 @@ protected <T> T convertToType(final Class<T> targetType, final Object value) thr
320325

321326
// Handle Date (includes java.sql.Date & java.sql.Time)
322327
if (value instanceof Date) {
323-
final Date date = (Date)value;
328+
final Date date = (Date) value;
324329
return toDate(targetType, date.getTime());
325330
}
326331

327332
// Handle Calendar
328333
if (value instanceof Calendar) {
329-
final Calendar calendar = (Calendar)value;
334+
final Calendar calendar = (Calendar) value;
330335
return toDate(targetType, calendar.getTime().getTime());
331336
}
332337

333338
// Handle Long
334339
if (value instanceof Long) {
335-
final Long longObj = (Long)value;
340+
final Long longObj = (Long) value;
336341
return toDate(targetType, longObj.longValue());
337342
}
338343

339344
// Handle LocalDate
340345
if (value instanceof LocalDate) {
341-
final LocalDate date = (LocalDate)value;
346+
final LocalDate date = (LocalDate) value;
342347
return toDate(targetType, date.atStartOfDay(getZoneId()).toInstant().toEpochMilli());
343348
}
344349

345350
// Handle LocalDateTime
346351
if (value instanceof LocalDateTime) {
347-
final LocalDateTime date = (LocalDateTime)value;
352+
final LocalDateTime date = (LocalDateTime) value;
348353
return toDate(targetType, date.atZone(getZoneId()).toInstant().toEpochMilli());
349354
}
350355

351356
// Handle ZonedDateTime
352357
if (value instanceof ZonedDateTime) {
353-
final ZonedDateTime date = (ZonedDateTime)value;
358+
final ZonedDateTime date = (ZonedDateTime) value;
354359
return toDate(targetType, date.toInstant().toEpochMilli());
355360
}
356361

357362
// Handle OffsetDateTime
358363
if (value instanceof OffsetDateTime) {
359-
final OffsetDateTime date = (OffsetDateTime)value;
364+
final OffsetDateTime date = (OffsetDateTime) value;
360365
return toDate(targetType, date.toInstant().toEpochMilli());
361366
}
362367

368+
if (value instanceof Instant) {
369+
final Instant date = (Instant) value;
370+
return toDate(targetType, date.toEpochMilli());
371+
}
372+
363373
// Convert all other types to String & handle
364374
final String stringValue = toTrim(value);
365375
if (stringValue.isEmpty()) {
@@ -454,6 +464,10 @@ private <T> T toDate(final Class<T> type, final long value) {
454464
return type.cast(offsetDateTime);
455465
}
456466

467+
if (type.equals(Instant.class)) {
468+
return type.cast(Instant.ofEpochMilli(value));
469+
}
470+
457471
// java.util.Calendar
458472
if (type.equals(Calendar.class)) {
459473
Calendar calendar = null;
@@ -487,6 +501,7 @@ private <T> T toDate(final Class<T> type, final long value) {
487501
* <li>{@code java.sql.Date}</li>
488502
* <li>{@code java.sql.Time}</li>
489503
* <li>{@code java.sql.Timestamp}</li>
504+
* <li>{@code java.time.Instant}</li>
490505
* </ul>
491506
* <p>
492507
* <strong>N.B.</strong> No default String conversion
@@ -530,6 +545,14 @@ private <T> T toDate(final Class<T> type, final String value) {
530545
}
531546
}
532547

548+
if (type.equals(Instant.class)) {
549+
try {
550+
return type.cast(Instant.parse(value));
551+
} catch (final DateTimeParseException ex) {
552+
throw new ConversionException("String must be in ISO-8601 format to create a java.time.Instant");
553+
}
554+
}
555+
533556
final String msg = toString(getClass()) + " does not support default String to '"
534557
+ toString(type) + "' conversion.";
535558
if (log().isWarnEnabled()) {

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,10 @@ protected Class<?> getDefaultType() {
7474
@Override
7575
protected <T> T convertToType(final Class<T> type, final Object value) throws Throwable {
7676
if (Duration.class.equals(type)) {
77-
return type.cast(Duration.parse((String.valueOf(value))));
77+
final String stringValue = toString(value);
78+
return type.cast(Duration.parse(stringValue));
7879
}
7980

8081
throw conversionException(type, value);
8182
}
82-
8383
}

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

+38-9
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.
@@ -29,6 +32,11 @@
2932
*/
3033
public final class EnumConverter extends AbstractConverter {
3134

35+
/** Matches if a given input is an enum string. */
36+
private static final Pattern ENUM_PATTERN = Pattern.compile(
37+
"((?:[a-z\\d.]+)*)\\.([A-Za-z\\d]+)[#.]([A-Z\\d_]+)"
38+
);
39+
3240
/**
3341
* Constructs a <b>java.lang.Enum</b> <i>Converter</i> that throws
3442
* a {@code ConversionException} if an error occurs.
@@ -71,21 +79,42 @@ protected Class<?> getDefaultType() {
7179
*/
7280
@SuppressWarnings({ "rawtypes" })
7381
@Override
74-
protected <T> T convertToType(final Class<T> type, final Object value) throws Throwable {
82+
protected <T> T convertToType(final Class<T> type, final Object value) throws Throwable {
7583
if (Enum.class.isAssignableFrom(type)) {
76-
final String enumValue = String.valueOf(value);
77-
final T[] constants = type.getEnumConstants();
78-
if (constants == null) {
79-
throw conversionException(type, value);
84+
final String stringValue = toString(value);
85+
86+
try {
87+
return type.cast((Enum) Enum.valueOf((Class) type, stringValue));
88+
} catch (IllegalArgumentException ex) {
89+
// Continue to check fully qualified name.
8090
}
81-
for (final T candidate : constants) {
82-
if (((Enum)candidate).name().equalsIgnoreCase(enumValue)) {
83-
return candidate;
91+
92+
Matcher matcher = ENUM_PATTERN.matcher(stringValue);
93+
94+
if (!matcher.matches()) {
95+
throw new IllegalArgumentException(
96+
"Value doesn't follow Java naming conventions, expecting input like: java.time.DayOfWeek.MONDAY");
97+
}
98+
99+
String className = matcher.group(1) + "." + matcher.group(2);
100+
101+
try {
102+
Class classForName = Class.forName(className);
103+
104+
if (!classForName.isEnum()) {
105+
throw new IllegalArgumentException("Value isn't an enumerated type.");
106+
}
107+
108+
if (!type.isAssignableFrom(classForName)) {
109+
throw new IllegalArgumentException("Class is not the required type.");
84110
}
111+
112+
return type.cast((Enum) Enum.valueOf(classForName, matcher.group(3)));
113+
} catch (ClassNotFoundException ex) {
114+
throw new IllegalArgumentException("Class \"" + className + "\" doesn't exist.", ex);
85115
}
86116
}
87117

88118
throw conversionException(type, value);
89119
}
90-
91120
}

0 commit comments

Comments
 (0)