1 package com.fasterxml.jackson.databind.deser;
2 
3 import java.io.IOException;
4 import java.util.*;
5 
6 import com.fasterxml.jackson.annotation.ObjectIdGenerator;
7 import com.fasterxml.jackson.annotation.ObjectIdGenerators;
8 import com.fasterxml.jackson.annotation.ObjectIdResolver;
9 
10 import com.fasterxml.jackson.core.*;
11 
12 import com.fasterxml.jackson.databind.*;
13 import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
14 import com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator;
15 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
16 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
17 import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
18 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
19 import com.fasterxml.jackson.databind.type.LogicalType;
20 
21 /**
22  * Deserializer only used for abstract types used as placeholders during polymorphic
23  * type handling deserialization. If so, there is no real deserializer associated
24  * with nominal type, just {@link TypeDeserializer}; and any calls that do not
25  * pass such resolver will result in an error.
26  */
27 public class AbstractDeserializer
28     extends JsonDeserializer<Object>
29     implements ContextualDeserializer, // since 2.9
30         java.io.Serializable
31 {
32     private static final long serialVersionUID = 1L;
33 
34     protected final JavaType _baseType;
35 
36     protected final ObjectIdReader _objectIdReader;
37 
38     protected final Map<String, SettableBeanProperty> _backRefProperties;
39 
40     protected transient Map<String,SettableBeanProperty> _properties;
41 
42     // support for "native" types, which require special care:
43 
44     protected final boolean _acceptString;
45     protected final boolean _acceptBoolean;
46     protected final boolean _acceptInt;
47     protected final boolean _acceptDouble;
48 
49     /*
50     /**********************************************************
51     /* Life cycle
52     /**********************************************************
53      */
54 
55     /**
56      * @since 2.9
57      *
58      * @param props Regular properties: currently only needed to support property-annotated
59      *    Object Id handling with property inclusion (needed for determining type of Object Id
60      *    to bind)
61      */
AbstractDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps, Map<String, SettableBeanProperty> props)62     public AbstractDeserializer(BeanDeserializerBuilder builder,
63             BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps,
64             Map<String, SettableBeanProperty> props)
65     {
66         _baseType = beanDesc.getType();
67         _objectIdReader = builder.getObjectIdReader();
68         _backRefProperties = backRefProps;
69         _properties = props;
70         Class<?> cls = _baseType.getRawClass();
71         _acceptString = cls.isAssignableFrom(String.class);
72         _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class);
73         _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class);
74         _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class);
75     }
76 
77     @Deprecated // since 2.9
AbstractDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps)78     public AbstractDeserializer(BeanDeserializerBuilder builder,
79             BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps) {
80         this(builder, beanDesc, backRefProps, null);
81     }
82 
AbstractDeserializer(BeanDescription beanDesc)83     protected AbstractDeserializer(BeanDescription beanDesc)
84     {
85         _baseType = beanDesc.getType();
86         _objectIdReader = null;
87         _backRefProperties = null;
88         Class<?> cls = _baseType.getRawClass();
89         _acceptString = cls.isAssignableFrom(String.class);
90         _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class);
91         _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class);
92         _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class);
93     }
94 
95     /**
96      * @since 2.9
97      */
AbstractDeserializer(AbstractDeserializer base, ObjectIdReader objectIdReader, Map<String, SettableBeanProperty> props)98     protected AbstractDeserializer(AbstractDeserializer base,
99             ObjectIdReader objectIdReader, Map<String, SettableBeanProperty> props)
100     {
101         _baseType = base._baseType;
102         _backRefProperties = base._backRefProperties;
103         _acceptString = base._acceptString;
104         _acceptBoolean = base._acceptBoolean;
105         _acceptInt = base._acceptInt;
106         _acceptDouble = base._acceptDouble;
107 
108         _objectIdReader = objectIdReader;
109         _properties = props;
110     }
111 
112     /**
113      * Factory method used when constructing instances for non-POJO types, like
114      * {@link java.util.Map}s.
115      *
116      * @since 2.3
117      */
constructForNonPOJO(BeanDescription beanDesc)118     public static AbstractDeserializer constructForNonPOJO(BeanDescription beanDesc) {
119         return new AbstractDeserializer(beanDesc);
120     }
121 
122     @Override
createContextual(DeserializationContext ctxt, BeanProperty property)123     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
124             BeanProperty property) throws JsonMappingException
125     {
126         final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
127         if (property != null && intr != null) {
128             final AnnotatedMember accessor = property.getMember();
129             if (accessor != null) {
130                 ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
131                 if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory)
132                     JavaType idType;
133                     ObjectIdGenerator<?> idGen;
134                     SettableBeanProperty idProp = null;
135                     ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);
136 
137                     // 2.1: allow modifications by "id ref" annotations as well:
138                     objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo);
139                     Class<?> implClass = objectIdInfo.getGeneratorType();
140 
141                     if (implClass == ObjectIdGenerators.PropertyGenerator.class) {
142                         PropertyName propName = objectIdInfo.getPropertyName();
143                         idProp = (_properties == null) ? null : _properties.get(propName.getSimpleName());
144                         if (idProp == null) {
145                             ctxt.reportBadDefinition(_baseType, String.format(
146                                     "Invalid Object Id definition for %s: cannot find property with name '%s'",
147                                     handledType().getName(), propName));
148                         }
149                         idType = idProp.getType();
150                         idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope());
151 /*
152                          ctxt.reportBadDefinition(_baseType, String.format(
153 /
154 "Invalid Object Id definition for abstract type %s: cannot use `PropertyGenerator` on polymorphic types using property annotation",
155 handledType().getName()));
156 */
157                     } else { // other types simpler
158                         resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);
159                         JavaType type = ctxt.constructType(implClass);
160                         idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
161                         idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo);
162                     }
163                     JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType);
164                     ObjectIdReader oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(),
165                              idGen, deser, idProp, resolver);
166                     return new AbstractDeserializer(this, oir, null);
167                 }
168             }
169         }
170         if (_properties == null) {
171             return this;
172         }
173         // Need to ensure properties are dropped at this point, regardless
174         return new AbstractDeserializer(this, _objectIdReader, null);
175     }
176 
177     /*
178     /**********************************************************
179     /* Public accessors
180     /**********************************************************
181      */
182 
183     @Override
handledType()184     public Class<?> handledType() {
185         return _baseType.getRawClass();
186     }
187 
188     @Override
isCachable()189     public boolean isCachable() { return true; }
190 
191     @Override // since 2.12
logicalType()192     public LogicalType logicalType() {
193         // 30-May-2020, tatu: Not sure if our choice here matters, but let's
194         //     guess "POJO" is most likely. If need be, could get more creative
195         return LogicalType.POJO;
196     }
197 
198     @Override // since 2.9
supportsUpdate(DeserializationConfig config)199     public Boolean supportsUpdate(DeserializationConfig config) {
200         /* 23-Oct-2016, tatu: Not exactly sure what to do with this; polymorphic
201          *   type handling seems bit risky so for now claim it "may or may not be"
202          *   possible, which does allow explicit per-type/per-property merging attempts,
203          *   but avoids general-configuration merges
204          */
205         return null;
206     }
207 
208     /**
209      * Overridden to return true for those instances that are
210      * handling value for which Object Identity handling is enabled
211      * (either via value type or referring property).
212      */
213     @Override
getObjectIdReader()214     public ObjectIdReader getObjectIdReader() {
215         return _objectIdReader;
216     }
217 
218     /**
219      * Method called by <code>BeanDeserializer</code> to resolve back reference
220      * part of managed references.
221      */
222     @Override
findBackReference(String logicalName)223     public SettableBeanProperty findBackReference(String logicalName) {
224         return (_backRefProperties == null) ? null : _backRefProperties.get(logicalName);
225     }
226 
227     /*
228     /**********************************************************
229     /* Deserializer implementation
230     /**********************************************************
231      */
232 
233     @Override
deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer)234     public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
235             TypeDeserializer typeDeserializer)
236         throws IOException
237     {
238         // Hmmh. One tricky question; for scalar, is it an Object Id, or "Natural" type?
239         // for now, prefer Object Id:
240         if (_objectIdReader != null) {
241             JsonToken t = p.currentToken();
242             if (t != null) {
243                 // Most commonly, a scalar (int id, uuid String, ...)
244                 if (t.isScalarValue()) {
245                     return _deserializeFromObjectId(p, ctxt);
246                 }
247                 // but, with 2.5+, a simple Object-wrapped value also legal:
248                 if (t == JsonToken.START_OBJECT) {
249                     t = p.nextToken();
250                 }
251                 if ((t == JsonToken.FIELD_NAME) && _objectIdReader.maySerializeAsObject()
252                         && _objectIdReader.isValidReferencePropertyName(p.currentName(), p)) {
253                     return _deserializeFromObjectId(p, ctxt);
254                 }
255             }
256         }
257         // First: support "natural" values (which are always serialized without type info!)
258         Object result = _deserializeIfNatural(p, ctxt);
259         if (result != null) {
260             return result;
261         }
262         return typeDeserializer.deserializeTypedFromObject(p, ctxt);
263     }
264 
265     @Override
deserialize(JsonParser p, DeserializationContext ctxt)266     public Object deserialize(JsonParser p, DeserializationContext ctxt)
267         throws IOException
268     {
269         // 16-Oct-2016, tatu: Let's pass non-null value instantiator so that we will
270         //    get proper exception type; needed to establish there are no creators
271         //    (since without ValueInstantiator this would not be known for certain)
272         ValueInstantiator bogus = new ValueInstantiator.Base(_baseType);
273         return ctxt.handleMissingInstantiator(_baseType.getRawClass(), bogus, p,
274                 "abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information");
275     }
276 
277     /*
278     /**********************************************************
279     /* Internal methods
280     /**********************************************************
281      */
282 
_deserializeIfNatural(JsonParser p, DeserializationContext ctxt)283     protected Object _deserializeIfNatural(JsonParser p, DeserializationContext ctxt) throws IOException
284     {
285         /* There is a chance we might be "natural" types
286          * (String, Boolean, Integer, Double), which do not include any type information...
287          * Care must be taken to only return this if return type matches, however.
288          * Finally, we may have to consider possibility of custom handlers for
289          * these values: but for now this should work ok.
290          */
291         switch (p.currentTokenId()) {
292         case JsonTokenId.ID_STRING:
293             if (_acceptString) {
294                 return p.getText();
295             }
296             break;
297         case JsonTokenId.ID_NUMBER_INT:
298             if (_acceptInt) {
299                 return p.getIntValue();
300             }
301             break;
302         case JsonTokenId.ID_NUMBER_FLOAT:
303             if (_acceptDouble) {
304                 return Double.valueOf(p.getDoubleValue());
305             }
306             break;
307         case JsonTokenId.ID_TRUE:
308             if (_acceptBoolean) {
309                 return Boolean.TRUE;
310             }
311             break;
312         case JsonTokenId.ID_FALSE:
313             if (_acceptBoolean) {
314                 return Boolean.FALSE;
315             }
316             break;
317         }
318         return null;
319     }
320 
321     /**
322      * Method called in cases where it looks like we got an Object Id
323      * to parse and use as a reference.
324      */
_deserializeFromObjectId(JsonParser p, DeserializationContext ctxt)325     protected Object _deserializeFromObjectId(JsonParser p, DeserializationContext ctxt) throws IOException
326     {
327         Object id = _objectIdReader.readObjectReference(p, ctxt);
328         ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver);
329         // do we have it resolved?
330         Object pojo = roid.resolve();
331         if (pojo == null) { // not yet; should wait...
332             throw new UnresolvedForwardReference(p,
333                     "Could not resolve Object Id ["+id+"] -- unresolved forward-reference?", p.getCurrentLocation(), roid);
334         }
335         return pojo;
336     }
337 }
338