diff --git a/src/main/java/com/cedarsoftware/util/ClassUtilities.java b/src/main/java/com/cedarsoftware/util/ClassUtilities.java index 622ee614..185f9342 100644 --- a/src/main/java/com/cedarsoftware/util/ClassUtilities.java +++ b/src/main/java/com/cedarsoftware/util/ClassUtilities.java @@ -40,7 +40,7 @@ public class ClassUtilities { private static final Set> prims = new HashSet<>(); - private static final Map> nameToClass = new HashMap(); + private static final Map> nameToClass = new HashMap<>(); static { diff --git a/src/main/java/com/cedarsoftware/util/convert/StringConversions.java b/src/main/java/com/cedarsoftware/util/convert/StringConversions.java index e7991697..5be5953b 100644 --- a/src/main/java/com/cedarsoftware/util/convert/StringConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/StringConversions.java @@ -75,16 +75,16 @@ static String asString(Object from) { } static Byte toByte(Object from, Converter converter) { - String s = asString(from); - if (s.isEmpty()) { + String str = StringUtilities.trimToEmpty((String) from); + if (str.isEmpty()) { return CommonValues.BYTE_ZERO; } try { - return Byte.valueOf(s); + return Byte.valueOf(str); } catch (NumberFormatException e) { - Long value = toLong(s, bigDecimalMinByte, bigDecimalMaxByte); + Long value = toLong(str, bigDecimalMinByte, bigDecimalMaxByte); if (value == null) { - throw new IllegalArgumentException("Value '" + s + "' not parseable as a byte value or outside " + Byte.MIN_VALUE + " to " + Byte.MAX_VALUE); + throw new IllegalArgumentException("Value '" + str + "' not parseable as a byte value or outside " + Byte.MIN_VALUE + " to " + Byte.MAX_VALUE); } return value.byteValue(); } @@ -420,7 +420,15 @@ static OffsetTime toOffsetTime(Object from, Converter converter) { } static Instant toInstant(Object from, Converter converter) { - return parseDate(from, converter).map(ZonedDateTime::toInstant).orElse(null); + String s = (String)from; + if (StringUtilities.isEmpty(s)) { + return null; + } + try { + return Instant.parse(s); + } catch (Exception e) { + return parseDate(s, converter).map(ZonedDateTime::toInstant).orElse(null); + } } static char[] toCharArray(Object from, Converter converter) { diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java index 93be6695..237c2603 100644 --- a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java +++ b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java @@ -52,26 +52,28 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /** - * @author John DeRegnaucourt (jdereg@gmail.com) & Ken Partlow - *
- * Copyright (c) Cedar Software LLC - *

- * Licensed 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. + * @author John DeRegnaucourt (jdereg@gmail.com) + * @author Kenny Partlow (kpartlow@gmail.com) + *
+ * Copyright (c) Cedar Software LLC + *

+ * Licensed 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 + *

+ * License + *

+ * 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. */ + class ConverterEverythingTest { private static final TimeZone TZ_TOKYO = TimeZone.getTimeZone("Asia/Tokyo"); private Converter converter; - private ConverterOptions options = new ConverterOptions() { + private final ConverterOptions options = new ConverterOptions() { public TimeZone getTimeZone() { return TZ_TOKYO; } @@ -232,6 +234,9 @@ public TimeZone getTimeZone() { { mapOf("_v", 128), Byte.MIN_VALUE }, { mapOf("_v", mapOf("_v", 128L)), Byte.MIN_VALUE }, // Prove use of recursive call to .convert() }); + TEST_DB.put(pair(Year.class, Byte.class), new Object[][] { + {Year.of(2024), new IllegalArgumentException("Unsupported conversion, source type [Year (2024)] target type 'Byte'")}, + }); TEST_DB.put(pair(String.class, Byte.class), new Object[][] { { "-1", (byte) -1 }, { "-1.1", (byte) -1 }, @@ -243,6 +248,7 @@ public TimeZone getTimeZone() { { "-128", (byte) -128 }, { "127", (byte) 127 }, { "", (byte) 0 }, + { " ", (byte) 0 }, { "crapola", new IllegalArgumentException("Value 'crapola' not parseable as a byte value or outside -128 to 127") }, { "54 crapola", new IllegalArgumentException("Value '54 crapola' not parseable as a byte value or outside -128 to 127") }, { "54crapola", new IllegalArgumentException("Value '54crapola' not parseable as a byte value or outside -128 to 127") }, @@ -406,6 +412,7 @@ public TimeZone getTimeZone() { { "-32768", (short) -32768 }, { "32767", (short) 32767 }, { "", (short) 0 }, + { " ", (short) 0 }, { "crapola", new IllegalArgumentException("Value 'crapola' not parseable as a short value or outside -32768 to 32767") }, { "54 crapola", new IllegalArgumentException("Value '54 crapola' not parseable as a short value or outside -32768 to 32767") }, { "54crapola", new IllegalArgumentException("Value '54crapola' not parseable as a short value or outside -32768 to 32767") }, @@ -425,6 +432,14 @@ public TimeZone getTimeZone() { { Year.of(9999), (short) 9999 }, }); + // Instant + TEST_DB.put(pair(String.class, Instant.class), new Object[][] { + { "", null }, + { " ", null }, + { "1980-01-01T00:00Z", Instant.parse("1980-01-01T00:00:00Z") }, + { "2024-12-31T23:59:59.999999999Z", Instant.parse("2024-12-31T23:59:59.999999999Z") }, + }); + // MonthDay TEST_DB.put(pair(Void.class, MonthDay.class), new Object[][] { { null, null }, @@ -886,9 +901,7 @@ private static Stream generateTestEverythingParams() { String targetName = Converter.getShortName(targetClass); Object[][] testData = entry.getValue(); - for (int i = 0; i < testData.length; i++) { - Object[] testPair = testData[i]; - + for (Object[] testPair : testData) { Object source = possiblyConvertSupplier(testPair[0]); Object target = possiblyConvertSupplier(testPair[1]); @@ -903,7 +916,11 @@ private static Stream generateTestEverythingParams() { @MethodSource("generateTestEverythingParams") void testConvert(String shortNameSource, String shortNameTarget, Object source, Object target, Class sourceClass, Class targetClass) { // Make sure source instance is of the sourceClass - assertTrue(source == null || Converter.toPrimitiveWrapperClass(sourceClass).isInstance(source), "source type mismatch"); + if (source == null) { + assertEquals(sourceClass, Void.class); + } else { + assertTrue(Converter.toPrimitiveWrapperClass(sourceClass).isInstance(source), "source type mismatch"); + } assertTrue(target == null || target instanceof Throwable || Converter.toPrimitiveWrapperClass(targetClass).isInstance(target), "target type mismatch"); // if the source/target are the same Class, then ensure identity lambda is used.