package org.xpect.util; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.Stack; import org.apache.log4j.Logger; import org.eclipse.emf.common.util.EList; import org.eclipse.xtext.common.types.JvmAnnotationReference; import org.eclipse.xtext.common.types.JvmAnnotationTarget; import org.eclipse.xtext.common.types.JvmAnnotationType; import org.eclipse.xtext.common.types.JvmAnnotationValue; import org.eclipse.xtext.common.types.JvmBooleanAnnotationValue; import org.eclipse.xtext.common.types.JvmDeclaredType; import org.eclipse.xtext.common.types.JvmEnumAnnotationValue; import org.eclipse.xtext.common.types.JvmEnumerationLiteral; import org.eclipse.xtext.common.types.JvmGenericArrayTypeReference; import org.eclipse.xtext.common.types.JvmIntAnnotationValue; import org.eclipse.xtext.common.types.JvmStringAnnotationValue; import org.eclipse.xtext.common.types.JvmType; import org.eclipse.xtext.common.types.JvmTypeAnnotationValue; import org.eclipse.xtext.common.types.JvmTypeReference; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Sets; public class JvmAnnotationUtil { private static Logger logger = Logger.getLogger(JvmAnnotationUtil.class); public static JvmAnnotationReference getAnnotation(JvmAnnotationTarget target, Class<? extends Annotation> ann) { if (target instanceof JvmDeclaredType) return getAnnotation((JvmDeclaredType) target, ann); for (JvmAnnotationReference ref : target.getAnnotations()) { JvmAnnotationType annotation = ref.getAnnotation(); if (annotation != null && !annotation.eIsProxy() && annotation.getQualifiedName().equals(ann.getName())) return ref; } return null; } public static JvmAnnotationReference getAnnotation(JvmDeclaredType type, Class<? extends Annotation> ann) { if (type == null || type.eIsProxy()) return null; Set<JvmDeclaredType> visited = Sets.newHashSet(type); Stack<JvmDeclaredType> unvisited = new Stack<JvmDeclaredType>(); unvisited.push(type); while (!unvisited.isEmpty()) { JvmDeclaredType current = unvisited.pop(); for (JvmAnnotationReference ref : current.getAnnotations()) { JvmAnnotationType annotation = ref.getAnnotation(); if (annotation != null && !annotation.eIsProxy() && annotation.getQualifiedName().equals(ann.getName())) return ref; } for (JvmTypeReference ref : current.getSuperTypes()) if (ref.getType() instanceof JvmDeclaredType && !ref.getType().eIsProxy()) { JvmDeclaredType t = (JvmDeclaredType) ref.getType(); if (!visited.add(t)) continue; unvisited.push(t); } } return null; } public static List<JvmDeclaredType> getAnnotationTypeValue(JvmAnnotationReference reference) { JvmTypeAnnotationValue value = getAnnotationValue(reference, null, JvmTypeAnnotationValue.class); List<JvmDeclaredType> result = Lists.newArrayList(); if (value != null) for (JvmTypeReference ref : value.getValues()) if (ref != null && !ref.eIsProxy() && ref.getType() instanceof JvmDeclaredType && !ref.getType().eIsProxy()) result.add((JvmDeclaredType) ref.getType()); return result; } public static List<JvmDeclaredType> getAnnotationTypeValue(JvmAnnotationTarget target, Class<? extends Annotation> ann) { return getAnnotationTypeValue(getAnnotation(target, ann)); } public static <A extends JvmAnnotationValue> A getAnnotationValue(JvmAnnotationReference reference, Class<A> c) { return getAnnotationValue(reference, null, c); } @SuppressWarnings("unchecked") public static <A extends JvmAnnotationValue> A getAnnotationValue(JvmAnnotationReference reference, String n, Class<A> c) { if (reference != null && !reference.eIsProxy()) for (JvmAnnotationValue value : reference.getValues()) if ((n == null || n.equals(value.getValueName())) && c.isInstance(value)) return (A) value; return null; } public static <A extends JvmAnnotationValue> A getAnnotationValue(JvmAnnotationTarget type, Class<? extends Annotation> ann, Class<A> c) { return getAnnotationValue(type, ann, null, c); } public static <A extends JvmAnnotationValue> A getAnnotationValue(JvmAnnotationTarget t, Class<? extends Annotation> a, String n, Class<A> c) { return getAnnotationValue(getAnnotation(t, a), n, c); } public static Annotation getJavaAnnotation(final JvmAnnotationReference ref) { final Class<?> annotation = IJavaReflectAccess.INSTANCE.getRawType(ref.getAnnotation()); if (annotation == null) return null; return (Annotation) Proxy.newProxyInstance(annotation.getClassLoader(), new Class<?>[] { annotation }, new InvocationHandler() { protected Object getValue(JvmAnnotationValue value) { if (value instanceof JvmStringAnnotationValue) return ((JvmStringAnnotationValue) value).getValues().get(0); if (value instanceof JvmBooleanAnnotationValue) return ((JvmBooleanAnnotationValue) value).getValues().get(0); if (value instanceof JvmIntAnnotationValue) return ((JvmIntAnnotationValue) value).getValues().get(0); if (value instanceof JvmTypeAnnotationValue) return IJavaReflectAccess.INSTANCE.getRawType(((JvmTypeAnnotationValue) value).getValues().get(0).getType()); if (value instanceof JvmEnumAnnotationValue) return getValue((JvmEnumAnnotationValue) value); throw new RuntimeException("Unhandled annotation value type: " + value.eClass().getName()); } private Object getValue(JvmEnumAnnotationValue annotationValue) { EList<JvmEnumerationLiteral> values = annotationValue.getValues(); JvmTypeReference type = annotationValue.getOperation().getReturnType(); if (type instanceof JvmGenericArrayTypeReference) { JvmType componentType = ((JvmGenericArrayTypeReference) type).getComponentType().getType(); Class<?> rawType = IJavaReflectAccess.INSTANCE.getRawType(componentType); Object[] result = (Object[]) Array.newInstance(rawType, values.size()); for (int i = 0; i < values.size(); i++) result[i] = getValue(values.get(i)); return result; } else { if (values.isEmpty()) return null; return getValue(values.get(0)); } } private Object getValue(JvmEnumerationLiteral value) { Field field = IJavaReflectAccess.INSTANCE.getField(value); if (field != null && field.isEnumConstant()) { try { return field.get(null); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } throw new RuntimeException(); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { for (JvmAnnotationValue value : ref.getValues()) if (value.getValueName().equals(method.getName())) return getValue(value); if ("toString".equals(method.getName())) return ref.toString(); if ("hashCode".equals(method.getName())) return ref.hashCode(); if ("equals".equals(method.getName())) return ref.equals(args[0]); if ("annotationType".equals(method.getName())) return annotation; throw new RuntimeException("method '" + method.getName() + "' not found in " + ref.getAnnotation().getIdentifier()); } }); } public static <T extends Annotation> T getJavaAnnotation(JvmAnnotationTarget type, Class<T> ann) { if (type == null || type.eIsProxy()) return null; for (JvmAnnotationReference ref : type.getAnnotations()) { JvmAnnotationType annotation = ref.getAnnotation(); if (annotation == null || annotation.eIsProxy()) continue; if (ann.getName().equals(annotation.getQualifiedName())) return ann.cast(getJavaAnnotation(ref)); } return null; } public static List<? extends Annotation> getJavaAnnotationsViaMetaAnnotation(JvmAnnotationTarget type, Class<? extends Annotation> ann) { if (type == null || type.eIsProxy()) return Collections.emptyList(); List<Annotation> result = Lists.newArrayList(); for (JvmAnnotationReference ref : type.getAnnotations()) { JvmAnnotationType annotation = ref.getAnnotation(); if (annotation == null || annotation.eIsProxy()) continue; JvmAnnotationReference metaAnnotation = getAnnotation(annotation, ann); if (metaAnnotation == null || metaAnnotation.eIsProxy()) continue; Annotation param = getJavaAnnotation(ref); if (param != null) result.add(param); } return ImmutableList.copyOf(result); } public static boolean isAnnotatedWith(JvmAnnotationTarget target, Class<? extends Annotation> annotation) { for (JvmAnnotationReference ref : target.getAnnotations()) { JvmAnnotationType type = ref.getAnnotation(); if (type != null && !type.eIsProxy() && type.getQualifiedName().equals(annotation.getName())) return true; } return false; } public static <T> T newInstanceFromAnnotation(JvmAnnotationTarget type, Class<T> expected, Class<? extends Annotation> ann) { List<T> list = newInstancesViaAnnotation(type, expected, ann); if (list.size() == 1) return list.get(0); return null; } @SuppressWarnings("unchecked") public static <T> List<T> newInstancesViaAnnotation(JvmAnnotationTarget type, Class<T> expected, Class<? extends Annotation> ann) { if (type == null || type.eIsProxy()) return Collections.emptyList(); JvmTypeAnnotationValue value = getAnnotationValue(type, ann, JvmTypeAnnotationValue.class); if (value == null || value.getValues().isEmpty()) return Collections.emptyList(); List<T> result = Lists.newArrayList(); for (JvmTypeReference ref : value.getValues()) if (ref != null && !ref.eIsProxy() && ref.getType() != null && !ref.getType().eIsProxy()) { Class<?> adapter = IJavaReflectAccess.INSTANCE.getRawType(ref.getType()); if (adapter != null) { try { Object instance = adapter.newInstance(); if (expected.isInstance(instance)) result.add((T) instance); } catch (InstantiationException e) { logger.error(e); } catch (IllegalAccessException e) { logger.error(e); } } } return result; } @SuppressWarnings("unchecked") public static <T> List<T> newInstancesViaMetaAnnotation(JvmAnnotationTarget type, Class<T> expected, Class<? extends Annotation> ann) { if (type == null || type.eIsProxy()) return Collections.emptyList(); List<T> result = Lists.newArrayList(); REF: for (JvmAnnotationReference ref : type.getAnnotations()) { if (ref.getAnnotation() == null || ref.getAnnotation().eIsProxy()) continue; JvmTypeAnnotationValue value = getAnnotationValue(ref.getAnnotation(), ann, JvmTypeAnnotationValue.class); if (value == null || value.getValues().isEmpty()) continue; for (JvmTypeReference val : value.getValues()) if (val != null && !val.eIsProxy() && val.getType() != null && !val.getType().eIsProxy()) { Class<?> adapter = IJavaReflectAccess.INSTANCE.getRawType(val.getType()); if (adapter != null) { try { Annotation param = getJavaAnnotation(ref); if (param == null) { logger.error("Coult not load " + ref); continue REF; } for (Constructor<?> cons : adapter.getConstructors()) { Class<?>[] parameterTypes = cons.getParameterTypes(); if (parameterTypes.length == 1 && parameterTypes[0].isInstance(param)) { Object instance = cons.newInstance(param); if (expected.isInstance(instance)) result.add((T) instance); break; } } } catch (InstantiationException e) { logger.error(e); } catch (IllegalAccessException e) { logger.error(e); } catch (SecurityException e) { logger.error(e); } catch (IllegalArgumentException e) { logger.error(e); } catch (InvocationTargetException e) { logger.error(e); } } } } return result; } }