package org.castor.jdo.jpa.processors; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.WildcardType; import org.castor.core.annotationprocessing.AnnotationTargetException; /** * Tool class that offers methods to retrieve information from * {@link AnnotatedElement}s (Methods or Fields). * * @author Peter Schmidt * @version 12.02.2009 * */ public final class ReflectionsHelper { /** * Private Constructor to prevent instantiation. */ private ReflectionsHelper() { } /** * Get the (raw) type of collection from the annotations target and optional * check for JPA 1.0 restrictions (only Collection, Map, List and Set are * allowed). * * @param target * The ManyToMany annotations target. * @param jpaTypesOnly * If set to true only Collection types of JPA 1.0 are allowed. * Others will lead to a thrown Exception. * @return The raw collection type. * @throws AnnotationTargetException * if the raw collection type can not be inferred from the type * definition or if the type is not supported by JPA 1.0 and * jpaTypesOnly was set to true. */ public static Class<?> getCollectionType(final AnnotatedElement target, final boolean jpaTypesOnly) throws AnnotationTargetException { Class<?> collectionType = null; Type fieldType; if (target instanceof Field) { fieldType = ((Field) target).getGenericType(); } else { fieldType = ((Method) target).getGenericReturnType(); } if (fieldType instanceof ParameterizedType) { Type rawType = ((ParameterizedType) fieldType).getRawType(); if (rawType instanceof Class<?>) { collectionType = (Class<?>) rawType; } else { String message = "Can not infer raw type of generic type definition for field " + ((Member) target).getName() + ". Raw type is not a Class object."; throw new AnnotationTargetException(message); } } else if (fieldType instanceof Class<?>) { collectionType = (Class<?>) fieldType; } String collectionTypeName = collectionType.getSimpleName(); if (jpaTypesOnly) { if ((!collectionTypeName.equals("Collection")) && (!collectionTypeName.equals("Map")) && (!collectionTypeName.equals("List")) && (!collectionTypeName.equals("Set"))) { throw new AnnotationTargetException( collectionTypeName + " is not supported by JPA 1.0! Only Collection, Set, " + "List or Map are allowed types of OneToMany properties!"); } } return collectionType; } /** * Get the target entity information from the target (necessary if it was * not specified by the annotation itself). * * @param target * The *ToMany annotations target. This method will fail if * target is not of Type {@link Field} or {@link Method}! * @return the relations target entity inferred by the targets generic * definition or null if no generic definition was found at all. * @throws AnnotationTargetException * if the generic definition is not sufficient */ public static Class<?> getTargetEntityFromGenerics( final AnnotatedElement target) throws AnnotationTargetException { Type fieldType = null; if (target instanceof Field) { fieldType = ((Field) target).getGenericType(); } else if (target instanceof Method) { fieldType = ((Method) target).getGenericReturnType(); } else { throw new IllegalArgumentException( "Can only read generic definitions from Methods or Fields."); } Class<?> targetEntity = null; if (fieldType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) fieldType) .getActualTypeArguments(); Type targetType; // check wildcard generic => use upper bound if (actualTypeArguments[0] instanceof WildcardType) { targetType = ((WildcardType) actualTypeArguments[0]) .getUpperBounds()[0]; } else { targetType = actualTypeArguments[0]; } if (targetType instanceof Class<?>) { targetEntity = (Class<?>) targetType; } else { // Error => can not infer targetEntity from generic // definition String message = "Can not infer target entity for ManyToOne relation on " + ((Member) target).getName() + " - use simplier generics or specify targetEntity!"; throw new AnnotationTargetException(message); } } else { return null; } return targetEntity; } /** * Little helper to get a field (bean property) name from a getter method. <br/> * Example: <code>getFoo() => "foo"</code><br/> * Example: <code>isBar() => "bar"</code> * * @param getter * the {@link Method} to analyse. * @return the field name determined by the methods name or NULL if the * method is not a getter (start with "get" or "is"). */ public static String getFieldnameFromGetter(final Method getter) { String[] getterPrefixes = {"get", "is"}; String methodName = getter.getName(); String fieldName = null; for (String prefix : getterPrefixes) { if (methodName.startsWith(prefix)) { fieldName = methodName.substring(prefix.length()); } } if (fieldName == null) { return null; } fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); return fieldName; } /** * Return a setter {@link Method} for a given getter {@link Method}. * * @param getter * The getter {@link Method} * @return The setter Method for the given getter Method. * @throws NoSuchMethodException * If the setter method does not exist * @throws SecurityException * If the setter method is not accessible */ public static Method getSetterMethodFromGetter(final Method getter) throws SecurityException, NoSuchMethodException { if (!isGetter(getter)) { throw new IllegalArgumentException("Method is not a getter method!"); } String[] prefixes = {"get", "is"}; String setterName; for (String prefix : prefixes) { if (getter.getName().startsWith(prefix)) { String name = getter.getName().substring(prefix.length()); setterName = "set" + name; return getter.getDeclaringClass().getDeclaredMethod(setterName, getter.getReturnType()); } } throw new IllegalArgumentException( "Method name does not start with 'get' or 'is'!"); } /** * Convenience method to check whether a {@link Method} is a getter method, * i.e. starts with "get" or "is". * * @param method * the {@link Method} to check. * @return true if the methods name starts with "get" or "is" (Java Beans * convention). */ public static boolean isGetter(final Method method) { if (method.getName().startsWith("get") || method.getName().startsWith("is")) { return true; } return false; } }