package net.techreadiness.util; /* Copyright 2008 Aaron Porter * * 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. */ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.persistence.Id; import org.apache.log4j.Logger; /** * EntityUtil provides some convenience functions for working with entities. * */ public class EntityUtils { private static Logger log = Logger.getLogger(EntityUtils.class); private static final Map<Class<?>, Class<?>> idTypeCache = new ConcurrentHashMap<>(); private EntityUtils() { throw new AssertionError(); } /** * Return the proxified class for passed class. If the passed class is not CGLIB enhanced, then it returns that same * class. * * Thanks Remi! * * @param proxifiedClass * the proxy class * @return the original class */ public static Class<? extends Object> deproxifyCglibClass(Class<? extends Object> proxifiedClass) { String proxifiedClassName = proxifiedClass.getName(); int i = proxifiedClassName.indexOf("$$"); if (i == -1) { return proxifiedClass; } String className = proxifiedClassName.replaceAll("[_]*\\$\\$.*", ""); try { Class<? extends Object> clazz = Class.forName(className); return clazz; } catch (ClassNotFoundException e) { return null; } } /** * Checks the fields and methods of <code>clazz</code> to determine the primary key's type. Results are cached to improve * performance. * * @param clazz * the class to examine * @return the primary key's type or null */ public static Class<?> getIdType(Class<? extends Object> clazz) { clazz = deproxifyCglibClass(clazz); Class<?> idType = idTypeCache.get(clazz); if (idType != null) { return idType; } idType = getIdTypeFromFields(clazz); if (idType == null) { idType = getIdTypeFromMethods(clazz); } if (idType == null) { Class<? extends Object> superclass = clazz.getSuperclass(); while (idType == null && superclass != null) { idType = getIdTypeFromFields(clazz); superclass = superclass.getSuperclass(); } } if (idType != null) { idTypeCache.put(clazz, idType); } return idType; } /** * Looks for an Id annotation on the fields of <code>clazz</code> to figure out the primary key's type. * * @param clazz * the class to examine * @return the primary key's type or null */ public static Class<?> getIdTypeFromFields(Class<? extends Object> clazz) { for (Field field : clazz.getDeclaredFields()) { if (field.getAnnotation(Id.class) == null) { continue; } try { return field.getType(); } catch (Exception e) { log.error(e); } } Class<? extends Object> superclass = clazz.getSuperclass(); if (superclass != null) { return getIdTypeFromFields(superclass); } return null; } /** * Looks for an Id annotation on the methods of <code>clazz</code> to figure out the primary key's type. * * @param clazz * the class to examine * @return the primary key's type or null */ public static Class<?> getIdTypeFromMethods(Class<? extends Object> clazz) { for (Method method : clazz.getMethods()) { if (method.getParameterTypes().length != 0 || method.getAnnotation(Id.class) == null) { continue; } try { return method.getReturnType(); } catch (Exception e) { log.error(e); } } return null; } private static final Map<Class<?>, Object> idAccessor = new ConcurrentHashMap<>(); /** * Gets the value of the primary key for the specified entity. * * @param entity * the target entity * @return the primary key or null */ public static Object getId(Object entity) { Class<?> clazz = deproxifyCglibClass(entity.getClass()); Object accessor = idAccessor.get(clazz); if (accessor == null) { accessor = findIdField(clazz); if (accessor == null) { accessor = findIdMethod(clazz); } if (accessor == null) { Class<? extends Object> superclass = clazz.getSuperclass(); while (accessor == null && superclass != null) { accessor = findIdField(superclass); if (accessor == null) { accessor = findIdMethod(superclass); } superclass = superclass.getSuperclass(); } } if (accessor != null) { idAccessor.put(clazz, accessor); } } if (accessor != null) { if (accessor instanceof Method) { try { return ((Method) accessor).invoke(entity, new Object[] {}); } catch (Exception e) { log.error(e); } } else if (accessor instanceof Field) { try { return ((Field) accessor).get(entity); } catch (Exception e) { log.error(e); } } } throw new IllegalStateException("Unable to get ID for entity, see error log for details"); } /** * Attempts to find a field annotated with Id. * * @param clazz * the class to examine * @return the annotated field or null */ public static Field findIdField(Class<?> clazz) { for (Field field : clazz.getDeclaredFields()) { if (field.getAnnotation(Id.class) == null) { continue; } field.setAccessible(true); return field; } Class<?> superclass = clazz.getSuperclass(); if (superclass != null) { return findIdField(superclass); } return null; } /** * Attempts to find a method annotated with Id. * * @param clazz * the class to examine * @return the annotated method or null */ public static Method findIdMethod(Class<?> clazz) { for (Method method : clazz.getMethods()) { if (method.getParameterTypes().length != 0 || method.getAnnotation(Id.class) == null) { continue; } return method; } return null; } }