com.fasterxml.jackson.databind.introspect.AnnotatedCreatorCollector Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package com.fasterxml.jackson.databind.introspect;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass.Creators;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ClassUtil;
/**
* Helper class used to contain details of how Creators (annotated constructors
* and static methods) are discovered to be accessed by and via {@link AnnotatedClass}.
*
* @since 2.9
*/
final class AnnotatedCreatorCollector
extends CollectorBase
{
// // // Configuration
private final TypeResolutionContext _typeContext;
/**
* @since 2.11
*/
private final boolean _collectAnnotations;
// // // Collected state
private AnnotatedConstructor _defaultConstructor;
AnnotatedCreatorCollector(AnnotationIntrospector intr,
TypeResolutionContext tc, boolean collectAnnotations)
{
super(intr);
_typeContext = tc;
_collectAnnotations = collectAnnotations;
}
/**
* @since 2.11.3
*/
public static Creators collectCreators(AnnotationIntrospector intr,
TypeFactory typeFactory, TypeResolutionContext tc,
JavaType type, Class> primaryMixIn, boolean collectAnnotations)
{
// 30-Sep-2020, tatu: [databind#2795] Even if annotations not otherwise
// requested (for JDK collections), force change if mix-in in use
collectAnnotations |= (primaryMixIn != null);
// Constructor also always members of resolved class, parent == resolution context
return new AnnotatedCreatorCollector(intr, tc, collectAnnotations)
.collect(typeFactory, type, primaryMixIn);
}
Creators collect(TypeFactory typeFactory, JavaType type, Class> primaryMixIn)
{
// 30-Apr-2016, tatu: [databind#1215]: Actually, while true, this does
// NOT apply to context since sub-class may have type bindings
// TypeResolutionContext typeContext = new TypeResolutionContext.Basic(_typeFactory, _type.getBindings());
List constructors = _findPotentialConstructors(type, primaryMixIn);
List factories = _findPotentialFactories(typeFactory, type, primaryMixIn);
/* And then... let's remove all constructors that are deemed
* ignorable after all annotations have been properly collapsed.
*/
// AnnotationIntrospector is null if annotations not enabled; if so, can skip:
if (_collectAnnotations) {
if (_defaultConstructor != null) {
if (_intr.hasIgnoreMarker(_defaultConstructor)) {
_defaultConstructor = null;
}
}
// count down to allow safe removal
for (int i = constructors.size(); --i >= 0; ) {
if (_intr.hasIgnoreMarker(constructors.get(i))) {
constructors.remove(i);
}
}
for (int i = factories.size(); --i >= 0; ) {
if (_intr.hasIgnoreMarker(factories.get(i))) {
factories.remove(i);
}
}
}
return new AnnotatedClass.Creators(_defaultConstructor, constructors, factories);
}
/**
* Helper method for locating constructors (and matching mix-in overrides)
* we might want to use; this is needed in order to mix information between
* the two and construct resulting {@link AnnotatedConstructor}s
*/
private List _findPotentialConstructors(JavaType type,
Class> primaryMixIn)
{
ClassUtil.Ctor defaultCtor = null;
List ctors = null;
// 18-Jun-2016, tatu: Enum constructors will never be useful (unlike
// possibly static factory methods); but they can be royal PITA
// due to some oddities by JVM; see:
// [https://github.com/FasterXML/jackson-module-parameter-names/issues/35]
// for more. So, let's just skip them.
if (!type.isEnumType()) {
ClassUtil.Ctor[] declaredCtors = ClassUtil.getConstructors(type.getRawClass());
for (ClassUtil.Ctor ctor : declaredCtors) {
if (!isIncludableConstructor(ctor.getConstructor())) {
continue;
}
if (ctor.getParamCount() == 0) {
defaultCtor = ctor;
} else {
if (ctors == null) {
ctors = new ArrayList<>();
}
ctors.add(ctor);
}
}
}
List result;
int ctorCount;
if (ctors == null) {
result = Collections.emptyList();
// Nothing found? Short-circuit
if (defaultCtor == null) {
return result;
}
ctorCount = 0;
} else {
ctorCount = ctors.size();
result = new ArrayList<>(ctorCount);
for (int i = 0; i < ctorCount; ++i) {
result.add(null);
}
}
// so far so good; but do we also need to find mix-ins overrides?
if (primaryMixIn != null) {
MemberKey[] ctorKeys = null;
for (ClassUtil.Ctor mixinCtor : ClassUtil.getConstructors(primaryMixIn)) {
if (mixinCtor.getParamCount() == 0) {
if (defaultCtor != null) {
_defaultConstructor = constructDefaultConstructor(defaultCtor, mixinCtor);
defaultCtor = null;
}
continue;
}
if (ctors != null) {
if (ctorKeys == null) {
ctorKeys = new MemberKey[ctorCount];
for (int i = 0; i < ctorCount; ++i) {
ctorKeys[i] = new MemberKey(ctors.get(i).getConstructor());
}
}
MemberKey key = new MemberKey(mixinCtor.getConstructor());
for (int i = 0; i < ctorCount; ++i) {
if (key.equals(ctorKeys[i])) {
result.set(i,
constructNonDefaultConstructor(ctors.get(i), mixinCtor));
break;
}
}
}
}
}
// Ok: anything within mix-ins has been resolved; anything remaining we must resolve
if (defaultCtor != null) {
_defaultConstructor = constructDefaultConstructor(defaultCtor, null);
}
for (int i = 0; i < ctorCount; ++i) {
AnnotatedConstructor ctor = result.get(i);
if (ctor == null) {
result.set(i,
constructNonDefaultConstructor(ctors.get(i), null));
}
}
return result;
}
private List _findPotentialFactories(TypeFactory typeFactory,
JavaType type, Class> primaryMixIn)
{
List candidates = null;
// First find all potentially relevant static methods
for (Method m : ClassUtil.getClassMethods(type.getRawClass())) {
if (!_isIncludableFactoryMethod(m)) {
continue;
}
// all factory methods are fine:
//int argCount = m.getParameterTypes().length;
if (candidates == null) {
candidates = new ArrayList<>();
}
candidates.add(m);
}
// and then locate mix-ins, if any
if (candidates == null) {
return Collections.emptyList();
}
// 05-Sep-2020, tatu: Important fix wrt [databind#2821] -- static methods
// do NOT have type binding context of the surrounding class and although
// passing that should not break things, it appears to... Regardless,
// it should not be needed or useful as those bindings are only available
// to non-static members
// final TypeResolutionContext typeResCtxt = new TypeResolutionContext.Empty(typeFactory);
// 27-Oct-2020, tatu: SIGH. As per [databind#2894] there is widespread use of
// incorrect bindings in the wild -- not supported (no tests) but used
// nonetheless. So, for 2.11.x, put back "Bad Bindings"...
//
// 03-Nov-2020, ckozak: Implement generic JsonCreator TypeVariable handling [databind#2895]
// final TypeResolutionContext emptyTypeResCtxt = new TypeResolutionContext.Empty(typeFactory);
// 23-Aug-2021, tatu: Double-d'oh! As per [databind#3220], we must revert to
// the Incorrect Illogical But Used By Users Bindings... for 2.x at least.
final TypeResolutionContext initialTypeResCtxt = _typeContext;
int factoryCount = candidates.size();
List result = new ArrayList<>(factoryCount);
for (int i = 0; i < factoryCount; ++i) {
result.add(null);
}
// so far so good; but do we also need to find mix-ins overrides?
if (primaryMixIn != null) {
MemberKey[] methodKeys = null;
for (Method mixinFactory : primaryMixIn.getDeclaredMethods()) {
if (!_isIncludableFactoryMethod(mixinFactory)) {
continue;
}
if (methodKeys == null) {
methodKeys = new MemberKey[factoryCount];
for (int i = 0; i < factoryCount; ++i) {
methodKeys[i] = new MemberKey(candidates.get(i));
}
}
MemberKey key = new MemberKey(mixinFactory);
for (int i = 0; i < factoryCount; ++i) {
if (key.equals(methodKeys[i])) {
result.set(i,
constructFactoryCreator(candidates.get(i),
initialTypeResCtxt, mixinFactory));
break;
}
}
}
}
// Ok: anything within mix-ins has been resolved; anything remaining we must resolve
for (int i = 0; i < factoryCount; ++i) {
AnnotatedMethod factory = result.get(i);
if (factory == null) {
Method candidate = candidates.get(i);
// 06-Nov-2020, tatu: Fix from [databind#2895] will try to resolve
// nominal static method type bindings into expected target type
// (if generic types involved)
// 23-Aug-2021, tatu: ... is this still needed, wrt un-fix in [databind#3220]?
TypeResolutionContext typeResCtxt = MethodGenericTypeResolver.narrowMethodTypeParameters(
candidate, type, typeFactory, initialTypeResCtxt);
result.set(i,
constructFactoryCreator(candidate, typeResCtxt, null));
}
}
return result;
}
private static boolean _isIncludableFactoryMethod(Method m)
{
return Modifier.isStatic(m.getModifiers())
// 09-Nov-2020, ckozak: Avoid considering synthetic methods such as
// lambdas used within methods because they're not relevant.
&& !m.isSynthetic();
}
protected AnnotatedConstructor constructDefaultConstructor(ClassUtil.Ctor ctor,
ClassUtil.Ctor mixin)
{
return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
collectAnnotations(ctor, mixin),
// 16-Jun-2019, tatu: default is zero-args, so can't have parameter annotations
NO_ANNOTATION_MAPS);
}
protected AnnotatedConstructor constructNonDefaultConstructor(ClassUtil.Ctor ctor,
ClassUtil.Ctor mixin)
{
final int paramCount = ctor.getParamCount();
if (_intr == null) { // when annotation processing is disabled
return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
_emptyAnnotationMap(), _emptyAnnotationMaps(paramCount));
}
/* Looks like JDK has discrepancy, whereas annotations for implicit 'this'
* (for non-static inner classes) are NOT included, but type is?
* Strange, sounds like a bug. Alas, we can't really fix that...
*/
if (paramCount == 0) { // no-arg default constructors, can simplify slightly
return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
collectAnnotations(ctor, mixin),
NO_ANNOTATION_MAPS);
}
// Also: enum value constructors
AnnotationMap[] resolvedAnnotations;
Annotation[][] paramAnns = ctor.getParameterAnnotations();
if (paramCount != paramAnns.length) {
// Limits of the work-around (to avoid hiding real errors):
// first, only applicable for member classes and then either:
resolvedAnnotations = null;
Class> dc = ctor.getDeclaringClass();
// (a) is enum, which have two extra hidden params (name, index)
if (ClassUtil.isEnumType(dc) && (paramCount == paramAnns.length + 2)) {
Annotation[][] old = paramAnns;
paramAnns = new Annotation[old.length+2][];
System.arraycopy(old, 0, paramAnns, 2, old.length);
resolvedAnnotations = collectAnnotations(paramAnns, null);
} else if (dc.isMemberClass()) {
// (b) non-static inner classes, get implicit 'this' for parameter, not annotation
if (paramCount == (paramAnns.length + 1)) {
// hack attack: prepend a null entry to make things match
Annotation[][] old = paramAnns;
paramAnns = new Annotation[old.length+1][];
System.arraycopy(old, 0, paramAnns, 1, old.length);
paramAnns[0] = NO_ANNOTATIONS;
resolvedAnnotations = collectAnnotations(paramAnns, null);
}
}
if (resolvedAnnotations == null) {
throw new IllegalStateException(String.format(
"Internal error: constructor for %s has mismatch: %d parameters; %d sets of annotations",
ctor.getDeclaringClass().getName(), paramCount, paramAnns.length));
}
} else {
resolvedAnnotations = collectAnnotations(paramAnns,
(mixin == null) ? null : mixin.getParameterAnnotations());
}
return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
collectAnnotations(ctor, mixin), resolvedAnnotations);
}
protected AnnotatedMethod constructFactoryCreator(Method m,
TypeResolutionContext typeResCtxt, Method mixin)
{
final int paramCount = m.getParameterTypes().length;
if (_intr == null) { // when annotation processing is disabled
return new AnnotatedMethod(typeResCtxt, m, _emptyAnnotationMap(),
_emptyAnnotationMaps(paramCount));
}
if (paramCount == 0) { // common enough we can slightly optimize
return new AnnotatedMethod(typeResCtxt, m, collectAnnotations(m, mixin),
NO_ANNOTATION_MAPS);
}
return new AnnotatedMethod(typeResCtxt, m, collectAnnotations(m, mixin),
collectAnnotations(m.getParameterAnnotations(),
(mixin == null) ? null : mixin.getParameterAnnotations()));
}
private AnnotationMap[] collectAnnotations(Annotation[][] mainAnns, Annotation[][] mixinAnns) {
if (_collectAnnotations) {
final int count = mainAnns.length;
AnnotationMap[] result = new AnnotationMap[count];
for (int i = 0; i < count; ++i) {
AnnotationCollector c = collectAnnotations(AnnotationCollector.emptyCollector(),
mainAnns[i]);
if (mixinAnns != null) {
c = collectAnnotations(c, mixinAnns[i]);
}
result[i] = c.asAnnotationMap();
}
return result;
}
return NO_ANNOTATION_MAPS;
}
// // NOTE: these are only called when we know we have AnnotationIntrospector
private AnnotationMap collectAnnotations(ClassUtil.Ctor main, ClassUtil.Ctor mixin) {
if (_collectAnnotations) {
AnnotationCollector c = collectAnnotations(main.getDeclaredAnnotations());
if (mixin != null) {
c = collectAnnotations(c, mixin.getDeclaredAnnotations());
}
return c.asAnnotationMap();
}
return _emptyAnnotationMap();
}
private final AnnotationMap collectAnnotations(AnnotatedElement main, AnnotatedElement mixin) {
AnnotationCollector c = collectAnnotations(main.getDeclaredAnnotations());
if (mixin != null) {
c = collectAnnotations(c, mixin.getDeclaredAnnotations());
}
return c.asAnnotationMap();
}
// for [databind#1005]: do not use or expose synthetic constructors
private static boolean isIncludableConstructor(Constructor> c) {
return !c.isSynthetic();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy