package org.hivedb.util.classgen;
import net.sf.cglib.proxy.Factory;
import org.hivedb.annotations.GeneratorIgnore;
import org.hivedb.util.*;
import org.hivedb.util.QuickCache;
import org.hivedb.util.functional.*;
import org.springframework.beans.BeanUtils;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
public class GenerateInstance<T> implements Generator<T> {
private final int COLLECTION_SIZE = 3;
private Class<T> clazz;
private Collection<Class<?>> excludedClasses = defaultExcludedClasses();
public GenerateInstance(Class<T> clazz)
{
this.clazz = clazz;
}
public T generateAndCopyProperties(Object templateInstance) {
if (PrimitiveUtils.isPrimitiveClass(clazz))
throw new RuntimeException(String.format("Attempt to generate instance and copy properties of a primitive class: %s", clazz.getName()));
T instance = newInstance();
for( Method getter : ReflectionTools.getGetters(clazz)) {
if (belongsToExcludedClass(getter))
continue;
else {
String propertyName = BeanUtils.findPropertyForMethod(getter).getName();
final Object value = ReflectionTools.invokeGetter(templateInstance, propertyName);
if (ReflectionTools.isCollectionProperty(clazz, propertyName)) {
Collection<Object> collection = value == null ? Lists.newArrayList() : (Collection<Object>)value;
final Class<?> itemClass = ReflectionTools.getCollectionItemType(clazz, propertyName);
final GenerateInstance<Object> generateInstance = new GenerateInstance<Object>((Class<Object>) itemClass);
GeneratedInstanceInterceptor.setProperty(
instance,
propertyName,
Transform.map(new Unary<Object,Object>() {
public Object f(Object item) {
return PrimitiveUtils.isPrimitiveClass(itemClass)
? item
: generateInstance.generateAndCopyProperties(item);
}}, collection));
}
else if(value == null)
continue;
else if(PrimitiveUtils.isPrimitiveClass(value.getClass()))
GeneratedInstanceInterceptor.setProperty(
instance,
propertyName,
value);
else
GeneratedInstanceInterceptor.setProperty(
instance,
propertyName,
new GenerateInstance<Object>((Class<Object>) getter.getReturnType()).generateAndCopyProperties(value));
}
}
return instance;
}
static QuickCache primitiveGenerators = new QuickCache(); // cache generators for sequential randomness
@SuppressWarnings("unchecked")
public T generate() {
if (PrimitiveUtils.isPrimitiveClass(clazz))
return new GeneratePrimitiveValue<T>(clazz).generate();
T instance = newInstance();
for (Method getter : ReflectionTools.getGetters(clazz))
{
if (belongsToExcludedClass(getter))
continue;
String propertyName = ReflectionTools.getPropertyNameOfAccessor(getter);
Class methodOwner = ReflectionTools.getOwnerOfMethod(clazz, propertyName);
Method method = ReflectionTools.getGetterOfProperty(methodOwner, propertyName);
if (ReflectionTools.getMethodOfOwner(method).getAnnotation(GeneratorIgnore.class) != null)
continue;
final Class<Object> returnType = (Class<Object>) getter.getReturnType();
if (ReflectionTools.doesImplementOrExtend(returnType, Collection.class)) {
Class<Object> collectionItemClass = (Class<Object>) ReflectionTools.getCollectionItemType(clazz,propertyName);
ReflectionTools.invokeSetter(instance, propertyName,
PrimitiveUtils.isPrimitiveClass(collectionItemClass)
? new GeneratePrimitiveCollection<Object>(collectionItemClass,COLLECTION_SIZE).generate()
: new GenerateInstanceCollection<Object>(collectionItemClass, COLLECTION_SIZE).generate());
} else if(returnType.isArray()) {
Class<Object> arrayItemClass = (Class<Object>) returnType.getComponentType();
ReflectionTools.invokeSetter(instance, propertyName,
PrimitiveUtils.isPrimitiveClass(arrayItemClass)
? new GeneratePrimitiveCollection<Object>(arrayItemClass,COLLECTION_SIZE).generate().toArray()
: new GenerateInstanceCollection<Object>(arrayItemClass, COLLECTION_SIZE).generate().toArray());
}
else
ReflectionTools.invokeSetter(instance, propertyName,
PrimitiveUtils.isPrimitiveClass(returnType)
? primitiveGenerators.get(returnType.hashCode(), new Delay<Generator>() {
public Generator f() {
return new GeneratePrimitiveValue<Object>(returnType);
}}).generate()
: new GenerateInstance<Object>(returnType).generate());
}
return instance;
}
private T newInstance() {
T instance;
try {
instance = clazz.isInterface() || ReflectionTools.doesImplementOrExtend(clazz, GeneratedImplementation.class)
? (T)GeneratedClassFactory.newInstance( clazz )
: clazz.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return instance;
}
public boolean belongsToExcludedClass(Method m) {
return Filter.grepItemAgainstList(ReflectionTools.getMethodOfOwner(m).getDeclaringClass(), excludedClasses);
}
public static Collection<Class<?>> defaultExcludedClasses() {
return Arrays.asList(new Class<?>[]{Object.class, Factory.class, MapBacked.class});
}
}