package com.fasterxml.jackson.databind.introspect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ClassUtil;
public class AnnotatedMethodCollector
extends CollectorBase
{
private final MixInResolver _mixInResolver;
private final boolean _collectAnnotations;
AnnotatedMethodCollector(AnnotationIntrospector intr,
MixInResolver mixins, boolean collectAnnotations)
{
super(intr);
_mixInResolver = (intr == null) ? null : mixins;
_collectAnnotations = collectAnnotations;
}
public static AnnotatedMethodMap collectMethods(AnnotationIntrospector intr,
TypeResolutionContext tc,
MixInResolver mixins, TypeFactory types,
JavaType type, List<JavaType> superTypes, Class<?> primaryMixIn,
boolean collectAnnotations)
{
return new AnnotatedMethodCollector(intr, mixins, collectAnnotations)
.collect(types, tc, type, superTypes, primaryMixIn);
}
AnnotatedMethodMap collect(TypeFactory typeFactory, TypeResolutionContext tc,
JavaType mainType, List<JavaType> superTypes, Class<?> primaryMixIn)
{
Map<MemberKey,MethodBuilder> methods = new LinkedHashMap<>();
_addMemberMethods(tc, mainType.getRawClass(), methods, primaryMixIn);
for (JavaType type : superTypes) {
Class<?> mixin = (_mixInResolver == null) ? null : _mixInResolver.findMixInClassFor(type.getRawClass());
_addMemberMethods(
new TypeResolutionContext.Basic(typeFactory, type.getBindings()),
type.getRawClass(), methods, mixin);
}
boolean checkJavaLangObject = false;
if (_mixInResolver != null) {
Class<?> mixin = _mixInResolver.findMixInClassFor(Object.class);
if (mixin != null) {
_addMethodMixIns(tc, mainType.getRawClass(), methods, mixin);
checkJavaLangObject = true;
}
}
if (checkJavaLangObject && (_intr != null) && !methods.isEmpty()) {
for (Map.Entry<MemberKey,MethodBuilder> entry : methods.entrySet()) {
MemberKey k = entry.getKey();
if (!"hashCode".equals(k.getName()) || (0 != k.argCount())) {
continue;
}
try {
Method m = Object.class.getDeclaredMethod(k.getName());
if (m != null) {
MethodBuilder b = entry.getValue();
b.annotations = collectDefaultAnnotations(b.annotations,
m.getDeclaredAnnotations());
b.method = m;
}
} catch (Exception e) { }
}
}
if (methods.isEmpty()) {
return new AnnotatedMethodMap();
}
Map<MemberKey,AnnotatedMethod> actual = new LinkedHashMap<>(methods.size());
for (Map.Entry<MemberKey,MethodBuilder> entry : methods.entrySet()) {
AnnotatedMethod am = entry.getValue().build();
if (am != null) {
actual.put(entry.getKey(), am);
}
}
return new AnnotatedMethodMap(actual);
}
private void _addMemberMethods(TypeResolutionContext tc,
Class<?> cls, Map<MemberKey,MethodBuilder> methods, Class<?> mixInCls)
{
if (mixInCls != null) {
_addMethodMixIns(tc, cls, methods, mixInCls);
}
if (cls == null) {
return;
}
for (Method m : ClassUtil.getClassMethods(cls)) {
if (!_isIncludableMemberMethod(m)) {
continue;
}
final MemberKey key = new MemberKey(m);
MethodBuilder b = methods.get(key);
if (b == null) {
AnnotationCollector c = (_intr == null) ? AnnotationCollector.emptyCollector()
: collectAnnotations(m.getDeclaredAnnotations());
methods.put(key, new MethodBuilder(tc, m, c));
} else {
if (_collectAnnotations) {
b.annotations = collectDefaultAnnotations(b.annotations, m.getDeclaredAnnotations());
}
Method old = b.method;
if (old == null) {
b.method = m;
} else if (Modifier.isAbstract(old.getModifiers())
&& !Modifier.isAbstract(m.getModifiers())) {
b.method = m;
b.typeContext = tc;
}
}
}
}
protected void _addMethodMixIns(TypeResolutionContext tc, Class<?> targetClass,
Map<MemberKey,MethodBuilder> methods, Class<?> mixInCls)
{
if (_intr == null) {
return;
}
for (Class<?> mixin : ClassUtil.findRawSuperTypes(mixInCls, targetClass, true)) {
for (Method m : mixin.getDeclaredMethods()) {
if (!_isIncludableMemberMethod(m)) {
continue;
}
final MemberKey key = new MemberKey(m);
MethodBuilder b = methods.get(key);
Annotation[] anns = m.getDeclaredAnnotations();
if (b == null) {
methods.put(key, new MethodBuilder(tc, null, collectAnnotations(anns)));
} else {
b.annotations = collectDefaultAnnotations(b.annotations, anns);
}
}
}
}
private static boolean _isIncludableMemberMethod(Method m)
{
if (Modifier.isStatic(m.getModifiers())
|| m.isSynthetic() || m.isBridge()) {
return false;
}
int pcount = m.getParameterTypes().length;
return (pcount <= 2);
}
private final static class MethodBuilder {
public TypeResolutionContext typeContext;
public Method method;
public AnnotationCollector annotations;
public MethodBuilder(TypeResolutionContext tc, Method m,
AnnotationCollector ann) {
typeContext = tc;
method = m;
annotations = ann;
}
public AnnotatedMethod build() {
if (method == null) {
return null;
}
return new AnnotatedMethod(typeContext, method, annotations.asAnnotationMap(), null);
}
}
}