package org.jblooming.utilities;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.hibernate.proxy.AbstractLazyInitializer;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.type.Type;
import org.hibernate.util.ReflectHelper;
import org.jblooming.PlatformRuntimeException;
import org.jblooming.ontology.Identifiable;
import org.jblooming.ontology.IdentifiableSupport;
import org.jblooming.ontology.LookupSupport;
import org.jblooming.ontology.PersistentFile;
import org.jblooming.persistence.exceptions.FindByPrimaryKeyException;
import org.jblooming.persistence.exceptions.PersistenceException;
import org.jblooming.persistence.hibernate.HibernateFactory;
import org.jblooming.persistence.hibernate.PersistenceContext;
import org.jblooming.waf.constants.Fields;
import org.jblooming.waf.view.ClientEntry;
import java.beans.Introspector;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* @author Roberto Bicchierai rbicchierai@open-lab.com
*/
public class ReflectionUtilities {
/**
* Returns the hierarchy of extended classes and all their implemented interfaces
*
* @param claz
*/
public static List<Class> getInheritedClasses(Class claz) {
List<Class> set = new ArrayList<Class>();
privateGetInheritedClasses(claz, set);
return set;
}
public static boolean extendsOrImplements(Class main, Class interfaceOrHigherClass) {
return getInheritedClasses(main).contains(interfaceOrHigherClass);
}
public static boolean directlyImplements(Class main, Class aninterface) {
boolean result = false;
Class[] interfaces = main.getInterfaces();
for (Class anInterfaceInLoop : interfaces) {
if (anInterfaceInLoop.equals(aninterface)) {
result = true;
break;
}
}
return result;
}
private static void privateGetInheritedClasses(Class claz, List set) {
set.add(claz);
if (!claz.isPrimitive()) {
// first the interfaces
Class[] interfaces = claz.getInterfaces();
for (Class anInterface : interfaces) {
privateGetInheritedClasses(anInterface, set);
}
// then the parent
Class parent = claz.getSuperclass();
if (parent != null)
privateGetInheritedClasses(parent, set);
}
}
public static Map<String, Field> getDeclaredInheritedFields(Class claz) {
return getDeclaredInheritedFields(claz, true);
}
public static Map<String, Field> getDeclaredInheritedFields(Class claz, boolean ignoreStatic) {
//@useConcrete warning: this MUST be a set so that most contrete field types do not get overwritten by more abstract ones
List<Class> set = new ArrayList<Class>();
privateGetInheritedClasses(claz, set);
Map<String, Field> flds = new HashMap<String, Field>();
for (Class clazz : set) {
for (int i = 0; i < clazz.getDeclaredFields().length; i++) {
Field f = clazz.getDeclaredFields()[i];
if (ignoreStatic && Modifier.isStatic(f.getModifiers()))
continue;
//@see @useConcrete
if (flds.get(f.getName()) == null)
flds.put(f.getName(), f);
}
}
return flds;
}
public static Map<String, Method> getDeclaredInheritedMethods(Class claz) {
//@useConcrete warning: this MUST be a set so that most contrete field types do not get overwritten by more abstract ones
List<Class> set = new ArrayList<Class>();
privateGetInheritedClasses(claz, set);
Map<String, Method> flds = new HashMap<String, Method>();
for (Class clazz : set) {
for (int i = 0; i < clazz.getDeclaredMethods().length; i++) {
Method f = clazz.getDeclaredMethods()[i];
if (Modifier.isStatic(f.getModifiers()))
continue;
//@see @useConcrete
if (flds.get(f.getName()) == null)
flds.put(f.getName(), f);
}
}
return flds;
}
public static List<Field> getDeclaredInheritedParameterFields(Class claz, Class annotation) {
//@useConcrete warning: this MUST be a set so that most contrete field types do not get overwritten by more abstract ones
List<Class> set = new ArrayList<Class>();
privateGetInheritedClasses(claz, set);
List<Field> flds = new ArrayList();
for (Class clazz : set) {
for (int i = 0; i < clazz.getDeclaredFields().length; i++) {
Field f = clazz.getDeclaredFields()[i];
if (Modifier.isStatic(f.getModifiers()))
continue;
if (!f.isAnnotationPresent(annotation))
continue;
//@see @useConcrete
if (!flds.contains(f))
flds.add(f);
}
}
return flds;
}
public static Field getField(String fieldName, Class mainObjectClass) {
Map<String, Field> declaredInheritedFields = getDeclaredInheritedFields(mainObjectClass);
if (fieldName.contains(".")) {
String topProperty = fieldName.substring(0, fieldName.indexOf('.'));
return getField(fieldName.substring(fieldName.indexOf('.') + 1), declaredInheritedFields.get(topProperty).getType());
} else {
//
Field field = declaredInheritedFields.get(fieldName);
return field;
}
}
public static Object getFieldValue(String fieldName, Object object) throws IllegalAccessException, HibernateException, FindByPrimaryKeyException, NoSuchMethodException, InvocationTargetException {
if (fieldName == null)
throw new PlatformRuntimeException("ReflectionUtilities.getFieldValue fieldName cannot be null");
if (object == null)
return null;
// todo AAAAAAAARG remove this peak of elegance
if (fieldName.indexOf("blank") != -1)
return null;
if (fieldName.contains(".")) {
String topProperty = fieldName.substring(0, fieldName.indexOf('.'));
// todo we will be back soon
PersistenceContext persistenceContext = PersistenceContext.get((IdentifiableSupport) object);
SessionFactory sf = persistenceContext.persistenceConfiguration.getSessionFactory();
EntityPersister cm = (EntityPersister) sf.getClassMetadata(Hibernate.getClass(object));
Object childObject = cm.getPropertyValue(object, topProperty, persistenceContext.session.getEntityMode());
childObject = getUnderlyingObjectAsObject(childObject);
return getFieldValue(fieldName.substring(fieldName.indexOf('.') + 1), childObject);
} else {
PersistenceContext persistenceContext = PersistenceContext.get((IdentifiableSupport) object);
SessionFactory sf = persistenceContext.persistenceConfiguration.getSessionFactory();
EntityPersister cm = (EntityPersister) sf.getClassMetadata(Hibernate.getClass(object));
//the field could be not persisted
Object value = null;
boolean isPersisted = isPersisted(cm, fieldName);
if (isPersisted)
value = cm.getPropertyValue(object, fieldName, persistenceContext.session.getEntityMode());
else {
Field field = getField(fieldName, object.getClass());
field.setAccessible(true);
value = field.get(object);
}
return value;
}
}
private static boolean isPersisted(EntityPersister cm, String fieldName) {
boolean isPersisted = false;
String[] propNames = cm.getPropertyNames();
for (String propName : propNames) {
if (fieldName.equals(propName)) {
isPersisted = true;
break;
}
}
return isPersisted;
}
public static void setFieldValue(String fieldName, Object object, Object value)
throws IllegalAccessException, HibernateException, PersistenceException, NoSuchMethodException, InvocationTargetException, InstantiationException {
if (fieldName == null)
throw new PlatformRuntimeException("ReflectionUtilities.setFieldValue fieldName cannot be null");
if (object == null)
return;
if (fieldName.contains(".")) {
String topProperty = fieldName.substring(0, fieldName.indexOf('.'));
PersistenceContext persistenceContext = PersistenceContext.get((IdentifiableSupport) object);
SessionFactory sf = persistenceContext.persistenceConfiguration.getSessionFactory();
SingleTableEntityPersister cm = (SingleTableEntityPersister) sf.getClassMetadata(Hibernate.getClass(object));
Object childObject = cm.getPropertyValue(object, topProperty, persistenceContext.session.getEntityMode());
if (childObject == null) {
//check whether is standalone a persistent object
Class returnedClass = cm.getPropertyType(topProperty).getReturnedClass();
if ((EntityPersister) sf.getClassMetadata(returnedClass) != null)
childObject = returnedClass.newInstance();
((IdentifiableSupport) childObject).store();
cm.setPropertyValue(object, topProperty, childObject, persistenceContext.session.getEntityMode());
}
setFieldValue(fieldName.substring(fieldName.indexOf('.') + 1), childObject, value);
} else {
PersistenceContext persistenceContext = PersistenceContext.get((IdentifiableSupport) object);
SessionFactory sf = persistenceContext.persistenceConfiguration.getSessionFactory();
SingleTableEntityPersister cm = (SingleTableEntityPersister) sf.getClassMetadata(Hibernate.getClass(object));
//cm.setPropertyValue(object, fieldName, value, HibernateFactory.getSession().getEntityMode());
//the field could be not persisted
boolean isPersisted = isPersisted(cm, fieldName);
if (isPersisted) {
cm.setPropertyValue(object, fieldName, value, persistenceContext.session.getEntityMode());
} else {
Field field = getField(fieldName, object.getClass());
field.setAccessible(true);
field.set(object, value);
}
}
}
/**
* beware: persistentObject will be initialized
*/
public static boolean instanceOfPersistent(Object persistentObject, Class aClass) {
boolean result = false;
Class pClass = null;
if (persistentObject instanceof HibernateProxy) {
HibernateProxy proxy = (HibernateProxy) persistentObject;
AbstractLazyInitializer li = (AbstractLazyInitializer) proxy.getHibernateLazyInitializer();
//this does not work
//Class bclass = HibernateProxyHelper.getClassWithoutInitializingProxy(persistentObject);
// hence I use this which is quite ugly, as I fear the object is uselessly initialized:
//this has the defect of forcing instantiation
pClass = li.getImplementation().getClass();
//pClass = ImprovedAbstractLazyInitializer.getRealClass(li);
} else {
pClass = persistentObject.getClass();
}
if (ReflectionUtilities.getInheritedClasses(pClass).contains(aClass))
result = true;
else
result = false;
return result;
}
public static boolean instanceOf(Object persistentObject, Class aClass) {
return ReflectionUtilities.getInheritedClasses(persistentObject.getClass()).contains(aClass);
}
/**
* This method will initialize proxy.
*
* @param persistentObject
* @return
* @throws ClassCastException If the argument, or the underlying object in case of proxied object, is not Identifiable
*/
public static Identifiable getUnderlyingObject(Object persistentObject) throws ClassCastException {
return (Identifiable) getUnderlyingObjectAsObject(persistentObject);
}
public static Object getUnderlyingObjectAsObject(Object persistentObject) throws ClassCastException {
if (persistentObject instanceof HibernateProxy) {
HibernateProxy proxy = (HibernateProxy) persistentObject;
AbstractLazyInitializer ali = (AbstractLazyInitializer) proxy.getHibernateLazyInitializer();
return ali.getImplementation();
} else
return persistentObject;
}
public static Class<? extends Identifiable> getUnderlyingObjectClass(Object persistentObject) throws ClassCastException {
return getUnderlyingObject(persistentObject).getClass();
}
public static ClientEntry makeCe(boolean required, String fieldName, String propertyName, Class mainObjectClass, Identifiable i) {
ClientEntry ce = null;
String ceValue = null;
Field field = getField(propertyName, mainObjectClass);
field.setAccessible(true);
Object fieldValue = null;
try {
fieldValue = getFieldValue(propertyName, i);
} catch (Exception e) {
throw new PlatformRuntimeException(e);
}
if (fieldValue != null) {
Class type = field.getType();
List classes = getInheritedClasses(type);
if (type.equals(String.class) || type.equals(Serializable.class)) {
ceValue = fieldValue + "";
} else if (classes.contains(Date.class)) {
ceValue = DateUtilities.dateToString((Date) fieldValue);
} else if (fieldValue instanceof Boolean) {
ceValue = ((Boolean) (fieldValue)) ? Fields.TRUE : Fields.FALSE;
} else if (classes.contains(LookupSupport.class)) {
ceValue = ((LookupSupport) fieldValue).getId() + "";
} else if (classes.contains(PersistentFile.class)) {
ceValue = ((PersistentFile) fieldValue).serialize() + "";
} else if (classes.contains(Identifiable.class)) {
ceValue = ((Identifiable) fieldValue).getId() + "";
} else if (type.equals(int.class) || type.equals(Integer.class)) {
ceValue = ((Integer) fieldValue) + "";
} else if (type.equals(float.class)) {
ceValue = ((Float) fieldValue) + "";
} else if (type.equals(double.class)) {
ceValue = ((Double) fieldValue) + "";
} else if (type.equals(long.class)) {
ceValue = ((Long) fieldValue) + "";
} else if (type.isEnum()) {
ceValue = fieldValue.toString();
}
ce = new ClientEntry(fieldName, ceValue);
ce.required = required;
}
return ce;
}
private static Method getSetterOrNull(Class theClass, String propertyName) {
if (theClass == Object.class || theClass == null)
return null;
Method method = setterMethod(theClass, propertyName);
if (method != null) {
if (!ReflectHelper.isPublic(theClass, method))
method.setAccessible(true);
return method;
} else {
Method setter = getSetterOrNull(theClass.getSuperclass(), propertyName);
if (setter == null) {
Class[] interfaces = theClass.getInterfaces();
for (int i = 0; setter == null && i < interfaces.length; i++) {
setter = getSetterOrNull(interfaces[i], propertyName);
}
}
return setter;
}
}
private static Method setterMethod(Class theClass, String propertyName) {
Method getter = getGetterOrNull(theClass, propertyName);
Class returnType = (getter == null) ? null : getter.getReturnType();
Method[] methods = theClass.getDeclaredMethods();
Method potentialSetter = null;
for (int i = 0; i < methods.length; i++) {
String methodName = methods[i].getName();
if (methods[i].getParameterTypes().length == 1 && methodName.startsWith("set")) {
String testStdMethod = Introspector.decapitalize(methodName.substring(3));
String testOldMethod = methodName.substring(3);
if (testStdMethod.equals(propertyName) || testOldMethod.equals(propertyName)) {
potentialSetter = methods[i];
if (returnType == null || methods[i].getParameterTypes()[0].equals(returnType)) return potentialSetter;
}
}
}
return potentialSetter;
}
private static Method getGetterOrNull(Class theClass, String propertyName) {
if (theClass == Object.class || theClass == null)
return null;
Method method = getterMethod(theClass, propertyName);
if (method != null) {
if (!ReflectHelper.isPublic(theClass, method))
method.setAccessible(true);
return method;
} else {
Method getter = getGetterOrNull(theClass.getSuperclass(), propertyName);
if (getter == null) {
Class[] interfaces = theClass.getInterfaces();
for (int i = 0; getter == null && i < interfaces.length; i++) {
getter = getGetterOrNull(interfaces[i], propertyName);
}
}
return getter;
}
}
private static Method getterMethod(Class theClass, String propertyName) {
Method[] methods = theClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
// only carry on if the method has no parameters
if (methods[i].getParameterTypes().length == 0) {
String methodName = methods[i].getName();
// try "get"
if (methodName.startsWith("get")) {
String testStdMethod = Introspector.decapitalize(methodName.substring(3));
String testOldMethod = methodName.substring(3);
if (testStdMethod.equals(propertyName) || testOldMethod.equals(propertyName))
return methods[i];
}
// if not "get" then try "is"
/*boolean isBoolean = methods[i].getReturnType().equals(Boolean.class) ||
methods[i].getReturnType().equals(boolean.class);*/
if (methodName.startsWith("is")) {
String testStdMethod = Introspector.decapitalize(methodName.substring(2));
String testOldMethod = methodName.substring(2);
if (testStdMethod.equals(propertyName) || testOldMethod.equals(propertyName)) return methods[i];
}
}
}
return null;
}
public static String unqualify(String qualifiedName) {
return qualifiedName.substring(qualifiedName.lastIndexOf(".") + 1);
}
public static String[] getPropertyNames(String className) {
PersistenceContext persistenceContext = null;
persistenceContext = PersistenceContext.get(className);
SessionFactory sf = persistenceContext.persistenceConfiguration.getSessionFactory();
Map acm = sf.getAllClassMetadata();
EntityPersister persEp = (EntityPersister) acm.get(className);
return persEp.getPropertyNames();
}
public static String[] getPropertyNames(Object object) {
PersistenceContext persistenceContext = PersistenceContext.get((IdentifiableSupport) object);
SessionFactory sf = persistenceContext.persistenceConfiguration.getSessionFactory();
EntityPersister cm = (EntityPersister) sf.getClassMetadata(Hibernate.getClass(object));
return cm.getPropertyNames();
}
public static Type[] getPropertyTypes(String className) {
PersistenceContext persistenceContext = null;
persistenceContext = PersistenceContext.get(className);
SessionFactory sf = persistenceContext.persistenceConfiguration.getSessionFactory();
Map acm = sf.getAllClassMetadata();
AbstractEntityPersister persEp = (AbstractEntityPersister) acm.get(className);
return persEp.getPropertyTypes();
}
/**
* A VERY approximative implementation - use at your own risk AHAHAHAHAHA
* @param o
* @param methodName
* @param params
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public static Object invoke(Object o, String methodName, Object... params) throws InvocationTargetException, IllegalAccessException {
Class[] types = new Class[params.length];
//List<Class> classes = new ArrayList();
int i = 0;
for (Object param : params) {
types[i]=param.getClass();
i++;
}
Class aClass = o.getClass();
for (Method m : aClass.getMethods()) {
if (m.getName().equals(methodName)) {
if (m.getParameterTypes().length==types.length) {
boolean ok = true;
int j=0;
for(Class paramType: m.getParameterTypes()) {
ok=ReflectionUtilities.extendsOrImplements(types[j],paramType);
if (!ok)
break;
j++;
}
if (ok) {
return m.invoke(o, params);
}
}
}
}
return null;
}
}