package org.hivedb.hibernate;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import org.hivedb.annotations.GeneratedClass;
import org.hivedb.annotations.IndexDelegate;
import org.hivedb.annotations.IndexType;
import org.hivedb.configuration.EntityConfig;
import org.hivedb.configuration.EntityHiveConfig;
import org.hivedb.configuration.EntityIndexConfig;
import org.hivedb.util.classgen.ReflectionTools;
import org.hivedb.util.classgen.GeneratedClassFactory;
import org.hivedb.util.classgen.GeneratedImplementation;
import org.hivedb.util.functional.Filter;
import org.hivedb.util.functional.Pair;
import org.hivedb.util.functional.Predicate;
import org.hivedb.util.functional.Transform;
import org.hivedb.util.functional.Unary;
public class EntityResolver {
private EntityHiveConfig entityHiveConfig;
public EntityResolver(EntityHiveConfig entityHiveConfig) {
this.entityHiveConfig = entityHiveConfig;
}
public Collection<Class<?>> getEntityClasses() {
return Transform.map(new Unary<EntityConfig, Class<?>>() {
public Class<?> f(EntityConfig entityConfig) {
return entityConfig.getRepresentedInterface();
}},
this.entityHiveConfig.getEntityConfigs());
}
// The given class may or may not be a Hive entity class. If it is return it's interface, otherwise
// search throught the EntityHiveConfigs for an IndexDelegate whose type matches the type given.
// This method grabs to first match, so it fill be inaccurate if two EntityHiveConfigs share a delegated
// property type, or if two methods within And EntityConfig have the same delegated property type
public Class<?> resolveToEntityOrRelatedEntity(Class<?> clazz) {
Class<?> entityInterface = resolveEntityInterface(clazz);
if (entityInterface != null)
return entityInterface;
try {
for (EntityConfig entityConfig : entityHiveConfig.getEntityConfigs()) {
for (EntityIndexConfig entityIndexConfig : entityConfig.getEntityIndexConfigs()) {
if (entityIndexConfig.getIndexType().equals(IndexType.Delegates)) {
final String propertyName = entityIndexConfig.getPropertyName();
final Class<?> representedInterface = entityConfig.getRepresentedInterface();
if (ReflectionTools.isCollectionProperty(representedInterface, propertyName)) {
if (ReflectionTools.doesImplementOrExtend(
clazz,
ReflectionTools.getCollectionItemType(representedInterface, propertyName))) {
Method method = ReflectionTools.getGetterOfProperty(representedInterface, propertyName);
return Class.forName(method.getAnnotation(IndexDelegate.class).value());
}
}
else
if (ReflectionTools.doesImplementOrExtend(
clazz,
ReflectionTools.getPropertyType(representedInterface, propertyName))) {
Method method = ReflectionTools.getGetterOfProperty(representedInterface, propertyName);
return Class.forName(method.getAnnotation(IndexDelegate.class).value());
}
}
}
}
return null;
} catch (ClassNotFoundException e) {
throw new RuntimeException("Class not found " + getIndexDelegateAnnotatedMethod(clazz).getAnnotation(IndexDelegate.class).value());
}
}
@SuppressWarnings("unchecked")
public Map.Entry<Class<?>, Object> resolveToEntityOrRelatedEntiyInterfaceAndId(Object entity) {
Class clazz = entity.getClass();
Class entityInterface = resolveEntityInterface(clazz);
if (entityInterface != null)
return new Pair<Class<?>, Object>(entityInterface,entityHiveConfig.getEntityConfig(entityInterface).getId(entity));
Method method = getIndexDelegateAnnotatedMethod(clazz);
try {
return new Pair<Class<?>, Object>(
Class.forName(method.getAnnotation(IndexDelegate.class).value()),
method.invoke(entity, new Object[]{}));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
public Class resolveEntityInterface(Class clazz) {
return ReflectionTools.whichIsImplemented(
clazz,
Transform.map(new Unary<EntityConfig, Class>() {
public Class f(EntityConfig entityConfig) {
return entityConfig.getRepresentedInterface();
}},
entityHiveConfig.getEntityConfigs()));
}
// Given a class that is not an entity itself, see it is the type of
// of a delegated index of an entity
@SuppressWarnings("unchecked")
private Method getIndexDelegateAnnotatedMethod(Class clazz) {
Method annotatedMethod = Filter.grepSingleOrNull(new Predicate<Method>() {
public boolean f(Method method) {
return method.getAnnotation(IndexDelegate.class) != null;
}}, Transform.flatMap(new Unary<Class, Collection<Method>>() {
public Collection<Method> f(Class interfase) {
return Arrays.asList(interfase.getMethods());
}}, Arrays.asList(clazz.getInterfaces())));
return annotatedMethod;
}
public static Class<?> getPersistedImplementation(Class<?> clazz) {
if(generatesImplementation(clazz))
return GeneratedClassFactory.getGeneratedClass(clazz);
else
return clazz;
}
public static boolean generatesImplementation(Class<?> clazz) {
return clazz.getAnnotation(GeneratedClass.class) != null;
}
public static boolean isGeneratedImplementation(Class<?> clazz) {
return Arrays.asList(clazz.getInterfaces()).contains(GeneratedImplementation.class);
}
}