package cz.cvut.fel.adaptiverestfulapi.meta.reflection;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import java.lang.reflect.*;
import java.util.*;
/**
* Helper class for reflection.
*
* Getter is a public method which starts with `get` or `is` followed by name and takes no parameters.
* Setter is a public method which starts with `set` followed by name and takes only one parameter.
*/
public class Reflection {
private final Reflections reflections;
/**
* Creates reflection for package and base class.
* @param pack
* @param clazz
*/
public Reflection(String pack, Class clazz) {
if (clazz.equals(Object.class)) {
// @see http://stackoverflow.com/a/9571146
List<ClassLoader> classLoadersList = new LinkedList();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());
this.reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false), new ResourcesScanner())
.addUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
.addUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[1])))
.addUrls(ClasspathHelper.forJavaClassPath())
.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(pack))));
} else {
this.reflections = new Reflections(clazz);
}
}
/**
* Finds all leaf classes of the base class.
* @param clazz base class
* @return set of leaf classes
*/
public Set<Class<?>> leafs(Class clazz) {
Set<Class<?>> all = this.reflections.getSubTypesOf(clazz);
Set<Class<?>> leafs = new HashSet<>();
for (Class<?> c : all) {
if (this.reflections.getSubTypesOf(c).isEmpty()) {
leafs.add(c);
}
}
return leafs;
}
/**
* Looks up for triplets of field, related getter and setter in the class.
* Triplet can contain combinations of
* - field, getter and/or setter,
* - getter and/or setter.
* @param clazz
* @return set of triplets
*/
public Set<Triplet<Field, Method, Method>> triplets(Class clazz) {
Set<Triplet<Field, Method, Method>> triplets = new HashSet<>();
Set<Field> fields = Reflection.fields(clazz);
Set<Method> getters = Reflection.getters(clazz);
Set<Method> setters = Reflection.setters(clazz);
Method getter, setter;
// step 1: add fields with getters, and setters (matched by field name)
for (Field field : fields) {
getter = Reflection.getter(field, getters);
setter = Reflection.setter(field, setters);
if (getter == null && setter == null) {
continue;
}
if (getter != null) {
getters.remove(getter);
}
if (setter != null) {
setters.remove(setter);
}
triplets.add(new Triplet<Field, Method, Method>(field, getter, setter));
}
// step 2a: add remaining getter and setter pairs (matched by method name)
// step 2b: add remaining setters
for (Method s : setters) {
getter = Reflection.getter(s, getters);
if (getter != null) {
triplets.add(new Triplet<Field, Method, Method>(null, getter, s));
getters.remove(getter);
} else {
triplets.add(new Triplet<Field, Method, Method>(null, null, s));
}
}
// step 3: add remaining getters
for (Method g : getters) {
triplets.add(new Triplet<Field, Method, Method>(null, g, null));
}
return triplets;
}
/**
* Returns all fields in the class.
* @param clazz
* @return set of fields
*/
private static Set<Field> fields(Class clazz) {
Set<Field> fields = new HashSet<>();
fields.addAll(ReflectionUtils.getAllFields(clazz));
return fields;
}
/**
* Returns getter method for the field
* @param field
* @param getters
* @return getter
*/
private static Method getter(Field field, Set<Method> getters) {
return getter(field.getName(), getters);
}
/**
* Returns getter for the corresponding setter method.
* @param setter
* @param getters
* @return getter
*/
private static Method getter(Method setter, Set<Method> getters) {
return getter(setter.getName().substring(3), getters);
}
/**
* Returns getter for the name.
* @param name
* @param getters
* @return getter
*/
private static Method getter(String name, Set<Method> getters) {
for (Method getter : getters) {
if (getter.getName().equalsIgnoreCase("is" + name.toUpperCase())
|| getter.getName().equalsIgnoreCase("get" + name.toUpperCase())) {
return getter;
}
}
return null;
}
/**
* Returns setter for the field.
* @param field
* @param setters
* @return setter
*/
private static Method setter(Field field, Set<Method> setters) {
for (Method setter : setters) {
if (setter.getName().equalsIgnoreCase("set" + field.getName().toUpperCase())) {
return setter;
}
}
return null;
}
/**
* Returns getters for the class.
* @param clazz
* @return set of getters
*/
private static Set<Method> getters(Class clazz) {
Set<Method> getters = new HashSet<>();
Set<Method> methods = null;
methods = ReflectionUtils.getAllMethods(clazz,
ReflectionUtils.withModifier(Modifier.PUBLIC),
ReflectionUtils.withPrefix("get"),
ReflectionUtils.withParametersCount(0));
for (Method method : methods) {
if (Modifier.isAbstract(method.getModifiers())
|| Modifier.isInterface(method.getModifiers())) {
continue;
}
getters.add(method);
}
methods = ReflectionUtils.getAllMethods(clazz,
ReflectionUtils.withModifier(Modifier.PUBLIC),
ReflectionUtils.withPrefix("is"),
ReflectionUtils.withParametersCount(0));
for (Method method : methods) {
if (Modifier.isAbstract(method.getModifiers())
|| Modifier.isInterface(method.getModifiers())) {
continue;
}
getters.add(method);
}
return getters;
}
/**
* Returns setters for the class.
* @param clazz
* @return set of setters
*/
private static Set<Method> setters(Class clazz) {
Set<Method> setters = new HashSet<>();
Set<Method> methods = ReflectionUtils.getAllMethods(clazz,
ReflectionUtils.withModifier(Modifier.PUBLIC),
ReflectionUtils.withPrefix("set"),
ReflectionUtils.withParametersCount(1));
for (Method method : methods) {
if (Modifier.isAbstract(method.getModifiers())
|| Modifier.isInterface(method.getModifiers())) {
continue;
}
setters.add(method);
}
return setters;
}
}