1 package com.fasterxml.jackson.databind.deser.std;
2
3 import java.io.IOException;
4 import java.util.*;
5
6 import com.fasterxml.jackson.core.*;
7
8 import com.fasterxml.jackson.databind.*;
9 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
10 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
11 import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
12 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
13 import com.fasterxml.jackson.databind.type.TypeFactory;
14 import com.fasterxml.jackson.databind.util.ClassUtil;
15 import com.fasterxml.jackson.databind.util.ObjectBuffer;
16
17 /**
18  * Deserializer implementation that is used if it is necessary to bind content of
19  * "unknown" type; something declared as basic {@link java.lang.Object}
20  * (either explicitly, or due to type erasure).
21  * If so, "natural" mapping is used to convert JSON values to their natural
22  * Java object matches: JSON arrays to Java {@link java.util.List}s (or, if configured,
23  * Object[]), JSON objects to {@link java.util.Map}s, numbers to
24  * {@link java.lang.Number}s, booleans to {@link java.lang.Boolean}s and
25  * strings to {@link java.lang.String} (and nulls to nulls).
26  */

27 @JacksonStdImpl
28 public class UntypedObjectDeserializer
29     extends StdDeserializer<Object>
30     implements ResolvableDeserializer, ContextualDeserializer
31 {
32     private static final long serialVersionUID = 1L;
33
34     protected final static Object[] NO_OBJECTS = new Object[0];
35
36     /*
37     /**********************************************************
38     /* Possible custom deserializer overrides we need to use
39     /**********************************************************
40      */

41
42     protected JsonDeserializer<Object> _mapDeserializer;
43
44     protected JsonDeserializer<Object> _listDeserializer;
45
46     protected JsonDeserializer<Object> _stringDeserializer;
47
48     protected JsonDeserializer<Object> _numberDeserializer;
49
50     /**
51      * If {@link java.util.List} has been mapped to non-default implementation,
52      * we'll store type here
53      *
54      * @since 2.6
55      */

56     protected JavaType _listType;
57
58     /**
59      * If {@link java.util.Map} has been mapped to non-default implementation,
60      * we'll store type here
61      *
62      * @since 2.6
63      */

64     protected JavaType _mapType;
65
66     /**
67      * @since 2.9
68      */

69     protected final boolean _nonMerging;
70     
71     /**
72      * @deprecated Since 2.6 use variant takes type arguments
73      */

74     @Deprecated
75     public UntypedObjectDeserializer() {
76         this(nullnull);
77     }
78
79     public UntypedObjectDeserializer(JavaType listType, JavaType mapType) {
80         super(Object.class);
81         _listType = listType;
82         _mapType = mapType;
83         _nonMerging = false;
84     }
85
86     @SuppressWarnings("unchecked")
87     public UntypedObjectDeserializer(UntypedObjectDeserializer base,
88             JsonDeserializer<?> mapDeser, JsonDeserializer<?> listDeser,
89             JsonDeserializer<?> stringDeser, JsonDeserializer<?> numberDeser)
90     {
91         super(Object.class);
92         _mapDeserializer = (JsonDeserializer<Object>) mapDeser;
93         _listDeserializer = (JsonDeserializer<Object>) listDeser;
94         _stringDeserializer = (JsonDeserializer<Object>) stringDeser;
95         _numberDeserializer = (JsonDeserializer<Object>) numberDeser;
96         _listType = base._listType;
97         _mapType = base._mapType;
98         _nonMerging = base._nonMerging;
99     }
100
101     /**
102      * @since 2.9
103      */

104     protected UntypedObjectDeserializer(UntypedObjectDeserializer base,
105             boolean nonMerging)
106     {
107         super(Object.class);
108         _mapDeserializer = base._mapDeserializer;
109         _listDeserializer = base._listDeserializer;
110         _stringDeserializer = base._stringDeserializer;
111         _numberDeserializer = base._numberDeserializer;
112         _listType = base._listType;
113         _mapType = base._mapType;
114         _nonMerging = nonMerging;
115     }
116
117     /*
118     /**********************************************************
119     /* Initialization
120     /**********************************************************
121      */

122
123     /**
124      * We need to implement this method to properly find things to delegate
125      * to: it cannot be done earlier since delegated deserializers almost
126      * certainly require access to this instance (at least "List" and "Map" ones)
127      */

128     @SuppressWarnings("unchecked")
129     @Override
130     public void resolve(DeserializationContext ctxt) throws JsonMappingException
131     {
132         JavaType obType = ctxt.constructType(Object.class);
133         JavaType stringType = ctxt.constructType(String.class);
134         TypeFactory tf = ctxt.getTypeFactory();
135
136         /* 26-Nov-2014, tatu: This is highly unusual, as in general contextualization
137          *    should always be called separately, from within "createContextual()".
138          *    But this is a very singular deserializer since it operates on `Object`
139          *    (and often for `?` type parameter), and as a result, easily and commonly
140          *    results in cycles, being value deserializer for various Maps and Collections.
141          *    Because of this, we must somehow break the cycles. This is done here by
142          *    forcing pseudo-contextualization with null property.
143          */

144
145         // So: first find possible custom instances
146         if (_listType == null) {
147             _listDeserializer = _clearIfStdImpl(_findCustomDeser(ctxt, tf.constructCollectionType(List.class, obType)));
148         } else {
149             // NOTE: if non-default List type, always consider to be non-standard deser
150             _listDeserializer = _findCustomDeser(ctxt, _listType);
151         }
152         if (_mapType == null) {
153             _mapDeserializer = _clearIfStdImpl(_findCustomDeser(ctxt, tf.constructMapType(Map.class, stringType, obType)));
154         } else {
155             // NOTE: if non-default Map type, always consider to be non-standard deser
156             _mapDeserializer = _findCustomDeser(ctxt, _mapType);
157         }
158         _stringDeserializer = _clearIfStdImpl(_findCustomDeser(ctxt, stringType));
159         _numberDeserializer = _clearIfStdImpl(_findCustomDeser(ctxt, tf.constructType(Number.class)));
160
161         // and then do bogus contextualization, in case custom ones need to resolve dependencies of
162         // their own
163         JavaType unknown = TypeFactory.unknownType();
164         _mapDeserializer = (JsonDeserializer<Object>) ctxt.handleSecondaryContextualization(_mapDeserializer, null, unknown);
165         _listDeserializer = (JsonDeserializer<Object>) ctxt.handleSecondaryContextualization(_listDeserializer, null, unknown);
166         _stringDeserializer = (JsonDeserializer<Object>) ctxt.handleSecondaryContextualization(_stringDeserializer, null, unknown);
167         _numberDeserializer = (JsonDeserializer<Object>) ctxt.handleSecondaryContextualization(_numberDeserializer, null, unknown);
168     }
169
170     protected JsonDeserializer<Object> _findCustomDeser(DeserializationContext ctxt, JavaType type)
171         throws JsonMappingException
172     {
173         // Since we are calling from `resolve`, we should NOT try to contextualize yet;
174         // contextualization will only occur at a later point
175         return ctxt.findNonContextualValueDeserializer(type);
176     }
177
178     protected JsonDeserializer<Object> _clearIfStdImpl(JsonDeserializer<Object> deser) {
179         return ClassUtil.isJacksonStdImpl(deser) ? null : deser;
180     }
181
182     /**
183      * We only use contextualization for optimizing the case where no customization
184      * occurred; if so, can slip in a more streamlined version.
185      */

186     @Override
187     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
188             BeanProperty property) throws JsonMappingException
189     {
190         // 14-Jun-2017, tatu: [databind#1625]: may want to block merging, for root value
191         boolean preventMerge = (property == null)
192                 && Boolean.FALSE.equals(ctxt.getConfig().getDefaultMergeable(Object.class));
193         // 20-Apr-2014, tatu: If nothing custom, let's use "vanilla" instance,
194         //     simpler and can avoid some of delegation
195         if ((_stringDeserializer == null) && (_numberDeserializer == null)
196                 && (_mapDeserializer == null) && (_listDeserializer == null)
197                 &&  getClass() == UntypedObjectDeserializer.class) {
198             return Vanilla.instance(preventMerge);
199         }
200         if (preventMerge != _nonMerging) {
201             return new UntypedObjectDeserializer(this, preventMerge);
202         }
203         return this;
204     }
205
206     /*
207     /**********************************************************
208     /* Deserializer API
209     /**********************************************************
210      */

211
212     /* 07-Nov-2014, tatu: When investigating [databind#604], realized that it makes
213      *   sense to also mark this is cachable, since lookup not exactly free, and
214      *   since it's not uncommon to "read anything"
215      */

216     @Override
217     public boolean isCachable() {
218         /* 26-Mar-2015, tatu: With respect to [databind#735], there are concerns over
219          *   cachability. It seems like we SHOULD be safe here; but just in case there
220          *   are problems with false sharing, this may need to be revisited.
221          */

222         return true;
223     }
224
225     @Override // since 2.9
226     public Boolean supportsUpdate(DeserializationConfig config) {
227         // 21-Apr-2017, tatu: Bit tricky... some values, yes. So let's say "dunno"
228         return null;
229     }
230
231     @Override
232     public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
233     {
234         switch (p.getCurrentTokenId()) {
235         case JsonTokenId.ID_START_OBJECT:
236         case JsonTokenId.ID_FIELD_NAME:
237             // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME),
238             //    if caller has advanced to the first token of Object, but for empty Object
239         case JsonTokenId.ID_END_OBJECT:
240             if (_mapDeserializer != null) {
241                 return _mapDeserializer.deserialize(p, ctxt);
242             }
243             return mapObject(p, ctxt);
244         case JsonTokenId.ID_START_ARRAY:
245             if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) {
246                 return mapArrayToArray(p, ctxt);
247             }
248             if (_listDeserializer != null) {
249                 return _listDeserializer.deserialize(p, ctxt);
250             }
251             return mapArray(p, ctxt);
252         case JsonTokenId.ID_EMBEDDED_OBJECT:
253             return p.getEmbeddedObject();
254         case JsonTokenId.ID_STRING:
255             if (_stringDeserializer != null) {
256                 return _stringDeserializer.deserialize(p, ctxt);
257             }
258             return p.getText();
259
260         case JsonTokenId.ID_NUMBER_INT:
261             if (_numberDeserializer != null) {
262                 return _numberDeserializer.deserialize(p, ctxt);
263             }
264             /* Caller may want to get all integral values returned as {@link java.math.BigInteger},
265              * or {@link java.lang.Long} for consistency
266              */

267             if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) {
268                 return _coerceIntegral(p, ctxt);
269             }
270             return p.getNumberValue(); // should be optimal, whatever it is
271
272         case JsonTokenId.ID_NUMBER_FLOAT:
273             if (_numberDeserializer != null) {
274                 return _numberDeserializer.deserialize(p, ctxt);
275             }
276             // Need to allow overriding the behavior regarding which type to use
277             if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
278                 return p.getDecimalValue();
279             }
280             // as per [databind#1453] should not assume Double but:
281             return p.getNumberValue();
282
283         case JsonTokenId.ID_TRUE:
284             return Boolean.TRUE;
285         case JsonTokenId.ID_FALSE:
286             return Boolean.FALSE;
287
288         case JsonTokenId.ID_NULL: // 08-Nov-2016, tatu: yes, occurs
289             return null;
290
291 //        case JsonTokenId.ID_END_ARRAY: // invalid
292         default:
293         }
294         return ctxt.handleUnexpectedToken(Object.class, p);
295     }
296
297     @Override
298     public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
299             TypeDeserializer typeDeserializer) throws IOException
300     {
301         switch (p.getCurrentTokenId()) {
302         // First: does it look like we had type id wrapping of some kind?
303         case JsonTokenId.ID_START_ARRAY:
304         case JsonTokenId.ID_START_OBJECT:
305         case JsonTokenId.ID_FIELD_NAME:
306             // Output can be as JSON Object, Array or scalar: no way to know at this point:
307             return typeDeserializer.deserializeTypedFromAny(p, ctxt);
308
309         case JsonTokenId.ID_EMBEDDED_OBJECT:
310             return p.getEmbeddedObject();
311
312         // Otherwise we probably got a "native" type (ones that map
313         // naturally and thus do not need or use type ids)
314         case JsonTokenId.ID_STRING:
315             if (_stringDeserializer != null) {
316                 return _stringDeserializer.deserialize(p, ctxt);
317             }
318             return p.getText();
319
320         case JsonTokenId.ID_NUMBER_INT:
321             if (_numberDeserializer != null) {
322                 return _numberDeserializer.deserialize(p, ctxt);
323             }
324             // May need coercion to "bigger" types:
325             if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) {
326                 return _coerceIntegral(p, ctxt);
327             }
328             return p.getNumberValue(); // should be optimal, whatever it is
329
330         case JsonTokenId.ID_NUMBER_FLOAT:
331             if (_numberDeserializer != null) {
332                 return _numberDeserializer.deserialize(p, ctxt);
333             }
334             if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
335                 return p.getDecimalValue();
336             }
337             return p.getNumberValue();
338
339         case JsonTokenId.ID_TRUE:
340             return Boolean.TRUE;
341         case JsonTokenId.ID_FALSE:
342             return Boolean.FALSE;
343
344         case JsonTokenId.ID_NULL: // should not get this far really but...
345             return null;
346         default:
347         }
348         return ctxt.handleUnexpectedToken(Object.class, p);
349     }
350
351     @SuppressWarnings("unchecked")
352     @Override // since 2.9 (to support deep merge)
353     public Object deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue)
354         throws IOException
355     {
356         if (_nonMerging) {
357             return deserialize(p, ctxt);
358         }
359
360         switch (p.getCurrentTokenId()) {
361         case JsonTokenId.ID_START_OBJECT:
362         case JsonTokenId.ID_FIELD_NAME:
363             // We may also be given END_OBJECT (similar to FIELD_NAME),
364             // if caller has advanced to the first token of Object, but for empty Object
365         case JsonTokenId.ID_END_OBJECT:
366             if (_mapDeserializer != null) {
367                 return _mapDeserializer.deserialize(p, ctxt, intoValue);
368             }
369             if (intoValue instanceof Map<?,?>) {
370                 return mapObject(p, ctxt, (Map<Object,Object>) intoValue);
371             }
372             return mapObject(p, ctxt);
373         case JsonTokenId.ID_START_ARRAY:
374             if (_listDeserializer != null) {
375                 return _listDeserializer.deserialize(p, ctxt, intoValue);
376             }
377             if (intoValue instanceof Collection<?>) {
378                 return mapArray(p, ctxt, (Collection<Object>) intoValue);
379             }
380             if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) {
381                 return mapArrayToArray(p, ctxt);
382             }
383             return mapArray(p, ctxt);
384         case JsonTokenId.ID_EMBEDDED_OBJECT:
385             return p.getEmbeddedObject();
386         case JsonTokenId.ID_STRING:
387             if (_stringDeserializer != null) {
388                 return _stringDeserializer.deserialize(p, ctxt, intoValue);
389             }
390             return p.getText();
391
392         case JsonTokenId.ID_NUMBER_INT:
393             if (_numberDeserializer != null) {
394                 return _numberDeserializer.deserialize(p, ctxt, intoValue);
395             }
396             if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) {
397                 return _coerceIntegral(p, ctxt);
398             }
399             return p.getNumberValue();
400
401         case JsonTokenId.ID_NUMBER_FLOAT:
402             if (_numberDeserializer != null) {
403                 return _numberDeserializer.deserialize(p, ctxt, intoValue);
404             }
405             if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
406                 return p.getDecimalValue();
407             }
408             return p.getNumberValue();
409         case JsonTokenId.ID_TRUE:
410             return Boolean.TRUE;
411         case JsonTokenId.ID_FALSE:
412             return Boolean.FALSE;
413
414         case JsonTokenId.ID_NULL:
415             // 21-Apr-2017, tatu: May need to consider "skip nulls" at some point but...
416             return null;
417         default:
418         }
419         // easiest to just delegate to "dumb" version for the rest?
420         return deserialize(p, ctxt);
421     }
422
423     /*
424     /**********************************************************
425     /* Internal methods
426     /**********************************************************
427      */

428
429     /**
430      * Method called to map a JSON Array into a Java value.
431      */

432     protected Object mapArray(JsonParser p, DeserializationContext ctxt) throws IOException
433     {
434         // Minor optimization to handle small lists (default size for ArrayList is 10)
435         if (p.nextToken()  == JsonToken.END_ARRAY) {
436             return new ArrayList<Object>(2);
437         }
438         Object value = deserialize(p, ctxt);
439         if (p.nextToken()  == JsonToken.END_ARRAY) {
440             ArrayList<Object> l = new ArrayList<Object>(2);
441             l.add(value);
442             return l;
443         }
444         Object value2 = deserialize(p, ctxt);
445         if (p.nextToken()  == JsonToken.END_ARRAY) {
446             ArrayList<Object> l = new ArrayList<Object>(2);
447             l.add(value);
448             l.add(value2);
449             return l;
450         }
451         ObjectBuffer buffer = ctxt.leaseObjectBuffer();
452         Object[] values = buffer.resetAndStart();
453         int ptr = 0;
454         values[ptr++] = value;
455         values[ptr++] = value2;
456         int totalSize = ptr;
457         do {
458             value = deserialize(p, ctxt);
459             ++totalSize;
460             if (ptr >= values.length) {
461                 values = buffer.appendCompletedChunk(values);
462                 ptr = 0;
463             }
464             values[ptr++] = value;
465         } while (p.nextToken() != JsonToken.END_ARRAY);
466         // let's create full array then
467         ArrayList<Object> result = new ArrayList<Object>(totalSize);
468         buffer.completeAndClearBuffer(values, ptr, result);
469         return result;
470     }
471
472     protected Object mapArray(JsonParser p, DeserializationContext ctxt,
473             Collection<Object> result) throws IOException
474     {
475         // we start by pointing to START_ARRAY. Also, no real merging; array/Collection
476         // just appends always
477         while (p.nextToken() != JsonToken.END_ARRAY) {
478             result.add(deserialize(p, ctxt));
479         }
480         return result;
481     }
482
483     /**
484      * Method called to map a JSON Object into a Java value.
485      */

486     protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOException
487     {
488         String key1;
489
490         JsonToken t = p.getCurrentToken();
491         
492         if (t == JsonToken.START_OBJECT) {
493             key1 = p.nextFieldName();
494         } else if (t == JsonToken.FIELD_NAME) {
495             key1 = p.getCurrentName();
496         } else {
497             if (t != JsonToken.END_OBJECT) {
498                 return ctxt.handleUnexpectedToken(handledType(), p);
499             }
500             key1 = null;
501         }
502         if (key1 == null) {
503             // empty map might work; but caller may want to modify... so better just give small modifiable
504             return new LinkedHashMap<String,Object>(2);
505         }
506         // minor optimization; let's handle 1 and 2 entry cases separately
507         // 24-Mar-2015, tatu: Ideally, could use one of 'nextXxx()' methods, but for
508         //   that we'd need new method(s) in JsonDeserializer. So not quite yet.
509         p.nextToken();
510         Object value1 = deserialize(p, ctxt);
511
512         String key2 = p.nextFieldName();
513         if (key2 == null) { // has to be END_OBJECT, then
514             // single entry; but we want modifiable
515             LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(2);
516             result.put(key1, value1);
517             return result;
518         }
519         p.nextToken();
520         Object value2 = deserialize(p, ctxt);
521
522         String key = p.nextFieldName();
523
524         if (key == null) {
525             LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(4);
526             result.put(key1, value1);
527             result.put(key2, value2);
528             return result;
529         }
530         // And then the general casedefault map size is 16
531         LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>();
532         result.put(key1, value1);
533         result.put(key2, value2);
534
535         do {
536             p.nextToken();
537             result.put(key, deserialize(p, ctxt));
538         } while ((key = p.nextFieldName()) != null);
539         return result;
540     }
541
542     /**
543      * Method called to map a JSON Array into a Java Object array (Object[]).
544      */

545     protected Object[] mapArrayToArray(JsonParser p, DeserializationContext ctxt) throws IOException
546     {
547         // Minor optimization to handle small lists (default size for ArrayList is 10)
548         if (p.nextToken()  == JsonToken.END_ARRAY) {
549             return NO_OBJECTS;
550         }
551         ObjectBuffer buffer = ctxt.leaseObjectBuffer();
552         Object[] values = buffer.resetAndStart();
553         int ptr = 0;
554         do {
555             Object value = deserialize(p, ctxt);
556             if (ptr >= values.length) {
557                 values = buffer.appendCompletedChunk(values);
558                 ptr = 0;
559             }
560             values[ptr++] = value;
561         } while (p.nextToken() != JsonToken.END_ARRAY);
562         return buffer.completeAndClearBuffer(values, ptr);
563     }
564
565     protected Object mapObject(JsonParser p, DeserializationContext ctxt,
566             Map<Object,Object> m) throws IOException
567     {
568         JsonToken t = p.getCurrentToken();
569         if (t == JsonToken.START_OBJECT) {
570             t = p.nextToken();
571         }
572         if (t == JsonToken.END_OBJECT) {
573             return m;
574         }
575         // NOTE: we are guaranteed to point to FIELD_NAME
576         String key = p.getCurrentName();
577         do {
578             p.nextToken();
579             // and possibly recursive merge here
580             Object old = m.get(key);
581             Object newV;
582
583             if (old != null) {
584                 newV = deserialize(p, ctxt, old);
585             } else {
586                 newV = deserialize(p, ctxt);
587             }
588             if (newV != old) {
589                 m.put(key, newV);
590             }
591         } while ((key = p.nextFieldName()) != null);
592         return m;
593     }
594
595     /*
596     /**********************************************************
597     /* Separate "vanilla" implementation for common case of
598     /* no custom deserializer overrides
599     /**********************************************************
600      */

601
602     /**
603      * Streamlined version of {@link UntypedObjectDeserializer} that has fewer checks and
604      * is only used when no custom deserializer overrides are applied.
605      */

606     @JacksonStdImpl
607     public static class Vanilla
608         extends StdDeserializer<Object>
609     {
610         private static final long serialVersionUID = 1L;
611
612         public final static Vanilla std = new Vanilla();
613
614         /**
615          * @since 2.9
616          */

617         protected final boolean _nonMerging;
618         
619         public Vanilla() { this(false); }
620
621         protected Vanilla(boolean nonMerging) {
622             super(Object.class);
623             _nonMerging = nonMerging;
624         }
625
626         public static Vanilla instance(boolean nonMerging) {
627             if (nonMerging) {
628                 return new Vanilla(true);
629             }
630             return std;
631         }
632         
633         @Override // since 2.9
634         public Boolean supportsUpdate(DeserializationConfig config) {
635             // 21-Apr-2017, tatu: Bit tricky... some values, yes. So let's say "dunno"
636             // 14-Jun-2017, tatu: Well, if merging blocked, can say no, as well.
637             return _nonMerging ? Boolean.FALSE : null;
638         }
639
640         @Override
641         public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
642         {
643             switch (p.getCurrentTokenId()) {
644             case JsonTokenId.ID_START_OBJECT:
645                 {
646                     JsonToken t = p.nextToken();
647                     if (t == JsonToken.END_OBJECT) {
648                         return new LinkedHashMap<String,Object>(2);
649                     }
650                 }
651             case JsonTokenId.ID_FIELD_NAME:
652                 return mapObject(p, ctxt);
653             case JsonTokenId.ID_START_ARRAY:
654                 {
655                     JsonToken t = p.nextToken();
656                     if (t == JsonToken.END_ARRAY) { // and empty one too
657                         if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) {
658                             return NO_OBJECTS;
659                         }
660                         return new ArrayList<Object>(2);
661                     }
662                 }
663                 if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) {
664                     return mapArrayToArray(p, ctxt);
665                 }
666                 return mapArray(p, ctxt);
667             case JsonTokenId.ID_EMBEDDED_OBJECT:
668                 return p.getEmbeddedObject();
669             case JsonTokenId.ID_STRING:
670                 return p.getText();
671
672             case JsonTokenId.ID_NUMBER_INT:
673                 if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) {
674                     return _coerceIntegral(p, ctxt);
675                 }
676                 return p.getNumberValue(); // should be optimal, whatever it is
677
678             case JsonTokenId.ID_NUMBER_FLOAT:
679                 if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
680                     return p.getDecimalValue();
681                 }
682                 return p.getNumberValue();
683
684             case JsonTokenId.ID_TRUE:
685                 return Boolean.TRUE;
686             case JsonTokenId.ID_FALSE:
687                 return Boolean.FALSE;
688
689             case JsonTokenId.ID_END_OBJECT:
690                 // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME),
691                 //    if caller has advanced to the first token of Object, but for empty Object
692                 return new LinkedHashMap<String,Object>(2);
693
694             case JsonTokenId.ID_NULL: // 08-Nov-2016, tatu: yes, occurs
695                 return null;
696
697             //case JsonTokenId.ID_END_ARRAY: // invalid
698             default:
699             }
700             return ctxt.handleUnexpectedToken(Object.class, p);
701         }
702
703         @Override
704         public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException
705         {
706             switch (p.getCurrentTokenId()) {
707             case JsonTokenId.ID_START_ARRAY:
708             case JsonTokenId.ID_START_OBJECT:
709             case JsonTokenId.ID_FIELD_NAME:
710                 return typeDeserializer.deserializeTypedFromAny(p, ctxt);
711
712             case JsonTokenId.ID_STRING:
713                 return p.getText();
714
715             case JsonTokenId.ID_NUMBER_INT:
716                 if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
717                     return p.getBigIntegerValue();
718                 }
719                 return p.getNumberValue();
720
721             case JsonTokenId.ID_NUMBER_FLOAT:
722                 if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
723                     return p.getDecimalValue();
724                 }
725                 return p.getNumberValue();
726
727             case JsonTokenId.ID_TRUE:
728                 return Boolean.TRUE;
729             case JsonTokenId.ID_FALSE:
730                 return Boolean.FALSE;
731             case JsonTokenId.ID_EMBEDDED_OBJECT:
732                 return p.getEmbeddedObject();
733
734             case JsonTokenId.ID_NULL: // should not get this far really but...
735                 return null;
736             default:
737             }
738             return ctxt.handleUnexpectedToken(Object.class, p);
739         }
740
741         @SuppressWarnings("unchecked")
742         @Override // since 2.9 (to support deep merge)
743         public Object deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue)
744             throws IOException
745         {
746             if (_nonMerging) {
747                 return deserialize(p, ctxt);
748             }
749
750             switch (p.getCurrentTokenId()) {
751             case JsonTokenId.ID_END_OBJECT:
752             case JsonTokenId.ID_END_ARRAY:
753                 return intoValue;
754             case JsonTokenId.ID_START_OBJECT:
755                 {
756                     JsonToken t = p.nextToken(); // to get to FIELD_NAME or END_OBJECT
757                     if (t == JsonToken.END_OBJECT) {
758                         return intoValue;
759                     }
760                 }
761             case JsonTokenId.ID_FIELD_NAME:
762                 if (intoValue instanceof Map<?,?>) {
763                     Map<Object,Object> m = (Map<Object,Object>) intoValue;
764                     // NOTE: we are guaranteed to point to FIELD_NAME
765                     String key = p.getCurrentName();
766                     do {
767                         p.nextToken();
768                         // and possibly recursive merge here
769                         Object old = m.get(key);
770                         Object newV;
771                         if (old != null) {
772                             newV = deserialize(p, ctxt, old);
773                         } else {
774                             newV = deserialize(p, ctxt);
775                         }
776                         if (newV != old) {
777                             m.put(key, newV);
778                         }
779                     } while ((key = p.nextFieldName()) != null);
780                     return intoValue;
781                 }
782                 break;
783             case JsonTokenId.ID_START_ARRAY:
784                 {
785                     JsonToken t = p.nextToken(); // to get to FIELD_NAME or END_OBJECT
786                     if (t == JsonToken.END_ARRAY) {
787                         return intoValue;
788                     }
789                 }
790
791                 if (intoValue instanceof Collection<?>) {
792                     Collection<Object> c = (Collection<Object>) intoValue;
793                     // NOTE: merge for arrays/Collections means append, can't merge contents
794                     do {
795                         c.add(deserialize(p, ctxt));
796                     } while (p.nextToken() != JsonToken.END_ARRAY);
797                     return intoValue;
798                 }
799                 // 21-Apr-2017, tatu: Should we try to support merging of Object[] values too?
800                 //    ... maybe future improvement
801                 break;
802             }
803             // Easiest handling for the rest, delegate. Only (?) question: how about nulls?
804             return deserialize(p, ctxt);
805         }
806
807         protected Object mapArray(JsonParser p, DeserializationContext ctxt) throws IOException
808         {
809             Object value = deserialize(p, ctxt);
810             if (p.nextToken()  == JsonToken.END_ARRAY) {
811                 ArrayList<Object> l = new ArrayList<Object>(2);
812                 l.add(value);
813                 return l;
814             }
815             Object value2 = deserialize(p, ctxt);
816             if (p.nextToken()  == JsonToken.END_ARRAY) {
817                 ArrayList<Object> l = new ArrayList<Object>(2);
818                 l.add(value);
819                 l.add(value2);
820                 return l;
821             }
822             ObjectBuffer buffer = ctxt.leaseObjectBuffer();
823             Object[] values = buffer.resetAndStart();
824             int ptr = 0;
825             values[ptr++] = value;
826             values[ptr++] = value2;
827             int totalSize = ptr;
828             do {
829                 value = deserialize(p, ctxt);
830                 ++totalSize;
831                 if (ptr >= values.length) {
832                     values = buffer.appendCompletedChunk(values);
833                     ptr = 0;
834                 }
835                 values[ptr++] = value;
836             } while (p.nextToken() != JsonToken.END_ARRAY);
837             // let's create full array then
838             ArrayList<Object> result = new ArrayList<Object>(totalSize);
839             buffer.completeAndClearBuffer(values, ptr, result);
840             return result;
841         }
842
843         /**
844          * Method called to map a JSON Array into a Java Object array (Object[]).
845          */

846         protected Object[] mapArrayToArray(JsonParser p, DeserializationContext ctxt) throws IOException {
847             ObjectBuffer buffer = ctxt.leaseObjectBuffer();
848             Object[] values = buffer.resetAndStart();
849             int ptr = 0;
850             do {
851                 Object value = deserialize(p, ctxt);
852                 if (ptr >= values.length) {
853                     values = buffer.appendCompletedChunk(values);
854                     ptr = 0;
855                 }
856                 values[ptr++] = value;
857             } while (p.nextToken() != JsonToken.END_ARRAY);
858             return buffer.completeAndClearBuffer(values, ptr);
859         }
860
861         /**
862          * Method called to map a JSON Object into a Java value.
863          */

864         protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOException
865         {
866             // will point to FIELD_NAME at this point, guaranteed
867             String key1 = p.getText();
868             p.nextToken();
869             Object value1 = deserialize(p, ctxt);
870
871             String key2 = p.nextFieldName();
872             if (key2 == null) { // single entry; but we want modifiable
873                 LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(2);
874                 result.put(key1, value1);
875                 return result;
876             }
877             p.nextToken();
878             Object value2 = deserialize(p, ctxt);
879
880             String key = p.nextFieldName();
881             if (key == null) {
882                 LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(4);
883                 result.put(key1, value1);
884                 result.put(key2, value2);
885                 return result;
886             }
887             // And then the general casedefault map size is 16
888             LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>();
889             result.put(key1, value1);
890             result.put(key2, value2);
891             do {
892                 p.nextToken();
893                 result.put(key, deserialize(p, ctxt));
894             } while ((key = p.nextFieldName()) != null);
895             return result;
896         }
897     }
898 }
899