aboutsummaryrefslogtreecommitdiff
path: root/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java
blob: 33117d4aabc7de77b86a8ebc754ed61090ebd7ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
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_SHORT = "out of range of Java short";

    final static String OVERFLOW_MSG_INT = "out of range of int";
    final static String OVERFLOW_MSG_LONG = "out of range of long";

    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_SHORT);
        }
        // Int overflow
        try {
            MAPPER.convertValue(new long[] { Long.MAX_VALUE }, int[].class);
        } catch (IllegalArgumentException e) {
            verifyException(e, OVERFLOW_MSG_INT);
        }
        // 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, long[].class);
        } catch (IllegalArgumentException e) {
            verifyException(e, OVERFLOW_MSG_LONG);
        }
    }

    /*
    /********************************************************
    /* 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);
        }
    }
}