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 AnnotatedClassResolver(MapperConfig<?> config, JavaType type, MixInResolver r)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 AnnotatedClassResolver(MapperConfig<?> config, Class<?> cls, MixInResolver r)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 resolve(MapperConfig<?> config, JavaType forType, MixInResolver r)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 resolveWithoutSuperTypes(MapperConfig<?> config, Class<?> forType)92 public static AnnotatedClass resolveWithoutSuperTypes(MapperConfig<?> config, Class<?> forType) { 93 return resolveWithoutSuperTypes(config, forType, config); 94 } 95 resolveWithoutSuperTypes(MapperConfig<?> config, JavaType forType, MixInResolver r)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 resolveWithoutSuperTypes(MapperConfig<?> config, Class<?> forType, MixInResolver r)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 skippableArray(MapperConfig<?> config, Class<?> type)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 */ createPrimordial(Class<?> raw)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 */ createArrayType(MapperConfig<?> config, Class<?> raw)130 static AnnotatedClass createArrayType(MapperConfig<?> config, Class<?> raw) { 131 return new AnnotatedClass(raw); 132 } 133 resolveFully()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 resolveWithoutSuperTypes()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 _addSuperTypes(JavaType type, List<JavaType> result, boolean addClassItself)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 _addSuperInterfaces(JavaType type, List<JavaType> result, boolean addClassItself)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 _contains(List<JavaType> found, Class<?> raw)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 */ resolveClassAnnotations(List<JavaType> superTypes)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 _addClassMixIns(AnnotationCollector annotations, Class<?> target, Class<?> mixin)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 _addAnnotationsIfNotPresent(AnnotationCollector c, Annotation[] anns)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 _addFromBundleIfNotPresent(AnnotationCollector c, Annotation bundle)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