package org.merka.stubgen;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import org.merka.stubgen.exception.MockGenException;
import org.merka.stubgen.exception.NotBlockingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MockObjectGenerator
{
public static final int DEFAULT_NESTING_VALUE = 10;
public static final int DEFAULT_ARRAY_LENGTH = 3;
public static final String DEFAULT_DTO_PACKAGE = "org.merka.stubgen.test";
private static transient Logger logger = LoggerFactory.getLogger(MockObjectGenerator.class);
protected Package dtoPackage;
protected Package getDtoPackage()
{
return dtoPackage;
}
protected void setDtoPackage(Package dtoPackage)
{
this.dtoPackage = dtoPackage;
}
public MockObjectGenerator()
{
Package dtoPack = Package.getPackage(DEFAULT_DTO_PACKAGE);
setDtoPackage(dtoPack);
}
public Object generate(Class<?> theClass) throws MockGenException
{
return generate(theClass, DEFAULT_NESTING_VALUE);
}
public <GeneratedType> List<GeneratedType> generateList(Class<?> GeneratedType, int length) throws MockGenException
{
ArrayList<GeneratedType> list = new ArrayList<GeneratedType>();
for(int i = 0; i < length; i++)
{
GeneratedType stub = (GeneratedType) generate(GeneratedType);
list.add(stub);
}
return list;
}
protected <GeneratedType> GeneratedType generateInstance(Class<?> GeneratedType, int nestingLevel) throws MockGenException
{
try
{
Object mock = null;
// switch through the possible cases of T
if(GeneratedType.isPrimitive())
{
if(GeneratedType.equals(boolean.class))
{
mock = true;
}
else if(GeneratedType.equals(int.class) )
{
mock = (int) 0;
}
else if(GeneratedType.equals(long.class))
{
mock = (long) 0L;
}
else if (GeneratedType.equals(short.class))
{
mock = (short) 0;
}
else if (GeneratedType.equals(byte.class))
{
mock = (byte) 0;
}
else if (GeneratedType.equals(char.class))
{
mock = (char) 0;
}
else if (GeneratedType.equals(double.class))
{
mock = (double) 0;
}
else if (GeneratedType.equals(float.class))
{
mock = (float) 0;
}
return (GeneratedType)mock;
}
if(nestingLevel == 0)
{
return (GeneratedType)mock;
}
else if(GeneratedType.isEnum())
{
mock = GeneratedType.getEnumConstants()[0];
}
else if(GeneratedType.isArray())
{
Object[] array = (Object[])Array.newInstance(GeneratedType.getComponentType(), DEFAULT_ARRAY_LENGTH);
for(int i = 0; i < DEFAULT_ARRAY_LENGTH; i++)
{
Object element = generate(GeneratedType.getComponentType(), nestingLevel - 1);
// array[i] = element; // this works too
Array.set(array, i, element);
}
mock = array;
}
else if (MockGenUtils.isList(GeneratedType))
{
throw new MockGenException("Impossible to get an instance of a List type. Please use the method generateList instead.");
// ArrayList<Object> list = new ArrayList<Object>();
// for(int i = 0; i < DEFAULT_ARRAY_LENGTH; i++)
// {
// Object element = generate( (Class<?>)(((ParameterizedType)GeneratedType.getGenericInterfaces()[0]).getActualTypeArguments()[0]) );
// list.add(element);
// }
// Type genericInterface = GeneratedType.getGenericInterfaces()[0];
// if(genericInterface instanceof ParameterizedType)
// {
//
// ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
// Class<?> componentType = (Class<?>) parameterizedType.getActualTypeArguments()[0]; // (Class<?>)parameterizedType.getActualTypeArguments()[0];
//
// Object element = generate(componentType, nestingLevel - 1);
//
// list.add(element);
//
// mock = list;
//
// }
}
else
{
IInstantiator instantiator = InstantiatorFactory.getInstantiator(GeneratedType);
try
{
mock = instantiator.newInstance(GeneratedType);
}
catch(NotBlockingException e)
{
String message = "Exception of type: " + e.getClass().getName() + " in generateInstance.";
logger.warn("Exception of type: " + e.getClass().getName() + " in generateInstance.");
if (logger.isDebugEnabled())
{
logger.debug("Full exception: ", e);
}
}
}
return (GeneratedType)mock;
}
catch(Exception e)
{
throw new MockGenException(e);
}
}
@SuppressWarnings("unchecked")
protected void setFields(Object mock, int nestingLevel) throws MockGenException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
if (mock == null)
{
return;
}
Method[] methodArray = mock.getClass().getDeclaredMethods();
for (Method method : methodArray)
{
int modifiers = method.getModifiers();
if (MockGenUtils.isSetter(method) && Modifier.isPublic(modifiers))
{
Class<?> paramType = method.getParameterTypes()[0];
try
{
Object param = null;
if (MockGenUtils.isList(paramType))
{
ArrayList list = new ArrayList();
for(int i = 0; i < DEFAULT_ARRAY_LENGTH; i++)
{
Object element = generate( (Class<?>)(((ParameterizedType) method.getGenericParameterTypes()[0]).getActualTypeArguments()[0]) );
list.add(element);
}
param = list;
}
else if (isMockable(paramType))
{
param = generate(paramType, nestingLevel - 1);
}
else
{
param = generateInstance(paramType, nestingLevel - 1);
}
try
{
method.invoke(mock, param);
}
catch (Exception e)
{
System.err.println("\n\n\n Class: " + paramType.getName() + "; method: " + method.getName());
throw new MockGenException(e);
}
}
catch (NotBlockingException e)
{
// TODO how to handle this exception?
}
}
}
}
private boolean isMockable(Class<?> generatedType)
{
if (generatedType != null && generatedType.getPackage() != null && generatedType.getPackage().getName() != null && getDtoPackage() != null)
{
return generatedType.getPackage().getName().equals(getDtoPackage().getName());
}
return false;
}
public <T> T generate(Class<?> T, int nestingLevel) throws MockGenException
{
try
{
T mock = generateInstance(T, nestingLevel);
setFields(mock, nestingLevel);
return mock;
}
catch (Exception t)
{
throw new MockGenException(t);
}
}
public <T> T generate(T prototype) throws MockGenException
{
try
{
Class<?> theClass = prototype.getClass();
T mock = (T) generate(theClass);
Method[] protoMethodArray = theClass.getDeclaredMethods();
for (Method prototypeMethod : protoMethodArray)
{
if (MockGenUtils.isGetter(prototypeMethod))
{
// find the correspondent setter method
String namePart;
if (prototypeMethod.getName().startsWith("is"))
{
namePart = prototypeMethod.getName().substring(2);
}
else
{
namePart = prototypeMethod.getName().substring(3);
}
String setterName = "set" + namePart;
Class<?> returnType = prototypeMethod.getReturnType();
try
{
Method setterMethod = theClass.getMethod(setterName, returnType);
Object param = prototypeMethod.invoke(prototype, (Object[]) null);
if (param != null)
{
setterMethod.invoke(mock, prototypeMethod.invoke(prototype, (Object[]) null));
}
}
catch (NoSuchMethodError ne)
{
}
}
}
return mock;
}
catch (Exception t)
{
throw new MockGenException(t);
}
}
// protected Object loadJSON() throws MockGenException
// {
// try
// {
// ObjectMapper mapper = new ObjectMapper();
// TestObject prototype = mapper.readValue("json/mockgen-templates.json", TestObject.class);
// return generate(prototype);
// }
// catch (Throwable t)
// {
// throw new MockGenException(t);
// }
// }
}