package org.ovirt.engine.api.restapi.types; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.UUID; import org.ovirt.engine.core.compat.Guid; public class MappingTestHelper { private static final String SET_ROOT = "set"; private static final String GET_ROOT = "get"; /** * Populate a JAXB model type by recursively walking element tree and * setting leaf nodes to randomized values. * * @param clz * the model type * @return a populated instance */ public static Object populate(Class<?> clz) { return populate(instantiate(clz), clz, new ArrayList<Class<?>>()); } /** * Populate a JAXB model type by recursively walking element tree and * setting leaf nodes to randomized values. * * @param model * the model instance * @param clz * the model type * @param seen * model types seen so far * @return a populated instance */ public static Object populate(Object model, Class<?> clz, List<Class<?>> seen) { for (Method method : clz.getMethods()) { if (isSetter(method)) { if (takesPrimitive(method)) { random(method, model); } else if (takesEnum(method)) { shuffle(method, model); } else { descend(method, model, scope(seen)); } } else if (isGetter(method) && returnsList(method)) { fill(method, model, seen); } } return model; } private static Object instantiate(Class<?> clz) { Object model = null; try { model = clz.newInstance(); } catch (Exception e) { // should never occur, trivial instantiation } return model; } private static boolean takesPrimitive(Method m) { return m.getParameterTypes().length == 1 && (takesString(m) || takesBoolean(m) || takesShort(m) || takesInteger(m) || takesLong(m)); } private static void random(Method m, Object model) { try { m.invoke( model, takesString(m) ? garble(m) : takesShort(m) ? Short.valueOf((short) rand(100)) : takesInteger(m) ? Integer.valueOf(rand(100)) : takesLong(m) ? Long.valueOf(rand(1000000000)) : takesBoolean(m) ? Boolean.valueOf(Math.random() < 0.5D) : null); } catch (Exception e) { // simple setter, exception should not be thrown } } public static <E extends Enum> E shuffle(Class<E> enumType) { E[] values = enumType.getEnumConstants(); return values[rand(values.length)]; } private static void shuffle(Method method, Object model) { Class<? extends Enum> enumType = (Class<? extends Enum>)method.getParameterTypes()[0]; try { method.invoke(model, shuffle(enumType)); } catch (Exception e) { // simple setter, exception should not be thrown } } private static void descend(Method method, Object model, List<Class<?>> seen) { try { Object child = method.getParameterTypes()[0].newInstance(); method.invoke(model, child); if (unseen(method, seen)) { populate(child, child.getClass(), seen); } } catch (Exception e) { // simple setter, exception should not be thrown } } @SuppressWarnings("unchecked") private static void fill(Method method, Object model, List<Class<?>> seen) { try { // List<T> type parameter removed by erasure, hence we attempt to // infer from method name String elementType = method.getName().substring(GET_ROOT.length()); Class<?> childType = coPackaged(model, elementType); if (unseen(childType, seen)) { List<Object> list = (List<Object>) method.invoke(model); Object child = null; if (childType.isEnum()) { Object[] labels = childType.getEnumConstants(); child = labels[rand(labels.length)]; } else { child = childType.newInstance(); } list.add(child); populate(child, child.getClass(), seen); } } catch (Exception e) { // simple getter, exception should not be thrown } } private static boolean isGetter(Method m) { return m.getName().startsWith(GET_ROOT); } private static boolean isSetter(Method m) { return m.getName().startsWith(SET_ROOT); } private static boolean takesString(Method m) { return String.class.equals(m.getParameterTypes()[0]); } private static boolean takesShort(Method m) { return Short.TYPE.equals(m.getParameterTypes()[0]) || Short.class.equals(m.getParameterTypes()[0]); } private static boolean takesInteger(Method m) { return Integer.TYPE.equals(m.getParameterTypes()[0]) || Integer.class.equals(m.getParameterTypes()[0]); } private static boolean takesLong(Method m) { return Long.TYPE.equals(m.getParameterTypes()[0]) || Long.class.equals(m.getParameterTypes()[0]); } private static boolean takesBoolean(Method m) { return Boolean.TYPE.equals(m.getParameterTypes()[0]) || Boolean.class.equals(m.getParameterTypes()[0]); } private static boolean takesEnum(Method m) { return m.getParameterTypes()[0].isEnum(); } private static boolean returnsList(Method m) { return List.class.equals(m.getReturnType()); } private static Class<?> coPackaged(Object model, String elementType) throws ClassNotFoundException { String packageRoot = model.getClass().getPackage().getName() + "."; try { return Class.forName(packageRoot + singular(elementType)); } catch (ClassNotFoundException cnf) { try { return Class.forName(packageRoot + elementType); } catch (ClassNotFoundException cnfe) { // try inner class return Class.forName(model.getClass().getName() + "$" + elementType); } } } private static String singular(String s) { return s.endsWith("s") ? s.substring(0, s.length() - 1) : s; } private static boolean unseen(Class<?> type, List<Class<?>> seen) { boolean ret = !seen.contains(type); if (ret) { seen.add(type); } return ret; } private static boolean unseen(Method m, List<Class<?>> seen) { return unseen(m.getParameterTypes()[0], seen); } private static List<Class<?>> scope(List<Class<?>> seen) { return new ArrayList<Class<?>>(seen); } public static int rand(int ceiling) { return (int) Math.floor(Math.random() * 0.9999 * ceiling); } private static Object garble(Method m) { return m.getName().endsWith("Id") ? new Guid(UUID.randomUUID()).toString() : new String(new byte[] { (byte) (65 + rand(26)), (byte) (65 + rand(26)), (byte) (65 + rand(26)) }); } }