package tc.oc.commons.core.reflect;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.function.Predicate;
import com.google.common.cache.LoadingCache;
import tc.oc.commons.core.util.CacheUtils;
import tc.oc.commons.core.util.ExceptionUtils;
public final class Annotations {
private Annotations() {}
public static Predicate<AnnotatedElement> annotatedWith(Class<? extends Annotation> annotation) {
return element -> element.isAnnotationPresent(annotation);
}
private static final LoadingCache<Class<? extends Annotation>, Class<? extends Annotation>> annotationTypeCache = CacheUtils.newCache(type -> {
Class<? extends Annotation> annotationType = null;
for(Class<?> cls = type; cls != null; cls = cls.getSuperclass()) {
for(Class<?> iface : cls.getInterfaces()) {
if(iface.isAnnotation()) {
if(annotationType != null) {
throw new IllegalStateException("Multiple annotation types found for " + type.getSimpleName() +
": " + annotationType.getName() + " and " + iface.getName());
}
annotationType = (Class<? extends Annotation>) iface;
}
}
}
if(annotationType == null) {
throw new IllegalStateException("Can't find annotation type of " + type.getName());
}
return annotationType;
});
public static Class<? extends Annotation> annotationType(Class<? extends Annotation> type) {
return annotationTypeCache.getUnchecked(type);
}
/**
* Implements {@link Annotation#equals(Object)} as specified
*/
public static boolean equals(Annotation a, Object obj) {
if(!(obj instanceof Annotation)) return false;
final Annotation b = (Annotation) obj;
if(!a.annotationType().equals(b.annotationType())) return false;
try {
for(Method method : a.annotationType().getDeclaredMethods()) {
method.setAccessible(true);
if(!Objects.equals(method.invoke(a), method.invoke(b))) return false;
}
} catch(ReflectiveOperationException e) {
throw ExceptionUtils.propagate(e);
}
return true;
}
/**
* Implements {@link Annotation#hashCode()} as specified
*/
public static int hashCode(Annotation annotation) {
int hashCode = 0;
try {
for(Method method : annotation.annotationType().getDeclaredMethods()) {
method.setAccessible(true);
hashCode += (127 * method.getName().hashCode()) ^ Objects.hashCode(method.invoke(annotation));
}
} catch(ReflectiveOperationException e) {
throw ExceptionUtils.propagate(e);
}
return hashCode;
}
public static String toString(Annotation annotation) {
String text = "@" + annotation.annotationType().getSimpleName();
boolean empty = false;
try {
for(Method method : annotation.annotationType().getDeclaredMethods()) {
if(empty) {
empty = false;
text += "(";
} else {
text += " ";
}
text += method.getName() + "=" + String.valueOf(method.invoke(annotation));
}
} catch(ReflectiveOperationException e) {
throw ExceptionUtils.propagate(e);
}
if(!empty) {
text += ")";
}
return text;
}
}