ru.yandex.qatools.camelot.common.Metadata Maven / Gradle / Ivy
package ru.yandex.qatools.camelot.common;
import ru.yandex.qatools.camelot.api.annotations.AggregationKey;
import ru.yandex.qatools.camelot.api.annotations.Processor;
import ru.yandex.qatools.camelot.api.annotations.Split;
import ru.yandex.qatools.camelot.error.MetadataException;
import ru.yandex.qatools.fsm.annotations.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static java.util.Arrays.asList;
import static ru.yandex.qatools.fsm.utils.ReflectUtils.collectAllSuperclassesAndInterfaces;
import static ru.yandex.qatools.fsm.utils.ReflectUtils.getMethodsInClassHierarchy;
/**
* @author Ilya Sadykov
*/
public abstract class Metadata {
Metadata() {
}
private static final Map, MetadataClassInfo> cache = new ConcurrentHashMap<>();
public static MetadataClassInfo getMeta(Class clazz, Class extends MetadataClassInfo> metaClass) {
if (!cache.containsKey(clazz)) {
try {
Constructor extends MetadataClassInfo> c = metaClass.getConstructor(Class.class);
cache.put(clazz, c.newInstance(clazz));
} catch (Exception e) {
throw new MetadataException("Failed to instantiate the metadata reader for class " + metaClass, e);
}
}
return cache.get(clazz);
}
public static MetadataClassInfo getMeta(Class clazz) {
if (!cache.containsKey(clazz)) {
cache.put(clazz, new ClassInfo<>(clazz));
}
return cache.get(clazz);
}
@ScanMethodsAnnotatedWith({
OnException.class, OnTransit.class, BeforeTransit.class,
AfterTransit.class, Processor.class, AggregationKey.class, Split.class, NewState.class
})
public static class ClassInfo implements MetadataClassInfo {
private final Class clazz;
private final Map, Method[]> annotatedMethods = new HashMap<>();
private final Map, Map, Set>> paramTypesMethods = new HashMap<>();
private final Map superClassesCache = new HashMap<>();
private final Class extends Annotation>[] methodAnnotations = getMethodAnnotations();
public ClassInfo(Class clazz) {
this.clazz = clazz;
buildMethodsCache();
collectStateSuperClassesCache();
}
@Override
public Method[] getAnnotatedMethods(Class aClass) {
if (annotatedMethods.containsKey(aClass)) {
return annotatedMethods.get(aClass);
}
return new Method[]{};
}
@Override
public Class[] getSuperClasses(Class clazz) {
if (superClassesCache.containsKey(clazz)) {
return superClassesCache.get(clazz);
}
final List classes = collectAllSuperclassesAndInterfaces(clazz);
final Class[] superClasses = classes.toArray(new Class[classes.size()]);
addCollectedSuperclasses(superClassesCache, superClasses);
return superClasses;
}
@Override
public Collection getMethodsByParamTypes(Class extends Annotation> aClass, Class... paramType) {
Set result = new HashSet<>();
if (paramTypesMethods.containsKey(aClass)) {
Set key = new HashSet<>();
key.addAll(asList(paramType));
if (paramTypesMethods.get(aClass).containsKey(key)) {
result.addAll(paramTypesMethods.get(aClass).get(key));
}
}
return result;
}
private void collectStateSuperClassesCache() {
for (Class extends Annotation> annClass : methodAnnotations) {
for (Method method : getAnnotatedMethods(annClass)) {
for (Class> paramClass : method.getParameterTypes()) {
addCollectedSuperclasses(superClassesCache, paramClass);
}
}
}
}
private static void addCollectedSuperclasses(Map superclasses, Class... eventClass) {
for (Class clazz : eventClass) {
if (!superclasses.containsKey(clazz)) {
final List classes = collectAllSuperclassesAndInterfaces(clazz);
final Class[] classesArray = classes.toArray(new Class[classes.size()]);
superclasses.put(clazz, classesArray);
addCollectedSuperclasses(superclasses, classesArray);
}
}
}
private void buildMethodsCache() {
for (Class extends Annotation> annClass : methodAnnotations) {
List methods = new ArrayList<>();
for (Method method : getMethodsInClassHierarchy(clazz)) {
if (method.getAnnotation(annClass) != null) {
methods.add(method);
buildMethodParamsInfoCache(annClass, method);
}
}
annotatedMethods.put(annClass, methods.toArray(new Method[methods.size()]));
}
}
private void buildMethodParamsInfoCache(Class extends Annotation> annClass, Method method) {
Set paramTypes = new HashSet<>();
paramTypes.addAll(asList(method.getParameterTypes()));
if (!paramTypesMethods.containsKey(annClass)) {
paramTypesMethods.put(annClass, new HashMap, Set>());
}
if (!paramTypesMethods.get(annClass).containsKey(paramTypes)) {
paramTypesMethods.get(annClass).put(paramTypes, new HashSet());
}
paramTypesMethods.get(annClass).get(paramTypes).add(method);
}
protected Class extends Annotation>[] getMethodAnnotations() {
ScanMethodsAnnotatedWith config = getClass().getAnnotation(ScanMethodsAnnotatedWith.class);
return config.value();
}
}
}