package com.fasterxml.jackson.databind.convert;
import java.math.*;
import java.util.*;
import java.lang.reflect.Array;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
public class TestArrayConversions
extends com.fasterxml.jackson.databind.BaseMapTest
{
final static String OVERFLOW_MSG_BYTE = "out of range of Java byte";
final static String OVERFLOW_MSG = "overflow";
final ObjectMapper mapper = new ObjectMapper();
public void testNullXform() throws Exception
{
/* when given null, null should be returned without conversion
* (Java null has no type)
*/
assertNull(mapper.convertValue(null, Integer.class));
assertNull(mapper.convertValue(null, String.class));
assertNull(mapper.convertValue(null, byte[].class));
}
/**
* Tests to verify that primitive number arrays round-trip
* correctly, i.e. type -> type gives equal (although
* not necessarily same) output
*/
public void testArrayIdentityTransforms() throws Exception
{
// first integral types
// (note: byte[] is ok, even if it goes to base64 and back)
verifyByteArrayConversion(bytes(), byte[].class);
verifyShortArrayConversion(shorts(), short[].class);
verifyIntArrayConversion(ints(), int[].class);
verifyLongArrayConversion(longs(), long[].class);
// then primitive decimal types
verifyFloatArrayConversion(floats(), float[].class);
verifyDoubleArrayConversion(doubles(), float[].class);
}
public void testByteArrayFrom() throws Exception
{
/* Note: byte arrays are tricky, since they are considered
* binary data primarily, not as array of numbers. Hence
* output will be base64 encoded...
*/
byte[] data = _convert("c3VyZS4=", byte[].class);
byte[] exp = "sure.".getBytes("Ascii");
verifyIntegralArrays(exp, data, exp.length);
}
public void testShortArrayToX() throws Exception
{
short[] data = shorts();
verifyShortArrayConversion(data, byte[].class);
verifyShortArrayConversion(data, int[].class);
verifyShortArrayConversion(data, long[].class);
}
public void testIntArrayToX() throws Exception
{
int[] data = ints();
verifyIntArrayConversion(data, byte[].class);
verifyIntArrayConversion(data, short[].class);
verifyIntArrayConversion(data, long[].class);
List<Number> expNums = _numberList(data, data.length);
// Alas, due to type erasure, need to use TypeRef, not just class
List<Integer> actNums = mapper.convertValue(data, new TypeReference<List<Integer>>() {});
assertEquals(expNums, actNums);
}
public void testLongArrayToX() throws Exception
{
long[] data = longs();
verifyLongArrayConversion(data, byte[].class);
verifyLongArrayConversion(data, short[].class);
verifyLongArrayConversion(data, int[].class);
List<Number> expNums = _numberList(data, data.length);
List<Long> actNums = mapper.convertValue(data, new TypeReference<List<Long>>() {});
assertEquals(expNums, actNums);
}
public void testOverflows()
{
// Byte overflow
try {
mapper.convertValue(new int[] { 1000 }, byte[].class);
} catch (IllegalArgumentException e) {
verifyException(e, OVERFLOW_MSG_BYTE);
}
// Short overflow
try {
mapper.convertValue(new int[] { -99999 }, short[].class);
} catch (IllegalArgumentException e) {
verifyException(e, OVERFLOW_MSG);
}
// Int overflow
try {
mapper.convertValue(new long[] { Long.MAX_VALUE }, int[].class);
} catch (IllegalArgumentException e) {
verifyException(e, OVERFLOW_MSG);
}
// Longs need help of BigInteger...
BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE);
biggie.add(BigInteger.ONE);
List<BigInteger> l = new ArrayList<BigInteger>();
l.add(biggie);
try {
mapper.convertValue(l, int[].class);
} catch (IllegalArgumentException e) {
verifyException(e, OVERFLOW_MSG);
}
}
/*
/********************************************************
/* Helper methods
/********************************************************
*/
// note: all value need to be within byte range
private byte[] bytes() { return new byte[] { 1, -1, 0, 98, 127 }; }
private short[] shorts() { return new short[] { 1, -1, 0, 98, 127 }; }
private int[] ints() { return new int[] { 1, -1, 0, 98, 127 }; }
private long[] longs() { return new long[] { 1, -1, 0, 98, 127 }; }
// note: use values that are exact in binary
private double[] doubles() { return new double[] { 0.0, 0.25, -0.125, 10.5, 9875.0 }; }
private float[] floats() { return new float[] {
0.0f, 0.25f, -0.125f, 10.5f, 9875.0f };
}
private <T> void verifyByteArrayConversion(byte[] data, Class<T> arrayType) {
T result = _convert(data, arrayType);
verifyIntegralArrays(data, result, data.length);
}
private <T> void verifyShortArrayConversion(short[] data, Class<T> arrayType) {
T result = _convert(data, arrayType);
verifyIntegralArrays(data, result, data.length);
}
private <T> void verifyIntArrayConversion(int[] data, Class<T> arrayType) {
T result = _convert(data, arrayType);
verifyIntegralArrays(data, result, data.length);
}
private <T> void verifyLongArrayConversion(long[] data, Class<T> arrayType) {
T result = _convert(data, arrayType);
verifyIntegralArrays(data, result, data.length);
}
private <T> void verifyFloatArrayConversion(float[] data, Class<T> arrayType) {
T result = _convert(data, arrayType);
verifyDoubleArrays(data, result, data.length);
}
private <T> void verifyDoubleArrayConversion(double[] data, Class<T> arrayType) {
T result = _convert(data, arrayType);
verifyDoubleArrays(data, result, data.length);
}
private <T> T _convert(Object input, Class<T> outputType)
{
// must be a primitive array, like "int[].class"
if (!outputType.isArray()) throw new IllegalArgumentException();
if (!outputType.getComponentType().isPrimitive()) throw new IllegalArgumentException();
T result = mapper.convertValue(input, outputType);
// sanity check first:
assertNotNull(result);
assertEquals(outputType, result.getClass());
return result;
}
private List<Number> _numberList(Object numberArray, int size)
{
ArrayList<Number> result = new ArrayList<Number>(size);
for (int i = 0; i < size; ++i) {
result.add((Number) Array.get(numberArray, i));
}
return result;
}
/**
* Helper method for checking that given collections contain integral Numbers
* that essentially contain same values in same order
*/
private void verifyIntegralArrays(Object inputArray, Object outputArray, int size)
{
for (int i = 0; i < size; ++i) {
Number n1 = (Number) Array.get(inputArray, i);
Number n2 = (Number) Array.get(outputArray, i);
double value1 = n1.longValue();
double value2 = n2.longValue();
assertEquals("Entry #"+i+"/"+size+" not equal", value1, value2);
}
}
private void verifyDoubleArrays(Object inputArray, Object outputArray, int size)
{
for (int i = 0; i < size; ++i) {
Number n1 = (Number) Array.get(inputArray, i);
Number n2 = (Number) Array.get(outputArray, i);
double value1 = n1.doubleValue();
double value2 = n2.doubleValue();
assertEquals("Entry #"+i+"/"+size+" not equal", value1, value2);
}
}
}