1 package com.fasterxml.jackson.databind.deser.std;
2
3 import java.io.IOException;
4 import java.lang.reflect.Array;
5
6 import com.fasterxml.jackson.annotation.JsonFormat;
7
8 import com.fasterxml.jackson.core.*;
9
10 import com.fasterxml.jackson.databind.*;
11 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
12 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
13 import com.fasterxml.jackson.databind.deser.NullValueProvider;
14 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
15 import com.fasterxml.jackson.databind.util.AccessPattern;
16 import com.fasterxml.jackson.databind.util.ObjectBuffer;
17
18 /**
19  * Basic serializer that can serialize non-primitive arrays.
20  */

21 @JacksonStdImpl
22 public class ObjectArrayDeserializer
23     extends ContainerDeserializerBase<Object[]>
24     implements ContextualDeserializer
25 {
26     private static final long serialVersionUID = 1L;
27
28     protected final static Object[] NO_OBJECTS = new Object[0];
29
30     // // Configuration
31
32     /**
33      * Flag that indicates whether the component type is Object or not.
34      * Used for minor optimization when constructing result.
35      */

36     protected final boolean _untyped;
37
38     /**
39      * Type of contained elements: needed for constructing actual
40      * result array
41      */

42     protected final Class<?> _elementClass;
43
44     /**
45      * Element deserializer
46      */

47     protected JsonDeserializer<Object> _elementDeserializer;
48
49     /**
50      * If element instances have polymorphic type information, this
51      * is the type deserializer that can handle it
52      */

53     protected final TypeDeserializer _elementTypeDeserializer;
54
55     /*
56     /**********************************************************
57     /* Life-cycle
58     /**********************************************************
59      */

60
61     public ObjectArrayDeserializer(JavaType arrayType,
62             JsonDeserializer<Object> elemDeser, TypeDeserializer elemTypeDeser)
63     {
64         super(arrayType, nullnull);
65         _elementClass = arrayType.getContentType().getRawClass();
66         _untyped = (_elementClass == Object.class);
67         _elementDeserializer = elemDeser;
68         _elementTypeDeserializer = elemTypeDeser;
69     }
70
71     protected ObjectArrayDeserializer(ObjectArrayDeserializer base,
72             JsonDeserializer<Object> elemDeser, TypeDeserializer elemTypeDeser,
73             NullValueProvider nuller, Boolean unwrapSingle)
74     {
75         super(base, nuller, unwrapSingle);
76         _elementClass = base._elementClass;
77         _untyped = base._untyped;
78
79         _elementDeserializer = elemDeser;
80         _elementTypeDeserializer = elemTypeDeser;
81     }
82
83     /**
84      * Overridable fluent-factory method used to create contextual instances
85      */

86     public ObjectArrayDeserializer withDeserializer(TypeDeserializer elemTypeDeser,
87             JsonDeserializer<?> elemDeser)
88     {
89         return withResolved(elemTypeDeser, elemDeser,
90                 _nullProvider, _unwrapSingle);
91     }
92
93     /**
94      * @since 2.7
95      */

96     @SuppressWarnings("unchecked")
97     public ObjectArrayDeserializer withResolved(TypeDeserializer elemTypeDeser,
98             JsonDeserializer<?> elemDeser, NullValueProvider nuller, Boolean unwrapSingle)
99     {
100         if ((unwrapSingle == _unwrapSingle) && (nuller == _nullProvider)
101                 && (elemDeser == _elementDeserializer)
102                 && (elemTypeDeser == _elementTypeDeserializer)) {
103             return this;
104         }
105         return new ObjectArrayDeserializer(this,
106                 (JsonDeserializer<Object>) elemDeser, elemTypeDeser,
107                 nuller, unwrapSingle);
108     }
109
110     @Override // since 2.5
111     public boolean isCachable() {
112         // Important: do NOT cache if polymorphic values, or if there are annotation-based
113         // custom deserializers
114         return (_elementDeserializer == null) && (_elementTypeDeserializer == null);
115     }
116
117     @Override
118     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
119             BeanProperty property) throws JsonMappingException
120     {
121         JsonDeserializer<?> valueDeser = _elementDeserializer;
122         Boolean unwrapSingle = findFormatFeature(ctxt, property, _containerType.getRawClass(),
123                 JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
124         // May have a content converter
125         valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
126         final JavaType vt = _containerType.getContentType();
127         if (valueDeser == null) {
128             valueDeser = ctxt.findContextualValueDeserializer(vt, property);
129         } else { // if directly assigned, probably not yet contextual, so:
130             valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
131         }
132         TypeDeserializer elemTypeDeser = _elementTypeDeserializer;
133         if (elemTypeDeser != null) {
134             elemTypeDeser = elemTypeDeser.forProperty(property);
135         }
136         NullValueProvider nuller = findContentNullProvider(ctxt, property, valueDeser);
137         return withResolved(elemTypeDeser, valueDeser, nuller, unwrapSingle);
138     }
139
140     /*
141     /**********************************************************
142     /* ContainerDeserializerBase API
143     /**********************************************************
144      */

145
146     @Override
147     public JsonDeserializer<Object> getContentDeserializer() {
148         return _elementDeserializer;
149     }
150
151     @Override // since 2.9
152     public AccessPattern getEmptyAccessPattern() {
153         // immutable, shareable so:
154         return AccessPattern.CONSTANT;
155     }
156
157     // need to override as we can't expose ValueInstantiator
158     @Override // since 2.9
159     public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
160         return NO_OBJECTS;
161     }
162
163     /*
164     /**********************************************************
165     /* JsonDeserializer API
166     /**********************************************************
167      */

168     
169     @Override
170     public Object[] deserialize(JsonParser p, DeserializationContext ctxt)
171         throws IOException
172     {
173         // Ok: must point to START_ARRAY (or equivalent)
174         if (!p.isExpectedStartArrayToken()) {
175             return handleNonArray(p, ctxt);
176         }
177
178         final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
179         Object[] chunk = buffer.resetAndStart();
180         int ix = 0;
181         JsonToken t;
182         final TypeDeserializer typeDeser = _elementTypeDeserializer;
183
184         try {
185             while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
186                 // Note: must handle null explicitly here; value deserializers won't
187                 Object value;
188                 
189                 if (t == JsonToken.VALUE_NULL) {
190                     if (_skipNullValues) {
191                         continue;
192                     }
193                     value = _nullProvider.getNullValue(ctxt);
194                 } else if (typeDeser == null) {
195                     value = _elementDeserializer.deserialize(p, ctxt);
196                 } else {
197                     value = _elementDeserializer.deserializeWithType(p, ctxt, typeDeser);
198                 }
199                 if (ix >= chunk.length) {
200                     chunk = buffer.appendCompletedChunk(chunk);
201                     ix = 0;
202                 }
203                 chunk[ix++] = value;
204             }
205         } catch (Exception e) {
206             throw JsonMappingException.wrapWithPath(e, chunk, buffer.bufferedSize() + ix);
207         }
208
209         Object[] result;
210
211         if (_untyped) {
212             result = buffer.completeAndClearBuffer(chunk, ix);
213         } else {
214             result = buffer.completeAndClearBuffer(chunk, ix, _elementClass);
215         }
216         ctxt.returnObjectBuffer(buffer);
217         return result;
218     }
219
220     @Override
221     public Object[] deserializeWithType(JsonParser p, DeserializationContext ctxt,
222             TypeDeserializer typeDeserializer)
223         throws IOException
224     {
225         // Should there be separate handling for base64 stuff?
226         // for now this should be enough:
227         return (Object[]) typeDeserializer.deserializeTypedFromArray(p, ctxt);
228     }
229
230     @Override // since 2.9
231     public Object[] deserialize(JsonParser p, DeserializationContext ctxt,
232             Object[] intoValue) throws IOException
233     {
234         if (!p.isExpectedStartArrayToken()) {
235             Object[] arr = handleNonArray(p, ctxt);
236             if (arr == null) {
237                 return intoValue;
238             }
239             final int offset = intoValue.length;
240             Object[] result = new Object[offset + arr.length];
241             System.arraycopy(intoValue, 0, result, 0, offset);
242             System.arraycopy(arr, 0, result, offset, arr.length);
243             return result;
244         }
245
246         final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
247         int ix = intoValue.length;
248         Object[] chunk = buffer.resetAndStart(intoValue, ix);
249         JsonToken t;
250         final TypeDeserializer typeDeser = _elementTypeDeserializer;
251
252         try {
253             while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
254                 Object value;
255                 
256                 if (t == JsonToken.VALUE_NULL) {
257                     if (_skipNullValues) {
258                         continue;
259                     }
260                     value = _nullProvider.getNullValue(ctxt);
261                 } else if (typeDeser == null) {
262                     value = _elementDeserializer.deserialize(p, ctxt);
263                 } else {
264                     value = _elementDeserializer.deserializeWithType(p, ctxt, typeDeser);
265                 }
266                 if (ix >= chunk.length) {
267                     chunk = buffer.appendCompletedChunk(chunk);
268                     ix = 0;
269                 }
270                 chunk[ix++] = value;
271             }
272         } catch (Exception e) {
273             throw JsonMappingException.wrapWithPath(e, chunk, buffer.bufferedSize() + ix);
274         }
275
276         Object[] result;
277
278         if (_untyped) {
279             result = buffer.completeAndClearBuffer(chunk, ix);
280         } else {
281             result = buffer.completeAndClearBuffer(chunk, ix, _elementClass);
282         }
283         ctxt.returnObjectBuffer(buffer);
284         return result;
285     }
286
287     /*
288     /**********************************************************
289     /* Internal methods
290     /**********************************************************
291      */

292     
293     protected Byte[] deserializeFromBase64(JsonParser p, DeserializationContext ctxt)
294         throws IOException
295     {
296         // First same as what PrimitiveArrayDeserializers.ByteDeser does:
297         byte[] b = p.getBinaryValue(ctxt.getBase64Variant());
298         // But then need to convert to wrappers
299         Byte[] result = new Byte[b.length];
300         for (int i = 0, len = b.length; i < len; ++i) {
301             result[i] = Byte.valueOf(b[i]);
302         }
303         return result;
304     }
305
306     protected Object[] handleNonArray(JsonParser p, DeserializationContext ctxt)
307         throws IOException
308     {
309         // Empty String can become null...
310         if (p.hasToken(JsonToken.VALUE_STRING)
311                 && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
312             String str = p.getText();
313             if (str.length() == 0) {
314                 return null;
315             }
316         }
317
318         // Can we do implicit coercion to a single-element array still?
319         boolean canWrap = (_unwrapSingle == Boolean.TRUE) ||
320                 ((_unwrapSingle == null) &&
321                         ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
322         if (!canWrap) {
323             // One exception; byte arrays are generally serialized as base64, so that should be handled
324             if (p.hasToken(JsonToken.VALUE_STRING)
325                     // note: not `byte[]`, but `Byte[]` -- former is primitive array
326                     && _elementClass == Byte.class) {
327                 return deserializeFromBase64(p, ctxt);
328             }
329             return (Object[]) ctxt.handleUnexpectedToken(_containerType.getRawClass(), p);
330         }
331
332         Object value;
333         if (p.hasToken(JsonToken.VALUE_NULL)) {
334             // 03-Feb-2017, tatu: Should this be skipped or not?
335             if (_skipNullValues) {
336                 return NO_OBJECTS;
337             }
338             value = _nullProvider.getNullValue(ctxt);
339         } else if (_elementTypeDeserializer == null) {
340             value = _elementDeserializer.deserialize(p, ctxt);
341         } else {
342             value = _elementDeserializer.deserializeWithType(p, ctxt, _elementTypeDeserializer);
343         }
344         // Ok: bit tricky, since we may want T[], not just Object[]
345         Object[] result;
346
347         if (_untyped) {
348             result = new Object[1];
349         } else {
350             result = (Object[]) Array.newInstance(_elementClass, 1);
351         }
352         result[0] = value;
353         return result;
354     }
355 }
356
357