package at.ac.tuwien.infosys.jaxb;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.xml.bind.v2.model.core.EnumConstant;
/**
* Utility methods for handling annotation types.
* @author Waldemar Hummer
*/
@SuppressWarnings("all")
public final class AnnotationUtils {
public static final Logger logger = Logger
.getLogger(SchemagenUtil.class.getName());
public static final Map<Object,AnnotationInvocationHandler> PROXY_HANDLERS =
new IdentityHashMap<Object, AnnotationUtils.AnnotationInvocationHandler>();
/**
* Generic annotation handler to instantiate annotation objects
* via java.lang.reflect.Proxy mechanism.
*/
public static class AnnotationInvocationHandler implements InvocationHandler {
public final Class<?> annoClass;
public final Map<String, Object> annoValues = new HashMap<String,Object>();
public AnnotationInvocationHandler(Class<?> annoClass,
Map<String, Object> annoValues) {
this.annoClass = annoClass;
this.annoValues.putAll(annoValues);
}
public Object invoke(Object o, Method m, Object[] args)
throws Throwable {
if(m.getName().equals("toString") && !annoValues.containsKey(m.getName())) {
return "annotation @" + annoClass.getName() + "(" + annoValues + ")";
} else if(m.getName().equals("hashCode")) {
return annoClass.hashCode() + annoValues.hashCode();
} else if(!annoValues.containsKey(m.getName())) {
throw new IllegalAccessException("Annotation proxy for '" + this +
"' does not have method '" + m.getName() + "'. Existing values: " + annoValues);
}
return annoValues.get(m.getName());
}
@Override
public String toString() {
return "[AnnotationInvocationHandler @" +
annoClass.getName().replace("interface ", "") +
", " + annoValues + "]";
}
}
/**
* Private constructor.
*/
private AnnotationUtils() {
}
public static <T extends Annotation> Map<String,Object> getAnnotationValues(Class<T> annoClass, T anno) {
Map<String, Object> annoValues = new HashMap<String,Object>();
for(Method m : annoClass.getDeclaredMethods()) {
try {
Object value = null;
if(anno != null) {
try {
value = m.invoke(anno);
} catch (Exception e) {
/* this may occur sometimes with our annotation proxying mechanism.
* --> Fallback to default values. */
value = m.getDefaultValue();
}
} else {
value = m.getDefaultValue();
}
annoValues.put(m.getName(), value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return annoValues;
}
/**
* Utility method to instantiate a proxy for a given annotation
* class and annotation instance.
* @param annoClass
* @param annoValueDefaults
* @return
*/
public static <T extends Annotation> T createAnnotationProxy(
Class<T> annoClass, T annoValueDefaults) {
Map<String, Object> annoValues = getAnnotationValues(annoClass, annoValueDefaults);
return createAnnotationProxy(annoClass, annoValues, annoClass.getClassLoader());
}
/**
* Utility method to instantiate a proxy for a given annotation
* class, using a map of values used for the new annotation instance.
* Uses the classloader of annoClass (first parameter).
* @param annoClass
* @param annoValues
* @return
*/
public static <T extends Annotation> T createAnnotationProxy(
Class<T> annoClass, Map<String, Object> annoValues) {
Map<String, Object> annoDefaults = getAnnotationValues(annoClass, null);
annoDefaults.putAll(annoValues);
Map<String, Object> finalValues = annoDefaults;
return createAnnotationProxy(annoClass, finalValues, annoClass.getClassLoader());
}
/**
* Utility method to instantiate a proxy for a given annotation
* class, using a map of values used for the new annotation instance.
* @param annoClass
* @param annoValues
* @param cl
* @return
*/
public static <T extends Annotation> T createAnnotationProxy(
final Class<T> annoClass, final Map<String, Object> annoValues, ClassLoader cl) {
AnnotationInvocationHandler h = new AnnotationInvocationHandler(annoClass, annoValues);
T proxy = (T) Proxy.newProxyInstance(
cl, new Class<?>[] { annoClass }, h);
PROXY_HANDLERS.put(proxy, h);
return proxy;
}
/**
* Get @Documentation annotation instance from an enum constant.
* @param c
* @return
*/
public static <T, C, AnnoT extends Annotation> AnnoT getAnnoFromEnum(EnumConstant<T, C> c,
Class<AnnoT> annoClass) {
try {
Object enumClazz = c.getEnclosingClass().getClazz();
if(enumClazz instanceof Class<?>) {
Class<?> enumClass = (Class<?>)enumClazz;
Field field = enumClass.getField(c.getName());
return field.getAnnotation(annoClass);
} else {
/* looks like we are running in a schemagen context.. */
return SchemagenUtil.extractAnnotation(enumClazz, annoClass);
}
} catch (Exception e) {
logger.log(Level.WARNING, "Unable to get annotation '@" + annoClass + "' for enum constant " + c, e);
}
return null;
}
}