package io.gsonfire.util.reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Class responsible for returning a set of members that are annotated with a particular {@link Annotation} class.
* The returned members can be mapped to a different type.
*
* The result after mapping will be cached for performance reasons
* @autor: julio
*/
public abstract class AnnotationInspector<T extends AccessibleObject, M> {
private final ConcurrentMap<Class, ConcurrentMap<Class<? extends Annotation>, Collection<M>>> cache = new ConcurrentHashMap<Class, ConcurrentMap<Class<? extends Annotation>, Collection<M>>>();
public Collection<M> getAnnotatedMembers(Class clazz, Class<? extends Annotation> annotation){
if(clazz != null) {
Collection<M> members = getFromCache(clazz, annotation);
if (members != null) {
return members;
}
//Cache miss, we need to use reflections to get the fields
members = getFromCache(clazz, annotation);
if (members == null) {
Set<M> memberList = new LinkedHashSet<M>();
//Add methods declared in the class
for (T m : getDeclaredMembers(clazz)) {
if (m.isAnnotationPresent(annotation)) {
m.setAccessible(true);
memberList.add(map(m));
}
}
//Add methods from super class
memberList.addAll(getAnnotatedMembers(clazz.getSuperclass(), annotation));
//Add methods from interfaces
for (Class interfaceClass : clazz.getInterfaces()) {
memberList.addAll(getAnnotatedMembers(interfaceClass, annotation));
}
ConcurrentMap<Class<? extends Annotation>, Collection<M>> newAnnotationMap = new ConcurrentHashMap<Class<? extends Annotation>, Collection<M>>();
ConcurrentMap<Class<? extends Annotation>, Collection<M>> storedAnnotationMap = cache.putIfAbsent(clazz, newAnnotationMap);
storedAnnotationMap = storedAnnotationMap == null ? newAnnotationMap : storedAnnotationMap;
storedAnnotationMap.put(annotation, memberList);
return memberList;
}
}
return Collections.emptyList();
}
protected abstract T[] getDeclaredMembers(Class clazz);
protected abstract M map(T member);
private Collection<M> getFromCache(Class clazz, Class<? extends Annotation> annotation) {
Map<Class<? extends Annotation>, Collection<M>> annotationMap = cache.get(clazz);
if(annotationMap != null){
Collection<M> methods = annotationMap.get(annotation);
if(methods != null){
return methods;
}
}
return null;
}
}