com.fasterxml.jackson.databind.introspect.AnnotatedMethodCollector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
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;
AnnotatedMethodCollector(AnnotationIntrospector intr,
MixInResolver mixins)
{
super(intr);
_mixInResolver = (intr == null) ? null : mixins;
}
public static AnnotatedMethodMap collectMethods(AnnotationIntrospector intr,
TypeResolutionContext tc,
MixInResolver mixins, TypeFactory types,
JavaType type, List superTypes, Class> primaryMixIn)
{
// Constructor also always members of resolved class, parent == resolution context
return new AnnotatedMethodCollector(intr, mixins)
.collect(types, tc, type, superTypes, primaryMixIn);
}
AnnotatedMethodMap collect(TypeFactory typeFactory, TypeResolutionContext tc,
JavaType mainType, List superTypes, Class> primaryMixIn)
{
Map methods = new LinkedHashMap<>();
// first: methods from the class itself
_addMemberMethods(tc, mainType.getRawClass(), methods, primaryMixIn);
// and then augment these with annotations from super-types:
for (JavaType type : superTypes) {
Class> mixin = (_mixInResolver == null) ? null : _mixInResolver.findMixInClassFor(type.getRawClass());
_addMemberMethods(
new TypeResolutionContext.Basic(typeFactory, type.getBindings()),
type.getRawClass(), methods, mixin);
}
// Special case: mix-ins for Object.class? (to apply to ALL classes)
boolean checkJavaLangObject = false;
if (_mixInResolver != null) {
Class> mixin = _mixInResolver.findMixInClassFor(Object.class);
if (mixin != null) {
_addMethodMixIns(tc, mainType.getRawClass(), methods, mixin); //, mixins);
checkJavaLangObject = true;
}
}
// Any unmatched mix-ins? Most likely error cases (not matching any method);
// but there is one possible real use case: exposing Object#hashCode
// (alas, Object#getClass can NOT be exposed)
// Since we only know of that ONE case, optimize for it
if (checkJavaLangObject && (_intr != null) && !methods.isEmpty()) {
// Could use lookup but probably as fast or faster to traverse
for (Map.Entry entry : methods.entrySet()) {
MemberKey k = entry.getKey();
if (!"hashCode".equals(k.getName()) || (0 != k.argCount())) {
continue;
}
try {
// And with that, we can generate it appropriately
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) { }
}
}
// And then let's create the lookup map
if (methods.isEmpty()) {
return new AnnotatedMethodMap();
}
Map actual = new LinkedHashMap<>(methods.size());
for (Map.Entry 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 methods, Class> mixInCls)
{
// first, mixIns, since they have higher priority then class methods
if (mixInCls != null) {
_addMethodMixIns(tc, cls, methods, mixInCls);
}
if (cls == null) { // just so caller need not check when passing super-class
return;
}
// then methods from the class itself
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 (_intr != null) {
b.annotations = collectDefaultAnnotations(b.annotations, m.getDeclaredAnnotations());
}
Method old = b.method;
if (old == null) { // had "mix-over", replace
b.method = m;
// } else if (old.getDeclaringClass().isInterface() && !m.getDeclaringClass().isInterface()) {
} else if (Modifier.isAbstract(old.getModifiers())
&& !Modifier.isAbstract(m.getModifiers())) {
// 06-Jan-2010, tatu: Except that if method we saw first is
// from an interface, and we now find a non-interface definition, we should
// use this method, but with combination of annotations.
// This helps (or rather, is essential) with JAXB annotations and
// may also result in faster method calls (interface calls are slightly
// costlier than regular method calls)
b.method = m;
// 23-Aug-2017, tatu: [databind#1705] Also need to change the type resolution context if so
// (note: mix-over case above shouldn't need it)
b.typeContext = tc;
}
}
}
}
protected void _addMethodMixIns(TypeResolutionContext tc, Class> targetClass,
Map methods, Class> mixInCls)
{
if (_intr == null) {
return;
}
for (Class> mixin : ClassUtil.findRawSuperTypes(mixInCls, targetClass, true)) {
for (Method m : ClassUtil.getDeclaredMethods(mixin)) {
if (!_isIncludableMemberMethod(m)) {
continue;
}
final MemberKey key = new MemberKey(m);
MethodBuilder b = methods.get(key);
Annotation[] anns = m.getDeclaredAnnotations();
if (b == null) {
// nothing yet; add but do NOT specify method -- this marks it
// as "mix-over", floating mix-in
methods.put(key, new MethodBuilder(tc, null, collectAnnotations(anns)));
} else {
b.annotations = collectDefaultAnnotations(b.annotations, anns);
}
}
}
}
private boolean _isIncludableMemberMethod(Method m)
{
if (Modifier.isStatic(m.getModifiers())
// Looks like generics can introduce hidden bridge and/or synthetic methods.
// I don't think we want to consider those...
|| m.isSynthetic() || m.isBridge()) {
return false;
}
// also, for now we have no use for methods with more than 2 arguments:
// (2 argument methods for "any setter", fwtw)
int pcount = m.getParameterTypes().length;
return (pcount <= 2);
}
private final static class MethodBuilder {
public TypeResolutionContext typeContext;
// Method left empty for "floating" mix-in, filled in as need be
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;
}
// 12-Apr-2017, tatu: Note that parameter annotations are NOT collected -- we could
// collect them if that'd make sense but...
return new AnnotatedMethod(typeContext, method, annotations.asAnnotationMap(), null);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy