1 package com.fasterxml.jackson.databind.deser.std;
2
3 import java.io.IOException;
4 import java.util.*;
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.*;
13 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
14 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
15 import com.fasterxml.jackson.databind.util.ClassUtil;
16
17 /**
18  * Basic serializer that can take JSON "Array" structure and
19  * construct a {@link java.util.Collection} instance, with typed contents.
20  *<p>
21  * Note: for untyped content (one indicated by passing Object.class
22  * as the type), {@link UntypedObjectDeserializer} is used instead.
23  * It can also construct {@link java.util.List}s, but not with specific
24  * POJO types, only other containers and primitives/wrappers.
25  */

26 @JacksonStdImpl
27 public class CollectionDeserializer
28     extends ContainerDeserializerBase<Collection<Object>>
29     implements ContextualDeserializer
30 {
31     private static final long serialVersionUID = -1L; // since 2.5
32
33     // // Configuration
34
35     /**
36      * Value deserializer.
37      */

38     protected final JsonDeserializer<Object> _valueDeserializer;
39
40     /**
41      * If element instances have polymorphic type information, this
42      * is the type deserializer that can handle it
43      */

44     protected final TypeDeserializer _valueTypeDeserializer;
45
46     // // Instance construction settings:
47
48     protected final ValueInstantiator _valueInstantiator;
49
50     /**
51      * Deserializer that is used iff delegate-based creator is
52      * to be used for deserializing from JSON Object.
53      */

54     protected final JsonDeserializer<Object> _delegateDeserializer;
55
56     // NOTE: no PropertyBasedCreator, as JSON Arrays have no properties
57
58     /*
59     /**********************************************************
60     /* Life-cycle
61     /**********************************************************
62      */

63
64     /**
65      * Constructor for context-free instances, where we do not yet know
66      * which property is using this deserializer.
67      */

68     public CollectionDeserializer(JavaType collectionType,
69             JsonDeserializer<Object> valueDeser,
70             TypeDeserializer valueTypeDeser, ValueInstantiator valueInstantiator)
71     {
72         this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, nullnullnull);
73     }
74
75     /**
76      * Constructor used when creating contextualized instances.
77      *
78      * @since 2.9
79      */

80     protected CollectionDeserializer(JavaType collectionType,
81             JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
82             ValueInstantiator valueInstantiator, JsonDeserializer<Object> delegateDeser,
83             NullValueProvider nuller, Boolean unwrapSingle)
84     {
85         super(collectionType, nuller, unwrapSingle);
86         _valueDeserializer = valueDeser;
87         _valueTypeDeserializer = valueTypeDeser;
88         _valueInstantiator = valueInstantiator;
89         _delegateDeserializer = delegateDeser;
90     }
91
92     /**
93      * Copy-constructor that can be used by sub-classes to allow
94      * copy-on-write styling copying of settings of an existing instance.
95      */

96     protected CollectionDeserializer(CollectionDeserializer src)
97     {
98         super(src);
99         _valueDeserializer = src._valueDeserializer;
100         _valueTypeDeserializer = src._valueTypeDeserializer;
101         _valueInstantiator = src._valueInstantiator;
102         _delegateDeserializer = src._delegateDeserializer;
103     }
104
105     /**
106      * Fluent-factory method call to construct contextual instance.
107      *
108      * @since 2.9
109      */

110     @SuppressWarnings("unchecked")
111     protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
112             JsonDeserializer<?> vd, TypeDeserializer vtd,
113             NullValueProvider nuller, Boolean unwrapSingle)
114     {
115         return new CollectionDeserializer(_containerType,
116                 (JsonDeserializer<Object>) vd, vtd,
117                 _valueInstantiator, (JsonDeserializer<Object>) dd,
118                 nuller, unwrapSingle);
119     }
120
121     // Important: do NOT cache if polymorphic values
122     @Override // since 2.5
123     public boolean isCachable() {
124         // 26-Mar-2015, tatu: As per [databind#735], need to be careful
125         return (_valueDeserializer == null)
126                 && (_valueTypeDeserializer == null)
127                 && (_delegateDeserializer == null)
128                 ;
129     }
130
131     /*
132     /**********************************************************
133     /* Validation, post-processing (ResolvableDeserializer)
134     /**********************************************************
135      */

136
137     /**
138      * Method called to finalize setup of this deserializer,
139      * when it is known for which property deserializer is needed
140      * for.
141      */

142     @Override
143     public CollectionDeserializer createContextual(DeserializationContext ctxt,
144             BeanProperty property) throws JsonMappingException
145     {
146         // May need to resolve types for delegate-based creators:
147         JsonDeserializer<Object> delegateDeser = null;
148         if (_valueInstantiator != null) {
149             if (_valueInstantiator.canCreateUsingDelegate()) {
150                 JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
151                 if (delegateType == null) {
152                     ctxt.reportBadDefinition(_containerType, String.format(
153 "Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'",
154 _containerType,
155                             _valueInstantiator.getClass().getName()));
156                 }
157                 delegateDeser = findDeserializer(ctxt, delegateType, property);
158             } else if (_valueInstantiator.canCreateUsingArrayDelegate()) {
159                 JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
160                 if (delegateType == null) {
161                     ctxt.reportBadDefinition(_containerType, String.format(
162 "Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'",
163                             _containerType,
164                             _valueInstantiator.getClass().getName()));
165                 }
166                 delegateDeser = findDeserializer(ctxt, delegateType, property);
167             }
168         }
169         // [databind#1043]: allow per-property allow-wrapping of single overrides:
170         // 11-Dec-2015, tatu: Should we pass basic `Collection.class`, or more refined? Mostly
171         //   comes down to "List vs Collection" I suppose... for now, pass Collection
172         Boolean unwrapSingle = findFormatFeature(ctxt, property, Collection.class,
173                 JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
174         // also, often value deserializer is resolved here:
175         JsonDeserializer<?> valueDeser = _valueDeserializer;
176         
177         // May have a content converter
178         valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
179         final JavaType vt = _containerType.getContentType();
180         if (valueDeser == null) {
181             valueDeser = ctxt.findContextualValueDeserializer(vt, property);
182         } else { // if directly assigned, probably not yet contextual, so:
183             valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
184         }
185         // and finally, type deserializer needs context as well
186         TypeDeserializer valueTypeDeser = _valueTypeDeserializer;
187         if (valueTypeDeser != null) {
188             valueTypeDeser = valueTypeDeser.forProperty(property);
189         }
190         NullValueProvider nuller = findContentNullProvider(ctxt, property, valueDeser);
191         if ((unwrapSingle != _unwrapSingle)
192                 || (nuller != _nullProvider)
193                 || (delegateDeser != _delegateDeserializer)
194                 || (valueDeser != _valueDeserializer)
195                 || (valueTypeDeser != _valueTypeDeserializer)
196         ) {
197             return withResolved(delegateDeser, valueDeser, valueTypeDeser,
198                     nuller, unwrapSingle);
199         }
200         return this;
201     }
202
203     /*
204     /**********************************************************
205     /* ContainerDeserializerBase API
206     /**********************************************************
207      */

208
209     @Override
210     public JsonDeserializer<Object> getContentDeserializer() {
211         return _valueDeserializer;
212     }
213
214     @Override
215     public ValueInstantiator getValueInstantiator() {
216         return _valueInstantiator;
217     }
218
219     /*
220     /**********************************************************
221     /* JsonDeserializer API
222     /**********************************************************
223      */

224
225     @SuppressWarnings("unchecked")
226     @Override
227     public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt)
228         throws IOException
229     {
230         if (_delegateDeserializer != null) {
231             return (Collection<Object>) _valueInstantiator.createUsingDelegate(ctxt,
232                     _delegateDeserializer.deserialize(p, ctxt));
233         }
234         // Empty String may be ok; bit tricky to check, however, since
235         // there is also possibility of "auto-wrapping" of single-element arrays.
236         // Hence we only accept empty String here.
237         if (p.hasToken(JsonToken.VALUE_STRING)) {
238             // 16-May-2020, tatu: As [dataformats-text#199] need to avoid blocking
239             //    check to `isExpectedStartArrayToken()` (needed for CSV in-field array/list logic)
240             // ... alas, trying to do this here leads to 2 unit test regressions so will
241             //   need to figure out safer mechanism.
242 //            if (_valueInstantiator.canCreateFromString()) {
243                 String str = p.getText();
244                 if (str.length() == 0) {
245                     return (Collection<Object>) _valueInstantiator.createFromString(ctxt, str);
246 //                }
247             }
248         }
249         return deserialize(p, ctxt, createDefaultInstance(ctxt));
250     }
251
252     /**
253      * @since 2.9
254      */

255     @SuppressWarnings("unchecked")
256     protected Collection<Object> createDefaultInstance(DeserializationContext ctxt)
257         throws IOException
258     {
259         return (Collection<Object>) _valueInstantiator.createUsingDefault(ctxt);
260     }
261     
262     @Override
263     public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt,
264             Collection<Object> result)
265         throws IOException
266     {
267         // Ok: must point to START_ARRAY (or equivalent)
268         if (!p.isExpectedStartArrayToken()) {
269             return handleNonArray(p, ctxt, result);
270         }
271         // [databind#631]: Assign current value, to be accessible by custom serializers
272         p.setCurrentValue(result);
273
274         JsonDeserializer<Object> valueDes = _valueDeserializer;
275         // Let's offline handling of values with Object Ids (simplifies code here)
276         if (valueDes.getObjectIdReader() != null) {
277             return _deserializeWithObjectId(p, ctxt, result);
278         }
279         final TypeDeserializer typeDeser = _valueTypeDeserializer;
280         JsonToken t;
281         while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
282             try {
283                 Object value;
284                 if (t == JsonToken.VALUE_NULL) {
285                     if (_skipNullValues) {
286                         continue;
287                     }
288                     value = _nullProvider.getNullValue(ctxt);
289                 } else if (typeDeser == null) {
290                     value = valueDes.deserialize(p, ctxt);
291                 } else {
292                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
293                 }
294                 result.add(value);
295
296                 /* 17-Dec-2017, tatu: should not occur at this level...
297             } catch (UnresolvedForwardReference reference) {
298                 throw JsonMappingException
299                     .from(p, "Unresolved forward reference but no identity info", reference);
300                 */

301             } catch (Exception e) {
302                 boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
303                 if (!wrap) {
304                     ClassUtil.throwIfRTE(e);
305                 }
306                 throw JsonMappingException.wrapWithPath(e, result, result.size());
307             }
308         }
309         return result;
310     }
311
312     @Override
313     public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
314             TypeDeserializer typeDeserializer)
315         throws IOException
316     {
317         // In future could check current token... for now this should be enough:
318         return typeDeserializer.deserializeTypedFromArray(p, ctxt);
319     }
320
321     /**
322      * Helper method called when current token is no START_ARRAY. Will either
323      * throw an exception, or try to handle value as if member of implicit
324      * array, depending on configuration.
325      */

326     @SuppressWarnings("unchecked")
327     protected final Collection<Object> handleNonArray(JsonParser p, DeserializationContext ctxt,
328             Collection<Object> result)
329         throws IOException
330     {
331         // Implicit arrays from single values?
332         boolean canWrap = (_unwrapSingle == Boolean.TRUE) ||
333                 ((_unwrapSingle == null) &&
334                         ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
335         if (!canWrap) {
336             return (Collection<Object>) ctxt.handleUnexpectedToken(_containerType, p);
337         }
338         JsonDeserializer<Object> valueDes = _valueDeserializer;
339         final TypeDeserializer typeDeser = _valueTypeDeserializer;
340
341         Object value;
342
343         try {
344             if (p.hasToken(JsonToken.VALUE_NULL)) {
345                 // 03-Feb-2017, tatu: Hmmh. I wonder... let's try skipping here, too
346                 if (_skipNullValues) {
347                     return result;
348                 }
349                 value = _nullProvider.getNullValue(ctxt);
350             } else if (typeDeser == null) {
351                 value = valueDes.deserialize(p, ctxt);
352             } else {
353                 value = valueDes.deserializeWithType(p, ctxt, typeDeser);
354             }
355         } catch (Exception e) {
356             boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
357             if (!wrap) {
358                 ClassUtil.throwIfRTE(e);
359             }
360             // note: pass Object.class, not Object[].class, as we need element type for error info
361             throw JsonMappingException.wrapWithPath(e, Object.class, result.size());
362         }
363         result.add(value);
364         return result;
365     }
366
367     protected Collection<Object> _deserializeWithObjectId(JsonParser p, DeserializationContext ctxt,
368             Collection<Object> result)
369         throws IOException
370     {
371         // Ok: must point to START_ARRAY (or equivalent)
372         if (!p.isExpectedStartArrayToken()) {
373             return handleNonArray(p, ctxt, result);
374         }
375         // [databind#631]: Assign current value, to be accessible by custom serializers
376         p.setCurrentValue(result);
377
378         final JsonDeserializer<Object> valueDes = _valueDeserializer;
379         final TypeDeserializer typeDeser = _valueTypeDeserializer;
380         CollectionReferringAccumulator referringAccumulator =
381                 new CollectionReferringAccumulator(_containerType.getContentType().getRawClass(), result);
382
383         JsonToken t;
384         while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
385             try {
386                 Object value;
387                 if (t == JsonToken.VALUE_NULL) {
388                     if (_skipNullValues) {
389                         continue;
390                     }
391                     value = _nullProvider.getNullValue(ctxt);
392                 } else if (typeDeser == null) {
393                     value = valueDes.deserialize(p, ctxt);
394                 } else {
395                     value = valueDes.deserializeWithType(p, ctxt, typeDeser);
396                 }
397                 referringAccumulator.add(value);
398             } catch (UnresolvedForwardReference reference) {
399                 Referring ref = referringAccumulator.handleUnresolvedReference(reference);
400                 reference.getRoid().appendReferring(ref);
401             } catch (Exception e) {
402                 boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
403                 if (!wrap) {
404                     ClassUtil.throwIfRTE(e);
405                 }
406                 throw JsonMappingException.wrapWithPath(e, result, result.size());
407             }
408         }
409         return result;
410     }
411
412     /**
413      * Helper class for dealing with Object Id references for values contained in
414      * collections being deserialized.
415      */

416     public static class CollectionReferringAccumulator {
417         private final Class<?> _elementType;
418         private final Collection<Object> _result;
419
420         /**
421          * A list of {@link CollectionReferring} to maintain ordering.
422          */

423         private List<CollectionReferring> _accumulator = new ArrayList<CollectionReferring>();
424
425         public CollectionReferringAccumulator(Class<?> elementType, Collection<Object> result) {
426             _elementType = elementType;
427             _result = result;
428         }
429
430         public void add(Object value)
431         {
432             if (_accumulator.isEmpty()) {
433                 _result.add(value);
434             } else {
435                 CollectionReferring ref = _accumulator.get(_accumulator.size() - 1);
436                 ref.next.add(value);
437             }
438         }
439
440         public Referring handleUnresolvedReference(UnresolvedForwardReference reference)
441         {
442             CollectionReferring id = new CollectionReferring(this, reference, _elementType);
443             _accumulator.add(id);
444             return id;
445         }
446
447         public void resolveForwardReference(Object id, Object value) throws IOException
448         {
449             Iterator<CollectionReferring> iterator = _accumulator.iterator();
450             // Resolve ordering after resolution of an id. This mean either:
451             // 1- adding to the result collection in case of the first unresolved id.
452             // 2- merge the content of the resolved id with its previous unresolved id.
453             Collection<Object> previous = _result;
454             while (iterator.hasNext()) {
455                 CollectionReferring ref = iterator.next();
456                 if (ref.hasId(id)) {
457                     iterator.remove();
458                     previous.add(value);
459                     previous.addAll(ref.next);
460                     return;
461                 }
462                 previous = ref.next;
463             }
464
465             throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id
466                     + "] that wasn't previously seen as unresolved.");
467         }
468     }
469
470     /**
471      * Helper class to maintain processing order of value. The resolved
472      * object associated with {@link #_id} comes before the values in
473      * {@link #next}.
474      */

475     private final static class CollectionReferring extends Referring {
476         private final CollectionReferringAccumulator _parent;
477         public final List<Object> next = new ArrayList<Object>();
478         
479         CollectionReferring(CollectionReferringAccumulator parent,
480                 UnresolvedForwardReference reference, Class<?> contentType)
481         {
482             super(reference, contentType);
483             _parent = parent;
484         }
485         
486         @Override
487         public void handleResolvedForwardReference(Object id, Object value) throws IOException {
488             _parent.resolveForwardReference(id, value);
489         }
490     }
491 }
492