• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.introspect;
2 
3 import java.lang.reflect.Modifier;
4 import java.util.*;
5 
6 import com.fasterxml.jackson.annotation.JacksonInject;
7 import com.fasterxml.jackson.annotation.JsonCreator;
8 
9 import com.fasterxml.jackson.databind.*;
10 
11 import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
12 import com.fasterxml.jackson.databind.cfg.MapperConfig;
13 import com.fasterxml.jackson.databind.util.BeanUtil;
14 import com.fasterxml.jackson.databind.util.ClassUtil;
15 
16 /**
17  * Helper class used for aggregating information about all possible
18  * properties of a POJO.
19  */
20 public class POJOPropertiesCollector
21 {
22     /*
23     /**********************************************************
24     /* Configuration
25     /**********************************************************
26      */
27 
28     /**
29      * Configuration settings
30      */
31     protected final MapperConfig<?> _config;
32 
33     /**
34      * True if introspection is done for serialization (giving
35      * precedence for serialization annotations), or not (false, deserialization)
36      */
37     protected final boolean _forSerialization;
38 
39     /**
40      * @since 2.5
41      */
42     protected final boolean _stdBeanNaming;
43 
44     /**
45      * Type of POJO for which properties are being collected.
46      */
47     protected final JavaType _type;
48 
49     /**
50      * Low-level introspected class information (methods, fields etc)
51      */
52     protected final AnnotatedClass _classDef;
53 
54     protected final VisibilityChecker<?> _visibilityChecker;
55 
56     protected final AnnotationIntrospector _annotationIntrospector;
57 
58     /**
59      * @since 2.9
60      */
61     protected final boolean _useAnnotations;
62 
63     /**
64      * Prefix used by auto-detected mutators ("setters"): usually "set",
65      * but differs for builder objects ("with" by default).
66      */
67     protected final String _mutatorPrefix;
68 
69     /*
70     /**********************************************************
71     /* Collected property information
72     /**********************************************************
73      */
74 
75     /**
76      * State flag we keep to indicate whether actual property information
77      * has been collected or not.
78      */
79     protected boolean _collected;
80 
81     /**
82      * Set of logical property information collected so far.
83      *<p>
84      * Since 2.6, this has been constructed (more) lazily, to defer
85      * throwing of exceptions for potential conflicts in cases where
86      * this may not be an actual problem.
87      */
88     protected LinkedHashMap<String, POJOPropertyBuilder> _properties;
89 
90     protected LinkedList<POJOPropertyBuilder> _creatorProperties;
91 
92     /**
93      * A set of "field renamings" that have been discovered, indicating
94      * intended renaming of other accesors: key is the implicit original
95      * name and value intended name to use instead.
96      *<p>
97      * Note that these renamings are applied earlier than "regular" (explicit)
98      * renamings and affect implicit name: their effect may be changed by
99      * further renaming based on explicit indicators.
100      * The main use case is to effectively relink accessors based on fields
101      * discovered, and used to sort of correct otherwise missing linkage between
102      * fields and other accessors.
103      *
104      * @since 2.11
105      */
106     protected Map<PropertyName, PropertyName> _fieldRenameMappings;
107 
108     protected LinkedList<AnnotatedMember> _anyGetters;
109 
110     protected LinkedList<AnnotatedMethod> _anySetters;
111 
112     protected LinkedList<AnnotatedMember> _anySetterField;
113 
114     /**
115      * Method(s) marked with 'JsonValue' annotation
116      *<p>
117      * NOTE: before 2.9, was `AnnotatedMethod`; with 2.9 allows fields too
118      */
119     protected LinkedList<AnnotatedMember> _jsonValueAccessors;
120 
121     /**
122      * Lazily collected list of properties that can be implicitly
123      * ignored during serialization; only updated when collecting
124      * information for deserialization purposes
125      */
126     protected HashSet<String> _ignoredPropertyNames;
127 
128     /**
129      * Lazily collected list of members that were annotated to
130      * indicate that they represent mutators for deserializer
131      * value injection.
132      */
133     protected LinkedHashMap<Object, AnnotatedMember> _injectables;
134 
135     /*
136     /**********************************************************
137     /* Life-cycle
138     /**********************************************************
139      */
140 
POJOPropertiesCollector(MapperConfig<?> config, boolean forSerialization, JavaType type, AnnotatedClass classDef, String mutatorPrefix)141     protected POJOPropertiesCollector(MapperConfig<?> config, boolean forSerialization,
142             JavaType type, AnnotatedClass classDef, String mutatorPrefix)
143     {
144         _config = config;
145         _stdBeanNaming = config.isEnabled(MapperFeature.USE_STD_BEAN_NAMING);
146         _forSerialization = forSerialization;
147         _type = type;
148         _classDef = classDef;
149         _mutatorPrefix = (mutatorPrefix == null) ? "set" : mutatorPrefix;
150         if (config.isAnnotationProcessingEnabled()) {
151             _useAnnotations = true;
152             _annotationIntrospector = _config.getAnnotationIntrospector();
153         } else {
154             _useAnnotations = false;
155             _annotationIntrospector = AnnotationIntrospector.nopInstance();
156         }
157         _visibilityChecker = _config.getDefaultVisibilityChecker(type.getRawClass(),
158                 classDef);
159     }
160 
161     /*
162     /**********************************************************
163     /* Public API
164     /**********************************************************
165      */
166 
getConfig()167     public MapperConfig<?> getConfig() {
168         return _config;
169     }
170 
getType()171     public JavaType getType() {
172         return _type;
173     }
174 
getClassDef()175     public AnnotatedClass getClassDef() {
176         return _classDef;
177     }
178 
getAnnotationIntrospector()179     public AnnotationIntrospector getAnnotationIntrospector() {
180         return _annotationIntrospector;
181     }
182 
getProperties()183     public List<BeanPropertyDefinition> getProperties() {
184         // make sure we return a copy, so caller can remove entries if need be:
185         Map<String, POJOPropertyBuilder> props = getPropertyMap();
186         return new ArrayList<BeanPropertyDefinition>(props.values());
187     }
188 
getInjectables()189     public Map<Object, AnnotatedMember> getInjectables() {
190         if (!_collected) {
191             collectAll();
192         }
193         return _injectables;
194     }
195 
196     /**
197      * @since 2.9
198      */
getJsonValueAccessor()199     public AnnotatedMember getJsonValueAccessor()
200     {
201         if (!_collected) {
202             collectAll();
203         }
204         // If @JsonValue defined, must have a single one
205         if (_jsonValueAccessors != null) {
206             if (_jsonValueAccessors.size() > 1) {
207                 reportProblem("Multiple 'as-value' properties defined (%s vs %s)",
208                         _jsonValueAccessors.get(0),
209                         _jsonValueAccessors.get(1));
210             }
211             // otherwise we won't greatly care
212             return _jsonValueAccessors.get(0);
213         }
214         return null;
215     }
216 
getAnyGetter()217     public AnnotatedMember getAnyGetter()
218     {
219         if (!_collected) {
220             collectAll();
221         }
222         if (_anyGetters != null) {
223             if (_anyGetters.size() > 1) {
224                 reportProblem("Multiple 'any-getters' defined (%s vs %s)",
225                         _anyGetters.get(0), _anyGetters.get(1));
226             }
227             return _anyGetters.getFirst();
228         }
229         return null;
230     }
231 
getAnySetterField()232     public AnnotatedMember getAnySetterField()
233     {
234         if (!_collected) {
235             collectAll();
236         }
237         if (_anySetterField != null) {
238             if (_anySetterField.size() > 1) {
239                 reportProblem("Multiple 'any-setter' fields defined (%s vs %s)",
240                         _anySetterField.get(0), _anySetterField.get(1));
241             }
242             return _anySetterField.getFirst();
243         }
244         return null;
245     }
246 
getAnySetterMethod()247     public AnnotatedMethod getAnySetterMethod()
248     {
249         if (!_collected) {
250             collectAll();
251         }
252         if (_anySetters != null) {
253             if (_anySetters.size() > 1) {
254                 reportProblem("Multiple 'any-setter' methods defined (%s vs %s)",
255                         _anySetters.get(0), _anySetters.get(1));
256             }
257             return _anySetters.getFirst();
258         }
259         return null;
260     }
261 
262     /**
263      * Accessor for set of properties that are explicitly marked to be ignored
264      * via per-property markers (but NOT class annotations).
265      */
getIgnoredPropertyNames()266     public Set<String> getIgnoredPropertyNames() {
267         return _ignoredPropertyNames;
268     }
269 
270     /**
271      * Accessor to find out whether type specified requires inclusion
272      * of Object Identifier.
273      */
getObjectIdInfo()274     public ObjectIdInfo getObjectIdInfo()
275     {
276         ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(_classDef);
277         if (info != null) { // 2.1: may also have different defaults for refs:
278             info = _annotationIntrospector.findObjectReferenceInfo(_classDef, info);
279         }
280         return info;
281     }
282 
283     // for unit tests:
getPropertyMap()284     protected Map<String, POJOPropertyBuilder> getPropertyMap() {
285         if (!_collected) {
286             collectAll();
287         }
288         return _properties;
289     }
290 
291     @Deprecated // since 2.9
getJsonValueMethod()292     public AnnotatedMethod getJsonValueMethod() {
293         AnnotatedMember m = getJsonValueAccessor();
294         if (m instanceof AnnotatedMethod) {
295             return (AnnotatedMethod) m;
296         }
297         return null;
298     }
299 
300     @Deprecated // since 2.11 (not used by anything at this point)
findPOJOBuilderClass()301     public Class<?> findPOJOBuilderClass() {
302         return _annotationIntrospector.findPOJOBuilder(_classDef);
303     }
304 
305     /*
306     /**********************************************************
307     /* Public API: main-level collection
308     /**********************************************************
309      */
310 
311     /**
312      * Internal method that will collect actual property information.
313      *
314      * @since 2.6
315      */
collectAll()316     protected void collectAll()
317     {
318         LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();
319 
320         // First: gather basic data
321         _addFields(props); // note: populates _fieldRenameMappings
322         _addMethods(props);
323         // 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static
324         //    inner classes, see [databind#1502]
325         if (!_classDef.isNonStaticInnerClass()) {
326             _addCreators(props);
327         }
328 
329         // Remove ignored properties, first; this MUST precede annotation merging
330         // since logic relies on knowing exactly which accessor has which annotation
331         _removeUnwantedProperties(props);
332         // and then remove unneeded accessors (wrt read-only, read-write)
333         _removeUnwantedAccessor(props);
334 
335         // Rename remaining properties
336         _renameProperties(props);
337 
338         // and now add injectables, but taking care to avoid overlapping ones
339         // via creator and regular properties
340         _addInjectables(props);
341 
342         // then merge annotations, to simplify further processing
343         // 26-Sep-2017, tatu: Before 2.9.2 was done earlier but that prevented some of
344         //   annotations from getting properly merged
345         for (POJOPropertyBuilder property : props.values()) {
346             property.mergeAnnotations(_forSerialization);
347         }
348 
349         // And use custom naming strategy, if applicable...
350         PropertyNamingStrategy naming = _findNamingStrategy();
351         if (naming != null) {
352             _renameUsing(props, naming);
353         }
354 
355         // Sort by visibility (explicit over implicit); drop all but first of member
356         // type (getter, setter etc) if there is visibility difference
357         for (POJOPropertyBuilder property : props.values()) {
358             property.trimByVisibility();
359         }
360 
361         // and, if required, apply wrapper name: note, MUST be done after
362         // annotations are merged.
363         if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) {
364             _renameWithWrappers(props);
365         }
366 
367         // well, almost last: there's still ordering...
368         _sortProperties(props);
369         _properties = props;
370         _collected = true;
371     }
372 
373     /*
374     /**********************************************************
375     /* Overridable internal methods, adding members
376     /**********************************************************
377      */
378 
379     /**
380      * Method for collecting basic information on all fields found
381      */
_addFields(Map<String, POJOPropertyBuilder> props)382     protected void _addFields(Map<String, POJOPropertyBuilder> props)
383     {
384         final AnnotationIntrospector ai = _annotationIntrospector;
385         /* 28-Mar-2013, tatu: For deserialization we may also want to remove
386          *   final fields, as often they won't make very good mutators...
387          *   (although, maybe surprisingly, JVM _can_ force setting of such fields!)
388          */
389         final boolean pruneFinalFields = !_forSerialization && !_config.isEnabled(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS);
390         final boolean transientAsIgnoral = _config.isEnabled(MapperFeature.PROPAGATE_TRANSIENT_MARKER);
391 
392         for (AnnotatedField f : _classDef.fields()) {
393             // @JsonValue?
394             if (Boolean.TRUE.equals(ai.hasAsValue(f))) {
395                 if (_jsonValueAccessors == null) {
396                     _jsonValueAccessors = new LinkedList<>();
397                 }
398                 _jsonValueAccessors.add(f);
399                 continue;
400             }
401             // @JsonAnySetter?
402             if (Boolean.TRUE.equals(ai.hasAnySetter(f))) {
403                 if (_anySetterField == null) {
404                     _anySetterField = new LinkedList<AnnotatedMember>();
405                 }
406                 _anySetterField.add(f);
407                 continue;
408             }
409             String implName = ai.findImplicitPropertyName(f);
410             if (implName == null) {
411                 implName = f.getName();
412             }
413             final PropertyName implNameP = _propNameFromSimple(implName);
414 
415             // [databind#2527: Field-based renaming can be applied early (here),
416             // or at a later point, but probably must be done before pruning
417             // final fields. So let's do it early here
418             final PropertyName rename = ai.findRenameByField(_config, f, implNameP);
419             if ((rename != null) && !rename.equals(implNameP)) {
420                 if (_fieldRenameMappings == null) {
421                     _fieldRenameMappings = new HashMap<>();
422                 }
423                 _fieldRenameMappings.put(rename, implNameP);
424                 // todo
425             }
426 
427             PropertyName pn;
428 
429             if (_forSerialization) {
430                 // 18-Aug-2011, tatu: As per existing unit tests, we should only
431                 //   use serialization annotation (@JsonSerialize) when serializing
432                 //   fields, and similarly for deserialize-only annotations... so
433                 //   no fallbacks in this particular case.
434                 pn = ai.findNameForSerialization(f);
435             } else {
436                 pn = ai.findNameForDeserialization(f);
437             }
438             boolean hasName = (pn != null);
439             boolean nameExplicit = hasName;
440 
441             if (nameExplicit && pn.isEmpty()) { // empty String meaning "use default name", here just means "same as field name"
442                 pn = _propNameFromSimple(implName);
443                 nameExplicit = false;
444             }
445             // having explicit name means that field is visible; otherwise need to check the rules
446             boolean visible = (pn != null);
447             if (!visible) {
448                 visible = _visibilityChecker.isFieldVisible(f);
449             }
450             // and finally, may also have explicit ignoral
451             boolean ignored = ai.hasIgnoreMarker(f);
452 
453             // 13-May-2015, tatu: Moved from earlier place (AnnotatedClass) in 2.6
454             if (f.isTransient()) {
455                 // 20-May-2016, tatu: as per [databind#1184] explicit annotation should override
456                 //    "default" `transient`
457                 if (!hasName) {
458                     visible = false;
459                     if (transientAsIgnoral) {
460                         ignored = true;
461                     }
462                 }
463             }
464             /* [databind#190]: this is the place to prune final fields, if they are not
465              *  to be used as mutators. Must verify they are not explicitly included.
466              *  Also: if 'ignored' is set, need to include until a later point, to
467              *  avoid losing ignoral information.
468              */
469             if (pruneFinalFields && (pn == null) && !ignored
470                     && Modifier.isFinal(f.getModifiers())) {
471                 continue;
472             }
473             _property(props, implName).addField(f, pn, nameExplicit, visible, ignored);
474         }
475     }
476 
477     /**
478      * Method for collecting basic information on constructor(s) found
479      */
_addCreators(Map<String, POJOPropertyBuilder> props)480     protected void _addCreators(Map<String, POJOPropertyBuilder> props)
481     {
482         // can be null if annotation processing is disabled...
483         if (!_useAnnotations) {
484             return;
485         }
486         for (AnnotatedConstructor ctor : _classDef.getConstructors()) {
487             if (_creatorProperties == null) {
488                 _creatorProperties = new LinkedList<POJOPropertyBuilder>();
489             }
490             for (int i = 0, len = ctor.getParameterCount(); i < len; ++i) {
491                 _addCreatorParam(props, ctor.getParameter(i));
492             }
493         }
494         for (AnnotatedMethod factory : _classDef.getFactoryMethods()) {
495             if (_creatorProperties == null) {
496                 _creatorProperties = new LinkedList<POJOPropertyBuilder>();
497             }
498             for (int i = 0, len = factory.getParameterCount(); i < len; ++i) {
499                 _addCreatorParam(props, factory.getParameter(i));
500             }
501         }
502     }
503 
504     /**
505      * @since 2.4
506      */
_addCreatorParam(Map<String, POJOPropertyBuilder> props, AnnotatedParameter param)507     protected void _addCreatorParam(Map<String, POJOPropertyBuilder> props,
508             AnnotatedParameter param)
509     {
510         // JDK 8, paranamer, Scala can give implicit name
511         String impl = _annotationIntrospector.findImplicitPropertyName(param);
512         if (impl == null) {
513             impl = "";
514         }
515         PropertyName pn = _annotationIntrospector.findNameForDeserialization(param);
516         boolean expl = (pn != null && !pn.isEmpty());
517         if (!expl) {
518             if (impl.isEmpty()) {
519                 // Important: if neither implicit nor explicit name, cannot make use of
520                 // this creator parameter -- may or may not be a problem, verified at a later point.
521                 return;
522             }
523             // Also: if this occurs, there MUST be explicit annotation on creator itself
524             JsonCreator.Mode creatorMode = _annotationIntrospector.findCreatorAnnotation(_config,
525                     param.getOwner());
526             if ((creatorMode == null) || (creatorMode == JsonCreator.Mode.DISABLED)) {
527                 return;
528             }
529             pn = PropertyName.construct(impl);
530         }
531 
532         // 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
533         impl = _checkRenameByField(impl);
534 
535         // shouldn't need to worry about @JsonIgnore, since creators only added
536         // if so annotated
537 
538         /* 13-May-2015, tatu: We should try to start with implicit name, similar to how
539          *   fields and methods work; but unlike those, we don't necessarily have
540          *   implicit name to use (pre-Java8 at least). So:
541          */
542         POJOPropertyBuilder prop = (expl && impl.isEmpty())
543                 ? _property(props, pn) : _property(props, impl);
544         prop.addCtor(param, pn, expl, true, false);
545         _creatorProperties.add(prop);
546     }
547 
548     /**
549      * Method for collecting basic information on all fields found
550      */
_addMethods(Map<String, POJOPropertyBuilder> props)551     protected void _addMethods(Map<String, POJOPropertyBuilder> props)
552     {
553         final AnnotationIntrospector ai = _annotationIntrospector;
554         for (AnnotatedMethod m : _classDef.memberMethods()) {
555             // For methods, handling differs between getters and setters; and
556             // we will also only consider entries that either follow the bean
557             // naming convention or are explicitly marked: just being visible
558             // is not enough (unlike with fields)
559 
560             int argCount = m.getParameterCount();
561             if (argCount == 0) { // getters (including 'any getter')
562             	_addGetterMethod(props, m, ai);
563             } else if (argCount == 1) { // setters
564             	_addSetterMethod(props, m, ai);
565             } else if (argCount == 2) { // any getter?
566                 if (ai != null) {
567                     if (Boolean.TRUE.equals(ai.hasAnySetter(m))) {
568                         if (_anySetters == null) {
569                             _anySetters = new LinkedList<AnnotatedMethod>();
570                         }
571                         _anySetters.add(m);
572                     }
573                 }
574             }
575         }
576     }
577 
_addGetterMethod(Map<String, POJOPropertyBuilder> props, AnnotatedMethod m, AnnotationIntrospector ai)578     protected void _addGetterMethod(Map<String, POJOPropertyBuilder> props,
579             AnnotatedMethod m, AnnotationIntrospector ai)
580     {
581         // Very first thing: skip if not returning any value
582         // 06-May-2020, tatu: [databind#2675] changes handling slightly...
583         {
584             final Class<?> rt = m.getRawReturnType();
585             if ((rt == Void.TYPE) ||
586                     ((rt == Void.class) && !_config.isEnabled(MapperFeature.ALLOW_VOID_VALUED_PROPERTIES))) {
587                 return;
588             }
589         }
590 
591         // any getter?
592         // @JsonAnyGetter?
593         if (Boolean.TRUE.equals(ai.hasAnyGetter(m))) {
594             if (_anyGetters == null) {
595                 _anyGetters = new LinkedList<AnnotatedMember>();
596             }
597             _anyGetters.add(m);
598             return;
599         }
600         // @JsonValue?
601         if (Boolean.TRUE.equals(ai.hasAsValue(m))) {
602             if (_jsonValueAccessors == null) {
603                 _jsonValueAccessors = new LinkedList<>();
604             }
605             _jsonValueAccessors.add(m);
606             return;
607         }
608         String implName; // from naming convention
609         boolean visible;
610 
611         PropertyName pn = ai.findNameForSerialization(m);
612         boolean nameExplicit = (pn != null);
613 
614         if (!nameExplicit) { // no explicit name; must consider implicit
615             implName = ai.findImplicitPropertyName(m);
616             if (implName == null) {
617                 implName = BeanUtil.okNameForRegularGetter(m, m.getName(), _stdBeanNaming);
618             }
619             if (implName == null) { // if not, must skip
620                 implName = BeanUtil.okNameForIsGetter(m, m.getName(), _stdBeanNaming);
621                 if (implName == null) {
622                     return;
623                 }
624                 visible = _visibilityChecker.isIsGetterVisible(m);
625             } else {
626                 visible = _visibilityChecker.isGetterVisible(m);
627             }
628         } else { // explicit indication of inclusion, but may be empty
629             // we still need implicit name to link with other pieces
630             implName = ai.findImplicitPropertyName(m);
631             if (implName == null) {
632                 implName = BeanUtil.okNameForGetter(m, _stdBeanNaming);
633             }
634             // if not regular getter name, use method name as is
635             if (implName == null) {
636                 implName = m.getName();
637             }
638             if (pn.isEmpty()) {
639                 // !!! TODO: use PropertyName for implicit names too
640                 pn = _propNameFromSimple(implName);
641                 nameExplicit = false;
642             }
643             visible = true;
644         }
645         // 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
646         implName = _checkRenameByField(implName);
647         boolean ignore = ai.hasIgnoreMarker(m);
648         _property(props, implName).addGetter(m, pn, nameExplicit, visible, ignore);
649     }
650 
_addSetterMethod(Map<String, POJOPropertyBuilder> props, AnnotatedMethod m, AnnotationIntrospector ai)651     protected void _addSetterMethod(Map<String, POJOPropertyBuilder> props,
652             AnnotatedMethod m, AnnotationIntrospector ai)
653     {
654         String implName; // from naming convention
655         boolean visible;
656         PropertyName pn = (ai == null) ? null : ai.findNameForDeserialization(m);
657         boolean nameExplicit = (pn != null);
658         if (!nameExplicit) { // no explicit name; must follow naming convention
659             implName = (ai == null) ? null : ai.findImplicitPropertyName(m);
660             if (implName == null) {
661                 implName = BeanUtil.okNameForMutator(m, _mutatorPrefix, _stdBeanNaming);
662             }
663             if (implName == null) { // if not, must skip
664             	return;
665             }
666             visible = _visibilityChecker.isSetterVisible(m);
667         } else { // explicit indication of inclusion, but may be empty
668             // we still need implicit name to link with other pieces
669             implName = (ai == null) ? null : ai.findImplicitPropertyName(m);
670             if (implName == null) {
671                 implName = BeanUtil.okNameForMutator(m, _mutatorPrefix, _stdBeanNaming);
672             }
673             // if not regular getter name, use method name as is
674             if (implName == null) {
675                 implName = m.getName();
676             }
677             if (pn.isEmpty()) {
678                 // !!! TODO: use PropertyName for implicit names too
679                 pn = _propNameFromSimple(implName);
680                 nameExplicit = false;
681             }
682             visible = true;
683         }
684         // 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
685         implName = _checkRenameByField(implName);
686         boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m);
687         _property(props, implName).addSetter(m, pn, nameExplicit, visible, ignore);
688     }
689 
_addInjectables(Map<String, POJOPropertyBuilder> props)690     protected void _addInjectables(Map<String, POJOPropertyBuilder> props)
691     {
692         final AnnotationIntrospector ai = _annotationIntrospector;
693         // first fields, then methods, to allow overriding
694         for (AnnotatedField f : _classDef.fields()) {
695             _doAddInjectable(ai.findInjectableValue(f), f);
696         }
697 
698         for (AnnotatedMethod m : _classDef.memberMethods()) {
699             // for now, only allow injection of a single arg (to be changed in future?)
700             if (m.getParameterCount() != 1) {
701                 continue;
702             }
703             _doAddInjectable(ai.findInjectableValue(m), m);
704         }
705     }
706 
_doAddInjectable(JacksonInject.Value injectable, AnnotatedMember m)707     protected void _doAddInjectable(JacksonInject.Value injectable, AnnotatedMember m)
708     {
709         if (injectable == null) {
710             return;
711         }
712         Object id = injectable.getId();
713         if (_injectables == null) {
714             _injectables = new LinkedHashMap<Object, AnnotatedMember>();
715         }
716         AnnotatedMember prev = _injectables.put(id, m);
717         if (prev != null) {
718             // 12-Apr-2017, tatu: Let's allow masking of Field by Method
719             if (prev.getClass() == m.getClass()) {
720                 String type = id.getClass().getName();
721                 throw new IllegalArgumentException("Duplicate injectable value with id '"
722                         +String.valueOf(id)+"' (of type "+type+")");
723             }
724         }
725     }
726 
_propNameFromSimple(String simpleName)727     private PropertyName _propNameFromSimple(String simpleName) {
728         return PropertyName.construct(simpleName, null);
729     }
730 
731     // @since 2.11
_checkRenameByField(String implName)732     private String _checkRenameByField(String implName) {
733         if (_fieldRenameMappings != null) {
734             PropertyName p = _fieldRenameMappings.get(_propNameFromSimple(implName));
735             if (p != null) {
736                 implName = p.getSimpleName();
737                 return implName;
738 
739             }
740         }
741         return implName;
742     }
743 
744     /*
745     /**********************************************************
746     /* Internal methods; removing ignored properties
747     /**********************************************************
748      */
749 
750     /**
751      * Method called to get rid of candidate properties that are marked
752      * as ignored.
753      */
_removeUnwantedProperties(Map<String, POJOPropertyBuilder> props)754     protected void _removeUnwantedProperties(Map<String, POJOPropertyBuilder> props)
755     {
756         Iterator<POJOPropertyBuilder> it = props.values().iterator();
757         while (it.hasNext()) {
758             POJOPropertyBuilder prop = it.next();
759 
760             // First: if nothing visible, just remove altogether
761             if (!prop.anyVisible()) {
762                 it.remove();
763                 continue;
764             }
765             // Otherwise, check ignorals
766             if (prop.anyIgnorals()) {
767                 // first: if one or more ignorals, and no explicit markers, remove the whole thing
768                 if (!prop.isExplicitlyIncluded()) {
769                     it.remove();
770                     _collectIgnorals(prop.getName());
771                     continue;
772                 }
773                 // otherwise just remove ones marked to be ignored
774                 prop.removeIgnored();
775                 if (!prop.couldDeserialize()) {
776                     _collectIgnorals(prop.getName());
777                 }
778             }
779         }
780     }
781 
782     /**
783      * Method called to further get rid of unwanted individual accessors,
784      * based on read/write settings and rules for "pulling in" accessors
785      * (or not).
786      */
_removeUnwantedAccessor(Map<String, POJOPropertyBuilder> props)787     protected void _removeUnwantedAccessor(Map<String, POJOPropertyBuilder> props)
788     {
789         final boolean inferMutators = _config.isEnabled(MapperFeature.INFER_PROPERTY_MUTATORS);
790         Iterator<POJOPropertyBuilder> it = props.values().iterator();
791 
792         while (it.hasNext()) {
793             POJOPropertyBuilder prop = it.next();
794             // 26-Jan-2017, tatu: [databind#935]: need to denote removal of
795             // 16-May-2020, tatu: [databind#2719]: need to pass `this` to allow
796             //    addition of ignorals wrt explicit name
797             prop.removeNonVisible(inferMutators, _forSerialization ? null : this);
798         }
799     }
800 
801     /**
802      * Helper method called to add explicitly ignored properties to a list
803      * of known ignored properties; this helps in proper reporting of
804      * errors.
805      */
_collectIgnorals(String name)806     protected void _collectIgnorals(String name)
807     {
808         if (!_forSerialization && (name != null)) {
809             if (_ignoredPropertyNames == null) {
810                 _ignoredPropertyNames = new HashSet<String>();
811             }
812             _ignoredPropertyNames.add(name);
813         }
814     }
815 
816     /*
817     /**********************************************************
818     /* Internal methods; renaming properties
819     /**********************************************************
820      */
821 
_renameProperties(Map<String, POJOPropertyBuilder> props)822     protected void _renameProperties(Map<String, POJOPropertyBuilder> props)
823     {
824         // With renaming need to do in phases: first, find properties to rename
825         Iterator<Map.Entry<String,POJOPropertyBuilder>> it = props.entrySet().iterator();
826         LinkedList<POJOPropertyBuilder> renamed = null;
827         while (it.hasNext()) {
828             Map.Entry<String, POJOPropertyBuilder> entry = it.next();
829             POJOPropertyBuilder prop = entry.getValue();
830 
831             Collection<PropertyName> l = prop.findExplicitNames();
832 
833             // no explicit names? Implicit one is fine as is
834             if (l.isEmpty()) {
835                 continue;
836             }
837             it.remove(); // need to replace with one or more renamed
838             if (renamed == null) {
839                 renamed = new LinkedList<POJOPropertyBuilder>();
840             }
841             // simple renaming? Just do it
842             if (l.size() == 1) {
843                 PropertyName n = l.iterator().next();
844                 renamed.add(prop.withName(n));
845                 continue;
846             }
847             // but this may be problematic...
848             renamed.addAll(prop.explode(l));
849 
850             /*
851             String newName = prop.findNewName();
852             if (newName != null) {
853                 if (renamed == null) {
854                     renamed = new LinkedList<POJOPropertyBuilder>();
855                 }
856                 prop = prop.withSimpleName(newName);
857                 renamed.add(prop);
858                 it.remove();
859             }
860             */
861         }
862 
863         // and if any were renamed, merge back in...
864         if (renamed != null) {
865             for (POJOPropertyBuilder prop : renamed) {
866                 String name = prop.getName();
867                 POJOPropertyBuilder old = props.get(name);
868                 if (old == null) {
869                     props.put(name, prop);
870                 } else {
871                     old.addAll(prop);
872                 }
873                 // replace the creatorProperty too, if there is one
874                 if (_updateCreatorProperty(prop, _creatorProperties)) {
875                     // [databind#2001]: New name of property was ignored previously? Remove from ignored
876                     // 01-May-2018, tatu: I have a feeling this will need to be revisited at some point,
877                     //   to avoid removing some types of removals, possibly. But will do for now.
878 
879                     // 16-May-2020, tatu: ... and so the day came, [databind#2118] failed
880                     //    when explicit rename added to ignorals (for READ_ONLY) was suddenly
881                     //    removed from ignoral list. So, added a guard statement above so that
882                     //    ignoral is ONLY removed if there was matching creator property.
883                     //
884                     //    Chances are this is not the last tweak we need but... that bridge then etc
885                     if (_ignoredPropertyNames != null) {
886                         _ignoredPropertyNames.remove(name);
887                     }
888                 }
889             }
890         }
891     }
892 
_renameUsing(Map<String, POJOPropertyBuilder> propMap, PropertyNamingStrategy naming)893     protected void _renameUsing(Map<String, POJOPropertyBuilder> propMap,
894             PropertyNamingStrategy naming)
895     {
896         POJOPropertyBuilder[] props = propMap.values().toArray(new POJOPropertyBuilder[propMap.size()]);
897         propMap.clear();
898         for (POJOPropertyBuilder prop : props) {
899             PropertyName fullName = prop.getFullName();
900             String rename = null;
901             // As per [databind#428] need to skip renaming if property has
902             // explicitly defined name, unless feature  is enabled
903             if (!prop.isExplicitlyNamed() || _config.isEnabled(MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING)) {
904                 if (_forSerialization) {
905                     if (prop.hasGetter()) {
906                         rename = naming.nameForGetterMethod(_config, prop.getGetter(), fullName.getSimpleName());
907                     } else if (prop.hasField()) {
908                         rename = naming.nameForField(_config, prop.getField(), fullName.getSimpleName());
909                     }
910                 } else {
911                     if (prop.hasSetter()) {
912                         rename = naming.nameForSetterMethod(_config, prop.getSetter(), fullName.getSimpleName());
913                     } else if (prop.hasConstructorParameter()) {
914                         rename = naming.nameForConstructorParameter(_config, prop.getConstructorParameter(), fullName.getSimpleName());
915                     } else if (prop.hasField()) {
916                         rename = naming.nameForField(_config, prop.getField(), fullName.getSimpleName());
917                     } else if (prop.hasGetter()) {
918                         /* Plus, when getter-as-setter is used, need to convert that too..
919                          * (should we verify that's enabled? For now, assume it's ok always)
920                          */
921                         rename = naming.nameForGetterMethod(_config, prop.getGetter(), fullName.getSimpleName());
922                     }
923                 }
924             }
925             final String simpleName;
926             if ((rename != null) && !fullName.hasSimpleName(rename)) {
927                 prop = prop.withSimpleName(rename);
928                 simpleName = rename;
929             } else {
930                 simpleName = fullName.getSimpleName();
931             }
932             // Need to consider case where there may already be something in there...
933             POJOPropertyBuilder old = propMap.get(simpleName);
934             if (old == null) {
935                 propMap.put(simpleName, prop);
936             } else {
937                 old.addAll(prop);
938             }
939 
940             // replace the creatorProperty too, if there is one
941             _updateCreatorProperty(prop, _creatorProperties);
942         }
943     }
944 
_renameWithWrappers(Map<String, POJOPropertyBuilder> props)945     protected void _renameWithWrappers(Map<String, POJOPropertyBuilder> props)
946     {
947         // 11-Sep-2012, tatu: To support 'MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME',
948         //   need another round of renaming...
949         Iterator<Map.Entry<String,POJOPropertyBuilder>> it = props.entrySet().iterator();
950         LinkedList<POJOPropertyBuilder> renamed = null;
951         while (it.hasNext()) {
952             Map.Entry<String, POJOPropertyBuilder> entry = it.next();
953             POJOPropertyBuilder prop = entry.getValue();
954             AnnotatedMember member = prop.getPrimaryMember();
955             if (member == null) {
956                 continue;
957             }
958             PropertyName wrapperName = _annotationIntrospector.findWrapperName(member);
959             // One trickier part (wrt [#24] of JAXB annotations: wrapper that
960             // indicates use of actual property... But hopefully has been taken care
961             // of previously
962             if (wrapperName == null || !wrapperName.hasSimpleName()) {
963                 continue;
964             }
965             if (!wrapperName.equals(prop.getFullName())) {
966                 if (renamed == null) {
967                     renamed = new LinkedList<POJOPropertyBuilder>();
968                 }
969                 prop = prop.withName(wrapperName);
970                 renamed.add(prop);
971                 it.remove();
972             }
973         }
974         // and if any were renamed, merge back in...
975         if (renamed != null) {
976             for (POJOPropertyBuilder prop : renamed) {
977                 String name = prop.getName();
978                 POJOPropertyBuilder old = props.get(name);
979                 if (old == null) {
980                     props.put(name, prop);
981                 } else {
982                     old.addAll(prop);
983                 }
984             }
985         }
986     }
987 
988     /*
989     /**********************************************************
990     /* Overridable internal methods, sorting, other stuff
991     /**********************************************************
992      */
993 
994     // First, order by(explicit ordering and/or alphabetic),
995     // then by (optional) index (if any)
996     // and then implicitly order creator properties before others)
997 
_sortProperties(Map<String, POJOPropertyBuilder> props)998     protected void _sortProperties(Map<String, POJOPropertyBuilder> props)
999     {
1000         // Then how about explicit ordering?
1001         final AnnotationIntrospector intr = _annotationIntrospector;
1002         Boolean alpha = intr.findSerializationSortAlphabetically((Annotated) _classDef);
1003         final boolean sort = (alpha == null)
1004                 ? _config.shouldSortPropertiesAlphabetically()
1005                 : alpha.booleanValue();
1006         final boolean indexed = _anyIndexed(props.values());
1007 
1008         String[] propertyOrder = intr.findSerializationPropertyOrder(_classDef);
1009 
1010         // no sorting? no need to shuffle, then
1011         if (!sort && !indexed && (_creatorProperties == null) && (propertyOrder == null)) {
1012             return;
1013         }
1014         int size = props.size();
1015         Map<String, POJOPropertyBuilder> all;
1016         // Need to (re)sort alphabetically?
1017         if (sort) {
1018             all = new TreeMap<String,POJOPropertyBuilder>();
1019         } else {
1020             all = new LinkedHashMap<String,POJOPropertyBuilder>(size+size);
1021         }
1022 
1023         for (POJOPropertyBuilder prop : props.values()) {
1024             all.put(prop.getName(), prop);
1025         }
1026         Map<String,POJOPropertyBuilder> ordered = new LinkedHashMap<>(size+size);
1027         // Ok: primarily by explicit order
1028         if (propertyOrder != null) {
1029             for (String name : propertyOrder) {
1030                 POJOPropertyBuilder w = all.remove(name);
1031                 if (w == null) { // will also allow use of "implicit" names for sorting
1032                     for (POJOPropertyBuilder prop : props.values()) {
1033                         if (name.equals(prop.getInternalName())) {
1034                             w = prop;
1035                             // plus re-map to external name, to avoid dups:
1036                             name = prop.getName();
1037                             break;
1038                         }
1039                     }
1040                 }
1041                 if (w != null) {
1042                     ordered.put(name, w);
1043                 }
1044             }
1045         }
1046 
1047         // Second (starting with 2.11): index, if any:
1048         if (indexed) {
1049             Map<Integer,POJOPropertyBuilder> byIndex = new TreeMap<>();
1050             Iterator<Map.Entry<String,POJOPropertyBuilder>> it = all.entrySet().iterator();
1051             while (it.hasNext()) {
1052                 Map.Entry<String,POJOPropertyBuilder> entry = it.next();
1053                 POJOPropertyBuilder prop = entry.getValue();
1054                 Integer index = prop.getMetadata().getIndex();
1055                 if (index != null) {
1056                     byIndex.put(index, prop);
1057                     it.remove();
1058                 }
1059             }
1060             for (POJOPropertyBuilder prop : byIndex.values()) {
1061                 ordered.put(prop.getName(), prop);
1062             }
1063         }
1064 
1065         // Third by sorting Creator properties before other unordered properties
1066         if (_creatorProperties != null) {
1067             /* As per [databind#311], this is bit delicate; but if alphabetic ordering
1068              * is mandated, at least ensure creator properties are in alphabetic
1069              * order. Related question of creator vs non-creator is punted for now,
1070              * so creator properties still fully predate non-creator ones.
1071              */
1072             Collection<POJOPropertyBuilder> cr;
1073             if (sort) {
1074                 TreeMap<String, POJOPropertyBuilder> sorted =
1075                         new TreeMap<String,POJOPropertyBuilder>();
1076                 for (POJOPropertyBuilder prop : _creatorProperties) {
1077                     sorted.put(prop.getName(), prop);
1078                 }
1079                 cr = sorted.values();
1080             } else {
1081                 cr = _creatorProperties;
1082             }
1083             for (POJOPropertyBuilder prop : cr) {
1084                 // 16-Jan-2016, tatu: Related to [databind#1317], make sure not to accidentally
1085                 //    add back pruned creator properties!
1086                 String name = prop.getName();
1087                 // 27-Nov-2019, tatu: Not sure why, but we should NOT remove it from `all` tho:
1088 //                if (all.remove(name) != null) {
1089                 if (all.containsKey(name)) {
1090                     ordered.put(name, prop);
1091                 }
1092             }
1093         }
1094         // And finally whatever is left (trying to put again will not change ordering)
1095         ordered.putAll(all);
1096         props.clear();
1097         props.putAll(ordered);
1098     }
1099 
_anyIndexed(Collection<POJOPropertyBuilder> props)1100     private boolean _anyIndexed(Collection<POJOPropertyBuilder> props) {
1101         for (POJOPropertyBuilder prop : props) {
1102             if (prop.getMetadata().hasIndex()) {
1103                 return true;
1104             }
1105         }
1106         return false;
1107     }
1108 
1109     /*
1110     /**********************************************************
1111     /* Internal methods; helpers
1112     /**********************************************************
1113      */
1114 
reportProblem(String msg, Object... args)1115     protected void reportProblem(String msg, Object... args) {
1116         if (args.length > 0) {
1117             msg = String.format(msg, args);
1118         }
1119         throw new IllegalArgumentException("Problem with definition of "+_classDef+": "+msg);
1120     }
1121 
_property(Map<String, POJOPropertyBuilder> props, PropertyName name)1122     protected POJOPropertyBuilder _property(Map<String, POJOPropertyBuilder> props,
1123             PropertyName name) {
1124         String simpleName = name.getSimpleName();
1125         POJOPropertyBuilder prop = props.get(simpleName);
1126         if (prop == null) {
1127             prop = new POJOPropertyBuilder(_config, _annotationIntrospector,
1128                     _forSerialization, name);
1129             props.put(simpleName, prop);
1130         }
1131         return prop;
1132     }
1133 
1134     // !!! TODO: deprecate, require use of PropertyName
_property(Map<String, POJOPropertyBuilder> props, String implName)1135     protected POJOPropertyBuilder _property(Map<String, POJOPropertyBuilder> props,
1136             String implName)
1137     {
1138         POJOPropertyBuilder prop = props.get(implName);
1139         if (prop == null) {
1140             prop = new POJOPropertyBuilder(_config, _annotationIntrospector, _forSerialization,
1141                     PropertyName.construct(implName));
1142             props.put(implName, prop);
1143         }
1144         return prop;
1145     }
1146 
_findNamingStrategy()1147     private PropertyNamingStrategy _findNamingStrategy()
1148     {
1149         Object namingDef = _annotationIntrospector.findNamingStrategy(_classDef);
1150         if (namingDef == null) {
1151             return _config.getPropertyNamingStrategy();
1152         }
1153         if (namingDef instanceof PropertyNamingStrategy) {
1154             return (PropertyNamingStrategy) namingDef;
1155         }
1156         /* Alas, there's no way to force return type of "either class
1157          * X or Y" -- need to throw an exception after the fact
1158          */
1159         if (!(namingDef instanceof Class)) {
1160             throw new IllegalStateException("AnnotationIntrospector returned PropertyNamingStrategy definition of type "
1161                     +namingDef.getClass().getName()+"; expected type PropertyNamingStrategy or Class<PropertyNamingStrategy> instead");
1162         }
1163         Class<?> namingClass = (Class<?>)namingDef;
1164         // 09-Nov-2015, tatu: Need to consider pseudo-value of STD, which means "use default"
1165         if (namingClass == PropertyNamingStrategy.class) {
1166             return null;
1167         }
1168 
1169         if (!PropertyNamingStrategy.class.isAssignableFrom(namingClass)) {
1170             throw new IllegalStateException("AnnotationIntrospector returned Class "
1171                     +namingClass.getName()+"; expected Class<PropertyNamingStrategy>");
1172         }
1173         HandlerInstantiator hi = _config.getHandlerInstantiator();
1174         if (hi != null) {
1175             PropertyNamingStrategy pns = hi.namingStrategyInstance(_config, _classDef, namingClass);
1176             if (pns != null) {
1177                 return pns;
1178             }
1179         }
1180         return (PropertyNamingStrategy) ClassUtil.createInstance(namingClass,
1181                     _config.canOverrideAccessModifiers());
1182     }
1183 
_updateCreatorProperty(POJOPropertyBuilder prop, List<POJOPropertyBuilder> creatorProperties)1184     protected boolean _updateCreatorProperty(POJOPropertyBuilder prop, List<POJOPropertyBuilder> creatorProperties) {
1185 
1186         if (creatorProperties != null) {
1187             final String intName = prop.getInternalName();
1188             for (int i = 0, len = creatorProperties.size(); i < len; ++i) {
1189                 if (creatorProperties.get(i).getInternalName().equals(intName)) {
1190                     creatorProperties.set(i, prop);
1191                     return true;
1192                 }
1193             }
1194         }
1195         return false;
1196     }
1197 }
1198