package com.fasterxml.jackson.databind.introspect;

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver;
import com.fasterxml.jackson.databind.type.TypeBindings;
import com.fasterxml.jackson.databind.util.Annotations;
import com.fasterxml.jackson.databind.util.ClassUtil;

Helper class that contains logic for resolving annotations to construct AnnotatedClass instances.
Since:2.9
/** * Helper class that contains logic for resolving annotations to construct * {@link AnnotatedClass} instances. * * @since 2.9 */
public class AnnotatedClassResolver { private final static Annotations NO_ANNOTATIONS = AnnotationCollector.emptyAnnotations(); private final static Class<?> CLS_OBJECT = Object.class; private final static Class<?> CLS_ENUM = Enum.class; private final static Class<?> CLS_LIST = List.class; private final static Class<?> CLS_MAP = Map.class; private final MapperConfig<?> _config; private final AnnotationIntrospector _intr; private final MixInResolver _mixInResolver; private final TypeBindings _bindings; private final JavaType _type; private final Class<?> _class; private final Class<?> _primaryMixin;
Since:2.11
/** * @since 2.11 */
private final boolean _collectAnnotations; AnnotatedClassResolver(MapperConfig<?> config, JavaType type, MixInResolver r) { _config = config; _type = type; _class = type.getRawClass(); _mixInResolver = r; _bindings = type.getBindings(); _intr = config.isAnnotationProcessingEnabled() ? config.getAnnotationIntrospector() : null; _primaryMixin = (r == null) ? null : r.findMixInClassFor(_class); // Also... JDK types do not have annotations that are of interest to us // At least JDK container types _collectAnnotations = (_intr != null) && (!ClassUtil.isJDKClass(_class) || !_type.isContainerType()); } AnnotatedClassResolver(MapperConfig<?> config, Class<?> cls, MixInResolver r) { _config = config; _type = null; _class = cls; _mixInResolver = r; _bindings = TypeBindings.emptyBindings(); if (config == null) { _intr = null; _primaryMixin = null; } else { _intr = config.isAnnotationProcessingEnabled() ? config.getAnnotationIntrospector() : null; _primaryMixin = (r == null) ? null : r.findMixInClassFor(_class); } _collectAnnotations = (_intr != null); } public static AnnotatedClass resolve(MapperConfig<?> config, JavaType forType, MixInResolver r) { if (forType.isArrayType() && skippableArray(config, forType.getRawClass())) { return createArrayType(config, forType.getRawClass()); } return new AnnotatedClassResolver(config, forType, r).resolveFully(); } public static AnnotatedClass resolveWithoutSuperTypes(MapperConfig<?> config, Class<?> forType) { return resolveWithoutSuperTypes(config, forType, config); } public static AnnotatedClass resolveWithoutSuperTypes(MapperConfig<?> config, JavaType forType, MixInResolver r) { if (forType.isArrayType() && skippableArray(config, forType.getRawClass())) { return createArrayType(config, forType.getRawClass()); } return new AnnotatedClassResolver(config, forType, r).resolveWithoutSuperTypes(); } public static AnnotatedClass resolveWithoutSuperTypes(MapperConfig<?> config, Class<?> forType, MixInResolver r) { if (forType.isArray() && skippableArray(config, forType)) { return createArrayType(config, forType); } return new AnnotatedClassResolver(config, forType, r).resolveWithoutSuperTypes(); } private static boolean skippableArray(MapperConfig<?> config, Class<?> type) { return (config == null) || (config.findMixInClassFor(type) == null); }
Internal helper method used for resolving a small set of "primordial" types for which we do not accept any annotation information or overrides.
/** * Internal helper method used for resolving a small set of "primordial" types for which * we do not accept any annotation information or overrides. */
static AnnotatedClass createPrimordial(Class<?> raw) { return new AnnotatedClass(raw); }
Internal helper method used for resolving array types, unless they happen to have associated mix-in to apply.
/** * Internal helper method used for resolving array types, unless they happen * to have associated mix-in to apply. */
static AnnotatedClass createArrayType(MapperConfig<?> config, Class<?> raw) { return new AnnotatedClass(raw); } AnnotatedClass resolveFully() { List<JavaType> superTypes = new ArrayList<JavaType>(8); if (!_type.hasRawClass(Object.class)) { if (_type.isInterface()) { _addSuperInterfaces(_type, superTypes, false); } else { _addSuperTypes(_type, superTypes, false); } } //System.err.println("resolveFully("+_type.getRawClass().getSimpleName()+") ("+superTypes.size()+") -> "+superTypes); return new AnnotatedClass(_type, _class, superTypes, _primaryMixin, resolveClassAnnotations(superTypes), _bindings, _intr, _mixInResolver, _config.getTypeFactory(), _collectAnnotations); } AnnotatedClass resolveWithoutSuperTypes() { List<JavaType> superTypes = Collections.<JavaType>emptyList(); return new AnnotatedClass(null, _class, superTypes, _primaryMixin, resolveClassAnnotations(superTypes), _bindings, _intr, _mixInResolver, _config.getTypeFactory(), _collectAnnotations); } private static void _addSuperTypes(JavaType type, List<JavaType> result, boolean addClassItself) { final Class<?> cls = type.getRawClass(); // 15-Oct-2019, tatu: certain paths do not lead to useful information, so prune // as optimization if ((cls == CLS_OBJECT) || (cls == CLS_ENUM)) { return; } if (addClassItself) { if (_contains(result, cls)) { // already added, no need to check supers return; } result.add(type); } for (JavaType intCls : type.getInterfaces()) { _addSuperInterfaces(intCls, result, true); } final JavaType superType = type.getSuperClass(); if (superType != null) { _addSuperTypes(superType, result, true); } } private static void _addSuperInterfaces(JavaType type, List<JavaType> result, boolean addClassItself) { final Class<?> cls = type.getRawClass(); if (addClassItself) { if (_contains(result, cls)) { // already added, no need to check supers return; } result.add(type); // 30-Oct-2019, tatu: Further, no point going beyond some containers if ((cls == CLS_LIST) || (cls == CLS_MAP)) { return; } } for (JavaType intCls : type.getInterfaces()) { _addSuperInterfaces(intCls, result, true); } } private static boolean _contains(List<JavaType> found, Class<?> raw) { for (int i = 0, end = found.size(); i < end; ++i) { if (found.get(i).getRawClass() == raw) { return true; } } return false; } /* /********************************************************** /* Class annotation resolution /********************************************************** */
Initialization method that will recursively collect Jackson annotations for this class and all super classes and interfaces.
/** * Initialization method that will recursively collect Jackson * annotations for this class and all super classes and * interfaces. */
private Annotations resolveClassAnnotations(List<JavaType> superTypes) { // Should skip processing if annotation processing disabled if (_intr == null) { return NO_ANNOTATIONS; } // Plus we may or may not have mix-ins to consider final boolean checkMixIns = (_mixInResolver != null) && (!(_mixInResolver instanceof SimpleMixInResolver) || ((SimpleMixInResolver) _mixInResolver).hasMixIns()); // also skip if there's nothing to do if (!checkMixIns && !_collectAnnotations) { return NO_ANNOTATIONS; } AnnotationCollector resolvedCA = AnnotationCollector.emptyCollector(); // add mix-in annotations first (overrides) if (_primaryMixin != null) { resolvedCA = _addClassMixIns(resolvedCA, _class, _primaryMixin); } // then annotations from the class itself: // 06-Oct-2019, tatu: [databind#2464] Skip class annotations for JDK classes if (_collectAnnotations) { resolvedCA = _addAnnotationsIfNotPresent(resolvedCA, ClassUtil.findClassAnnotations(_class)); } // and then from super types for (JavaType type : superTypes) { // and mix mix-in annotations in-between if (checkMixIns) { Class<?> cls = type.getRawClass(); resolvedCA = _addClassMixIns(resolvedCA, cls, _mixInResolver.findMixInClassFor(cls)); } if (_collectAnnotations) { resolvedCA = _addAnnotationsIfNotPresent(resolvedCA, ClassUtil.findClassAnnotations(type.getRawClass())); } } // and finally... any annotations there might be for plain old Object.class: // separate because otherwise it is just ignored (not included in super types) // 12-Jul-2009, tatu: Should this be done for interfaces too? // For now, yes, seems useful for some cases, and not harmful for any? if (checkMixIns) { resolvedCA = _addClassMixIns(resolvedCA, Object.class, _mixInResolver.findMixInClassFor(Object.class)); } return resolvedCA.asAnnotations(); } private AnnotationCollector _addClassMixIns(AnnotationCollector annotations, Class<?> target, Class<?> mixin) { if (mixin != null) { // Ok, first: annotations from mix-in class itself: annotations = _addAnnotationsIfNotPresent(annotations, ClassUtil.findClassAnnotations(mixin)); // And then from its supertypes, if any. But note that we will only consider // super-types up until reaching the masked class (if found); this because // often mix-in class is a sub-class (for convenience reasons). // And if so, we absolutely must NOT include super types of masked class, // as that would inverse precedence of annotations. for (Class<?> parent : ClassUtil.findSuperClasses(mixin, target, false)) { annotations = _addAnnotationsIfNotPresent(annotations, ClassUtil.findClassAnnotations(parent)); } } return annotations; } private AnnotationCollector _addAnnotationsIfNotPresent(AnnotationCollector c, Annotation[] anns) { if (anns != null) { for (Annotation ann : anns) { // first: direct annotations // note: we will NOT filter out non-Jackson annotations any more if (!c.isPresent(ann)) { c = c.addOrOverride(ann); if (_intr.isAnnotationBundle(ann)) { c = _addFromBundleIfNotPresent(c, ann); } } } } return c; } private AnnotationCollector _addFromBundleIfNotPresent(AnnotationCollector c, Annotation bundle) { for (Annotation ann : ClassUtil.findClassAnnotations(bundle.annotationType())) { // minor optimization: by-pass 2 common JDK meta-annotations if ((ann instanceof Target) || (ann instanceof Retention)) { continue; } if (!c.isPresent(ann)) { c = c.addOrOverride(ann); if (_intr.isAnnotationBundle(ann)) { c = _addFromBundleIfNotPresent(c, ann); } } } return c; } }