1 package com.fasterxml.jackson.databind.introspect; 2 3 import java.lang.annotation.Annotation; 4 import java.lang.reflect.AnnotatedElement; 5 import java.lang.reflect.Constructor; 6 import java.lang.reflect.Method; 7 import java.lang.reflect.Modifier; 8 import java.util.ArrayList; 9 import java.util.Collections; 10 import java.util.List; 11 12 import com.fasterxml.jackson.databind.AnnotationIntrospector; 13 import com.fasterxml.jackson.databind.JavaType; 14 import com.fasterxml.jackson.databind.introspect.AnnotatedClass.Creators; 15 import com.fasterxml.jackson.databind.util.ClassUtil; 16 17 /** 18 * Helper class used to contain details of how Creators (annotated constructors 19 * and static methods) are discovered to be accessed by and via {@link AnnotatedClass}. 20 * 21 * @since 2.9 22 */ 23 final class AnnotatedCreatorCollector 24 extends CollectorBase 25 { 26 // // // Configuration 27 28 private final TypeResolutionContext _typeContext; 29 30 /** 31 * @since 2.11 32 */ 33 private final boolean _collectAnnotations; 34 35 // // // Collected state 36 37 private AnnotatedConstructor _defaultConstructor; 38 AnnotatedCreatorCollector(AnnotationIntrospector intr, TypeResolutionContext tc, boolean collectAnnotations)39 AnnotatedCreatorCollector(AnnotationIntrospector intr, 40 TypeResolutionContext tc, boolean collectAnnotations) 41 { 42 super(intr); 43 _typeContext = tc; 44 _collectAnnotations = collectAnnotations; 45 } 46 collectCreators(AnnotationIntrospector intr, TypeResolutionContext tc, JavaType type, Class<?> primaryMixIn, boolean collectAnnotations)47 public static Creators collectCreators(AnnotationIntrospector intr, 48 TypeResolutionContext tc, 49 JavaType type, Class<?> primaryMixIn, boolean collectAnnotations) 50 { 51 final boolean checkClassAnnotations = (intr != null) 52 && !ClassUtil.isJDKClass(type.getRawClass()); 53 54 // Constructor also always members of resolved class, parent == resolution context 55 return new AnnotatedCreatorCollector(intr, tc, checkClassAnnotations) 56 .collect(type, primaryMixIn); 57 } 58 collect(JavaType type, Class<?> primaryMixIn)59 Creators collect(JavaType type, Class<?> primaryMixIn) 60 { 61 // 30-Apr-2016, tatu: [databind#1215]: Actually, while true, this does 62 // NOT apply to context since sub-class may have type bindings 63 // TypeResolutionContext typeContext = new TypeResolutionContext.Basic(_typeFactory, _type.getBindings()); 64 65 List<AnnotatedConstructor> constructors = _findPotentialConstructors(type, primaryMixIn); 66 List<AnnotatedMethod> factories = _findPotentialFactories(type, primaryMixIn); 67 68 /* And then... let's remove all constructors that are deemed 69 * ignorable after all annotations have been properly collapsed. 70 */ 71 // AnnotationIntrospector is null if annotations not enabled; if so, can skip: 72 if (_collectAnnotations) { 73 if (_defaultConstructor != null) { 74 if (_intr.hasIgnoreMarker(_defaultConstructor)) { 75 _defaultConstructor = null; 76 } 77 } 78 // count down to allow safe removal 79 for (int i = constructors.size(); --i >= 0; ) { 80 if (_intr.hasIgnoreMarker(constructors.get(i))) { 81 constructors.remove(i); 82 } 83 } 84 for (int i = factories.size(); --i >= 0; ) { 85 if (_intr.hasIgnoreMarker(factories.get(i))) { 86 factories.remove(i); 87 } 88 } 89 } 90 return new AnnotatedClass.Creators(_defaultConstructor, constructors, factories); 91 } 92 93 /** 94 * Helper method for locating constructors (and matching mix-in overrides) 95 * we might want to use; this is needed in order to mix information between 96 * the two and construct resulting {@link AnnotatedConstructor}s 97 */ _findPotentialConstructors(JavaType type, Class<?> primaryMixIn)98 private List<AnnotatedConstructor> _findPotentialConstructors(JavaType type, 99 Class<?> primaryMixIn) 100 { 101 ClassUtil.Ctor defaultCtor = null; 102 List<ClassUtil.Ctor> ctors = null; 103 104 // 18-Jun-2016, tatu: Enum constructors will never be useful (unlike 105 // possibly static factory methods); but they can be royal PITA 106 // due to some oddities by JVM; see: 107 // [https://github.com/FasterXML/jackson-module-parameter-names/issues/35] 108 // for more. So, let's just skip them. 109 if (!type.isEnumType()) { 110 ClassUtil.Ctor[] declaredCtors = ClassUtil.getConstructors(type.getRawClass()); 111 for (ClassUtil.Ctor ctor : declaredCtors) { 112 if (!isIncludableConstructor(ctor.getConstructor())) { 113 continue; 114 } 115 if (ctor.getParamCount() == 0) { 116 defaultCtor = ctor; 117 } else { 118 if (ctors == null) { 119 ctors = new ArrayList<>(); 120 } 121 ctors.add(ctor); 122 } 123 } 124 } 125 List<AnnotatedConstructor> result; 126 int ctorCount; 127 if (ctors == null) { 128 result = Collections.emptyList(); 129 // Nothing found? Short-circuit 130 if (defaultCtor == null) { 131 return result; 132 } 133 ctorCount = 0; 134 } else { 135 ctorCount = ctors.size(); 136 result = new ArrayList<>(ctorCount); 137 for (int i = 0; i < ctorCount; ++i) { 138 result.add(null); 139 } 140 } 141 142 // so far so good; but do we also need to find mix-ins overrides? 143 if (primaryMixIn != null) { 144 MemberKey[] ctorKeys = null; 145 for (ClassUtil.Ctor mixinCtor : ClassUtil.getConstructors(primaryMixIn)) { 146 if (mixinCtor.getParamCount() == 0) { 147 if (defaultCtor != null) { 148 _defaultConstructor = constructDefaultConstructor(defaultCtor, mixinCtor); 149 defaultCtor = null; 150 } 151 continue; 152 } 153 if (ctors != null) { 154 if (ctorKeys == null) { 155 ctorKeys = new MemberKey[ctorCount]; 156 for (int i = 0; i < ctorCount; ++i) { 157 ctorKeys[i] = new MemberKey(ctors.get(i).getConstructor()); 158 } 159 } 160 MemberKey key = new MemberKey(mixinCtor.getConstructor()); 161 162 for (int i = 0; i < ctorCount; ++i) { 163 if (key.equals(ctorKeys[i])) { 164 result.set(i, 165 constructNonDefaultConstructor(ctors.get(i), mixinCtor)); 166 break; 167 } 168 } 169 } 170 } 171 } 172 // Ok: anything within mix-ins has been resolved; anything remaining we must resolve 173 if (defaultCtor != null) { 174 _defaultConstructor = constructDefaultConstructor(defaultCtor, null); 175 } 176 for (int i = 0; i < ctorCount; ++i) { 177 AnnotatedConstructor ctor = result.get(i); 178 if (ctor == null) { 179 result.set(i, 180 constructNonDefaultConstructor(ctors.get(i), null)); 181 } 182 } 183 return result; 184 } 185 _findPotentialFactories(JavaType type, Class<?> primaryMixIn)186 private List<AnnotatedMethod> _findPotentialFactories(JavaType type, Class<?> primaryMixIn) 187 { 188 List<Method> candidates = null; 189 190 // First find all potentially relevant static methods 191 for (Method m : ClassUtil.getClassMethods(type.getRawClass())) { 192 if (!Modifier.isStatic(m.getModifiers())) { 193 continue; 194 } 195 // all factory methods are fine: 196 //int argCount = m.getParameterTypes().length; 197 if (candidates == null) { 198 candidates = new ArrayList<>(); 199 } 200 candidates.add(m); 201 } 202 // and then locate mix-ins, if any 203 if (candidates == null) { 204 return Collections.emptyList(); 205 } 206 int factoryCount = candidates.size(); 207 List<AnnotatedMethod> result = new ArrayList<>(factoryCount); 208 for (int i = 0; i < factoryCount; ++i) { 209 result.add(null); 210 } 211 // so far so good; but do we also need to find mix-ins overrides? 212 if (primaryMixIn != null) { 213 MemberKey[] methodKeys = null; 214 for (Method mixinFactory : primaryMixIn.getDeclaredMethods()) { 215 if (!Modifier.isStatic(mixinFactory.getModifiers())) { 216 continue; 217 } 218 if (methodKeys == null) { 219 methodKeys = new MemberKey[factoryCount]; 220 for (int i = 0; i < factoryCount; ++i) { 221 methodKeys[i] = new MemberKey(candidates.get(i)); 222 } 223 } 224 MemberKey key = new MemberKey(mixinFactory); 225 for (int i = 0; i < factoryCount; ++i) { 226 if (key.equals(methodKeys[i])) { 227 result.set(i, 228 constructFactoryCreator(candidates.get(i), mixinFactory)); 229 break; 230 } 231 } 232 } 233 } 234 // Ok: anything within mix-ins has been resolved; anything remaining we must resolve 235 for (int i = 0; i < factoryCount; ++i) { 236 AnnotatedMethod factory = result.get(i); 237 if (factory == null) { 238 result.set(i, 239 constructFactoryCreator(candidates.get(i), null)); 240 } 241 } 242 return result; 243 } 244 constructDefaultConstructor(ClassUtil.Ctor ctor, ClassUtil.Ctor mixin)245 protected AnnotatedConstructor constructDefaultConstructor(ClassUtil.Ctor ctor, 246 ClassUtil.Ctor mixin) 247 { 248 return new AnnotatedConstructor(_typeContext, ctor.getConstructor(), 249 collectAnnotations(ctor, mixin), 250 // 16-Jun-2019, tatu: default is zero-args, so can't have parameter annotations 251 NO_ANNOTATION_MAPS); 252 } 253 constructNonDefaultConstructor(ClassUtil.Ctor ctor, ClassUtil.Ctor mixin)254 protected AnnotatedConstructor constructNonDefaultConstructor(ClassUtil.Ctor ctor, 255 ClassUtil.Ctor mixin) 256 { 257 final int paramCount = ctor.getParamCount(); 258 if (_intr == null) { // when annotation processing is disabled 259 return new AnnotatedConstructor(_typeContext, ctor.getConstructor(), 260 _emptyAnnotationMap(), _emptyAnnotationMaps(paramCount)); 261 } 262 263 /* Looks like JDK has discrepancy, whereas annotations for implicit 'this' 264 * (for non-static inner classes) are NOT included, but type is? 265 * Strange, sounds like a bug. Alas, we can't really fix that... 266 */ 267 if (paramCount == 0) { // no-arg default constructors, can simplify slightly 268 return new AnnotatedConstructor(_typeContext, ctor.getConstructor(), 269 collectAnnotations(ctor, mixin), 270 NO_ANNOTATION_MAPS); 271 } 272 // Also: enum value constructors 273 AnnotationMap[] resolvedAnnotations; 274 Annotation[][] paramAnns = ctor.getParameterAnnotations(); 275 if (paramCount != paramAnns.length) { 276 // Limits of the work-around (to avoid hiding real errors): 277 // first, only applicable for member classes and then either: 278 279 resolvedAnnotations = null; 280 Class<?> dc = ctor.getDeclaringClass(); 281 // (a) is enum, which have two extra hidden params (name, index) 282 if (ClassUtil.isEnumType(dc) && (paramCount == paramAnns.length + 2)) { 283 Annotation[][] old = paramAnns; 284 paramAnns = new Annotation[old.length+2][]; 285 System.arraycopy(old, 0, paramAnns, 2, old.length); 286 resolvedAnnotations = collectAnnotations(paramAnns, null); 287 } else if (dc.isMemberClass()) { 288 // (b) non-static inner classes, get implicit 'this' for parameter, not annotation 289 if (paramCount == (paramAnns.length + 1)) { 290 // hack attack: prepend a null entry to make things match 291 Annotation[][] old = paramAnns; 292 paramAnns = new Annotation[old.length+1][]; 293 System.arraycopy(old, 0, paramAnns, 1, old.length); 294 paramAnns[0] = NO_ANNOTATIONS; 295 resolvedAnnotations = collectAnnotations(paramAnns, null); 296 } 297 } 298 if (resolvedAnnotations == null) { 299 throw new IllegalStateException(String.format( 300 "Internal error: constructor for %s has mismatch: %d parameters; %d sets of annotations", 301 ctor.getDeclaringClass().getName(), paramCount, paramAnns.length)); 302 } 303 } else { 304 resolvedAnnotations = collectAnnotations(paramAnns, 305 (mixin == null) ? null : mixin.getParameterAnnotations()); 306 } 307 return new AnnotatedConstructor(_typeContext, ctor.getConstructor(), 308 collectAnnotations(ctor, mixin), resolvedAnnotations); 309 } 310 constructFactoryCreator(Method m, Method mixin)311 protected AnnotatedMethod constructFactoryCreator(Method m, Method mixin) 312 { 313 final int paramCount = m.getParameterTypes().length; 314 if (_intr == null) { // when annotation processing is disabled 315 return new AnnotatedMethod(_typeContext, m, _emptyAnnotationMap(), 316 _emptyAnnotationMaps(paramCount)); 317 } 318 if (paramCount == 0) { // common enough we can slightly optimize 319 return new AnnotatedMethod(_typeContext, m, collectAnnotations(m, mixin), 320 NO_ANNOTATION_MAPS); 321 } 322 return new AnnotatedMethod(_typeContext, m, collectAnnotations(m, mixin), 323 collectAnnotations(m.getParameterAnnotations(), 324 (mixin == null) ? null : mixin.getParameterAnnotations())); 325 } 326 collectAnnotations(Annotation[][] mainAnns, Annotation[][] mixinAnns)327 private AnnotationMap[] collectAnnotations(Annotation[][] mainAnns, Annotation[][] mixinAnns) { 328 if (_collectAnnotations) { 329 final int count = mainAnns.length; 330 AnnotationMap[] result = new AnnotationMap[count]; 331 for (int i = 0; i < count; ++i) { 332 AnnotationCollector c = collectAnnotations(AnnotationCollector.emptyCollector(), 333 mainAnns[i]); 334 if (mixinAnns != null) { 335 c = collectAnnotations(c, mixinAnns[i]); 336 } 337 result[i] = c.asAnnotationMap(); 338 } 339 return result; 340 } 341 return NO_ANNOTATION_MAPS; 342 } 343 344 // // NOTE: these are only called when we know we have AnnotationIntrospector 345 collectAnnotations(ClassUtil.Ctor main, ClassUtil.Ctor mixin)346 private AnnotationMap collectAnnotations(ClassUtil.Ctor main, ClassUtil.Ctor mixin) { 347 if (_collectAnnotations) { 348 AnnotationCollector c = collectAnnotations(main.getDeclaredAnnotations()); 349 if (mixin != null) { 350 c = collectAnnotations(c, mixin.getDeclaredAnnotations()); 351 } 352 return c.asAnnotationMap(); 353 } 354 return _emptyAnnotationMap(); 355 } 356 collectAnnotations(AnnotatedElement main, AnnotatedElement mixin)357 private final AnnotationMap collectAnnotations(AnnotatedElement main, AnnotatedElement mixin) { 358 AnnotationCollector c = collectAnnotations(main.getDeclaredAnnotations()); 359 if (mixin != null) { 360 c = collectAnnotations(c, mixin.getDeclaredAnnotations()); 361 } 362 return c.asAnnotationMap(); 363 } 364 365 // for [databind#1005]: do not use or expose synthetic constructors isIncludableConstructor(Constructor<?> c)366 private static boolean isIncludableConstructor(Constructor<?> c) { 367 return !c.isSynthetic(); 368 } 369 } 370