1 package com.fasterxml.jackson.databind.introspect;
2
3 import java.lang.annotation.Annotation;
4 import java.lang.annotation.Retention;
5 import java.lang.annotation.Target;
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.List;
9 import java.util.Map;
10
11 import com.fasterxml.jackson.databind.AnnotationIntrospector;
12 import com.fasterxml.jackson.databind.JavaType;
13 import com.fasterxml.jackson.databind.cfg.MapperConfig;
14 import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver;
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
19 /**
20  * Helper class that contains logic for resolving annotations to construct
21  * {@link AnnotatedClass} instances.
22  *
23  * @since 2.9
24  */

25 public class AnnotatedClassResolver
26 {
27     private final static Annotations NO_ANNOTATIONS = AnnotationCollector.emptyAnnotations();
28
29     private final static Class<?> CLS_OBJECT = Object.class;
30     private final static Class<?> CLS_ENUM = Enum.class;
31
32     private final static Class<?> CLS_LIST = List.class;
33     private final static Class<?> CLS_MAP = Map.class;
34     
35     private final MapperConfig<?> _config;
36     private final AnnotationIntrospector _intr;
37     private final MixInResolver _mixInResolver;
38     private final TypeBindings _bindings;
39
40     private final JavaType _type;
41     private final Class<?> _class;
42     private final Class<?> _primaryMixin;
43
44     /**
45      * @since 2.11
46      */

47     private final boolean _collectAnnotations;
48
49     AnnotatedClassResolver(MapperConfig<?> config, JavaType type, MixInResolver r) {
50         _config = config;
51         _type = type;
52         _class = type.getRawClass();
53         _mixInResolver = r;
54         _bindings = type.getBindings();
55         _intr = config.isAnnotationProcessingEnabled()
56                 ? config.getAnnotationIntrospector() : null;
57         _primaryMixin = (r == null) ? null : r.findMixInClassFor(_class);
58
59         // Also... JDK types do not have annotations that are of interest to us
60         // At least JDK container types
61         _collectAnnotations = (_intr != null) &&
62                 (!ClassUtil.isJDKClass(_class) || !_type.isContainerType());
63     }
64
65     AnnotatedClassResolver(MapperConfig<?> config, Class<?> cls, MixInResolver r) {
66         _config = config;
67         _type = null;
68         _class = cls;
69         _mixInResolver = r;
70         _bindings = TypeBindings.emptyBindings();
71         if (config == null) {
72             _intr = null;
73             _primaryMixin = null;
74         } else {
75             _intr = config.isAnnotationProcessingEnabled()
76                     ? config.getAnnotationIntrospector() : null;
77             _primaryMixin = (r == null) ? null : r.findMixInClassFor(_class);
78         }
79
80         _collectAnnotations = (_intr != null);
81     }
82
83     public static AnnotatedClass resolve(MapperConfig<?> config, JavaType forType,
84             MixInResolver r)
85     {
86         if (forType.isArrayType() && skippableArray(config, forType.getRawClass())) {
87             return createArrayType(config, forType.getRawClass());
88         }
89         return new AnnotatedClassResolver(config, forType, r).resolveFully();
90     }
91
92     public static AnnotatedClass resolveWithoutSuperTypes(MapperConfig<?> config, Class<?> forType) {
93         return resolveWithoutSuperTypes(config, forType, config);
94     }
95
96     public static AnnotatedClass resolveWithoutSuperTypes(MapperConfig<?> config, JavaType forType,
97             MixInResolver r)
98     {
99         if (forType.isArrayType() && skippableArray(config, forType.getRawClass())) {
100             return createArrayType(config, forType.getRawClass());
101         }
102         return new AnnotatedClassResolver(config, forType, r).resolveWithoutSuperTypes();
103     }
104
105     public static AnnotatedClass resolveWithoutSuperTypes(MapperConfig<?> config, Class<?> forType,
106             MixInResolver r)
107     {
108         if (forType.isArray() && skippableArray(config, forType)) {
109             return createArrayType(config, forType);
110         }
111         return new AnnotatedClassResolver(config, forType, r).resolveWithoutSuperTypes();
112     }
113
114     private static boolean skippableArray(MapperConfig<?> config, Class<?> type) {
115         return (config == null) || (config.findMixInClassFor(type) == null);
116     }
117
118     /**
119      * Internal helper method used for resolving a small set of "primordial" types for which
120      * we do not accept any annotation information or overrides. 
121      */

122     static AnnotatedClass createPrimordial(Class<?> raw) {
123         return new AnnotatedClass(raw);
124     }
125
126     /**
127      * Internal helper method used for resolving array types, unless they happen
128      * to have associated mix-in to apply.
129      */

130     static AnnotatedClass createArrayType(MapperConfig<?> config, Class<?> raw) {
131         return new AnnotatedClass(raw);
132     }
133
134     AnnotatedClass resolveFully() {
135         List<JavaType> superTypes = new ArrayList<JavaType>(8);
136         if (!_type.hasRawClass(Object.class)) {
137             if (_type.isInterface()) {
138                 _addSuperInterfaces(_type, superTypes, false);
139             } else {
140                 _addSuperTypes(_type, superTypes, false);
141             }
142         }
143 //System.err.println("resolveFully("+_type.getRawClass().getSimpleName()+") ("+superTypes.size()+") -> "+superTypes);
144         return new AnnotatedClass(_type, _class, superTypes, _primaryMixin,
145                 resolveClassAnnotations(superTypes),
146                 _bindings, _intr, _mixInResolver, _config.getTypeFactory(),
147                 _collectAnnotations);
148
149     }
150
151     AnnotatedClass resolveWithoutSuperTypes() {
152         List<JavaType> superTypes = Collections.<JavaType>emptyList();
153         return new AnnotatedClass(null, _class, superTypes, _primaryMixin,
154                 resolveClassAnnotations(superTypes),
155                 _bindings, _intr, _mixInResolver, _config.getTypeFactory(),
156                 _collectAnnotations);
157     }
158
159     private static void _addSuperTypes(JavaType type, List<JavaType> result,
160             boolean addClassItself)
161     {
162         final Class<?> cls = type.getRawClass();
163         // 15-Oct-2019, tatu: certain paths do not lead to useful information, so prune
164         //    as optimization
165         if ((cls == CLS_OBJECT) || (cls == CLS_ENUM)) {
166             return;
167         }
168         if (addClassItself) {
169             if (_contains(result, cls)) { // already added, no need to check supers
170                 return;
171             }
172             result.add(type);
173         }
174         for (JavaType intCls : type.getInterfaces()) {
175             _addSuperInterfaces(intCls, result, true);
176         }
177         final JavaType superType = type.getSuperClass();
178         if (superType != null) {
179             _addSuperTypes(superType, result, true);
180         }
181     }
182
183     private static void _addSuperInterfaces(JavaType type, List<JavaType> result,
184             boolean addClassItself)
185     {
186         final Class<?> cls = type.getRawClass();
187         if (addClassItself) {
188             if (_contains(result, cls)) { // already added, no need to check supers
189                 return;
190             }
191             result.add(type);
192             // 30-Oct-2019, tatu: Further, no point going beyond some containers
193             if ((cls == CLS_LIST) || (cls == CLS_MAP)) {
194                 return;
195             }
196         }
197         for (JavaType intCls : type.getInterfaces()) {
198             _addSuperInterfaces(intCls, result, true);
199         }
200     }
201
202     private static boolean _contains(List<JavaType> found, Class<?> raw) {
203         for (int i = 0, end = found.size(); i < end; ++i) {
204             if (found.get(i).getRawClass() == raw) {
205                 return true;
206             }
207         }
208         return false;
209     }
210     
211     /*
212     /**********************************************************
213     /* Class annotation resolution
214     /**********************************************************
215      */

216
217     /**
218      * Initialization method that will recursively collect Jackson
219      * annotations for this class and all super classes and
220      * interfaces.
221      */

222     private Annotations resolveClassAnnotations(List<JavaType> superTypes)
223     {
224         // Should skip processing if annotation processing disabled
225         if (_intr == null) {
226             return NO_ANNOTATIONS;
227         }
228         // Plus we may or may not have mix-ins to consider
229         final boolean checkMixIns = (_mixInResolver != null)
230                 && (!(_mixInResolver instanceof SimpleMixInResolver)
231                         || ((SimpleMixInResolver) _mixInResolver).hasMixIns());
232
233         // also skip if there's nothing to do
234         if (!checkMixIns && !_collectAnnotations) {
235             return NO_ANNOTATIONS;
236         }
237
238         AnnotationCollector resolvedCA = AnnotationCollector.emptyCollector();
239         // add mix-in annotations first (overrides)
240         if (_primaryMixin != null) {
241             resolvedCA = _addClassMixIns(resolvedCA, _class, _primaryMixin);
242         }
243         // then annotations from the class itself:
244         // 06-Oct-2019, tatu: [databind#2464] Skip class annotations for JDK classes
245         if (_collectAnnotations) {
246             resolvedCA = _addAnnotationsIfNotPresent(resolvedCA,
247                     ClassUtil.findClassAnnotations(_class));
248         }
249
250         // and then from super types
251         for (JavaType type : superTypes) {
252             // and mix mix-in annotations in-between
253             if (checkMixIns) {
254                 Class<?> cls = type.getRawClass();
255                 resolvedCA = _addClassMixIns(resolvedCA, cls,
256                         _mixInResolver.findMixInClassFor(cls));
257             }
258             if (_collectAnnotations) {
259                 resolvedCA = _addAnnotationsIfNotPresent(resolvedCA,
260                         ClassUtil.findClassAnnotations(type.getRawClass()));
261             }
262         }
263
264         // and finally... any annotations there might be for plain old Object.class:
265         // separate because otherwise it is just ignored (not included in super types)
266
267         // 12-Jul-2009, tatu: Should this be done for interfaces too?
268         //  For now, yes, seems useful for some cases, and not harmful for any?
269         if (checkMixIns) {
270             resolvedCA = _addClassMixIns(resolvedCA, Object.class,
271                     _mixInResolver.findMixInClassFor(Object.class));
272         }
273         return resolvedCA.asAnnotations();
274     }
275
276     private AnnotationCollector _addClassMixIns(AnnotationCollector annotations,
277             Class<?> target, Class<?> mixin)
278     {
279         if (mixin != null) {
280             // Ok, first: annotations from mix-in class itself:
281             annotations = _addAnnotationsIfNotPresent(annotations, ClassUtil.findClassAnnotations(mixin));
282     
283             // And then from its supertypes, if any. But note that we will only consider
284             // super-types up until reaching the masked class (if found); this because
285             // often mix-in class is a sub-class (for convenience reasons).
286             // And if so, we absolutely must NOT include super types of masked class,
287             // as that would inverse precedence of annotations.
288             for (Class<?> parent : ClassUtil.findSuperClasses(mixin, target, false)) {
289                 annotations = _addAnnotationsIfNotPresent(annotations, ClassUtil.findClassAnnotations(parent));
290             }
291         }
292         return annotations;
293     }
294
295     private AnnotationCollector _addAnnotationsIfNotPresent(AnnotationCollector c,
296             Annotation[] anns)
297     {
298         if (anns != null) {
299             for (Annotation ann : anns) { // first: direct annotations
300                 // note: we will NOT filter out non-Jackson annotations any more
301                 if (!c.isPresent(ann)) {
302                     c = c.addOrOverride(ann);
303                     if (_intr.isAnnotationBundle(ann)) {
304                         c = _addFromBundleIfNotPresent(c, ann);
305                     }
306                 }
307             }
308         }
309         return c;
310     }
311
312     private AnnotationCollector _addFromBundleIfNotPresent(AnnotationCollector c,
313             Annotation bundle)
314     {
315         for (Annotation ann : ClassUtil.findClassAnnotations(bundle.annotationType())) {
316             // minor optimization: by-pass 2 common JDK meta-annotations
317             if ((ann instanceof Target) || (ann instanceof Retention)) {
318                 continue;
319             }
320             if (!c.isPresent(ann)) {
321                 c = c.addOrOverride(ann);
322                 if (_intr.isAnnotationBundle(ann)) {
323                     c = _addFromBundleIfNotPresent(c, ann);
324                 }
325             }
326         }
327         return c;
328     }
329 }
330