/* * Copyright (c) 2009-2010 Clark & Parsia, LLC. <http://www.clarkparsia.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.clarkparsia.empire.util; import com.clarkparsia.empire.annotation.RdfProperty; import com.clarkparsia.empire.annotation.InvalidRdfException; import com.clarkparsia.empire.annotation.RdfId; import com.clarkparsia.empire.annotation.RdfsClass; import com.clarkparsia.empire.EmpireOptions; import com.clarkparsia.empire.SupportsRdfId; import com.clarkparsia.empire.EmpireGenerated; import com.complexible.common.util.PrefixMapping; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import javax.persistence.FetchType; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.ManyToOne; import javax.persistence.ManyToMany; import javax.persistence.CascadeType; import javax.persistence.MappedSuperclass; import javax.persistence.Transient; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.AccessibleObject; import java.lang.annotation.Annotation; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.ArrayList; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.LinkedHashSet; import java.util.Date; import java.util.Arrays; import java.util.Map; import java.util.HashMap; /** * <p>Some utility methods which use the Java reflect stuff to do a lot of the runtime accessing of fields and methods * on objects used to transform between Java and RDF.</p> * * @author Michael Grove * @since 0.5.1 * @version 0.7 */ public final class BeanReflectUtil { /** * Small cache so we don't have to recalcuation information via java.lang.reflect every time, which can be expensive */ private final static Map<Class<?>, BeanReflectCacheEntry> cache = new HashMap<Class<?>, BeanReflectCacheEntry>(); /** * Cannot create instances of this class */ private BeanReflectUtil() { } /** * More or less a more robust version of Class.forName. Attempts to get around custom class loaders and * different class loaders in the current Thread context by trying *all* of them to load a class. * @param theName the class to load * @return the loaded class * @throws ClassNotFoundException if the class is not found. */ public static Class<?> loadClass(String theName) throws ClassNotFoundException { try { return Class.forName(theName); } catch (ClassNotFoundException e) { try { return Thread.currentThread().getContextClassLoader().loadClass(theName); } catch (ClassNotFoundException ex) { return ClassLoader.getSystemClassLoader().loadClass(theName); } } } /** * Return the field on the class which has an {@link RdfId} annotation. If the fields on the class do not have the * annotation, the super class will be checked. * @param theClass the class * @return the field with an RdfId annotation, or null if one is not found * @throws InvalidRdfException thrown if there are multiple fields with the RdfId annotation */ public static Field getIdField(Class theClass) throws InvalidRdfException { Field aIdField = null; for (Field aField : getAllDeclaredFields(theClass)) { if (aField.getAnnotation(RdfId.class) != null) { if (aIdField != null) { throw new InvalidRdfException("Cannot have multiple id properties"); } else { aIdField = aField; } } } if (aIdField == null && shouldInspectSuperClass(theClass)) { aIdField = getIdField(theClass.getSuperclass()); } return aIdField; } /** * Return the given annotation from the class. If the class does not have the annotation, it's parent class and any * interfaces will also be checked. * @param theClass the class to inspect * @param theAnnotation the annotation to retrieve * @return the class's annotation, or it's "inherited" annotation, or null if the annotation cannot be found. */ public static <T extends Annotation> T getAnnotation(Class<?> theClass, Class<T> theAnnotation) { BeanReflectCacheEntry entry = cache.get(theClass); if (entry == null) { entry = new BeanReflectCacheEntry(); cache.put(theClass, entry); } if (entry.mAnnotations.containsKey(theAnnotation)) { return (T) entry.mAnnotations.get(theAnnotation); } T aAnnotation = null; if (theClass.isAnnotationPresent(theAnnotation)) { aAnnotation = theClass.getAnnotation(theAnnotation); } else { if (shouldInspectSuperClass(theClass)) { aAnnotation = getAnnotation(theClass.getSuperclass(), theAnnotation); } if (aAnnotation == null) { for (Class aInt : theClass.getInterfaces()) { aAnnotation = getAnnotation(aInt, theAnnotation); if (aAnnotation != null) { break; } } } } entry.mAnnotations.put(theAnnotation, aAnnotation); return aAnnotation; } /** * Return whether or not it is ok to inspect the super class of the provided class for persistence information. This can be done * if there is a super class, and if so, if that superclass is either annotated with the JPA annotation @MappedSuperclass OR the * *current* class is an instance of EmpireGenerated. In the latter case, this means that the superclass is the actual persistent * object type as EmpireGenerated is just a stub created internally for some bookkeeping purposes. * * @param theClass the class * @return true if its ok to inspect the superclass for persistence information, false otherwise */ private static boolean shouldInspectSuperClass(final Class theClass) { return theClass.getSuperclass() != null && (hasAnnotation(theClass.getSuperclass(), MappedSuperclass.class) || EmpireGenerated.class.isAssignableFrom(theClass)); } /** * Returns a Method on the object with the given annotation * @param theClass the class whose methods should be scanned * @param theAnnotation the annotation to look for * @return a method with the given annotation, or null if one is not found. */ public static Collection<Method> getAnnotatedMethods(final Class theClass, final Class<? extends Annotation> theAnnotation) { Collection<Method> aMethods = new HashSet<Method>(); for (Method aMethod : theClass.getMethods()) { if (aMethod.getAnnotation(theAnnotation) != null) { aMethods.add(aMethod); } } if (shouldInspectSuperClass(theClass)) { aMethods.addAll(getAnnotatedMethods(theClass.getSuperclass(), theAnnotation)); } for (Class aInterface : theClass.getInterfaces()) { aMethods.addAll(getAnnotatedMethods(aInterface, theAnnotation)); } return aMethods; } /** * Return whether or not the class has the given annotation. If the class itself does not have the annotation, * it's super class and the interfaces it implements are checked. * @param theClass the class to check * @param theAnnotation the annotation to look for * @return if the class has the annotation, or one of its parents does and it "inherited" the annotation, false otherwise */ public static boolean hasAnnotation(Class theClass, Class<? extends Annotation> theAnnotation) { return getAnnotation(theClass, theAnnotation) != null; } /** * Toggle the accessibility of the parameter, which should be an instance of {@link java.lang.reflect.Field} or {@link java.lang.reflect.Method}. If * not, then nothing will occur. * @param theAccessor the object toggle accessibility of * @param theAccess the new accessibility level, true to make it accessible, false otherwise * @return the old accessibility of the accessor */ public static boolean setAccessible(AccessibleObject theAccessor, boolean theAccess) { boolean aOldAccess = theAccessor.isAccessible(); theAccessor.setAccessible(theAccess); return aOldAccess; } /** * Set the specified value on the the given object using the provided accessor, either a {@link Field} or a * {@link Method}. If the accessor is neither a field or a method, then no operation takes place. This method * will toggle the accessibility of the accessor so an IllegalAccessException is not thrown. * @param theAccessor the accessor, a Field or a Method * @param theObj the object to set the value on * @param theValue the value to set via the aceessor * @throws java.lang.reflect.InvocationTargetException thrown if there was an error setting the value on the field/method */ public static void safeSet(AccessibleObject theAccessor, Object theObj, Object theValue) throws InvocationTargetException { boolean aOldAccess = setAccessible(theAccessor, true); try { if (theAccessor instanceof Field) { ((Field) theAccessor).set(theObj, theValue); } else if (theAccessor instanceof Method) { ((Method) theAccessor).invoke(theObj, theValue); } } catch (IllegalAccessException e) { throw new RuntimeException(e); } finally { setAccessible(theAccessor, aOldAccess); } } /** * Set the specified value on the the given object using the provided accessor, either a {@link Field} or a * {@link Method}. If the accessor is neither a field or a method, then no operation takes place. * @param theAccessor the accessor, a Field or a Method * @param theObj the object to set the value on * @param theValue the value to set via the aceessor * @throws java.lang.reflect.InvocationTargetException thrown if there was an error setting the value on the field/method * @throws IllegalAccessException thrown if you cannot access the field/method */ public static void set(AccessibleObject theAccessor, Object theObj, Object theValue) throws InvocationTargetException, IllegalAccessException { if (theAccessor instanceof Field) { ((Field) theAccessor).set(theObj, theValue); } else if (theAccessor instanceof Method) { ((Method) theAccessor).invoke(theObj, theValue); } } /** * Return the list of annotated setter methods on the class. Anything with an {@link com.clarkparsia.empire.annotation.RdfProperty} annotation * that returns a (non-void) value will be found by this method. When the infer flag is set to true, we will * inspect the getter methods, if there is a annotated getter, but no annotated setter for a property, we'll infer * the annotation for the property so you get the expected paired behavior of the annotation. * @param theClass the class * @param theInfer true to infer setters from annotated getters, false otherwise. * @return the list of annotated setter methods */ public static Collection<Method> getAnnotatedSetters(Class theClass, boolean theInfer) { BeanReflectCacheEntry entry = cache.get(theClass); if (entry == null) { entry = new BeanReflectCacheEntry(); cache.put(theClass, entry); } if (theInfer && entry.mInferredSetters != null) { return entry.mInferredSetters; } else if (!theInfer && entry.mSetters != null) { return entry.mSetters; } Collection<Method> aMethods = new HashSet<Method>(); for (Method aMethod : theClass.getDeclaredMethods()) { if (aMethod.getAnnotation(RdfProperty.class) != null && (aMethod.getGenericReturnType().equals(Void.class) || aMethod.getGenericReturnType().toString().equals("void")) && aMethod.getParameterTypes().length == 1) { aMethods.add(aMethod); } } // now let's infer setters. if you only applied the annotation to the getter, we'll carry it over to the // setter if one exists. if (theInfer) { Collection<Method> aGetters = getAnnotatedGetters(theClass, false); for (Method aGetterMethod : aGetters) { String aSetterName = aGetterMethod.getName().replaceFirst("get", "set"); boolean tryIs = false; try { Method aSetter = theClass.getMethod(aSetterName, aGetterMethod.getReturnType()); // so we have a setter for a annotated getter, so here we will add this to the list // of setters to infer the annotation on the setter even though its not explicit if (!aMethods.contains(aSetter)) { aMethods.add(aSetter); } } catch (NoSuchMethodException e) { // no biggie, setter doesn't exist, we'll just move on, try looking for isXXXX tryIs = true; } if (tryIs) { try { Method aSetter = theClass.getMethod(aGetterMethod.getName().replaceFirst("is", "set"), aGetterMethod.getReturnType()); // so we have a setter for a annotated getter, so here we will add this to the list // of setters to infer the annotation on the setter even though its not explicit if (!aMethods.contains(aSetter)) { aMethods.add(aSetter); } } catch (NoSuchMethodException e) { // no biggie, setter doesn't exist, we'll just move on } } } } if (shouldInspectSuperClass(theClass)) { aMethods.addAll(getAnnotatedSetters(theClass.getSuperclass(), theInfer)); } for (Class aInterface : theClass.getInterfaces()) { aMethods.addAll(getAnnotatedSetters(aInterface, theInfer)); } if (theInfer) { entry.mInferredSetters = aMethods; } else { entry.mSetters = aMethods; } return aMethods; } /** * Return the list of annotated get style methods from the class. Anything w/ an {@link RdfProperty} annotation * that does not return a value will be found by this method. When the inter flag is set to true, we will inspect * the setter methods, if there is an annotated setter, but no annotatted getter for a property, we'll infer * the annotation for the property so you get the expected paired behavior of the annotation. * @param theClass the class * @param theInfer true to infer getters from annotated setters, false otherwise. * @return the list of annotated get methods */ public static Collection<Method> getAnnotatedGetters(Class theClass, boolean theInfer) { BeanReflectCacheEntry entry = cache.get(theClass); if (entry == null) { entry = new BeanReflectCacheEntry(); cache.put(theClass, entry); } if (theInfer && entry.mInferredGetters != null) { return entry.mInferredGetters; } else if (!theInfer && entry.mGetters != null) { return entry.mGetters; } Map<String, Method> aMethods = new HashMap<String, Method>(); for (Method aMethod : theClass.getDeclaredMethods()) { if (aMethod.getAnnotation(RdfProperty.class) != null && !aMethod.getGenericReturnType().equals(Void.class) && aMethod.getParameterTypes().length == 0) { if (!aMethods.containsKey(aMethod.getName())) { aMethods.put(aMethod.getName(), aMethod); } } } if (theInfer) { Collection<Method> aSetters = getAnnotatedSetters(theClass, false); for (Method aSetterMethod : aSetters) { String aGetterName = aSetterMethod.getName().replaceFirst("set", "get"); boolean tryIs = false; try { Method aGetter = theClass.getMethod(aGetterName); // so we have a setter for a annotated getter, so here we will add this to the list // of setters to infer the annotation on the setter even though its not explicit if (!aMethods.containsKey(aGetter.getName())) { aMethods.put(aGetter.getName(), aGetter); } } catch (NoSuchMethodException e) { // no biggie, setter doesn't exist, we'll just move on tryIs = true; } if (tryIs) { // didn't find a getter w/ the getXXX form, so lets try isXXXX which I think is normal (valid) // convention for boolean properties aGetterName = aSetterMethod.getName().replaceFirst("set", "is"); try { Method aGetter = theClass.getMethod(aGetterName); // so we have a setter for a annotated getter, so here we will add this to the list // of setters to infer the annotation on the setter even though its not explicit if (!aMethods.containsKey(aGetter.getName())) { aMethods.put(aGetter.getName(), aGetter); } } catch (NoSuchMethodException e) { // no biggie, setter doesn't exist, we'll just move on } } } } if (shouldInspectSuperClass(theClass)) { for (Method m : getAnnotatedGetters(theClass.getSuperclass(), theInfer)) { if (!aMethods.containsKey(m.getName())) { aMethods.put(m.getName(), m); } } } for (Class aInterface : theClass.getInterfaces()) { for (Method m : getAnnotatedGetters(aInterface, theInfer)) { if (!aMethods.containsKey(m.getName())) { aMethods.put(m.getName(), m); } } } if (theInfer) { entry.mInferredGetters = aMethods.values(); } else { entry.mGetters = aMethods.values(); } return aMethods.values(); } /** * Return a list of all the fields on the given class which are annotated with the {@link RdfProperty} annotation. * @param theClass the class to scan * @return the list of annotated fields on the class */ public static Collection<Field> getAnnotatedFields(Class theClass) { BeanReflectCacheEntry entry = cache.get(theClass); if (entry == null) { entry = new BeanReflectCacheEntry(); cache.put(theClass, entry); } if (entry.mFields != null) { return entry.mFields; } Collection<Field> aProps = new HashSet<Field>(); for (Field aField : getAllDeclaredFields(theClass)) { if (aField.getAnnotation(Transient.class) != null || javassist.util.proxy.ProxyObject.class.isAssignableFrom(theClass)) { continue; } if (aField.getAnnotation(RdfProperty.class) != null) { aProps.add(aField); } else if (!EmpireOptions.USE_LEGACY_TRANSIENT_BEHAVIOR && !aField.getType().isAssignableFrom(SupportsRdfId.class)) { // we want to auto-include fields not marked w/ transient, but lacking an @RdfProperty assertion when this // mode is disabled, however, we always want to ignore the implementation/support of SupportsRdfId from concrete classes. aProps.add(aField); } } if (shouldInspectSuperClass(theClass)) { aProps.addAll(getAnnotatedFields(theClass.getSuperclass())); } for (Class aInterface : theClass.getInterfaces()) { aProps.addAll(getAnnotatedFields(aInterface)); } entry.mFields = aProps; return aProps; } /** * Get the value on the object from the specified accessor, either a {@link Field} or {@link Method}. This method * will toggle the accessibility of the accessor so an IllegalAccessException is not thrown. * @param theAccess the accessor * @param theObject the object to get a value from * @return the value of the field or (getter) method on an object. * @throws InvocationTargetException thrown if there is an error while invoking the getter method. Usually because * it's not a bean-style getter w/ no parameters. */ public static Object safeGet(AccessibleObject theAccess, Object theObject) throws InvocationTargetException { boolean aOldAccess = setAccessible(theAccess, true); try { if (theAccess instanceof Field) { return ((Field)theAccess).get(theObject); } else if (theAccess instanceof Method) { return ((Method)theAccess).invoke(theObject); } else { return null; } } catch (IllegalAccessException ex) { // we should not get this since we toggled the access, if we do, something foul is afoot. throw new RuntimeException(ex); } finally { setAccessible(theAccess, aOldAccess); } } /** * Get the value on the object from the specified accessor, either a {@link Field} or {@link Method} * @param theAccess the accessor * @param theObject the object to get a value from * @return the value of the field or (getter) method on an object. * @throws IllegalAccessException thrown if you cannot access the field or method * @throws InvocationTargetException thrown if there is an error while invoking the getter method. Usually because * it's not a bean-style getter w/ no parameters. */ public static Object get(AccessibleObject theAccess, Object theObject) throws IllegalAccessException, InvocationTargetException { if (theAccess instanceof Field) { return ((Field)theAccess).get(theObject); } else if (theAccess instanceof Method) { return ((Method)theAccess).invoke(theObject); } else { return null; } } /** * Given a class and an accessor, either a {@link Field} or a {@link Method}, try to return a setter which can * set the value of the property represented by the accessor. * @param theClass the class * @param theAccess the accessor * @return a setter object, or null if one is not found. */ public static AccessibleObject asSetter(final Class<?> theClass, final AccessibleObject theAccess) { // use the class provided ... Class<?> aClass = theClass; // unless its an instance of EmpireGenerated, in which case we want to directly inspect the superclass // since that is the actual object and not our Empire stub if (EmpireGenerated.class.equals(theClass)) { aClass = theClass.getSuperclass(); } // field can be used for access just fine if (theAccess instanceof Field && getAllDeclaredFields(theClass).contains(theAccess)) { return theAccess; } else { // try to find a setter method StringBuffer aName = new StringBuffer(); if (theAccess instanceof Field) { // this probably cannot happen, this would mean the accessor is a field, but not in the list of // declared fields, which i dont think will ever occur. so this is probably overkill. // Update: Yes, this can actually happen. Declared fields do not include inherited fields, // but they can still be accessor (even more, if the field was annotated in one class, and then Empire // generated a subclass implementation for that class ...) aName.append(((Field)theAccess).getName()); aName.setCharAt(0, String.valueOf(aName.charAt(0)).toUpperCase().charAt(0)); } else if (theAccess instanceof Method) { aName.append(((Method)theAccess).getName()); String aPrefix = aName.substring(0,3); if (aPrefix.startsWith("get")) { aName.delete(0, 3); } else if (aPrefix.startsWith("is")) { aName.delete(0, 2); } } aName.insert(0, "set"); for (Method aMethod : aClass.getMethods()) { if (aMethod.getName().equals(aName.toString())) { return aMethod; } } // ok, so no method by the name of the setter. // so lets rip off the set prefix, toLower the first letter, and search for a field with that name aName.delete(0, 3); aName.setCharAt(0, String.valueOf(aName.charAt(0)).toLowerCase().charAt(0)); try { return aClass.getDeclaredField(aName.toString()); } catch (NoSuchFieldException e) { return null; } } } public static Set<Field> getAllDeclaredFields(final Class<?> theClass) { Set<Field> aFields = Sets.newHashSet(); Class<?> aClass = theClass; while (aClass != null) { aFields.addAll(Sets.newHashSet(aClass.getDeclaredFields())); aClass = aClass.getSuperclass(); } return aFields; } /** * Return whether or not the array contains the object * @param theArray the array to search * @param theObj the object to look for * @param <T> the type of the objects in the array * @return true if the element was found, false otherwise */ private static <T> boolean arrayContains(T[] theArray, T theObj) { if (theObj == null) { return false; } for (T aObj : theArray) { if (aObj.equals(theObj)) { return true; } } return false; } /** * Create an instance of the specifiec collection. If the type cannot be instantiated directly, such as List, or Set, * we'll try and figure out which type of collection is desired and return the standard implementation for * that type, for example ArrayList or HashSet. * @param theValueType the type of collection to instantiate * @return the instantiated collection * @throws RuntimeException if there is no way to instantiate any matching collection */ public static Collection<Object> instantiateCollectionFromField(Class theValueType) { try { // try creating a new instance. this will work if they've specified a concrete type return (Collection<Object>) theValueType.newInstance(); } catch (Throwable e) { // TODO: make this less brittle -- should we have some sort of facade collection or something in front // that we generate at runtime? or is there a better way to handle this situation? // if the above failed, that means the type of the field is something like List, or Set, which is not // directly instantiable. If it's a known type, we'll hand instantiate something here. if (List.class.isAssignableFrom(theValueType)) { return new ArrayList<Object>(); } else if (Set.class.isAssignableFrom(theValueType)) { if (SortedSet.class.isAssignableFrom(theValueType)) { return new TreeSet<Object>(); } else { return new LinkedHashSet<Object>(); } } else if (Collection.class.equals(theValueType)) { return new LinkedHashSet<Object>(); } else { // last option is Map, but i dunno what the hell to do in that case, it doesn't map to our use here. throw new RuntimeException("Unknown or unsupported collection type for a field: " + theValueType); } } } /** * Return whether or not the given object is a Java primitive type (String is included as a primitive). * @param theObj the object * @return true if its a primitive, false otherwise. */ public static boolean isPrimitive(Object theObj) { return (Boolean.class.isInstance(theObj) || Integer.class.isInstance(theObj) || Long.class.isInstance(theObj) || Short.class.isInstance(theObj) || Double.class.isInstance(theObj) || Float.class.isInstance(theObj) || Date.class.isInstance(theObj) || String.class.isInstance(theObj) || Character.class.isInstance(theObj)); } /** * Return whether or not the given class represents a Java primitive type (String is included as a primitive). * @param theObj the object * @return true if its a primitive, false otherwise. */ public static boolean isPrimitive(Class theObj) { return (Boolean.class.equals(theObj) || Integer.class.equals(theObj) || Long.class.equals(theObj) || Short.class.equals(theObj) || Double.class.equals(theObj) || Float.class.equals(theObj) || Date.class.equals(theObj) || String.class.equals(theObj) || Character.class.equals(theObj)); } /** * Return whether or not the accessor is marked with a {@link FetchType#LAZY} annotation. If there is no * {@link OneToOne}, {@link OneToMany}, {@link ManyToOne}, {@link OneToOne}, or {@link ManyToMany} annotation, * or they do not specify a fetch type, the default value is assumed to be {@link FetchType#EAGER}. If the * provided accessor is not a Field or Method, the FetchType is also assumed to be EAGER. * @param theAccessor the accessor * @return true if the accessor is marked with {@link FetchType#LAZY}, false otherwise. */ public static boolean isFetchTypeLazy(Object theAccessor) { FetchType aFetchType = null; if (theAccessor instanceof AccessibleObject) { AccessibleObject aObject = (AccessibleObject) theAccessor; if (aObject instanceof Method) { Method aMethod = (Method)aObject; // if we got a setter, actually we should check that the corresponding // *getter* was annotated with one of the JPA "relationship" annotations if (aMethod.getName().startsWith("set") && (aMethod.getGenericReturnType().equals(Void.class) || aMethod.getGenericReturnType().toString().equals("void") && aMethod.getParameterTypes().length == 1)) { for (String prefix : new String[]{"get", "is"}) { String aGetterName = aMethod.getName().replaceFirst("set", prefix); try { aObject = (AccessibleObject)(aMethod.getDeclaringClass().getMethod(aGetterName)); break; } catch (NoSuchMethodException e) { // let it go } } } } if (aObject.getAnnotation(OneToMany.class) != null) { aFetchType = aObject.getAnnotation(OneToMany.class).fetch(); } else if (aObject.getAnnotation(OneToOne.class) != null) { aFetchType = aObject.getAnnotation(OneToOne.class).fetch(); } else if (aObject.getAnnotation(ManyToOne.class) != null) { aFetchType = aObject.getAnnotation(ManyToOne.class).fetch(); } else if (aObject.getAnnotation(ManyToMany.class) != null) { aFetchType = aObject.getAnnotation(ManyToMany.class).fetch(); } } if (aFetchType == null) { aFetchType = FetchType.EAGER; } return aFetchType.equals(FetchType.LAZY); } /** * Return the value of the targetEntity for the accessor if it has a {@link OneToOne}, {@link OneToMany}, * {@link ManyToOne}, {@link OneToOne}, or {@link ManyToMany} annotation. This will return null if the accessor * is not an {@link AccessibleObject} or if it does not have one of the aforementioned annotations, or if the * targetEntity is not set for the annotation * @param theAccessor the accessor * @return the targetEntity for the accessor, or null if not specified. */ public static Class<?> getTargetEntity(Object theAccessor) { Class<?> aClass = null; if (theAccessor instanceof AccessibleObject) { AccessibleObject aObject = (AccessibleObject) theAccessor; if (aObject.getAnnotation(OneToMany.class) != null) { aClass = aObject.getAnnotation(OneToMany.class).targetEntity(); } else if (aObject.getAnnotation(OneToOne.class) != null) { aClass = aObject.getAnnotation(OneToOne.class).targetEntity(); } else if (aObject.getAnnotation(ManyToOne.class) != null) { aClass = aObject.getAnnotation(ManyToOne.class).targetEntity(); } else if (aObject.getAnnotation(ManyToMany.class) != null) { aClass = aObject.getAnnotation(ManyToMany.class).targetEntity(); } } return aClass; } /** * Check whether or not the accessor has a {@link CascadeType} specified, and if so, if that cascade type indicates * that Remove operations should be cascaded. * @param theAccessor the accessor to inspect * @return true if remove operations should be cascaded, false otherwise. */ public static boolean isRemoveCascade(Object theAccessor) { Collection<CascadeType> aCascade = getCascadeTypes(theAccessor); return aCascade.contains(CascadeType.REMOVE) || aCascade.contains(CascadeType.ALL); } /** * Check whether or not the accessor has a {@link CascadeType} specified, and if so, if that cascade type indicates * that Persist operations should be cascaded. * @param theAccessor the accessor to inspect * @return true if persist operations should be cascaded, false otherwise. */ public static boolean isPersistCascade(Object theAccessor) { Collection<CascadeType> aCascade = getCascadeTypes(theAccessor); return aCascade.contains(CascadeType.PERSIST) || aCascade.contains(CascadeType.ALL); } /** * Check whether or not the accessor has a {@link CascadeType} specified, and if so, if that cascade type indicates * that refresh operations should be cascaded. * @param theAccessor the accessor to inspect * @return true if refresh operations should be cascaded, false otherwise. */ public static boolean isRefreshCascade(Object theAccessor) { Collection<CascadeType> aCascade = getCascadeTypes(theAccessor); return aCascade.contains(CascadeType.REFRESH) || aCascade.contains(CascadeType.ALL); } /** * Check whether or not the accessor has a {@link CascadeType} specified, and if so, if that cascade type indicates * that Merge operations should be cascaded. * @param theAccessor the accessor to inspect * @return true if merge operations should be cascaded, false otherwise. */ public static boolean isMergeCascade(Object theAccessor) { Collection<CascadeType> aCascade = getCascadeTypes(theAccessor); return aCascade.contains(CascadeType.MERGE) || aCascade.contains(CascadeType.ALL); } /** * Return all {@link CascadeType CascadeTypes} specified for the provided accessor. If the access is not a Field * or Method, or if it does not have any of the value multiplicity annotations ({@link OneToOne}, {@link OneToMany}, * {@link ManyToOne}, {@link OneToOne}, or {@link ManyToMany}) this will return false, otherwise it will collect * the values of the casade property of any of the aforementioned annotations used on the accessor. * @param theAccessor the accessor to inspect * @return the collection of CascadeTypes specified, or an empty list if none is specified. */ private static Collection<CascadeType> getCascadeTypes(Object theAccessor) { Collection<CascadeType> aCascade = new HashSet<CascadeType>(); if (theAccessor instanceof AccessibleObject) { AccessibleObject aObject = (AccessibleObject) theAccessor; if (aObject.getAnnotation(OneToMany.class) != null) { aCascade.addAll(Arrays.asList(aObject.getAnnotation(OneToMany.class).cascade())); } else if (aObject.getAnnotation(OneToOne.class) != null) { aCascade.addAll(Arrays.asList(aObject.getAnnotation(OneToOne.class).cascade())); } else if (aObject.getAnnotation(ManyToOne.class) != null) { aCascade.addAll(Arrays.asList(aObject.getAnnotation(ManyToOne.class).cascade())); } else if (aObject.getAnnotation(ManyToMany.class) != null) { aCascade.addAll(Arrays.asList(aObject.getAnnotation(ManyToMany.class).cascade())); } } return aCascade; } /** * Return the Annotation of the specified type on the accessor, either a {@link java.lang.reflect.Field} or a {@link java.lang.reflect.Method} * @param theAccess the accessor * @param theAnnotation the annotation to get * @param <T> the type of annotation to retrieve * @return the value of the annotation on the accessor, or null if one is not found, or the accessor is not a Field or Method. */ public static <T extends Annotation> T getAnnotation(Object theAccess, Class<T> theAnnotation) { if (theAccess instanceof Field) { return ((Field)theAccess).getAnnotation(theAnnotation); } else if (theAccess instanceof Method) { Method aMethod = (Method) theAccess; T aAnnotation = aMethod.getAnnotation(theAnnotation); if (aAnnotation == null) { // if this is the case, it might be that this is from an "inferred" method. so let's check it's twin, // either a getter or setter to see if it has the annotation. If not, I don't know what the hell // happened try { Method aPairedMethod = null; if (aMethod.getName().startsWith("get")) { aPairedMethod = aMethod.getDeclaringClass().getMethod(aMethod.getName().replaceFirst("get", "set"), aMethod.getReturnType()); } else if (aMethod.getName().startsWith("set")) { try { aPairedMethod = aMethod.getDeclaringClass().getMethod(aMethod.getName().replaceFirst("set", "get")); } catch (Exception e) { // no-op } if (aPairedMethod == null) { aPairedMethod = aMethod.getDeclaringClass().getMethod(aMethod.getName().replaceFirst("set", "is")); } } if (aPairedMethod != null) { aAnnotation = aPairedMethod.getAnnotation(theAnnotation); } } catch (NoSuchMethodException e) { // could not find a paired getter/setter. don't know why. probably the user put the annotations // on methods of non-bean compliant code, which is screwing things up. we'll try to fail as // gracefully as possible } } return aAnnotation; } else { return null; } } /** * Return the Class type of the accessor. For a {@link java.lang.reflect.Field} it's the declared type of the field, for a * {@link java.lang.reflect.Method}, which should be a bean-style setter, it's the type of the single parameter to the method. * @param theAccessor the accessor * @return the Class type of the Field/Method * @throws RuntimeException thrown if you don't pass in a Field or Method, or if the Method is not of the expected * bean-style setter variety. */ public static Class classFrom(Object theAccessor) { Class<?> aClass; if (theAccessor instanceof Field) { aClass = ((Field)theAccessor).getType(); } else if (theAccessor instanceof Method) { // this should be a setter style bean method, taking one param which corresponds to the type of the property // it represents Method aMethod = (Method) theAccessor; if (aMethod.getParameterTypes().length == 1) { aClass = aMethod.getParameterTypes()[0]; } else { throw new RuntimeException("Unknown or unsupported accessor method type"); } } else if (theAccessor instanceof Class) { aClass = (Class) theAccessor; } else { throw new RuntimeException("Unknown or unsupported accessor type: " + theAccessor); } return aClass; } /** * Checks whether both classes have RdfsClass annotation that refers to the same type. * * @param clazz1 the first class to be compared * @param clazz2 the second class to be compared * @return true if and only if the both classes have RdfsClass annotation and both of the annotations refer * to the same type. */ public static boolean sameRdfsClass(Class clazz1, Class clazz2) { if (!BeanReflectUtil.hasAnnotation(clazz1, RdfsClass.class)) { return false; } if (!BeanReflectUtil.hasAnnotation(clazz2, RdfsClass.class)) { return false; } RdfsClass rdfsClass1 = BeanReflectUtil.getAnnotation(clazz1, RdfsClass.class); RdfsClass rdfsClass2 = BeanReflectUtil.getAnnotation(clazz2, RdfsClass.class); String type1 = PrefixMapping.GLOBAL.uri(rdfsClass1.value()); String type2 = PrefixMapping.GLOBAL.uri(rdfsClass2.value()); return type1.equals(type2); } private static class BeanReflectCacheEntry { public Field mIdField; public Collection<Field> mFields; public Collection<Method> mSetters; public Collection<Method> mGetters; public Collection<Method> mInferredSetters; public Collection<Method> mInferredGetters; public Map<Class<? extends Annotation>, Annotation> mAnnotations = Maps.newHashMap(); } }