1 package com.fasterxml.jackson.databind.introspect;
2
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.Method;
5 import java.util.*;
6
7 import com.fasterxml.jackson.annotation.JsonCreator;
8 import com.fasterxml.jackson.annotation.JsonFormat;
9 import com.fasterxml.jackson.annotation.JsonInclude;
10
11 import com.fasterxml.jackson.databind.*;
12 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
13 import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
14 import com.fasterxml.jackson.databind.cfg.MapperConfig;
15 import com.fasterxml.jackson.databind.type.TypeBindings;
16 import com.fasterxml.jackson.databind.util.Annotations;
17 import com.fasterxml.jackson.databind.util.ClassUtil;
18 import com.fasterxml.jackson.databind.util.Converter;
19
20 /**
21  * Default {@link BeanDescription} implementation used by Jackson.
22  *<p>
23  * Although sub-classing is a theoretical possibility there are no known
24  * use cases for that, nor is such usage tested or supported.
25  * Separation from API is mostly to isolate some implementation details
26  * here and keep API simple.
27  */

28 public class BasicBeanDescription extends BeanDescription
29 {
30     // since 2.9
31     private final static Class<?>[] NO_VIEWS = new Class<?>[0];
32
33     /*
34     /**********************************************************
35     /* General configuration
36     /**********************************************************
37      */

38
39     /**
40      * We will hold a reference to the collector in cases where
41      * information is lazily accessed and constructed; properties
42      * are only accessed when they are actually needed.
43      */

44     final protected POJOPropertiesCollector _propCollector;
45     
46     final protected MapperConfig<?> _config;
47
48     final protected AnnotationIntrospector _annotationIntrospector;
49
50     /*
51     /**********************************************************
52     /* Information about type itself
53     /**********************************************************
54      */

55     
56     /**
57      * Information collected about the class introspected.
58      */

59     final protected AnnotatedClass _classInfo;
60
61     /**
62      * @since 2.9
63      */

64     protected Class<?>[] _defaultViews;
65
66     /**
67      * @since 2.9
68      */

69     protected boolean _defaultViewsResolved;
70
71     /*
72     /**********************************************************
73     /* Member information
74     /**********************************************************
75      */

76
77     /**
78      * Properties collected for the POJO; initialized as needed.
79      */

80     protected List<BeanPropertyDefinition> _properties;
81
82     /**
83      * Details of Object Id to include, if any
84      */

85     protected ObjectIdInfo _objectIdInfo;
86
87     /*
88     /**********************************************************
89     /* Life-cycle
90     /**********************************************************
91      */

92
93     protected BasicBeanDescription(POJOPropertiesCollector coll,
94             JavaType type, AnnotatedClass classDef)
95     {
96         super(type);
97         _propCollector = coll;
98         _config = coll.getConfig();
99         // NOTE: null config only for some pre-constructed types
100         if (_config == null) {
101             _annotationIntrospector = null;
102         } else {
103             _annotationIntrospector = _config.getAnnotationIntrospector();
104         }
105         _classInfo = classDef;
106     }
107
108     /**
109      * Alternate constructor used in cases where property information is not needed,
110      * only class info.
111      */

112     protected BasicBeanDescription(MapperConfig<?> config,
113             JavaType type, AnnotatedClass classDef, List<BeanPropertyDefinition> props)
114     {
115         super(type);
116         _propCollector = null;
117         _config = config;
118         // NOTE: null config only for some pre-constructed types
119         if (_config == null) {
120             _annotationIntrospector = null;
121         } else {
122             _annotationIntrospector = _config.getAnnotationIntrospector();
123         }
124         _classInfo = classDef;
125         _properties = props;
126     }
127     
128     protected BasicBeanDescription(POJOPropertiesCollector coll)
129     {
130         this(coll, coll.getType(), coll.getClassDef());
131         _objectIdInfo = coll.getObjectIdInfo();
132     }
133
134     /**
135      * Factory method to use for constructing an instance to use for building
136      * deserializers.
137      */

138     public static BasicBeanDescription forDeserialization(POJOPropertiesCollector coll) {
139         return new BasicBeanDescription(coll);
140     }
141
142     /**
143      * Factory method to use for constructing an instance to use for building
144      * serializers.
145      */

146     public static BasicBeanDescription forSerialization(POJOPropertiesCollector coll) {
147         return new BasicBeanDescription(coll);
148     }
149
150     /**
151      * Factory method to use for constructing an instance to use for purposes
152      * other than building serializers or deserializers; will only have information
153      * on class, not on properties.
154      */

155     public static BasicBeanDescription forOtherUse(MapperConfig<?> config,
156             JavaType type, AnnotatedClass ac)
157     {
158         return new BasicBeanDescription(config, type,
159                 ac, Collections.<BeanPropertyDefinition>emptyList());
160     }
161
162     protected List<BeanPropertyDefinition> _properties() {
163         if (_properties == null) {
164             _properties = _propCollector.getProperties();
165         }
166         return _properties;
167     }
168
169     /*
170     /**********************************************************
171     /* Limited modifications by core databind functionality
172     /**********************************************************
173      */

174
175     /**
176      * Method that can be used to prune unwanted properties, during
177      * construction of serializers and deserializers.
178      * Use with utmost care, if at all...
179      * 
180      * @since 2.1
181      */

182     public boolean removeProperty(String propName)
183     {
184         Iterator<BeanPropertyDefinition> it = _properties().iterator();
185         while (it.hasNext()) {
186             BeanPropertyDefinition prop = it.next();
187             if (prop.getName().equals(propName)) {
188                 it.remove();
189                 return true;
190             }
191         }
192         return false;
193     }
194
195     public boolean addProperty(BeanPropertyDefinition def)
196     {
197         // first: ensure we do not have such property
198         if (hasProperty(def.getFullName())) {
199             return false;
200         }
201         _properties().add(def);
202         return true;
203     }
204     
205     /**
206      * @since 2.6
207      */

208     public boolean hasProperty(PropertyName name) {
209         return findProperty(name) != null;
210     }
211     
212     /**
213      * @since 2.6
214      */

215     public BeanPropertyDefinition findProperty(PropertyName name)
216     {
217         for (BeanPropertyDefinition prop : _properties()) {
218             if (prop.hasName(name)) {
219                 return prop;
220             }
221         }
222         return null;
223     }
224     
225     /*
226     /**********************************************************
227     /* Simple accessors from BeanDescription
228     /**********************************************************
229      */

230
231     @Override
232     public AnnotatedClass getClassInfo() { return _classInfo; }
233
234     @Override
235     public ObjectIdInfo getObjectIdInfo() { return  _objectIdInfo; }
236
237     @Override
238     public List<BeanPropertyDefinition> findProperties() {
239         return _properties();
240     }
241
242     @Override
243     @Deprecated // since 2.9
244     public AnnotatedMethod findJsonValueMethod() {
245         return (_propCollector == null) ? null
246                 : _propCollector.getJsonValueMethod();
247     }
248
249     @Override // since 2.9
250     public AnnotatedMember findJsonValueAccessor() {
251         return (_propCollector == null) ? null
252                 : _propCollector.getJsonValueAccessor();
253     }
254  
255     @Override
256     public Set<String> getIgnoredPropertyNames() {
257         Set<String> ign = (_propCollector == null) ? null
258                 : _propCollector.getIgnoredPropertyNames();
259         if (ign == null) {
260             return Collections.emptySet();
261         }
262         return ign;
263     }
264
265     @Override
266     public boolean hasKnownClassAnnotations() {
267         return _classInfo.hasAnnotations();
268     }
269
270     @Override
271     public Annotations getClassAnnotations() {
272         return _classInfo.getAnnotations();
273     }
274
275     @Override
276     @Deprecated // since 2.7
277     public TypeBindings bindingsForBeanType() {
278         return _type.getBindings();
279     }
280
281     @Override
282     @Deprecated // since 2.8
283     public JavaType resolveType(java.lang.reflect.Type jdkType) {
284         if (jdkType == null) {
285             return null;
286         }
287         return _config.getTypeFactory().constructType(jdkType, _type.getBindings());
288     }
289
290     @Override
291     public AnnotatedConstructor findDefaultConstructor() {
292         return _classInfo.getDefaultConstructor();
293     }
294
295     @Override
296     public AnnotatedMember findAnySetterAccessor() throws IllegalArgumentException
297     {
298         if (_propCollector != null) {
299             AnnotatedMethod anyMethod = _propCollector.getAnySetterMethod();
300             if (anyMethod != null) {
301                 // Also, let's be somewhat strict on how field name is to be
302                 // passed; String, Object make sense, others not so much.
303     
304                 /* !!! 18-May-2009, tatu: how about enums? Can add support if
305                  *  requested; easy enough for devs to add support within method.
306                  */

307                 Class<?> type = anyMethod.getRawParameterType(0);
308                 if ((type != String.class) && (type != Object.class)) {
309                     throw new IllegalArgumentException(String.format(
310 "Invalid 'any-setter' annotation on method '%s()': first argument not of type String or Object, but %s",
311 anyMethod.getName(), type.getName()));
312                 }
313                 return anyMethod;
314             }
315             AnnotatedMember anyField = _propCollector.getAnySetterField();
316             if (anyField != null) {
317                 // For now let's require a Map; in future can add support for other
318                 // types like perhaps Iterable<Map.Entry>?
319                 Class<?> type = anyField.getRawType();
320                 if (!Map.class.isAssignableFrom(type)) {
321                     throw new IllegalArgumentException(String.format(
322 "Invalid 'any-setter' annotation on field '%s': type is not instance of java.util.Map",
323 anyField.getName()));
324                 }
325                 return anyField;
326             }
327         }
328         return null;
329     }
330
331     @Override
332     public Map<Object, AnnotatedMember> findInjectables() {
333         if (_propCollector != null) {
334             return _propCollector.getInjectables();
335         }
336         return Collections.emptyMap();
337     }
338
339     @Override
340     public List<AnnotatedConstructor> getConstructors() {
341         return _classInfo.getConstructors();
342     }
343
344     @Override
345     public Object instantiateBean(boolean fixAccess) {
346         AnnotatedConstructor ac = _classInfo.getDefaultConstructor();
347         if (ac == null) {
348             return null;
349         }
350         if (fixAccess) {
351             ac.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
352         }
353         try {
354             return ac.getAnnotated().newInstance();
355         } catch (Exception e) {
356             Throwable t = e;
357             while (t.getCause() != null) {
358                 t = t.getCause();
359             }
360             ClassUtil.throwIfError(t);
361             ClassUtil.throwIfRTE(t);
362             throw new IllegalArgumentException("Failed to instantiate bean of type "
363                     +_classInfo.getAnnotated().getName()+": ("+t.getClass().getName()+") "
364                     +ClassUtil.exceptionMessage(t), t);
365         }
366     }
367
368     /*
369     /**********************************************************
370     /* Simple accessors, extended
371     /**********************************************************
372      */

373
374     @Override
375     public AnnotatedMethod findMethod(String name, Class<?>[] paramTypes) {
376         return _classInfo.findMethod(name, paramTypes);
377     }
378
379     /*
380     /**********************************************************
381     /* General per-class annotation introspection
382     /**********************************************************
383      */

384
385     @Override
386     public JsonFormat.Value findExpectedFormat(JsonFormat.Value defValue)
387     {
388         // 15-Apr-2016, tatu: Let's check both per-type defaults and annotations; per-type
389         //   defaults having higher precedence, so start with that
390         if (_annotationIntrospector != null) {
391             JsonFormat.Value v = _annotationIntrospector.findFormat(_classInfo);
392             if (v != null) {
393                 if (defValue == null) {
394                     defValue = v;
395                 } else {
396                     defValue = defValue.withOverrides(v);
397                 }
398             }
399         }
400         JsonFormat.Value v = _config.getDefaultPropertyFormat(_classInfo.getRawType());
401         if (v != null) {
402             if (defValue == null) {
403                 defValue = v;
404             } else {
405                 defValue = defValue.withOverrides(v);
406             }
407         }
408         return defValue;
409     }
410
411     @Override // since 2.9
412     public Class<?>[] findDefaultViews()
413     {
414         if (!_defaultViewsResolved) {
415             _defaultViewsResolved = true;
416             Class<?>[] def = (_annotationIntrospector == null) ? null
417                     : _annotationIntrospector.findViews(_classInfo);
418             // one more twist: if default inclusion disabled, need to force empty set of views
419             if (def == null) {
420                 if (!_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
421                     def = NO_VIEWS;
422                 }
423             }
424             _defaultViews = def;
425         }
426         return _defaultViews;
427     }
428
429     /*
430     /**********************************************************
431     /* Introspection for serialization
432     /**********************************************************
433      */

434
435     @Override
436     public Converter<Object,Object> findSerializationConverter()
437     {
438         if (_annotationIntrospector == null) {
439             return null;
440         }
441         return _createConverter(_annotationIntrospector.findSerializationConverter(_classInfo));
442     }
443
444     /**
445      * Method for determining whether null properties should be written
446      * out for a Bean of introspected type. This is based on global
447      * feature (lowest priority, passed as argument)
448      * and per-class annotation (highest priority).
449      */

450     @Override
451     public JsonInclude.Value findPropertyInclusion(JsonInclude.Value defValue) {
452         if (_annotationIntrospector != null) {
453             JsonInclude.Value incl = _annotationIntrospector.findPropertyInclusion(_classInfo);
454             if (incl != null) {
455                 return (defValue == null) ? incl : defValue.withOverrides(incl);
456             }
457         }
458         return defValue;
459     }
460
461     /**
462      * Method used to locate the method of introspected class that
463      * implements {@link com.fasterxml.jackson.annotation.JsonAnyGetter}.
464      * If no such method exists null is returned.
465      * If more than one are found, an exception is thrown.
466      */

467     @Override
468     public AnnotatedMember findAnyGetter() throws IllegalArgumentException
469     {
470         AnnotatedMember anyGetter = (_propCollector == null) ? null
471                 : _propCollector.getAnyGetter();
472         if (anyGetter != null) {
473             /* For now let's require a Map; in future can add support for other
474              * types like perhaps Iterable<Map.Entry>?
475              */

476             Class<?> type = anyGetter.getRawType();
477             if (!Map.class.isAssignableFrom(type)) {
478                 throw new IllegalArgumentException("Invalid 'any-getter' annotation on method "+anyGetter.getName()+"(): return type is not instance of java.util.Map");
479             }
480         }
481         return anyGetter;
482     }
483
484     @Override
485     public List<BeanPropertyDefinition> findBackReferences()
486     {
487         List<BeanPropertyDefinition> result = null;
488         HashSet<String> names = null;
489         for (BeanPropertyDefinition property : _properties()) {
490             AnnotationIntrospector.ReferenceProperty refDef = property.findReferenceType();
491             if ((refDef == null) || !refDef.isBackReference()) {
492                 continue;
493             }
494             final String refName = refDef.getName();
495             if (result == null) {
496                 result = new ArrayList<BeanPropertyDefinition>();
497                 names = new HashSet<>();
498                 names.add(refName);
499             } else {
500                 if (!names.add(refName)) {
501                     throw new IllegalArgumentException("Multiple back-reference properties with name '"+refName+"'");
502                 }
503             }
504             result.add(property);
505         }
506         return result;
507     }
508
509     @Deprecated // since 2.9
510     @Override
511     public Map<String,AnnotatedMember> findBackReferenceProperties()
512     {
513         List<BeanPropertyDefinition> props = findBackReferences();
514         if (props == null) {
515             return null;
516         }
517         Map<String,AnnotatedMember> result = new HashMap<>();
518         for (BeanPropertyDefinition prop : props) {
519             result.put(prop.getName(), prop.getMutator());
520         }
521         return result;
522     }
523
524     /*
525     /**********************************************************
526     /* Introspection for deserialization, factories
527     /**********************************************************
528      */

529
530     @Override
531     public List<AnnotatedMethod> getFactoryMethods()
532     {
533         // must filter out anything that clearly is not a factory method
534         List<AnnotatedMethod> candidates = _classInfo.getFactoryMethods();
535         if (candidates.isEmpty()) {
536             return candidates;
537         }
538         List<AnnotatedMethod> result = null;
539         for (AnnotatedMethod am : candidates) {
540             if (isFactoryMethod(am)) {
541                 if (result == null) {
542                     result = new ArrayList<AnnotatedMethod>();
543                 }
544                 result.add(am);
545             }
546         }
547         if (result == null) {
548             return Collections.emptyList();
549         }
550         return result;
551     }
552
553     @Override
554     public Constructor<?> findSingleArgConstructor(Class<?>... argTypes)
555     {
556         for (AnnotatedConstructor ac : _classInfo.getConstructors()) {
557             // This list is already filtered to only include accessible
558             /* (note: for now this is a redundant check; but in future
559              * that may change; thus leaving here for now)
560              */

561             if (ac.getParameterCount() == 1) {
562                 Class<?> actArg = ac.getRawParameterType(0);
563                 for (Class<?> expArg : argTypes) {
564                     if (expArg == actArg) {
565                         return ac.getAnnotated();
566                     }
567                 }
568             }
569         }
570         return null;
571     }
572
573     @Override
574     public Method findFactoryMethod(Class<?>... expArgTypes)
575     {
576         // So, of all single-arg static methods:
577         for (AnnotatedMethod am : _classInfo.getFactoryMethods()) {
578             // 24-Oct-2016, tatu: Better ensure it only takes 1 arg, no matter what
579             if (isFactoryMethod(am) && am.getParameterCount() == 1) {
580                 // And must take one of expected arg types (or supertype)
581                 Class<?> actualArgType = am.getRawParameterType(0);
582                 for (Class<?> expArgType : expArgTypes) {
583                     // And one that matches what we would pass in
584                     if (actualArgType.isAssignableFrom(expArgType)) {
585                         return am.getAnnotated();
586                     }
587                 }
588             }
589         }
590         return null;
591     }
592
593     protected boolean isFactoryMethod(AnnotatedMethod am)
594     {
595         // First: return type must be compatible with the introspected class
596         // (i.e. allowed to be sub-class, although usually is the same class)
597         Class<?> rt = am.getRawReturnType();
598         if (!getBeanClass().isAssignableFrom(rt)) {
599             return false;
600         }
601         /* Also: must be a recognized factory method, meaning:
602          * (a) marked with @JsonCreator annotation, or
603          * (b) "valueOf" (at this point, need not be public)
604          */

605         JsonCreator.Mode mode = _annotationIntrospector.findCreatorAnnotation(_config, am);
606         if ((mode != null) && (mode != JsonCreator.Mode.DISABLED)) {
607             return true;
608         }
609         final String name = am.getName();
610         // 24-Oct-2016, tatu: As per [databind#1429] must ensure takes exactly one arg
611         if ("valueOf".equals(name)) {
612             if (am.getParameterCount() == 1) {
613                 return true;
614             }
615         }
616         // [databind#208] Also accept "fromString()"if takes String or CharSequence
617         if ("fromString".equals(name)) {
618             if (am.getParameterCount() == 1) {
619                 Class<?> cls = am.getRawParameterType(0);
620                 if (cls == String.class || CharSequence.class.isAssignableFrom(cls)) {
621                     return true;
622                 }
623             }
624         }
625         return false;
626     }
627
628     /**
629      * @deprecated since 2.8
630      */

631     @Deprecated // since 2.8, not used at least since 2.7
632     protected PropertyName _findCreatorPropertyName(AnnotatedParameter param)
633     {
634         PropertyName name = _annotationIntrospector.findNameForDeserialization(param);
635         if (name == null || name.isEmpty()) {
636             String str = _annotationIntrospector.findImplicitPropertyName(param);
637             if (str != null && !str.isEmpty()) {
638                 name = PropertyName.construct(str);
639             }
640         }
641         return name;
642     }
643
644     /*
645     /**********************************************************
646     /* Introspection for deserialization, other
647     /**********************************************************
648      */

649
650     @Override
651     public Class<?> findPOJOBuilder() {
652         return (_annotationIntrospector == null) ?
653                 null : _annotationIntrospector.findPOJOBuilder(_classInfo);
654     }
655
656     @Override
657     public JsonPOJOBuilder.Value findPOJOBuilderConfig()
658     {
659         return (_annotationIntrospector == null) ?
660                 null : _annotationIntrospector.findPOJOBuilderConfig(_classInfo);
661     }
662
663     @Override
664     public Converter<Object,Object> findDeserializationConverter()
665     {
666         if (_annotationIntrospector == null) {
667             return null;
668         }
669         return _createConverter(_annotationIntrospector.findDeserializationConverter(_classInfo));
670     }
671
672     @Override
673     public String findClassDescription() {
674         return (_annotationIntrospector == null) ?
675                 null : _annotationIntrospector.findClassDescription(_classInfo);
676     }
677
678     /*
679     /**********************************************************
680     /* Helper methods for field introspection
681     /**********************************************************
682      */

683
684     /**
685      * @param ignoredProperties (optional) names of properties to ignore;
686      *   any fields that would be recognized as one of these properties
687      *   is ignored.
688      * @param forSerialization If true, will collect serializable property
689      *    fields; if false, deserializable
690      *
691      * @return Ordered Map with logical property name as key, and
692      *    matching field as value.
693      *
694      * @deprecated Since 2.7.2, does not seem to be used?
695      */

696     @Deprecated
697     public LinkedHashMap<String,AnnotatedField> _findPropertyFields(
698             Collection<String> ignoredProperties, boolean forSerialization)
699     {
700         LinkedHashMap<String,AnnotatedField> results = new LinkedHashMap<String,AnnotatedField>();
701         for (BeanPropertyDefinition property : _properties()) {
702             AnnotatedField f = property.getField();
703             if (f != null) {
704                 String name = property.getName();
705                 if (ignoredProperties != null) {
706                     if (ignoredProperties.contains(name)) {
707                         continue;
708                     }
709                 }
710                 results.put(name, f);
711             }
712         }
713         return results;
714     }
715
716     /*
717     /**********************************************************
718     /* Helper methods, other
719     /**********************************************************
720      */

721     
722     @SuppressWarnings("unchecked")
723     protected Converter<Object,Object> _createConverter(Object converterDef)
724     {
725         if (converterDef == null) {
726             return null;
727         }
728         if (converterDef instanceof Converter<?,?>) {
729             return (Converter<Object,Object>) converterDef;
730         }
731         if (!(converterDef instanceof Class)) {
732             throw new IllegalStateException("AnnotationIntrospector returned Converter definition of type "
733                     +converterDef.getClass().getName()+"; expected type Converter or Class<Converter> instead");
734         }
735         Class<?> converterClass = (Class<?>)converterDef;
736         // there are some known "no class" markers to consider too:
737         if (converterClass == Converter.None.class || ClassUtil.isBogusClass(converterClass)) {
738             return null;
739         }
740         if (!Converter.class.isAssignableFrom(converterClass)) {
741             throw new IllegalStateException("AnnotationIntrospector returned Class "
742                     +converterClass.getName()+"; expected Class<Converter>");
743         }
744         HandlerInstantiator hi = _config.getHandlerInstantiator();
745         Converter<?,?> conv = (hi == null) ? null : hi.converterInstance(_config, _classInfo, converterClass);
746         if (conv == null) {
747             conv = (Converter<?,?>) ClassUtil.createInstance(converterClass,
748                     _config.canOverrideAccessModifiers());
749         }
750         return (Converter<Object,Object>) conv;
751     }
752 }
753