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.JsonIgnoreProperties;
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.PropertyBasedCreator;
14 import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer;
15 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
16 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
17 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
18 import com.fasterxml.jackson.databind.util.ArrayBuilders;
19
20
29 @JacksonStdImpl
30 public class MapDeserializer
31 extends ContainerDeserializerBase<Map<Object,Object>>
32 implements ContextualDeserializer, ResolvableDeserializer
33 {
34 private static final long serialVersionUID = 1L;
35
36
37
38
43 protected final KeyDeserializer _keyDeserializer;
44
45
52 protected boolean _standardStringKey;
53
54
57 protected final JsonDeserializer<Object> _valueDeserializer;
58
59
63 protected final TypeDeserializer _valueTypeDeserializer;
64
65
66
67 protected final ValueInstantiator _valueInstantiator;
68
69
73 protected JsonDeserializer<Object> _delegateDeserializer;
74
75
81 protected PropertyBasedCreator _propertyBasedCreator;
82
83 protected final boolean _hasDefaultCreator;
84
85
86
87 protected Set<String> _ignorableProperties;
88
89
94
95 public MapDeserializer(JavaType mapType, ValueInstantiator valueInstantiator,
96 KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
97 TypeDeserializer valueTypeDeser)
98 {
99 super(mapType, null, null);
100 _keyDeserializer = keyDeser;
101 _valueDeserializer = valueDeser;
102 _valueTypeDeserializer = valueTypeDeser;
103 _valueInstantiator = valueInstantiator;
104 _hasDefaultCreator = valueInstantiator.canCreateUsingDefault();
105 _delegateDeserializer = null;
106 _propertyBasedCreator = null;
107 _standardStringKey = _isStdKeyDeser(mapType, keyDeser);
108 }
109
110
114 protected MapDeserializer(MapDeserializer src)
115 {
116 super(src);
117 _keyDeserializer = src._keyDeserializer;
118 _valueDeserializer = src._valueDeserializer;
119 _valueTypeDeserializer = src._valueTypeDeserializer;
120 _valueInstantiator = src._valueInstantiator;
121 _propertyBasedCreator = src._propertyBasedCreator;
122 _delegateDeserializer = src._delegateDeserializer;
123 _hasDefaultCreator = src._hasDefaultCreator;
124
125 _ignorableProperties = src._ignorableProperties;
126
127 _standardStringKey = src._standardStringKey;
128 }
129
130 protected MapDeserializer(MapDeserializer src,
131 KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
132 TypeDeserializer valueTypeDeser,
133 NullValueProvider nuller,
134 Set<String> ignorable)
135 {
136 super(src, nuller, src._unwrapSingle);
137 _keyDeserializer = keyDeser;
138 _valueDeserializer = valueDeser;
139 _valueTypeDeserializer = valueTypeDeser;
140 _valueInstantiator = src._valueInstantiator;
141 _propertyBasedCreator = src._propertyBasedCreator;
142 _delegateDeserializer = src._delegateDeserializer;
143 _hasDefaultCreator = src._hasDefaultCreator;
144 _ignorableProperties = ignorable;
145
146 _standardStringKey = _isStdKeyDeser(_containerType, keyDeser);
147 }
148
149
153 @SuppressWarnings("unchecked")
154 protected MapDeserializer withResolved(KeyDeserializer keyDeser,
155 TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser,
156 NullValueProvider nuller,
157 Set<String> ignorable)
158 {
159
160 if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser)
161 && (_valueTypeDeserializer == valueTypeDeser) && (_nullProvider == nuller)
162 && (_ignorableProperties == ignorable)) {
163 return this;
164 }
165 return new MapDeserializer(this,
166 keyDeser, (JsonDeserializer<Object>) valueDeser, valueTypeDeser,
167 nuller, ignorable);
168 }
169
170
174 protected final boolean _isStdKeyDeser(JavaType mapType, KeyDeserializer keyDeser)
175 {
176 if (keyDeser == null) {
177 return true;
178 }
179 JavaType keyType = mapType.getKeyType();
180 if (keyType == null) {
181 return true;
182 }
183 Class<?> rawKeyType = keyType.getRawClass();
184 return ((rawKeyType == String.class || rawKeyType == Object.class)
185 && isDefaultKeyDeserializer(keyDeser));
186 }
187
188 public void setIgnorableProperties(String[] ignorable) {
189 _ignorableProperties = (ignorable == null || ignorable.length == 0) ?
190 null : ArrayBuilders.arrayToSet(ignorable);
191 }
192
193 public void setIgnorableProperties(Set<String> ignorable) {
194 _ignorableProperties = (ignorable == null || ignorable.size() == 0) ?
195 null : ignorable;
196 }
197
198
203
204 @Override
205 public void resolve(DeserializationContext ctxt) throws JsonMappingException
206 {
207
208 if (_valueInstantiator.canCreateUsingDelegate()) {
209 JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
210 if (delegateType == null) {
211 ctxt.reportBadDefinition(_containerType, String.format(
212 "Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'",
213 _containerType,
214 _valueInstantiator.getClass().getName()));
215 }
216
217
218
219 _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
220 } else if (_valueInstantiator.canCreateUsingArrayDelegate()) {
221 JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
222 if (delegateType == null) {
223 ctxt.reportBadDefinition(_containerType, String.format(
224 "Invalid delegate-creator definition for %s: value instantiator (%s) returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'",
225 _containerType,
226 _valueInstantiator.getClass().getName()));
227 }
228 _delegateDeserializer = findDeserializer(ctxt, delegateType, null);
229 }
230 if (_valueInstantiator.canCreateFromObjectWith()) {
231 SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
232 _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps,
233 ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
234 }
235 _standardStringKey = _isStdKeyDeser(_containerType, _keyDeserializer);
236 }
237
238
242 @Override
243 public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
244 BeanProperty property) throws JsonMappingException
245 {
246 KeyDeserializer keyDeser = _keyDeserializer;
247 if (keyDeser == null) {
248 keyDeser = ctxt.findKeyDeserializer(_containerType.getKeyType(), property);
249 } else {
250 if (keyDeser instanceof ContextualKeyDeserializer) {
251 keyDeser = ((ContextualKeyDeserializer) keyDeser).createContextual(ctxt, property);
252 }
253 }
254
255 JsonDeserializer<?> valueDeser = _valueDeserializer;
256
257 if (property != null) {
258 valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
259 }
260 final JavaType vt = _containerType.getContentType();
261 if (valueDeser == null) {
262 valueDeser = ctxt.findContextualValueDeserializer(vt, property);
263 } else {
264 valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
265 }
266 TypeDeserializer vtd = _valueTypeDeserializer;
267 if (vtd != null) {
268 vtd = vtd.forProperty(property);
269 }
270 Set<String> ignored = _ignorableProperties;
271 AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
272 if (_neitherNull(intr, property)) {
273 AnnotatedMember member = property.getMember();
274 if (member != null) {
275 JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(member);
276 if (ignorals != null) {
277 Set<String> ignoresToAdd = ignorals.findIgnoredForDeserialization();
278 if (!ignoresToAdd.isEmpty()) {
279 ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
280 for (String str : ignoresToAdd) {
281 ignored.add(str);
282 }
283 }
284 }
285 }
286 }
287 return withResolved(keyDeser, vtd, valueDeser,
288 findContentNullProvider(ctxt, property, valueDeser), ignored);
289 }
290
291
296
297 @Override
298 public JsonDeserializer<Object> getContentDeserializer() {
299 return _valueDeserializer;
300 }
301
302 @Override
303 public ValueInstantiator getValueInstantiator() {
304 return _valueInstantiator;
305 }
306
307
312
313
326 @Override
327 public boolean isCachable() {
328
329
330 return (_valueDeserializer == null)
331 && (_keyDeserializer == null)
332 && (_valueTypeDeserializer == null)
333 && (_ignorableProperties == null);
334 }
335
336 @Override
337 @SuppressWarnings("unchecked")
338 public Map<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
339 {
340 if (_propertyBasedCreator != null) {
341 return _deserializeUsingCreator(p, ctxt);
342 }
343 if (_delegateDeserializer != null) {
344 return (Map<Object,Object>) _valueInstantiator.createUsingDelegate(ctxt,
345 _delegateDeserializer.deserialize(p, ctxt));
346 }
347 if (!_hasDefaultCreator) {
348 return (Map<Object,Object> ) ctxt.handleMissingInstantiator(getMapClass(),
349 getValueInstantiator(), p,
350 "no default constructor found");
351 }
352
353 JsonToken t = p.getCurrentToken();
354 if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
355
356 if (t == JsonToken.VALUE_STRING) {
357 return (Map<Object,Object>) _valueInstantiator.createFromString(ctxt, p.getText());
358 }
359 if (t == JsonToken.START_ARRAY) {
360 if (p.nextToken() == JsonToken.END_ARRAY) {
361 if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
362 return null;
363 }
364 } else if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
365 final Object value = deserialize(p, ctxt);
366 if (p.nextToken() != JsonToken.END_ARRAY) {
367 handleMissingEndArrayForSingle(p, ctxt);
368 }
369 return (Map<Object,Object>) value;
370 }
371
372 }
373 return (Map<Object,Object>) ctxt.handleUnexpectedToken(getValueType(ctxt), t, p, null);
374 }
375 final Map<Object,Object> result = (Map<Object,Object>) _valueInstantiator.createUsingDefault(ctxt);
376 if (_standardStringKey) {
377 _readAndBindStringKeyMap(p, ctxt, result);
378 return result;
379 }
380 _readAndBind(p, ctxt, result);
381 return result;
382 }
383
384 @SuppressWarnings("unchecked")
385 @Override
386 public Map<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt,
387 Map<Object,Object> result)
388 throws IOException
389 {
390
391 p.setCurrentValue(result);
392
393
394 JsonToken t = p.getCurrentToken();
395 if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME) {
396 return (Map<Object,Object>) ctxt.handleUnexpectedToken(getMapClass(), p);
397 }
398
399 if (_standardStringKey) {
400 _readAndUpdateStringKeyMap(p, ctxt, result);
401 return result;
402 }
403 _readAndUpdate(p, ctxt, result);
404 return result;
405 }
406
407 @Override
408 public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
409 TypeDeserializer typeDeserializer)
410 throws IOException
411 {
412
413 return typeDeserializer.deserializeTypedFromObject(p, ctxt);
414 }
415
416
421
422 @SuppressWarnings("unchecked")
423 public final Class<?> getMapClass() { return (Class<Map<Object,Object>>) _containerType.getRawClass(); }
424
425 @Override public JavaType getValueType() { return _containerType; }
426
427
432
433 protected final void _readAndBind(JsonParser p, DeserializationContext ctxt,
434 Map<Object,Object> result) throws IOException
435 {
436 final KeyDeserializer keyDes = _keyDeserializer;
437 final JsonDeserializer<Object> valueDes = _valueDeserializer;
438 final TypeDeserializer typeDeser = _valueTypeDeserializer;
439
440 MapReferringAccumulator referringAccumulator = null;
441 boolean useObjectId = valueDes.getObjectIdReader() != null;
442 if (useObjectId) {
443 referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(),
444 result);
445 }
446
447 String keyStr;
448 if (p.isExpectedStartObjectToken()) {
449 keyStr = p.nextFieldName();
450 } else {
451 JsonToken t = p.getCurrentToken();
452 if (t != JsonToken.FIELD_NAME) {
453 if (t == JsonToken.END_OBJECT) {
454 return;
455 }
456 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
457 }
458 keyStr = p.getCurrentName();
459 }
460
461 for (; keyStr != null; keyStr = p.nextFieldName()) {
462 Object key = keyDes.deserializeKey(keyStr, ctxt);
463
464 JsonToken t = p.nextToken();
465 if (_ignorableProperties != null && _ignorableProperties.contains(keyStr)) {
466 p.skipChildren();
467 continue;
468 }
469 try {
470
471 Object value;
472 if (t == JsonToken.VALUE_NULL) {
473 if (_skipNullValues) {
474 continue;
475 }
476 value = _nullProvider.getNullValue(ctxt);
477 } else if (typeDeser == null) {
478 value = valueDes.deserialize(p, ctxt);
479 } else {
480 value = valueDes.deserializeWithType(p, ctxt, typeDeser);
481 }
482 if (useObjectId) {
483 referringAccumulator.put(key, value);
484 } else {
485 result.put(key, value);
486 }
487 } catch (UnresolvedForwardReference reference) {
488 handleUnresolvedReference(ctxt, referringAccumulator, key, reference);
489 } catch (Exception e) {
490 wrapAndThrow(e, result, keyStr);
491 }
492 }
493 }
494
495
500 protected final void _readAndBindStringKeyMap(JsonParser p, DeserializationContext ctxt,
501 Map<Object,Object> result) throws IOException
502 {
503 final JsonDeserializer<Object> valueDes = _valueDeserializer;
504 final TypeDeserializer typeDeser = _valueTypeDeserializer;
505 MapReferringAccumulator referringAccumulator = null;
506 boolean useObjectId = (valueDes.getObjectIdReader() != null);
507 if (useObjectId) {
508 referringAccumulator = new MapReferringAccumulator(_containerType.getContentType().getRawClass(), result);
509 }
510
511 String key;
512 if (p.isExpectedStartObjectToken()) {
513 key = p.nextFieldName();
514 } else {
515 JsonToken t = p.getCurrentToken();
516 if (t == JsonToken.END_OBJECT) {
517 return;
518 }
519 if (t != JsonToken.FIELD_NAME) {
520 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
521 }
522 key = p.getCurrentName();
523 }
524
525 for (; key != null; key = p.nextFieldName()) {
526 JsonToken t = p.nextToken();
527 if (_ignorableProperties != null && _ignorableProperties.contains(key)) {
528 p.skipChildren();
529 continue;
530 }
531 try {
532
533 Object value;
534 if (t == JsonToken.VALUE_NULL) {
535 if (_skipNullValues) {
536 continue;
537 }
538 value = _nullProvider.getNullValue(ctxt);
539 } else if (typeDeser == null) {
540 value = valueDes.deserialize(p, ctxt);
541 } else {
542 value = valueDes.deserializeWithType(p, ctxt, typeDeser);
543 }
544 if (useObjectId) {
545 referringAccumulator.put(key, value);
546 } else {
547 result.put(key, value);
548 }
549 } catch (UnresolvedForwardReference reference) {
550 handleUnresolvedReference(ctxt, referringAccumulator, key, reference);
551 } catch (Exception e) {
552 wrapAndThrow(e, result, key);
553 }
554 }
555
556 }
557
558 @SuppressWarnings("unchecked")
559 public Map<Object,Object> _deserializeUsingCreator(JsonParser p, DeserializationContext ctxt) throws IOException
560 {
561 final PropertyBasedCreator creator = _propertyBasedCreator;
562
563 PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, null);
564
565 final JsonDeserializer<Object> valueDes = _valueDeserializer;
566 final TypeDeserializer typeDeser = _valueTypeDeserializer;
567
568 String key;
569 if (p.isExpectedStartObjectToken()) {
570 key = p.nextFieldName();
571 } else if (p.hasToken(JsonToken.FIELD_NAME)) {
572 key = p.getCurrentName();
573 } else {
574 key = null;
575 }
576
577 for (; key != null; key = p.nextFieldName()) {
578 JsonToken t = p.nextToken();
579 if (_ignorableProperties != null && _ignorableProperties.contains(key)) {
580 p.skipChildren();
581 continue;
582 }
583
584 SettableBeanProperty prop = creator.findCreatorProperty(key);
585 if (prop != null) {
586
587 if (buffer.assignParameter(prop, prop.deserialize(p, ctxt))) {
588 p.nextToken();
589 Map<Object,Object> result;
590 try {
591 result = (Map<Object,Object>)creator.build(ctxt, buffer);
592 } catch (Exception e) {
593 return wrapAndThrow(e, _containerType.getRawClass(), key);
594 }
595 _readAndBind(p, ctxt, result);
596 return result;
597 }
598 continue;
599 }
600
601 Object actualKey = _keyDeserializer.deserializeKey(key, ctxt);
602 Object value;
603
604 try {
605 if (t == JsonToken.VALUE_NULL) {
606 if (_skipNullValues) {
607 continue;
608 }
609 value = _nullProvider.getNullValue(ctxt);
610 } else if (typeDeser == null) {
611 value = valueDes.deserialize(p, ctxt);
612 } else {
613 value = valueDes.deserializeWithType(p, ctxt, typeDeser);
614 }
615 } catch (Exception e) {
616 wrapAndThrow(e, _containerType.getRawClass(), key);
617 return null;
618 }
619 buffer.bufferMapProperty(actualKey, value);
620 }
621
622
623 try {
624 return (Map<Object,Object>)creator.build(ctxt, buffer);
625 } catch (Exception e) {
626 wrapAndThrow(e, _containerType.getRawClass(), key);
627 return null;
628 }
629 }
630
631
636
637
640 protected final void _readAndUpdate(JsonParser p, DeserializationContext ctxt,
641 Map<Object,Object> result) throws IOException
642 {
643 final KeyDeserializer keyDes = _keyDeserializer;
644 final JsonDeserializer<Object> valueDes = _valueDeserializer;
645 final TypeDeserializer typeDeser = _valueTypeDeserializer;
646
647
648
649
650 String keyStr;
651 if (p.isExpectedStartObjectToken()) {
652 keyStr = p.nextFieldName();
653 } else {
654 JsonToken t = p.getCurrentToken();
655 if (t == JsonToken.END_OBJECT) {
656 return;
657 }
658 if (t != JsonToken.FIELD_NAME) {
659 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
660 }
661 keyStr = p.getCurrentName();
662 }
663
664 for (; keyStr != null; keyStr = p.nextFieldName()) {
665 Object key = keyDes.deserializeKey(keyStr, ctxt);
666
667 JsonToken t = p.nextToken();
668 if (_ignorableProperties != null && _ignorableProperties.contains(keyStr)) {
669 p.skipChildren();
670 continue;
671 }
672 try {
673
674 if (t == JsonToken.VALUE_NULL) {
675 if (_skipNullValues) {
676 continue;
677 }
678 result.put(key, _nullProvider.getNullValue(ctxt));
679 continue;
680 }
681 Object old = result.get(key);
682 Object value;
683 if (old != null) {
684 if (typeDeser == null) {
685 value = valueDes.deserialize(p, ctxt, old);
686 } else {
687 value = valueDes.deserializeWithType(p, ctxt, typeDeser, old);
688 }
689 } else if (typeDeser == null) {
690 value = valueDes.deserialize(p, ctxt);
691 } else {
692 value = valueDes.deserializeWithType(p, ctxt, typeDeser);
693 }
694 if (value != old) {
695 result.put(key, value);
696 }
697 } catch (Exception e) {
698 wrapAndThrow(e, result, keyStr);
699 }
700 }
701 }
702
703
710 protected final void _readAndUpdateStringKeyMap(JsonParser p, DeserializationContext ctxt,
711 Map<Object,Object> result) throws IOException
712 {
713 final JsonDeserializer<Object> valueDes = _valueDeserializer;
714 final TypeDeserializer typeDeser = _valueTypeDeserializer;
715
716
717
718
719 String key;
720 if (p.isExpectedStartObjectToken()) {
721 key = p.nextFieldName();
722 } else {
723 JsonToken t = p.getCurrentToken();
724 if (t == JsonToken.END_OBJECT) {
725 return;
726 }
727 if (t != JsonToken.FIELD_NAME) {
728 ctxt.reportWrongTokenException(this, JsonToken.FIELD_NAME, null);
729 }
730 key = p.getCurrentName();
731 }
732
733 for (; key != null; key = p.nextFieldName()) {
734 JsonToken t = p.nextToken();
735 if (_ignorableProperties != null && _ignorableProperties.contains(key)) {
736 p.skipChildren();
737 continue;
738 }
739 try {
740
741 if (t == JsonToken.VALUE_NULL) {
742 if (_skipNullValues) {
743 continue;
744 }
745 result.put(key, _nullProvider.getNullValue(ctxt));
746 continue;
747 }
748 Object old = result.get(key);
749 Object value;
750 if (old != null) {
751 if (typeDeser == null) {
752 value = valueDes.deserialize(p, ctxt, old);
753 } else {
754 value = valueDes.deserializeWithType(p, ctxt, typeDeser, old);
755 }
756 } else if (typeDeser == null) {
757 value = valueDes.deserialize(p, ctxt);
758 } else {
759 value = valueDes.deserializeWithType(p, ctxt, typeDeser);
760 }
761 if (value != old) {
762 result.put(key, value);
763 }
764 } catch (Exception e) {
765 wrapAndThrow(e, result, key);
766 }
767 }
768 }
769
770
775
776 private void handleUnresolvedReference(DeserializationContext ctxt,
777 MapReferringAccumulator accumulator,
778 Object key, UnresolvedForwardReference reference)
779 throws JsonMappingException
780 {
781 if (accumulator == null) {
782 ctxt.reportInputMismatch(this,
783 "Unresolved forward reference but no identity info: "+reference);
784 }
785 Referring referring = accumulator.handleUnresolvedReference(reference, key);
786 reference.getRoid().appendReferring(referring);
787 }
788
789 private final static class MapReferringAccumulator {
790 private final Class<?> _valueType;
791 private Map<Object,Object> _result;
792
795 private List<MapReferring> _accumulator = new ArrayList<MapReferring>();
796
797 public MapReferringAccumulator(Class<?> valueType, Map<Object, Object> result) {
798 _valueType = valueType;
799 _result = result;
800 }
801
802 public void put(Object key, Object value)
803 {
804 if (_accumulator.isEmpty()) {
805 _result.put(key, value);
806 } else {
807 MapReferring ref = _accumulator.get(_accumulator.size() - 1);
808 ref.next.put(key, value);
809 }
810 }
811
812 public Referring handleUnresolvedReference(UnresolvedForwardReference reference, Object key)
813 {
814 MapReferring id = new MapReferring(this, reference, _valueType, key);
815 _accumulator.add(id);
816 return id;
817 }
818
819 public void resolveForwardReference(Object id, Object value) throws IOException
820 {
821 Iterator<MapReferring> iterator = _accumulator.iterator();
822
823
824
825 Map<Object,Object> previous = _result;
826 while (iterator.hasNext()) {
827 MapReferring ref = iterator.next();
828 if (ref.hasId(id)) {
829 iterator.remove();
830 previous.put(ref.key, value);
831 previous.putAll(ref.next);
832 return;
833 }
834 previous = ref.next;
835 }
836
837 throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id
838 + "] that wasn't previously seen as unresolved.");
839 }
840 }
841
842
847 static class MapReferring extends Referring {
848 private final MapReferringAccumulator _parent;
849
850 public final Map<Object, Object> next = new LinkedHashMap<Object, Object>();
851 public final Object key;
852
853 MapReferring(MapReferringAccumulator parent, UnresolvedForwardReference ref,
854 Class<?> valueType, Object key)
855 {
856 super(ref, valueType);
857 _parent = parent;
858 this.key = key;
859 }
860
861 @Override
862 public void handleResolvedForwardReference(Object id, Object value) throws IOException {
863 _parent.resolveForwardReference(id, value);
864 }
865 }
866 }
867