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
26 @JacksonStdImpl
27 public class CollectionDeserializer
28 extends ContainerDeserializerBase<Collection<Object>>
29 implements ContextualDeserializer
30 {
31 private static final long serialVersionUID = -1L;
32
33
34
35
38 protected final JsonDeserializer<Object> _valueDeserializer;
39
40
44 protected final TypeDeserializer _valueTypeDeserializer;
45
46
47
48 protected final ValueInstantiator _valueInstantiator;
49
50
54 protected final JsonDeserializer<Object> _delegateDeserializer;
55
56
57
58
63
64
68 public CollectionDeserializer(JavaType collectionType,
69 JsonDeserializer<Object> valueDeser,
70 TypeDeserializer valueTypeDeser, ValueInstantiator valueInstantiator)
71 {
72 this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, null, null, null);
73 }
74
75
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
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
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
122 @Override
123 public boolean isCachable() {
124
125 return (_valueDeserializer == null)
126 && (_valueTypeDeserializer == null)
127 && (_delegateDeserializer == null)
128 ;
129 }
130
131
136
137
142 @Override
143 public CollectionDeserializer createContextual(DeserializationContext ctxt,
144 BeanProperty property) throws JsonMappingException
145 {
146
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
170
171
172 Boolean unwrapSingle = findFormatFeature(ctxt, property, Collection.class,
173 JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
174
175 JsonDeserializer<?> valueDeser = _valueDeserializer;
176
177
178 valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
179 final JavaType vt = _containerType.getContentType();
180 if (valueDeser == null) {
181 valueDeser = ctxt.findContextualValueDeserializer(vt, property);
182 } else {
183 valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
184 }
185
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
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
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
235
236
237 if (p.hasToken(JsonToken.VALUE_STRING)) {
238
239
240
241
242
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
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
268 if (!p.isExpectedStartArrayToken()) {
269 return handleNonArray(p, ctxt, result);
270 }
271
272 p.setCurrentValue(result);
273
274 JsonDeserializer<Object> valueDes = _valueDeserializer;
275
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
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
318 return typeDeserializer.deserializeTypedFromArray(p, ctxt);
319 }
320
321
326 @SuppressWarnings("unchecked")
327 protected final Collection<Object> handleNonArray(JsonParser p, DeserializationContext ctxt,
328 Collection<Object> result)
329 throws IOException
330 {
331
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
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
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
372 if (!p.isExpectedStartArrayToken()) {
373 return handleNonArray(p, ctxt, result);
374 }
375
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
416 public static class CollectionReferringAccumulator {
417 private final Class<?> _elementType;
418 private final Collection<Object> _result;
419
420
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
451
452
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
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