package ru.vyarus.dropwizard.guice.module.installer.util;
import ru.vyarus.dropwizard.guice.module.installer.FeatureInstaller;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
/**
* Utility methods to simplify checks for feature installers.
*
* @author Vyacheslav Rusakov
* @since 01.09.2014
*/
public final class FeatureUtils {
private FeatureUtils() {
}
/**
* @param type type to check
* @param annotation annotation to find
* @return true if annotation found on class or super class and type is not abstract, false otherwise
*/
public static boolean hasAnnotation(final Class<?> type, final Class<? extends Annotation> annotation) {
return getAnnotation(type, annotation) != null;
}
/**
* @param type type to check
* @param annotation annotation to find
* @return true if annotation found on one of class or super class annotations and type is not abstract,
* false otherwise
*/
public static boolean hasAnnotatedAnnotation(final Class<?> type, final Class<? extends Annotation> annotation) {
return getAnnotatedAnnotation(type, annotation) != null;
}
/**
* @param type type to examine
* @param annotation annotation to search
* @param <T> annotation type
* @return found annotation or null
*/
public static <T extends Annotation> T getAnnotation(final Class<?> type, final Class<T> annotation) {
T res = null;
if (!Modifier.isAbstract(type.getModifiers())) {
Class<?> supertype = type;
while (supertype != null && Object.class != supertype) {
if (supertype.isAnnotationPresent(annotation)) {
res = supertype.getAnnotation(annotation);
break;
}
supertype = supertype.getSuperclass();
}
}
return res;
}
/**
* @param type type to examine
* @param annotation annotation which must be found on target annotation
* @param <T> annotation type
* @return annotation annotated with provided annotation type or null if not found
*/
public static <T extends Annotation> Annotation getAnnotatedAnnotation(
final Class<?> type, final Class<T> annotation) {
Annotation res = null;
if (!Modifier.isAbstract(type.getModifiers())) {
Class<?> supertype = type;
while (supertype != null && Object.class != supertype) {
for (Annotation ann : supertype.getAnnotations()) {
if (ann.annotationType().isAnnotationPresent(annotation)) {
res = ann;
break;
}
}
if (res != null) {
break;
}
supertype = supertype.getSuperclass();
}
}
return res;
}
/**
* @param type type to check
* @param required required marker superclass or interface
* @return if type implements interface or extends required type and type is not abstract
*/
public static boolean is(final Class<?> type, final Class<?> required) {
return !Modifier.isAbstract(type.getModifiers()) && required.isAssignableFrom(type);
}
/**
* Utility call to prettify logs.
*
* @param installer installer class
* @return installer name to use in logs
*/
public static String getInstallerExtName(final Class<? extends FeatureInstaller> installer) {
return installer.getSimpleName().replace("Installer", "").toLowerCase();
}
/**
* @param type examining type
* @param name method name
* @param args method argument types
* @return found method
*/
@SafeVarargs
public static Method findMethod(final Class<?> type, final String name, final Class<Object>... args) {
try {
return type.getMethod(name, args);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(String.format("Failed to obtain method '%s#%s(%s)' of class ",
type, name, Arrays.toString(args)), e);
}
}
/**
* @param method method to call
* @param instance object instance to call method on
* @param args optional arguments
* @param <T> expected type of execution result
* @return method execution result
*/
@SuppressWarnings("unchecked")
public static <T> T invokeMethod(final Method method, final Object instance, final Object... args) {
final boolean acc = method.isAccessible();
method.setAccessible(true);
try {
return (T) method.invoke(instance, args);
} catch (Exception e) {
throw new IllegalStateException(String.format("Failed to invoke method '%s#%s(%s)'",
method.getDeclaringClass(), method.getName(), Arrays.toString(args)), e);
} finally {
method.setAccessible(acc);
}
}
/**
* Used to get correct object type even if it's guice proxy.
*
* @param object object to get class of
* @param <T> object type
* @return object class
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> getInstanceClass(final T object) {
final Class cls = object.getClass();
return cls.getName().contains("$$EnhancerByGuice") ? (Class<T>) cls.getSuperclass() : cls;
}
}