/*
* Copyright (C) 2014 konik.io
*
* This file is part of Konik library.
*
* Konik library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Konik library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Konik library. If not, see <http://www.gnu.org/licenses/>.
*/
package io.konik.utils;
import io.konik.zugferd.entity.Tax;
import io.konik.zugferd.profile.ConformanceLevel;
import io.konik.zugferd.profile.Profile;
import io.konik.zugferd.unqualified.ZfDate;
import io.konik.zugferd.unqualified.ZfDateFactory;
import net._01001111.text.LoremIpsum;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.Random;
import static java.lang.Boolean.TRUE;
import static org.apache.commons.lang3.ClassUtils.isAssignable;
import static org.apache.commons.lang3.ClassUtils.isPrimitiveOrWrapper;
import static org.apache.commons.lang3.RandomStringUtils.randomNumeric;
import static org.apache.commons.lang3.reflect.MethodUtils.getAccessibleMethod;
import static org.apache.commons.lang3.reflect.MethodUtils.invokeMethod;
@SuppressWarnings("javadoc")
public class RandomInvoiceGenerator {
LoremIpsum jlorem = new LoremIpsum();
Random random = new Random();
String[] supportedDateFormatts = new String[] {"102","610","616"};
@SuppressWarnings({ "unchecked" })
public <T> T generate(Class<T> root) {
try {
return (T) populteData(root, null);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public Object populteData(Class<?> root, String name) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Object rootObj;
if (isLeafType(root)) {//final type
return generatePrimitveValue(root,name);
}
rootObj = createNewInstance(root);
// get method and populate each of them
Method[] methods = root.getMethods();
for (Method method : methods) {
int methodModifiers = method.getModifiers();
Class<?> methodParameter = null;
if (Modifier.isAbstract(methodModifiers) || method.isSynthetic()) continue;
if (method.getName().startsWith("add")) {
methodParameter = method.getParameterTypes()[0];
if (methodParameter != null && !methodParameter.isArray() && (methodParameter.isInterface() || Modifier.isAbstract(methodParameter.getModifiers()))) {
continue;
}
}
//getter
else if (method.getName().startsWith("get") && !Collection.class.isAssignableFrom(method.getReturnType()) && !method.getName().equals("getClass") && !Modifier.isAbstract(methodModifiers)) {
methodParameter = method.getReturnType();
}else {
continue;// next on setter
}
if (methodParameter==null || methodParameter.isInterface()){
continue;
}
Object popultedData = populteData(methodParameter,method.getName());
setValue(rootObj,method,popultedData);
}
return rootObj;
}
private Object createNewInstance(Class<?> root) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
try {
if (root.isArray()) {
Object[] array = (Object[])Array.newInstance(root.getComponentType(),1);
Class<?> componentType = root.getComponentType();
array[0] = populteData(componentType,componentType.getName());
return array;
}
return root.newInstance();
}catch(IllegalAccessException e) {
Constructor<?> biggestConstructor = findBiggestConstructor(root);
//for each parameter populate data
Class<?>[] constructorParameters = biggestConstructor.getParameterTypes();
Object[] constructorParameterObjects = new Object[constructorParameters.length];
for (int i = 0; i < constructorParameters.length; i++) {
Class<?> cp = constructorParameters[i];
constructorParameterObjects[i] = populteData(cp,biggestConstructor.getName());
}
return biggestConstructor.newInstance(constructorParameterObjects);
}
catch (InstantiationException e) {
if(root.equals(Tax.class)){
// return ZfDateFactory.create(supportedDateFormatts[random.nextInt(3)]);
}
// throw e;
// e.printStackTrace();
return null;
}
}
private static Constructor<?> findBiggestConstructor(Class<?> root) {
Constructor<?>[] constructors = root.getConstructors();
Constructor<?> biggestConstructor = null;
for (Constructor<?> constructor : constructors) {
if (biggestConstructor == null) {
biggestConstructor = constructor;
continue;
}
if(constructor.getTypeParameters().length > biggestConstructor.getParameterTypes().length) {
biggestConstructor = constructor;
}
}
return biggestConstructor;
}
private static void setValue(Object entity, Method entityMethod, Object paramValue) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
if(paramValue == null || entity == null)return;
String methodToCall = entityMethod.getName().replace("get", "set");
// int repeadAdder = 1;
if (entityMethod.getName().startsWith("add")) {
Collection.class.isAssignableFrom(entityMethod.getReturnType());
methodToCall = entityMethod.getName();//overwrite
// repeadAdder += random.nextInt(5);//repeat call for adder
}
Method setterOrAdder = MethodUtils.getMatchingAccessibleMethod(entity.getClass(), methodToCall, paramValue.getClass());
if (setterOrAdder == null) {
setterOrAdder = getAccessibleMethod(entity.getClass(), methodToCall, paramValue.getClass().getSuperclass());
}
if (setterOrAdder == null) {
System.out.println("Could not find setter on Class Instnace :"+entity.getClass().getSimpleName() + " Getter :" + entityMethod.getName() + " has no setter. Ignoring value:" + paramValue.toString());
return;
}
//repeat a 2 times for adder
for (int i = 0; i < 2; i++) {
invokeMethod(entity, setterOrAdder.getName(), paramValue);
}
}
private static boolean isLeafType(Class<?> type) {
if (type.equals(Class.class))return true;
if (isAssignable(ZfDate.class,type)) return true;
if (isAssignable(Date.class,type)) return true;
if (isAssignable(String.class,type)) return true;
if (BigDecimal.class.isAssignableFrom(type)) return true;
if (type.isEnum()) return true;
if (type.equals(Profile.class)) return true;
return isPrimitiveOrWrapper(type);
}
private Object generatePrimitveValue(Class<?> type, String methodName) {
if (methodName.equals("getClass")) return type;
if (String.class.isAssignableFrom(type)) return generateStringBasedOnName(methodName);
if (BigDecimal.class.isAssignableFrom(type)) return new BigDecimal(randomNumeric(3)+"."+randomNumeric(8));
if (isAssignable(type,Boolean.class,true)) return TRUE;
if (isAssignable(type,Integer.class,true)) return Integer.valueOf(random.nextInt(100));
if (type.isEnum()) return getEnum(type);
if (isAssignable(type,ZfDate.class,true)) return ZfDateFactory.create(supportedDateFormatts[random.nextInt(3)]);
if (isAssignable(type,Date.class,true)) return new Date();
if (isAssignable(type,Long.class,true)) return Long.valueOf(random.nextInt(100));
if (Profile.class.equals(type)) return new Profile(ConformanceLevel.EXTENDED);
throw new IllegalArgumentException("Type " + type + " was not found");
}
private Object getEnum(Class<?> type) {
Object[] enumConstants = type.getEnumConstants();
return enumConstants[random.nextInt(enumConstants.length)];
}
private String generateStringBasedOnName(String methodName) {
String name = jlorem.randomWord();
if (methodName.contains("UnitCode")) {
return name.substring(0, name.length()<2?name.length():2);
}
if (methodName.contains("currencyId") ) {
return RandomStringUtils.randomAlphabetic(3).toUpperCase();
}
if (methodName.contains("Number") ) {
return String.valueOf(new Random().nextInt(1000));
}
if (methodName.contains("Telephone") || methodName.contains("Fax") ) {
return "+4178906011";
}
if (methodName.contains("Description")) {
return jlorem.sentences(2);
}
if (methodName.contains("Email")) {
return "hello@konik.io";
}
if (methodName.contains("Id") ) {
if(random.nextBoolean()) {
return String.valueOf(random.nextInt(999));
}
return RandomStringUtils.randomAlphabetic(3).toUpperCase();
}
return name;
}
}