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