1 package com.fasterxml.jackson.databind.deser;
2
3 import java.util.*;
4
5 import com.fasterxml.jackson.databind.*;
6 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
7 import com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap;
8 import com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty;
9 import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
10 import com.fasterxml.jackson.databind.deser.impl.ValueInjector;
11 import com.fasterxml.jackson.databind.introspect.*;
12 import com.fasterxml.jackson.databind.util.Annotations;
13
14 /**
15  * Builder class used for aggregating deserialization information about
16  * a POJO, in order to build a {@link JsonDeserializer} for deserializing
17  * instances.
18  */

19 public class BeanDeserializerBuilder
20 {
21     /*
22     /**********************************************************
23     /* Configuration
24     /**********************************************************
25      */

26
27     final protected DeserializationConfig _config;
28
29     /**
30      * @since 2.9
31      */

32     final protected DeserializationContext _context;
33
34     /*
35     /**********************************************************
36     /* General information about POJO
37     /**********************************************************
38      */

39
40     /**
41      * Introspected information about POJO for deserializer to handle
42      */

43     final protected BeanDescription _beanDesc;
44
45     /*
46     /**********************************************************
47     /* Accumulated information about properties
48     /**********************************************************
49      */

50
51     /**
52      * Properties to deserialize collected so far.
53      */

54     final protected Map<String, SettableBeanProperty> _properties
55         = new LinkedHashMap<String, SettableBeanProperty>();
56
57     /**
58      * Value injectors for deserialization
59      */

60     protected List<ValueInjector> _injectables;
61
62     /**
63      * Back-reference properties this bean contains (if any)
64      */

65     protected HashMap<String, SettableBeanProperty> _backRefProperties;
66
67     /**
68      * Set of names of properties that are recognized but are to be ignored for deserialization
69      * purposes (meaning no exception is thrown, value is just skipped).
70      */

71     protected HashSet<String> _ignorableProps;
72
73     /**
74      * Object that will handle value instantiation for the bean type.
75      */

76     protected ValueInstantiator _valueInstantiator;
77
78     /**
79      * Handler for Object Id values, if Object Ids are enabled for the
80      * bean type.
81      */

82     protected ObjectIdReader _objectIdReader;
83
84     /**
85      * Fallback setter used for handling any properties that are not
86      * mapped to regular setters. If setter is not null, it will be
87      * called once for each such property.
88      */

89     protected SettableAnyProperty _anySetter;
90
91     /**
92      * Flag that can be set to ignore and skip unknown properties.
93      * If set, will not throw an exception for unknown properties.
94      */

95     protected boolean _ignoreAllUnknown;
96
97     /**
98      * When creating Builder-based deserializers, this indicates
99      * method to call on builder to finalize value.
100      */

101     protected AnnotatedMethod _buildMethod;
102
103     /**
104      * In addition, Builder may have additional configuration
105      */

106     protected JsonPOJOBuilder.Value _builderConfig;
107
108     /*
109     /**********************************************************
110     /* Life-cycle: construction
111     /**********************************************************
112      */

113
114     public BeanDeserializerBuilder(BeanDescription beanDesc,
115             DeserializationContext ctxt)
116     { 
117         _beanDesc = beanDesc;
118         _context = ctxt;
119         _config = ctxt.getConfig();
120     }
121
122     /**
123      * Copy constructor for sub-classes to use, when constructing
124      * custom builder instances
125      */

126     protected BeanDeserializerBuilder(BeanDeserializerBuilder src)
127     {
128         _beanDesc = src._beanDesc;
129         _context = src._context;
130         _config = src._config;
131
132         // let's make copy of properties
133         _properties.putAll(src._properties);
134         _injectables = _copy(src._injectables);
135         _backRefProperties = _copy(src._backRefProperties);
136         // Hmmh. Should we create defensive copies here? For now, not yet
137         _ignorableProps = src._ignorableProps;        
138         _valueInstantiator = src._valueInstantiator;
139         _objectIdReader = src._objectIdReader;
140
141         _anySetter = src._anySetter;
142         _ignoreAllUnknown = src._ignoreAllUnknown;
143
144         _buildMethod = src._buildMethod;
145         _builderConfig = src._builderConfig;
146     }
147
148     private static HashMap<String, SettableBeanProperty> _copy(HashMap<String, SettableBeanProperty> src) {
149         return (src == null) ? null
150                 : new HashMap<String, SettableBeanProperty>(src);
151     }
152
153     private static <T> List<T> _copy(List<T> src) {
154         return (src == null) ? null : new ArrayList<T>(src);
155     }
156
157     /*
158     /**********************************************************
159     /* Life-cycle: state modification (adders, setters)
160     /**********************************************************
161      */

162
163     /**
164      * Method for adding a new property or replacing a property.
165      */

166     public void addOrReplaceProperty(SettableBeanProperty prop, boolean allowOverride) {
167         _properties.put(prop.getName(), prop);
168     }
169
170     /**
171      * Method to add a property setter. Will ensure that there is no
172      * unexpected override; if one is found will throw a
173      * {@link IllegalArgumentException}.
174      */

175     public void addProperty(SettableBeanProperty prop)
176     {
177         SettableBeanProperty old =  _properties.put(prop.getName(), prop);
178         if (old != null && old != prop) { // should never occur...
179             throw new IllegalArgumentException("Duplicate property '"+prop.getName()+"' for "+_beanDesc.getType());
180         }
181     }
182
183     /**
184      * Method called to add a property that represents so-called back reference;
185      * reference that "points back" to object that has forward reference to
186      * currently built bean.
187      */

188     public void  addBackReferenceProperty(String referenceName, SettableBeanProperty prop)
189     {
190         if (_backRefProperties == null) {
191             _backRefProperties = new HashMap<String, SettableBeanProperty>(4);
192         }
193         // 15-Sep-2016, tatu: For some reason fixing access at point of `build()` does
194         //    NOT work (2 failing unit tests). Not 100% clear why, but for now force
195         //    access set early; unfortunate, but since it works....
196         if (_config.canOverrideAccessModifiers()) {
197             prop.fixAccess(_config);
198         }
199         _backRefProperties.put(referenceName, prop);
200         // 16-Jan-2018, tatu: As per [databind#1878] we may want to leave it as is, to allow
201         //    population for cases of "wrong direction", traversing parent first
202         //   If this causes problems should probably instead include in "ignored properties" list
203         //   Alternatively could also extend annotation to allow/disallow explicit value from input
204         /*
205         if (_properties != null) {
206             _properties.remove(prop.getName());
207         }
208         */

209     }
210
211     public void addInjectable(PropertyName propName, JavaType propType,
212             Annotations contextAnnotations, AnnotatedMember member,
213             Object valueId)
214     {
215         if (_injectables == null) {
216             _injectables = new ArrayList<ValueInjector>();
217         }
218         if ( _config.canOverrideAccessModifiers()) {
219             member.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
220         }
221         _injectables.add(new ValueInjector(propName, propType, member, valueId));
222     }
223
224     /**
225      * Method that will add property name as one of properties that can
226      * be ignored if not recognized.
227      */

228     public void addIgnorable(String propName)
229     {
230         if (_ignorableProps == null) {
231             _ignorableProps = new HashSet<String>();
232         }
233         _ignorableProps.add(propName);
234     }
235
236     /**
237      * Method called by deserializer factory, when a "creator property"
238      * (something that is passed via constructor- or factory method argument;
239      * instead of setter or field).
240      *<p>
241      * Default implementation does not do anything; we may need to revisit this
242      * decision if these properties need to be available through accessors.
243      * For now, however, we just have to ensure that we don't try to resolve
244      * types that masked setter/field has (see [JACKSON-700] for details).
245      */

246     public void addCreatorProperty(SettableBeanProperty prop)
247     {
248         addProperty(prop);
249     }
250
251     public void setAnySetter(SettableAnyProperty s)
252     {
253         if (_anySetter != null && s != null) {
254             throw new IllegalStateException("_anySetter already set to non-null");
255         }
256         _anySetter = s;
257     }
258
259     public void setIgnoreUnknownProperties(boolean ignore) {
260         _ignoreAllUnknown = ignore;
261     }
262
263     public void setValueInstantiator(ValueInstantiator inst) {
264         _valueInstantiator = inst;
265     }
266
267     public void setObjectIdReader(ObjectIdReader r) {
268         _objectIdReader = r;
269     }
270
271     public void setPOJOBuilder(AnnotatedMethod buildMethod, JsonPOJOBuilder.Value config) {
272         _buildMethod = buildMethod;
273         _builderConfig = config;
274     }
275     
276     /*
277     /**********************************************************
278     /* Public accessors
279     /**********************************************************
280      */

281     
282     /**
283      * Method that allows accessing all properties that this
284      * builder currently contains.
285      *<p>
286      * Note that properties are returned in order that properties
287      * are ordered (explictly, or by rule), which is the serialization
288      * order.
289      */

290     public Iterator<SettableBeanProperty> getProperties() {
291         return _properties.values().iterator();
292     }
293
294     public SettableBeanProperty findProperty(PropertyName propertyName) {
295         return _properties.get(propertyName.getSimpleName());
296     }
297
298     public boolean hasProperty(PropertyName propertyName) {
299         return findProperty(propertyName) != null;
300     }
301
302     public SettableBeanProperty removeProperty(PropertyName name) {
303         return _properties.remove(name.getSimpleName());
304     }
305
306     public SettableAnyProperty getAnySetter() {
307         return _anySetter;
308     }
309     
310     public ValueInstantiator getValueInstantiator() {
311         return _valueInstantiator;
312     }
313
314     public List<ValueInjector> getInjectables() {
315         return _injectables;
316     }
317
318     public ObjectIdReader getObjectIdReader() {
319         return _objectIdReader;
320     }
321
322     public AnnotatedMethod getBuildMethod() {
323         return _buildMethod;
324     }
325
326     public JsonPOJOBuilder.Value getBuilderConfig() {
327         return _builderConfig;
328     }
329
330     /**
331      * @since 2.9.4
332      */

333     public boolean hasIgnorable(String name) {
334         return (_ignorableProps != null) && _ignorableProps.contains(name);
335     }
336
337     /*
338     /**********************************************************
339     /* Build method(s)
340     /**********************************************************
341      */

342
343     /**
344      * Method for constructing a {@link BeanDeserializer}, given all
345      * information collected.
346      */

347     public JsonDeserializer<?> build()
348     {
349         Collection<SettableBeanProperty> props = _properties.values();
350         _fixAccess(props);
351         BeanPropertyMap propertyMap = BeanPropertyMap.construct(_config, props,
352                 _collectAliases(props));
353         propertyMap.assignIndexes();
354
355         // view processing must be enabled if:
356         // (a) fields are not included by default (when deserializing with view), OR
357         // (b) one of properties has view(s) to included in defined
358         boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
359         if (!anyViews) {
360             for (SettableBeanProperty prop : props) {
361                 if (prop.hasViews()) {
362                     anyViews = true;
363                     break;
364                 }
365             }
366         }
367
368         // one more thing: may need to create virtual ObjectId property:
369         if (_objectIdReader != null) {
370             /* 18-Nov-2012, tatu: May or may not have annotations for id property;
371              *   but no easy access. But hard to see id property being optional,
372              *   so let's consider required at this point.
373              */

374             ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, PropertyMetadata.STD_REQUIRED);
375             propertyMap = propertyMap.withProperty(prop);
376         }
377
378         return new BeanDeserializer(this,
379                 _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
380                 anyViews);
381     }
382
383     /**
384      * Alternate build method used when we must be using some form of
385      * abstract resolution, usually by using addition Type Id
386      * ("polymorphic deserialization")
387      * 
388      * @since 2.0
389      */

390     public AbstractDeserializer buildAbstract() {
391         return new AbstractDeserializer(this, _beanDesc, _backRefProperties, _properties);
392     }
393
394     /**
395      * Method for constructing a specialized deserializer that uses
396      * additional external Builder object during data binding.
397      */

398     public JsonDeserializer<?> buildBuilderBased(JavaType valueType, String expBuildMethodName)
399         throws JsonMappingException
400     {
401         // First: validation; must have build method that returns compatible type
402         if (_buildMethod == null) {
403             // as per [databind#777], allow empty name
404             if (!expBuildMethodName.isEmpty()) {
405                 _context.reportBadDefinition(_beanDesc.getType(),
406                         String.format("Builder class %s does not have build method (name: '%s')",
407                         _beanDesc.getBeanClass().getName(),
408                         expBuildMethodName));
409             }
410         } else {
411             // also: type of the method must be compatible
412             Class<?> rawBuildType = _buildMethod.getRawReturnType();
413             Class<?> rawValueType = valueType.getRawClass();
414             if ((rawBuildType != rawValueType)
415                     && !rawBuildType.isAssignableFrom(rawValueType)
416                     && !rawValueType.isAssignableFrom(rawBuildType)) {
417                 _context.reportBadDefinition(_beanDesc.getType(),
418                         String.format("Build method '%s' has wrong return type (%s), not compatible with POJO type (%s)",
419                         _buildMethod.getFullName(),
420                         rawBuildType.getName(),
421                         valueType.getRawClass().getName()));
422             }
423         }
424         // And if so, we can try building the deserializer
425         Collection<SettableBeanProperty> props = _properties.values();
426         _fixAccess(props);
427         BeanPropertyMap propertyMap = BeanPropertyMap.construct(_config, props,
428                 _collectAliases(props));
429         propertyMap.assignIndexes();
430
431         boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
432
433         if (!anyViews) {
434             for (SettableBeanProperty prop : props) {
435                 if (prop.hasViews()) {
436                     anyViews = true;
437                     break;
438                 }
439             }
440         }
441
442         if (_objectIdReader != null) {
443             // May or may not have annotations for id property; but no easy access.
444             // But hard to see id property being optional, so let's consider required at this point.
445             ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader,
446                     PropertyMetadata.STD_REQUIRED);
447             propertyMap = propertyMap.withProperty(prop);
448         }
449
450         return createBuilderBasedDeserializer(valueType, propertyMap, anyViews);
451     }
452
453     /**
454      * Extension point for overriding the actual creation of the builder deserializer.
455      *
456      * @since 2.11
457      */

458     protected JsonDeserializer<?> createBuilderBasedDeserializer(JavaType valueType,
459             BeanPropertyMap propertyMap, boolean anyViews) {
460         return new BuilderBasedDeserializer(this,
461                 _beanDesc, valueType, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
462                 anyViews);
463     }
464
465     /*
466     /**********************************************************
467     /* Internal helper method(s)
468     /**********************************************************
469      */

470
471     protected void _fixAccess(Collection<SettableBeanProperty> mainProps)
472     {
473         /* 07-Sep-2016, tatu: Ideally we should be able to avoid forcing
474          *   access to properties that are likely ignored, but due to
475          *   renaming it seems this is not a safe thing to do (there was
476          *   at least one failing test). May need to dig deeper in future;
477          *   for now let's just play it safe.
478          */

479         /*
480         Set<String> ignorable = _ignorableProps;
481         if (ignorable == null) {
482             ignorable = Collections.emptySet();
483         }
484         */

485
486         // 17-Jun-2020, tatu: [databind#2760] means we should not force access
487         //   if we are not configured to... at least not "regular" properties
488
489         if (_config.canOverrideAccessModifiers()) {
490             for (SettableBeanProperty prop : mainProps) {
491                 /*
492                 // first: no point forcing access on to-be-ignored properties
493                 if (ignorable.contains(prop.getName())) {
494                     continue;
495                 }
496                 */

497                 prop.fixAccess(_config);
498             }
499         }
500
501         // 15-Sep-2016, tatu: Access via back-ref properties has been done earlier
502         //   as it has to, for some reason, so not repeated here.
503 /*        
504         if (_backRefProperties != null) {
505             for (SettableBeanProperty prop : _backRefProperties.values()) {
506                 prop.fixAccess(_config);
507             }
508         }
509         */

510
511         // 17-Jun-2020, tatu: Despite [databind#2760], it seems that methods that
512         //    are explicitly defined (any setter via annotation, builder too) can not
513         //    be left as-is? May reconsider based on feedback
514         
515         if (_anySetter != null) {
516             _anySetter.fixAccess(_config);
517         }
518         if (_buildMethod != null) {
519             _buildMethod.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
520         }
521     }
522
523     protected Map<String,List<PropertyName>> _collectAliases(Collection<SettableBeanProperty> props)
524     {
525         Map<String,List<PropertyName>> mapping = null;
526         AnnotationIntrospector intr = _config.getAnnotationIntrospector();
527         if (intr != null) {
528             for (SettableBeanProperty prop : props) {
529                 List<PropertyName> aliases = intr.findPropertyAliases(prop.getMember());
530                 if ((aliases == null) || aliases.isEmpty()) {
531                     continue;
532                 }
533                 if (mapping == null) {
534                     mapping = new HashMap<>();
535                 }
536                 mapping.put(prop.getName(), aliases);
537             }
538         }
539         if (mapping == null) {
540             return Collections.emptyMap();
541         }
542         return mapping;
543     }
544 }
545