1 package com.fasterxml.jackson.databind.convert; 2 3 import java.math.*; 4 import java.util.*; 5 import java.lang.reflect.Array; 6 7 8 import com.fasterxml.jackson.core.type.TypeReference; 9 import com.fasterxml.jackson.databind.*; 10 11 public class TestArrayConversions 12 extends com.fasterxml.jackson.databind.BaseMapTest 13 { 14 final static String OVERFLOW_MSG_BYTE = "out of range of Java byte"; 15 final static String OVERFLOW_MSG_SHORT = "out of range of Java short"; 16 17 final static String OVERFLOW_MSG_INT = "out of range of int"; 18 final static String OVERFLOW_MSG_LONG = "out of range of long"; 19 20 final ObjectMapper MAPPER = new ObjectMapper(); 21 testNullXform()22 public void testNullXform() throws Exception 23 { 24 /* when given null, null should be returned without conversion 25 * (Java null has no type) 26 */ 27 assertNull(MAPPER.convertValue(null, Integer.class)); 28 assertNull(MAPPER.convertValue(null, String.class)); 29 assertNull(MAPPER.convertValue(null, byte[].class)); 30 } 31 32 /** 33 * Tests to verify that primitive number arrays round-trip 34 * correctly, i.e. type -> type gives equal (although 35 * not necessarily same) output 36 */ testArrayIdentityTransforms()37 public void testArrayIdentityTransforms() throws Exception 38 { 39 // first integral types 40 // (note: byte[] is ok, even if it goes to base64 and back) 41 verifyByteArrayConversion(bytes(), byte[].class); 42 verifyShortArrayConversion(shorts(), short[].class); 43 verifyIntArrayConversion(ints(), int[].class); 44 verifyLongArrayConversion(longs(), long[].class); 45 // then primitive decimal types 46 verifyFloatArrayConversion(floats(), float[].class); 47 verifyDoubleArrayConversion(doubles(), float[].class); 48 } 49 testByteArrayFrom()50 public void testByteArrayFrom() throws Exception 51 { 52 /* Note: byte arrays are tricky, since they are considered 53 * binary data primarily, not as array of numbers. Hence 54 * output will be base64 encoded... 55 */ 56 byte[] data = _convert("c3VyZS4=", byte[].class); 57 byte[] exp = "sure.".getBytes("Ascii"); 58 verifyIntegralArrays(exp, data, exp.length); 59 } 60 testShortArrayToX()61 public void testShortArrayToX() throws Exception 62 { 63 short[] data = shorts(); 64 verifyShortArrayConversion(data, byte[].class); 65 verifyShortArrayConversion(data, int[].class); 66 verifyShortArrayConversion(data, long[].class); 67 } 68 testIntArrayToX()69 public void testIntArrayToX() throws Exception 70 { 71 int[] data = ints(); 72 verifyIntArrayConversion(data, byte[].class); 73 verifyIntArrayConversion(data, short[].class); 74 verifyIntArrayConversion(data, long[].class); 75 76 List<Number> expNums = _numberList(data, data.length); 77 // Alas, due to type erasure, need to use TypeRef, not just class 78 List<Integer> actNums = MAPPER.convertValue(data, new TypeReference<List<Integer>>() {}); 79 assertEquals(expNums, actNums); 80 } 81 testLongArrayToX()82 public void testLongArrayToX() throws Exception 83 { 84 long[] data = longs(); 85 verifyLongArrayConversion(data, byte[].class); 86 verifyLongArrayConversion(data, short[].class); 87 verifyLongArrayConversion(data, int[].class); 88 89 List<Number> expNums = _numberList(data, data.length); 90 List<Long> actNums = MAPPER.convertValue(data, new TypeReference<List<Long>>() {}); 91 assertEquals(expNums, actNums); 92 } 93 testOverflows()94 public void testOverflows() 95 { 96 // Byte overflow 97 try { 98 MAPPER.convertValue(new int[] { 1000 }, byte[].class); 99 } catch (IllegalArgumentException e) { 100 verifyException(e, OVERFLOW_MSG_BYTE); 101 } 102 // Short overflow 103 try { 104 MAPPER.convertValue(new int[] { -99999 }, short[].class); 105 } catch (IllegalArgumentException e) { 106 verifyException(e, OVERFLOW_MSG_SHORT); 107 } 108 // Int overflow 109 try { 110 MAPPER.convertValue(new long[] { Long.MAX_VALUE }, int[].class); 111 } catch (IllegalArgumentException e) { 112 verifyException(e, OVERFLOW_MSG_INT); 113 } 114 // Longs need help of BigInteger... 115 BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE); 116 biggie.add(BigInteger.ONE); 117 List<BigInteger> l = new ArrayList<BigInteger>(); 118 l.add(biggie); 119 try { 120 MAPPER.convertValue(l, long[].class); 121 } catch (IllegalArgumentException e) { 122 verifyException(e, OVERFLOW_MSG_LONG); 123 } 124 } 125 126 /* 127 /******************************************************** 128 /* Helper methods 129 /******************************************************** 130 */ 131 132 // note: all value need to be within byte range 133 bytes()134 private byte[] bytes() { return new byte[] { 1, -1, 0, 98, 127 }; } shorts()135 private short[] shorts() { return new short[] { 1, -1, 0, 98, 127 }; } ints()136 private int[] ints() { return new int[] { 1, -1, 0, 98, 127 }; } longs()137 private long[] longs() { return new long[] { 1, -1, 0, 98, 127 }; } 138 139 // note: use values that are exact in binary 140 doubles()141 private double[] doubles() { return new double[] { 0.0, 0.25, -0.125, 10.5, 9875.0 }; } floats()142 private float[] floats() { return new float[] { 143 0.0f, 0.25f, -0.125f, 10.5f, 9875.0f }; 144 } 145 verifyByteArrayConversion(byte[] data, Class<T> arrayType)146 private <T> void verifyByteArrayConversion(byte[] data, Class<T> arrayType) { 147 T result = _convert(data, arrayType); 148 verifyIntegralArrays(data, result, data.length); 149 } verifyShortArrayConversion(short[] data, Class<T> arrayType)150 private <T> void verifyShortArrayConversion(short[] data, Class<T> arrayType) { 151 T result = _convert(data, arrayType); 152 verifyIntegralArrays(data, result, data.length); 153 } verifyIntArrayConversion(int[] data, Class<T> arrayType)154 private <T> void verifyIntArrayConversion(int[] data, Class<T> arrayType) { 155 T result = _convert(data, arrayType); 156 verifyIntegralArrays(data, result, data.length); 157 } verifyLongArrayConversion(long[] data, Class<T> arrayType)158 private <T> void verifyLongArrayConversion(long[] data, Class<T> arrayType) { 159 T result = _convert(data, arrayType); 160 verifyIntegralArrays(data, result, data.length); 161 } verifyFloatArrayConversion(float[] data, Class<T> arrayType)162 private <T> void verifyFloatArrayConversion(float[] data, Class<T> arrayType) { 163 T result = _convert(data, arrayType); 164 verifyDoubleArrays(data, result, data.length); 165 } verifyDoubleArrayConversion(double[] data, Class<T> arrayType)166 private <T> void verifyDoubleArrayConversion(double[] data, Class<T> arrayType) { 167 T result = _convert(data, arrayType); 168 verifyDoubleArrays(data, result, data.length); 169 } 170 _convert(Object input, Class<T> outputType)171 private <T> T _convert(Object input, Class<T> outputType) 172 { 173 // must be a primitive array, like "int[].class" 174 if (!outputType.isArray()) throw new IllegalArgumentException(); 175 if (!outputType.getComponentType().isPrimitive()) throw new IllegalArgumentException(); 176 T result = MAPPER.convertValue(input, outputType); 177 // sanity check first: 178 assertNotNull(result); 179 assertEquals(outputType, result.getClass()); 180 return result; 181 } 182 _numberList(Object numberArray, int size)183 private List<Number> _numberList(Object numberArray, int size) 184 { 185 ArrayList<Number> result = new ArrayList<Number>(size); 186 for (int i = 0; i < size; ++i) { 187 result.add((Number) Array.get(numberArray, i)); 188 } 189 return result; 190 } 191 192 /** 193 * Helper method for checking that given collections contain integral Numbers 194 * that essentially contain same values in same order 195 */ verifyIntegralArrays(Object inputArray, Object outputArray, int size)196 private void verifyIntegralArrays(Object inputArray, Object outputArray, int size) 197 { 198 for (int i = 0; i < size; ++i) { 199 Number n1 = (Number) Array.get(inputArray, i); 200 Number n2 = (Number) Array.get(outputArray, i); 201 double value1 = n1.longValue(); 202 double value2 = n2.longValue(); 203 assertEquals("Entry #"+i+"/"+size+" not equal", value1, value2); 204 } 205 } 206 verifyDoubleArrays(Object inputArray, Object outputArray, int size)207 private void verifyDoubleArrays(Object inputArray, Object outputArray, int size) 208 { 209 for (int i = 0; i < size; ++i) { 210 Number n1 = (Number) Array.get(inputArray, i); 211 Number n2 = (Number) Array.get(outputArray, i); 212 double value1 = n1.doubleValue(); 213 double value2 = n2.doubleValue(); 214 assertEquals("Entry #"+i+"/"+size+" not equal", value1, value2); 215 } 216 } 217 } 218