diff --git a/src/main/java/com/cedarsoftware/util/ArrayUtilities.java b/src/main/java/com/cedarsoftware/util/ArrayUtilities.java index 4ce99439..0456e6a5 100644 --- a/src/main/java/com/cedarsoftware/util/ArrayUtilities.java +++ b/src/main/java/com/cedarsoftware/util/ArrayUtilities.java @@ -31,6 +31,10 @@ public final class ArrayUtilities * Immutable common arrays. */ public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + + public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + public static final char[] EMPTY_CHAR_ARRAY = new char[0]; + public static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; /** diff --git a/src/main/java/com/cedarsoftware/util/convert/ByteArrayConversions.java b/src/main/java/com/cedarsoftware/util/convert/ByteArrayConversions.java new file mode 100644 index 00000000..a8021ffb --- /dev/null +++ b/src/main/java/com/cedarsoftware/util/convert/ByteArrayConversions.java @@ -0,0 +1,44 @@ +package com.cedarsoftware.util.convert; + +import com.cedarsoftware.util.StringUtilities; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.util.concurrent.atomic.AtomicInteger; + +public class ByteArrayConversions { + + static String toString(Object from, ConverterOptions options) { + byte[] bytes = (byte[])from; + return (bytes == null) ? StringUtilities.EMPTY : new String(bytes, options.getCharset()); + } + + static ByteBuffer toByteBuffer(Object from) { + return ByteBuffer.wrap((byte[])from); + } + + static String toString(Object from, Converter converter, ConverterOptions options) { + return toString(from, options); + } + + static ByteBuffer toByteBuffer(Object from, Converter converter, ConverterOptions options) { + return toByteBuffer(from); + } + + static CharBuffer toCharBuffer(Object from, Converter converter, ConverterOptions options) { + return CharBuffer.wrap(toString(from, options)); + } + + static char[] toCharArray(Object from, Converter converter, ConverterOptions options) { + return toString(from, options).toCharArray(); + } + + static StringBuffer toStringBuffer(Object from, Converter converter, ConverterOptions options) { + return new StringBuffer(toString(from, options)); + } + + static StringBuilder toStringBuilder(Object from, Converter converter, ConverterOptions options) { + return new StringBuilder(toString(from, options)); + } + +} diff --git a/src/main/java/com/cedarsoftware/util/convert/ByteBufferConversions.java b/src/main/java/com/cedarsoftware/util/convert/ByteBufferConversions.java new file mode 100644 index 00000000..47546f04 --- /dev/null +++ b/src/main/java/com/cedarsoftware/util/convert/ByteBufferConversions.java @@ -0,0 +1,66 @@ +package com.cedarsoftware.util.convert; + +import com.cedarsoftware.util.StringUtilities; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import static com.cedarsoftware.util.ArrayUtilities.EMPTY_BYTE_ARRAY; + +public class ByteBufferConversions { + + static ByteBuffer asReadOnlyBuffer(Object from) { + // Create a readonly buffer so we aren't changing + // the original buffers mark and position when + // working with this buffer. This could be inefficient + // if constantly fed with writeable buffers so should be documented + return ((ByteBuffer)from).asReadOnlyBuffer(); + } + + static byte[] toByteArray(Object from) { + ByteBuffer buffer = asReadOnlyBuffer(from); + + if (buffer == null || !buffer.hasRemaining()) { + return EMPTY_BYTE_ARRAY; + } + + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + return bytes; + } + + static CharBuffer toCharBuffer(Object from, ConverterOptions options) { + ByteBuffer buffer = asReadOnlyBuffer(from); + return options.getCharset().decode(buffer); + } + + + static CharBuffer toCharBuffer(Object from, Converter converter, ConverterOptions options) { + return toCharBuffer(from, options); + } + + static ByteBuffer toByteBuffer(Object from, Converter converter, ConverterOptions options) { + return asReadOnlyBuffer(from); + } + + static byte[] toByteArray(Object from, Converter converter, ConverterOptions options) { + return toByteArray(from); + } + + static String toString(Object from, Converter converter, ConverterOptions options) { + return toCharBuffer(from, options).toString(); + } + + static char[] toCharArray(Object from, Converter converter, ConverterOptions options) { + return CharBufferConversions.toCharArray(toCharBuffer(from, options)); + } + + static StringBuffer toStringBuffer(Object from, Converter converter, ConverterOptions options) { + return new StringBuffer(toCharBuffer(from, options)); + } + + static StringBuilder toStringBuilder(Object from, Converter converter, ConverterOptions options) { + return new StringBuilder(toCharBuffer(from, options)); + } + +} diff --git a/src/main/java/com/cedarsoftware/util/convert/CharArrayConversions.java b/src/main/java/com/cedarsoftware/util/convert/CharArrayConversions.java new file mode 100644 index 00000000..a00485ca --- /dev/null +++ b/src/main/java/com/cedarsoftware/util/convert/CharArrayConversions.java @@ -0,0 +1,59 @@ +package com.cedarsoftware.util.convert; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.util.Arrays; + +public class CharArrayConversions { + + static String toString(Object from) { + char[] chars = (char[])from; + return new String(chars); + } + + static CharBuffer toCharBuffer(Object from) { + char[] chars = (char[])from; + return CharBuffer.wrap(chars); + } + + static ByteBuffer toByteBuffer(Object from, ConverterOptions options) { + return options.getCharset().encode(toCharBuffer(from)); + } + + + static String toString(Object from, Converter converter, ConverterOptions options) { + return toString(from); + } + + + static CharBuffer toCharBuffer(Object from, Converter converter, ConverterOptions options) { + return toCharBuffer(from); + } + + static StringBuffer toStringBuffer(Object from, Converter converter, ConverterOptions options) { + return new StringBuffer(toCharBuffer(from)); + } + + static StringBuilder toStringBuilder(Object from, Converter converter, ConverterOptions options) { + return new StringBuilder(toCharBuffer(from)); + } + + static ByteBuffer toByteBuffer(Object from, Converter converter, ConverterOptions options) { + return toByteBuffer(from, options); + } + + static byte[] toByteArray(Object from, Converter converter, ConverterOptions options) { + ByteBuffer buffer = toByteBuffer(from, options); + byte[] byteArray = new byte[buffer.remaining()]; + buffer.get(byteArray); + return byteArray; + } + + static char[] toCharArray(Object from, Converter converter, ConverterOptions options) { + char[] chars = (char[])from; + if (chars == null) { + return null; + } + return Arrays.copyOf(chars, chars.length); + } +} diff --git a/src/main/java/com/cedarsoftware/util/convert/CharBufferConversions.java b/src/main/java/com/cedarsoftware/util/convert/CharBufferConversions.java new file mode 100644 index 00000000..9c8c4299 --- /dev/null +++ b/src/main/java/com/cedarsoftware/util/convert/CharBufferConversions.java @@ -0,0 +1,57 @@ +package com.cedarsoftware.util.convert; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import static com.cedarsoftware.util.ArrayUtilities.EMPTY_BYTE_ARRAY; +import static com.cedarsoftware.util.ArrayUtilities.EMPTY_CHAR_ARRAY; + +public class CharBufferConversions { + static CharBuffer asReadOnlyBuffer(Object from) { + // Create a readonly buffer so we aren't changing + // the original buffers mark and position when + // working with this buffer. This could be inefficient + // if constantly fed with writeable buffers so should be documented + return ((CharBuffer)from).asReadOnlyBuffer(); + } + + static char[] toCharArray(Object from) { + CharBuffer buffer = asReadOnlyBuffer(from); + + if (buffer == null || !buffer.hasRemaining()) { + return EMPTY_CHAR_ARRAY; + } + + char[] chars = new char[buffer.remaining()]; + buffer.get(chars); + return chars; + } + + static CharBuffer toCharBuffer(Object from, Converter converter, ConverterOptions options) { + return asReadOnlyBuffer(from); + } + + static byte[] toByteArray(Object from, Converter converter, ConverterOptions options) { + return ByteBufferConversions.toByteArray(toByteBuffer(from, converter, options)); + } + + static ByteBuffer toByteBuffer(Object from, Converter converter, ConverterOptions options) { + return options.getCharset().encode(asReadOnlyBuffer(from)); + } + + static String toString(Object from, Converter converter, ConverterOptions options) { + return asReadOnlyBuffer(from).toString(); + } + + static char[] toCharArray(Object from, Converter converter, ConverterOptions options) { + return toCharArray(from); + } + + static StringBuffer toStringBuffer(Object from, Converter converter, ConverterOptions options) { + return new StringBuffer(asReadOnlyBuffer(from)); + } + + static StringBuilder toStringBuilder(Object from, Converter converter, ConverterOptions options) { + return new StringBuilder(asReadOnlyBuffer(from)); + } +} diff --git a/src/main/java/com/cedarsoftware/util/convert/ClassConversions.java b/src/main/java/com/cedarsoftware/util/convert/ClassConversions.java new file mode 100644 index 00000000..6311ec40 --- /dev/null +++ b/src/main/java/com/cedarsoftware/util/convert/ClassConversions.java @@ -0,0 +1,8 @@ +package com.cedarsoftware.util.convert; + +public class ClassConversions { + static String toString(Object from, Converter converter, ConverterOptions options) { + Class cls = (Class) from; + return cls.getName(); + } +} diff --git a/src/main/java/com/cedarsoftware/util/convert/Converter.java b/src/main/java/com/cedarsoftware/util/convert/Converter.java index a56a4480..d310f898 100644 --- a/src/main/java/com/cedarsoftware/util/convert/Converter.java +++ b/src/main/java/com/cedarsoftware/util/convert/Converter.java @@ -3,6 +3,8 @@ import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.sql.Timestamp; import java.time.Duration; import java.time.Instant; @@ -94,7 +96,7 @@ private static void buildPrimitiveWrappers() { } private static void buildFactoryConversions() { - // Byte/byte Conversions supported + // toByte DEFAULT_FACTORY.put(pair(Void.class, byte.class), NumberConversions::toByteZero); DEFAULT_FACTORY.put(pair(Void.class, Byte.class), VoidConversions::toNull); DEFAULT_FACTORY.put(pair(Byte.class, Byte.class), Converter::identity); @@ -115,7 +117,7 @@ private static void buildFactoryConversions() { DEFAULT_FACTORY.put(pair(Map.class, Byte.class), MapConversions::toByte); DEFAULT_FACTORY.put(pair(String.class, Byte.class), StringConversions::toByte); - // Short/short conversions supported + // toShort DEFAULT_FACTORY.put(pair(Void.class, short.class), NumberConversions::toShortZero); DEFAULT_FACTORY.put(pair(Void.class, Short.class), VoidConversions::toNull); DEFAULT_FACTORY.put(pair(Byte.class, Short.class), NumberConversions::toShort); @@ -135,7 +137,7 @@ private static void buildFactoryConversions() { DEFAULT_FACTORY.put(pair(Map.class, Short.class), MapConversions::toShort); DEFAULT_FACTORY.put(pair(String.class, Short.class), StringConversions::toShort); - // Integer/int conversions supported + // toInteger DEFAULT_FACTORY.put(pair(Void.class, int.class), NumberConversions::toIntZero); DEFAULT_FACTORY.put(pair(Void.class, Integer.class), VoidConversions::toNull); DEFAULT_FACTORY.put(pair(Byte.class, Integer.class), NumberConversions::toInt); @@ -155,7 +157,7 @@ private static void buildFactoryConversions() { DEFAULT_FACTORY.put(pair(Map.class, Integer.class), MapConversions::toInt); DEFAULT_FACTORY.put(pair(String.class, Integer.class), StringConversions::toInt); - // Long/long conversions supported + // toLong DEFAULT_FACTORY.put(pair(Void.class, long.class), NumberConversions::toLongZero); DEFAULT_FACTORY.put(pair(Void.class, Long.class), VoidConversions::toNull); DEFAULT_FACTORY.put(pair(Byte.class, Long.class), NumberConversions::toLong); @@ -183,7 +185,7 @@ private static void buildFactoryConversions() { DEFAULT_FACTORY.put(pair(Map.class, Long.class), MapConversions::toLong); DEFAULT_FACTORY.put(pair(String.class, Long.class), StringConversions::toLong); - // Float/float conversions supported + // toFloat DEFAULT_FACTORY.put(pair(Void.class, float.class), NumberConversions::toFloatZero); DEFAULT_FACTORY.put(pair(Void.class, Float.class), VoidConversions::toNull); DEFAULT_FACTORY.put(pair(Byte.class, Float.class), NumberConversions::toFloat); @@ -577,6 +579,10 @@ private static void buildFactoryConversions() { DEFAULT_FACTORY.put(pair(AtomicBoolean.class, String.class), StringConversions::toString); DEFAULT_FACTORY.put(pair(AtomicInteger.class, String.class), StringConversions::toString); DEFAULT_FACTORY.put(pair(AtomicLong.class, String.class), StringConversions::toString); + DEFAULT_FACTORY.put(pair(byte[].class, String.class), ByteArrayConversions::toString); + DEFAULT_FACTORY.put(pair(char[].class, String.class), CharArrayConversions::toString); + DEFAULT_FACTORY.put(pair(ByteBuffer.class, String.class), ByteBufferConversions::toString); + DEFAULT_FACTORY.put(pair(CharBuffer.class, String.class), CharBufferConversions::toString); DEFAULT_FACTORY.put(pair(Class.class, String.class), StringConversions::classToString); DEFAULT_FACTORY.put(pair(Date.class, String.class), DateConversions::dateToString); DEFAULT_FACTORY.put(pair(java.sql.Date.class, String.class), DateConversions::sqlDateToString); @@ -636,6 +642,66 @@ private static void buildFactoryConversions() { DEFAULT_FACTORY.put(pair(String.class, MonthDay.class), StringConversions::toMonthDay); DEFAULT_FACTORY.put(pair(Map.class, MonthDay.class), MapConversions::toMonthDay); + // toStringBuffer + DEFAULT_FACTORY.put(pair(Void.class, StringBuffer.class), VoidConversions::toNull); + DEFAULT_FACTORY.put(pair(String.class, StringBuffer.class), StringConversions::toStringBuffer); + DEFAULT_FACTORY.put(pair(StringBuilder.class, StringBuffer.class), StringConversions::toStringBuffer); + DEFAULT_FACTORY.put(pair(StringBuffer.class, StringBuffer.class), StringConversions::toStringBuffer); + DEFAULT_FACTORY.put(pair(ByteBuffer.class, StringBuffer.class), ByteBufferConversions::toStringBuffer); + DEFAULT_FACTORY.put(pair(CharBuffer.class, StringBuffer.class), CharBufferConversions::toStringBuffer); + DEFAULT_FACTORY.put(pair(char[].class, StringBuffer.class), CharArrayConversions::toStringBuffer); + DEFAULT_FACTORY.put(pair(byte[].class, StringBuffer.class), ByteArrayConversions::toStringBuffer); + + // toStringBuilder + DEFAULT_FACTORY.put(pair(Void.class, StringBuilder.class), VoidConversions::toNull); + DEFAULT_FACTORY.put(pair(String.class, StringBuilder.class), StringConversions::toStringBuilder); + DEFAULT_FACTORY.put(pair(StringBuilder.class, StringBuilder.class), StringConversions::toStringBuilder); + DEFAULT_FACTORY.put(pair(StringBuffer.class, StringBuilder.class), StringConversions::toStringBuilder); + DEFAULT_FACTORY.put(pair(ByteBuffer.class, StringBuilder.class), ByteBufferConversions::toStringBuilder); + DEFAULT_FACTORY.put(pair(CharBuffer.class, StringBuilder.class), CharBufferConversions::toStringBuilder); + DEFAULT_FACTORY.put(pair(char[].class, StringBuilder.class), CharArrayConversions::toStringBuilder); + DEFAULT_FACTORY.put(pair(byte[].class, StringBuilder.class), ByteArrayConversions::toStringBuilder); + + // toByteArray + DEFAULT_FACTORY.put(pair(Void.class, byte[].class), VoidConversions::toNull); + DEFAULT_FACTORY.put(pair(String.class, byte[].class), StringConversions::toByteArray); + DEFAULT_FACTORY.put(pair(StringBuilder.class, byte[].class), StringConversions::toByteArray); + DEFAULT_FACTORY.put(pair(StringBuffer.class, byte[].class), StringConversions::toByteArray); + DEFAULT_FACTORY.put(pair(ByteBuffer.class, byte[].class), ByteBufferConversions::toByteArray); + DEFAULT_FACTORY.put(pair(CharBuffer.class, byte[].class), CharBufferConversions::toByteArray); + DEFAULT_FACTORY.put(pair(char[].class, byte[].class), CharArrayConversions::toByteArray); + DEFAULT_FACTORY.put(pair(byte[].class, byte[].class), Converter::identity); + + // toCharArray + DEFAULT_FACTORY.put(pair(Void.class, char[].class), VoidConversions::toNull); + DEFAULT_FACTORY.put(pair(String.class, char[].class), StringConversions::toCharArray); + DEFAULT_FACTORY.put(pair(StringBuilder.class, char[].class), StringConversions::toCharArray); + DEFAULT_FACTORY.put(pair(StringBuffer.class, char[].class), StringConversions::toCharArray); + DEFAULT_FACTORY.put(pair(ByteBuffer.class, char[].class), ByteBufferConversions::toCharArray); + DEFAULT_FACTORY.put(pair(CharBuffer.class, char[].class), CharBufferConversions::toCharArray); + DEFAULT_FACTORY.put(pair(char[].class, char[].class), CharArrayConversions::toCharArray); + DEFAULT_FACTORY.put(pair(byte[].class, char[].class), ByteArrayConversions::toCharArray); + + //toCharBuffer + DEFAULT_FACTORY.put(pair(Void.class, CharBuffer.class), VoidConversions::toNull); + DEFAULT_FACTORY.put(pair(String.class, CharBuffer.class), StringConversions::toCharBuffer); + DEFAULT_FACTORY.put(pair(StringBuilder.class, CharBuffer.class), StringConversions::toCharBuffer); + DEFAULT_FACTORY.put(pair(StringBuffer.class, CharBuffer.class), StringConversions::toCharBuffer); + DEFAULT_FACTORY.put(pair(ByteBuffer.class, CharBuffer.class), ByteBufferConversions::toCharBuffer); + DEFAULT_FACTORY.put(pair(CharBuffer.class, CharBuffer.class), CharBufferConversions::toCharBuffer); + DEFAULT_FACTORY.put(pair(char[].class, CharBuffer.class), CharArrayConversions::toCharBuffer); + DEFAULT_FACTORY.put(pair(byte[].class, CharBuffer.class), ByteArrayConversions::toCharBuffer); + + // toByteBuffer + DEFAULT_FACTORY.put(pair(Void.class, ByteBuffer.class), VoidConversions::toNull); + DEFAULT_FACTORY.put(pair(String.class, ByteBuffer.class), StringConversions::toByteBuffer); + DEFAULT_FACTORY.put(pair(StringBuilder.class, ByteBuffer.class), StringConversions::toByteBuffer); + DEFAULT_FACTORY.put(pair(StringBuffer.class, ByteBuffer.class), StringConversions::toByteBuffer); + DEFAULT_FACTORY.put(pair(ByteBuffer.class, ByteBuffer.class), ByteBufferConversions::toByteBuffer); + DEFAULT_FACTORY.put(pair(CharBuffer.class, ByteBuffer.class), CharBufferConversions::toByteBuffer); + DEFAULT_FACTORY.put(pair(char[].class, ByteBuffer.class), CharArrayConversions::toByteBuffer); + DEFAULT_FACTORY.put(pair(byte[].class, ByteBuffer.class), ByteArrayConversions::toByteBuffer); + // Map conversions supported DEFAULT_FACTORY.put(pair(Void.class, Map.class), VoidConversions::toNull); DEFAULT_FACTORY.put(pair(Byte.class, Map.class), MapConversions::initMap); @@ -963,4 +1029,4 @@ private static Class toPrimitiveWrapperClass(Class primitiveClass) { private static T identity(T one, Converter converter, ConverterOptions options) { return one; } -} \ No newline at end of file +} diff --git a/src/main/java/com/cedarsoftware/util/convert/StringConversions.java b/src/main/java/com/cedarsoftware/util/convert/StringConversions.java index 46ac077d..48dd6a5c 100644 --- a/src/main/java/com/cedarsoftware/util/convert/StringConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/StringConversions.java @@ -3,6 +3,8 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.sql.Timestamp; import java.time.Duration; import java.time.Instant; @@ -23,6 +25,9 @@ import com.cedarsoftware.util.DateUtilities; import com.cedarsoftware.util.StringUtilities; +import static com.cedarsoftware.util.ArrayUtilities.EMPTY_BYTE_ARRAY; +import static com.cedarsoftware.util.ArrayUtilities.EMPTY_CHAR_ARRAY; + /** * @author John DeRegnaucourt (jdereg@gmail.com) *
@@ -50,8 +55,12 @@ public class StringConversions { private static final BigDecimal bigDecimalMaxLong = BigDecimal.valueOf(Long.MAX_VALUE); private static final BigDecimal bigDecimalMinLong = BigDecimal.valueOf(Long.MIN_VALUE); + static String asString(Object from) { + return from == null ? null : from.toString(); + } + static Byte toByte(Object from, Converter converter, ConverterOptions options) { - return toByte((String)from); + return toByte(asString(from)); } private static Byte toByte(String s) { @@ -94,7 +103,7 @@ static Integer toInt(Object from, Converter converter, ConverterOptions options) } private static Integer toInt(Object from) { - String str = StringUtilities.trimToEmpty((String)from); + String str = StringUtilities.trimToEmpty(asString(from)); if (str.isEmpty()) { return CommonValues.INTEGER_ZERO; } @@ -114,7 +123,7 @@ static Long toLong(Object from, Converter converter, ConverterOptions options) { } private static Long toLong(Object from) { - String str = StringUtilities.trimToEmpty((String)from); + String str = StringUtilities.trimToEmpty(asString(from)); if (str.isEmpty()) { return CommonValues.LONG_ZERO; } @@ -144,7 +153,7 @@ private static Long toLong(String s, BigDecimal low, BigDecimal high) { } static Float toFloat(Object from, Converter converter, ConverterOptions options) { - String str = StringUtilities.trimToEmpty((String)from); + String str = StringUtilities.trimToEmpty(asString(from)); if (str.isEmpty()) { return CommonValues.FLOAT_ZERO; } @@ -156,7 +165,7 @@ static Float toFloat(Object from, Converter converter, ConverterOptions options) } static Double toDouble(Object from, Converter converter, ConverterOptions options) { - String str = StringUtilities.trimToEmpty((String)from); + String str = StringUtilities.trimToEmpty(asString(from)); if (str.isEmpty()) { return CommonValues.DOUBLE_ZERO; } @@ -168,7 +177,7 @@ static Double toDouble(Object from, Converter converter, ConverterOptions option } static AtomicBoolean toAtomicBoolean(Object from, Converter converter, ConverterOptions options) { - return new AtomicBoolean(toBoolean((String)from)); + return new AtomicBoolean(toBoolean(asString(from))); } static AtomicInteger toAtomicInteger(Object from, Converter converter, ConverterOptions options) { @@ -194,11 +203,11 @@ private static Boolean toBoolean(String from) { } static Boolean toBoolean(Object from, Converter converter, ConverterOptions options) { - return toBoolean((String)from); + return toBoolean(asString(from)); } static char toCharacter(Object from, Converter converter, ConverterOptions options) { - String str = StringUtilities.trimToNull((String)from); + String str = StringUtilities.trimToNull(asString(from)); if (str == null) { return CommonValues.CHARACTER_ZERO; } @@ -210,7 +219,7 @@ static char toCharacter(Object from, Converter converter, ConverterOptions optio } static BigInteger toBigInteger(Object from, Converter converter, ConverterOptions options) { - String str = StringUtilities.trimToNull((String)from); + String str = StringUtilities.trimToNull(asString(from)); if (str == null) { return BigInteger.ZERO; } @@ -223,7 +232,7 @@ static BigInteger toBigInteger(Object from, Converter converter, ConverterOption } static BigDecimal toBigDecimal(Object from, Converter converter, ConverterOptions options) { - String str = StringUtilities.trimToEmpty((String)from); + String str = StringUtilities.trimToEmpty(asString(from)); if (str.isEmpty()) { return BigDecimal.ZERO; } @@ -336,7 +345,7 @@ static ZonedDateTime toZonedDateTime(Object from, Converter converter, Converter } static Instant toInstant(Object from, Converter converter, ConverterOptions options) { - String s = StringUtilities.trimToNull((String)from); + String s = StringUtilities.trimToNull(asString(from)); if (s == null) { return null; } @@ -365,7 +374,49 @@ private static Instant getInstant(String from, ConverterOptions options) { return instant; } + static char[] toCharArray(Object from, Converter converter, ConverterOptions options) { + String s = asString(from); + + if (s == null || s.isEmpty()) { + return EMPTY_CHAR_ARRAY; + } + + return s.toCharArray(); + } + + static CharBuffer toCharBuffer(Object from, Converter converter, ConverterOptions options) { + return CharBuffer.wrap(asString(from)); + } + + static byte[] toByteArray(Object from, ConverterOptions options) { + String s = asString(from); + + if (s == null || s.isEmpty()) { + return EMPTY_BYTE_ARRAY; + } + + return s.getBytes(options.getCharset()); + } + + + static byte[] toByteArray(Object from, Converter converter, ConverterOptions options) { + return toByteArray(from, options); + } + + static ByteBuffer toByteBuffer(Object from, Converter converter, ConverterOptions options) { + return ByteBuffer.wrap(toByteArray(from, options)); + } + static String toString(Object from, Converter converter, ConverterOptions options) { - return from.toString(); + return from == null ? null : from.toString(); + } + + static StringBuffer toStringBuffer(Object from, Converter converter, ConverterOptions options) { + return from == null ? null : new StringBuffer(from.toString()); } + + static StringBuilder toStringBuilder(Object from, Converter converter, ConverterOptions options) { + return from == null ? null : new StringBuilder(from.toString()); + } + } diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java index 27898d7d..fb23a31d 100644 --- a/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java +++ b/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java @@ -2,6 +2,10 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.sql.Timestamp; import java.time.Instant; import java.time.LocalDate; @@ -34,7 +38,10 @@ import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.NullSource; +import static com.cedarsoftware.util.ArrayUtilities.EMPTY_BYTE_ARRAY; +import static com.cedarsoftware.util.ArrayUtilities.EMPTY_CHAR_ARRAY; import static com.cedarsoftware.util.Converter.zonedDateTimeToMillis; +import static com.cedarsoftware.util.StringUtilities.EMPTY; import static com.cedarsoftware.util.convert.ConverterTest.fubar.bar; import static com.cedarsoftware.util.convert.ConverterTest.fubar.foo; import static org.assertj.core.api.Assertions.assertThat; @@ -1347,6 +1354,28 @@ void testCalendarToInstant(long epochMilli, ZoneId zoneId, LocalDateTime expecte assertThat(actual.toEpochMilli()).isEqualTo(epochMilli); } + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testCalendarToBigDecimal(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(epochMilli); + + BigDecimal actual = this.converter.convert(calendar, BigDecimal.class, createCustomZones(null, zoneId)); + assertThat(actual.longValue()).isEqualTo(epochMilli); + } + + @ParameterizedTest + @MethodSource("epochMillis_withLocalDateTimeInformation") + void testCalendarToBigInteger(long epochMilli, ZoneId zoneId, LocalDateTime expected) + { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(epochMilli); + + BigInteger actual = this.converter.convert(calendar, BigInteger.class, createCustomZones(null, zoneId)); + assertThat(actual.longValue()).isEqualTo(epochMilli); + } + @ParameterizedTest @MethodSource("epochMillis_withLocalDateTimeInformation") void testDateToLocalTime(long epochMilli, ZoneId zoneId, LocalDateTime expected) { @@ -4080,7 +4109,7 @@ void testNormieToWeirdoAndBack() assert this.converter.isConversionSupportedFor(Weirdo.class, Normie.class); } - private static Stream emptyStringToType_params() { + private static Stream emptyStringTypes_withSameAsReturns() { return Stream.of( Arguments.of("", byte.class, CommonValues.BYTE_ZERO), Arguments.of("", Byte.class, CommonValues.BYTE_ZERO), @@ -4099,18 +4128,38 @@ private static Stream emptyStringToType_params() { Arguments.of("", char.class, CommonValues.CHARACTER_ZERO), Arguments.of("", Character.class, CommonValues.CHARACTER_ZERO), Arguments.of("", BigDecimal.class, BigDecimal.ZERO), - Arguments.of("", BigInteger.class, BigInteger.ZERO) + Arguments.of("", BigInteger.class, BigInteger.ZERO), + Arguments.of("", String.class, EMPTY), + Arguments.of("", byte[].class, EMPTY_BYTE_ARRAY), + Arguments.of("", char[].class, EMPTY_CHAR_ARRAY) ); } @ParameterizedTest - @MethodSource("emptyStringToType_params") - void emptyStringToType(Object value, Class type, Object expected) + @MethodSource("emptyStringTypes_withSameAsReturns") + void testEmptyStringToType_whereTypeReturnsSpecificObject(Object value, Class type, Object expected) { Object converted = this.converter.convert(value, type); assertThat(converted).isSameAs(expected); } + private static Stream emptyStringTypes_notSameObject() { + return Stream.of( + Arguments.of("", ByteBuffer.class, ByteBuffer.wrap(EMPTY_BYTE_ARRAY)), + Arguments.of("", CharBuffer.class, CharBuffer.wrap(EMPTY_CHAR_ARRAY)) + ); + } + + @ParameterizedTest + @MethodSource("emptyStringTypes_notSameObject") + void testEmptyStringToType_whereTypeIsEqualButNotSameAs(Object value, Class type, Object expected) + { + Object converted = this.converter.convert(value, type); + assertThat(converted).isNotSameAs(expected); + assertThat(converted).isEqualTo(expected); + } + + @Test void emptyStringToAtomicBoolean() { @@ -4132,6 +4181,81 @@ void emptyStringToAtomicLong() assertThat(converted.get()).isEqualTo(0); } + private static Stream stringToByteArrayParams() { + return Stream.of( + Arguments.of("$1,000", StandardCharsets.US_ASCII, new byte[] { 36, 49, 44, 48, 48, 48 }), + Arguments.of("$1,000", StandardCharsets.ISO_8859_1, new byte[] { 36, 49, 44, 48, 48, 48 }), + Arguments.of("$1,000", StandardCharsets.UTF_8, new byte[] { 36, 49, 44, 48, 48, 48 }), + Arguments.of("£1,000", StandardCharsets.ISO_8859_1, new byte[] { -93, 49, 44, 48, 48, 48 }), + Arguments.of("£1,000", StandardCharsets.UTF_8, new byte[] { -62, -93, 49, 44, 48, 48, 48 }), + Arguments.of("€1,000", StandardCharsets.UTF_8, new byte[] { -30, -126, -84, 49, 44, 48, 48, 48 }) + ); + } + + private static Stream stringToCharArrayParams() { + return Stream.of( + Arguments.of("$1,000", StandardCharsets.US_ASCII, new char[] { '$', '1', ',', '0', '0', '0' }), + Arguments.of("$1,000", StandardCharsets.ISO_8859_1, new char[] { '$', '1', ',', '0', '0', '0' }), + Arguments.of("$1,000", StandardCharsets.UTF_8, new char[] { '$', '1', ',', '0', '0', '0' }), + Arguments.of("£1,000", StandardCharsets.ISO_8859_1, new char[] { '£', '1', ',', '0', '0', '0' }), + Arguments.of("£1,000", StandardCharsets.UTF_8, new char[] { '£', '1', ',', '0', '0', '0' }), + Arguments.of("€1,000", StandardCharsets.UTF_8, new char[] { '€', '1', ',', '0', '0', '0' }) + ); + } + + @ParameterizedTest + @MethodSource("stringToByteArrayParams") + void testStringToByteArray(String source, Charset charSet, byte[] expected) { + byte[] actual = this.converter.convert(source, byte[].class, createCharsetOptions(charSet)); + assertThat(actual).isEqualTo(expected); + } + + @ParameterizedTest + @MethodSource("stringToByteArrayParams") + void testStringToByteBuffer(String source, Charset charSet, byte[] expected) { + ByteBuffer actual = this.converter.convert(source, ByteBuffer.class, createCharsetOptions(charSet)); + assertThat(actual).isEqualTo(ByteBuffer.wrap(expected)); + } + + @ParameterizedTest + @MethodSource("stringToByteArrayParams") + void testByteArrayToString(String expected, Charset charSet, byte[] source) { + String actual = this.converter.convert(source, String.class, createCharsetOptions(charSet)); + assertThat(actual).isEqualTo(expected); + } + + @ParameterizedTest + @MethodSource("stringToCharArrayParams") + void testCharArrayToString(String expected, Charset charSet, char[] source) { + String actual = this.converter.convert(source, String.class, createCharsetOptions(charSet)); + assertThat(actual).isEqualTo(expected); + } + + @ParameterizedTest + @MethodSource("stringToCharArrayParams") + void testStringToCharArray(String source, Charset charSet, char[] expected) { + char[] actual = this.converter.convert(source, char[].class, createCharsetOptions(charSet)); + assertThat(actual).isEqualTo(expected); + } + + + + private ConverterOptions createCharsetOptions(final Charset charset) + { + return new ConverterOptions() { + @Override + public T getCustomOption(String name) { + return null; + } + + @Override + public Charset getCharset () { + return charset; + } + }; + } + + private ConverterOptions createCustomZones(final ZoneId sourceZoneId, final ZoneId targetZoneId) { return new ConverterOptions() {