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;
final class AnnotatedCreatorCollector
extends CollectorBase
{
private final TypeResolutionContext _typeContext;
private final boolean _collectAnnotations;
private AnnotatedConstructor _defaultConstructor;
AnnotatedCreatorCollector(AnnotationIntrospector intr,
TypeResolutionContext tc, boolean collectAnnotations)
{
super(intr);
_typeContext = tc;
_collectAnnotations = collectAnnotations;
}
public static Creators collectCreators(AnnotationIntrospector intr,
TypeFactory typeFactory, TypeResolutionContext tc,
JavaType type, Class<?> primaryMixIn, boolean collectAnnotations)
{
collectAnnotations |= (primaryMixIn != null);
return new AnnotatedCreatorCollector(intr, tc, collectAnnotations)
.collect(typeFactory, type, primaryMixIn);
}
Creators collect(TypeFactory typeFactory, JavaType type, Class<?> primaryMixIn)
{
List<AnnotatedConstructor> constructors = _findPotentialConstructors(type, primaryMixIn);
List<AnnotatedMethod> factories = _findPotentialFactories(typeFactory, type, primaryMixIn);
if (_collectAnnotations) {
if (_defaultConstructor != null) {
if (_intr.hasIgnoreMarker(_defaultConstructor)) {
_defaultConstructor = null;
}
}
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);
}
private List<AnnotatedConstructor> _findPotentialConstructors(JavaType type,
Class<?> primaryMixIn)
{
ClassUtil.Ctor defaultCtor = null;
List<ClassUtil.Ctor> ctors = null;
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<AnnotatedConstructor> result;
int ctorCount;
if (ctors == null) {
result = Collections.emptyList();
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);
}
}
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;
}
}
}
}
}
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<AnnotatedMethod> _findPotentialFactories(TypeFactory typeFactory,
JavaType type, Class<?> primaryMixIn)
{
List<Method> candidates = null;
for (Method m : ClassUtil.getClassMethods(type.getRawClass())) {
if (!_isIncludableFactoryMethod(m)) {
continue;
}
if (candidates == null) {
candidates = new ArrayList<>();
}
candidates.add(m);
}
if (candidates == null) {
return Collections.emptyList();
}
final TypeResolutionContext initialTypeResCtxt = _typeContext;
int factoryCount = candidates.size();
List<AnnotatedMethod> result = new ArrayList<>(factoryCount);
for (int i = 0; i < factoryCount; ++i) {
result.add(null);
}
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;
}
}
}
}
for (int i = 0; i < factoryCount; ++i) {
AnnotatedMethod factory = result.get(i);
if (factory == null) {
Method candidate = candidates.get(i);
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())
&& !m.isSynthetic();
}
protected AnnotatedConstructor constructDefaultConstructor(ClassUtil.Ctor ctor,
ClassUtil.Ctor mixin)
{
return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
collectAnnotations(ctor, mixin),
NO_ANNOTATION_MAPS);
}
protected AnnotatedConstructor constructNonDefaultConstructor(ClassUtil.Ctor ctor,
ClassUtil.Ctor mixin)
{
final int paramCount = ctor.getParamCount();
if (_intr == null) {
return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
_emptyAnnotationMap(), _emptyAnnotationMaps(paramCount));
}
if (paramCount == 0) {
return new AnnotatedConstructor(_typeContext, ctor.getConstructor(),
collectAnnotations(ctor, mixin),
NO_ANNOTATION_MAPS);
}
AnnotationMap[] resolvedAnnotations;
Annotation[][] paramAnns = ctor.getParameterAnnotations();
if (paramCount != paramAnns.length) {
resolvedAnnotations = null;
Class<?> dc = ctor.getDeclaringClass();
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()) {
if (paramCount == (paramAnns.length + 1)) {
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) {
return new AnnotatedMethod(typeResCtxt, m, _emptyAnnotationMap(),
_emptyAnnotationMaps(paramCount));
}
if (paramCount == 0) {
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;
}
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();
}
private static boolean isIncludableConstructor(Constructor<?> c) {
return !c.isSynthetic();
}
}