All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.amygdalum.testrecorder.runtime.GenericObject Maven / Gradle / Ivy

The newest version!
package net.amygdalum.testrecorder.runtime;

import static java.util.Arrays.asList;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static net.amygdalum.testrecorder.runtime.Params.NONE;
import static net.amygdalum.testrecorder.util.Reflections.accessing;
import static net.amygdalum.testrecorder.util.Types.getDeclaredField;
import static net.amygdalum.testrecorder.util.Types.isFinal;
import static net.amygdalum.testrecorder.util.Types.isStatic;
import static net.amygdalum.testrecorder.util.Types.isUnhandledSynthetic;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;

import net.amygdalum.testrecorder.util.Instantiations;
import net.amygdalum.testrecorder.util.Types;

public abstract class GenericObject {

	public static  T forward(Class clazz) {
		return newInstance(clazz);
	}

	public static Wrapped forward(Wrapped wrapped) {
		return wrapped;
	}

	public static void define(Object o, GenericObject genericObject) {
		Object value = o instanceof Wrapped ? ((Wrapped) o).value() : o;
		for (Field field : genericObject.getGenericFields(o.getClass())) {
			try {
				accessing(field).exec(f -> setField(value, f.getName(), f.get(genericObject)));
			} catch (ReflectiveOperationException e) {
				throw new GenericObjectException("definition of object failed.", e);
			}
		}
	}

	public  T as(Class clazz) {
		return as(newInstance(clazz));
	}

	@SuppressWarnings("unchecked")
	public static  T newProxy(Class clazz) {
		return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, new InvocationHandler() {

			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				return null;
			}
		});
	}

	public static  T newEnum(Class clazz) {
		try {
			Method valuesMethod = clazz.getDeclaredMethod("values");
			T value = accessing(valuesMethod).call(m -> {
				Object values = m.invoke(null);
				if (values != null && Array.getLength(values) > 0) {
					return clazz.cast(Array.get(values, 0));
				} else {
					return null;
				}
			});
			return value;
		} catch (ReflectiveOperationException e) {
			throw new GenericObjectException("creation of enum failed", e);
		}
	}

	@SuppressWarnings("unchecked")
	public static  T newInstance(Class clazz) {
		List tries = new ArrayList<>();
		List suppressed = new ArrayList<>();
		try {
			for (Constructor constructor : (Constructor[]) clazz.getDeclaredConstructors()) {
				try {
					return accessing(constructor).call(c -> {
						for (Params params : bestParams(c.getParameterTypes())) {
							try {
								return c.newInstance(params.values());
							} catch (ReflectiveOperationException | RuntimeException e) {
								suppressed.add(e);
								tries.add("new " + clazz.getSimpleName() + params.getDescription());
							}
						}
						throw new InstantiationException();
					});
				} catch (ReflectiveOperationException e) {
					continue;
				}
			}
			return Instantiations.newInstance(clazz);
		} catch (RuntimeException | Error e) {
			suppressed.add(e);
			tries.add("newConstructorForSerialization(" + clazz.getSimpleName() + ")");
			String msg = "failed to instantiate " + clazz.getName() + ", tried:\n" + tries.stream()
				.map(trie -> "\t- " + trie)
				.collect(joining("\n")) + ":";
			throw new GenericObjectException(msg, suppressed.toArray(new Throwable[0]));
		}
	}

	private static Iterable bestParams(Class[] parameterTypes) {
		if (parameterTypes.length == 0) {
			return asList(NONE);
		} else {
			return asList(
				new Params(parameterTypes, DefaultValue.INSTANCE),
				new Params(parameterTypes, NonNullValue.INSTANCE),
				new Params(parameterTypes, NonDefaultValue.INSTANCE));
		}
	}

	public  T as(Supplier constructor) {
		return as(constructor.get());
	}

	public Wrapped as(Wrapped wrapped) {
		for (Field field : getGenericFields(wrapped.getWrappedClass())) {
			try {
				accessing(field).exec(f -> wrapped.setField(f.getName(), f.get(this)));
			} catch (ReflectiveOperationException e) {
				throw new GenericObjectException("setting fields failed:", e);
			}
		}
		return wrapped;
	}

	public  T as(T o) {
		for (Field field : getGenericFields(o.getClass())) {
			try {
				accessing(field).exec(f -> setField(o, f.getName(), f.get(this)));
			} catch (ReflectiveOperationException e) {
				throw new GenericObjectException("settings fields failed:", e);
			}
		}
		return o;
	}

	public static void setField(Object o, String name, Object value) {
		if (value instanceof Wrapped) {
			value = ((Wrapped) value).value();
		}
		Field to = findField(name, o.getClass());
		if (isNonAssignableArray(value, to)) {
			value = copyToAssignableArray(value, to);
		}
		setField(o, to, value);
	}

	private static boolean isNonAssignableArray(Object value, Field to) {
		return to.getType().isArray()
			&& value != null
			&& value.getClass().isArray()
			&& !to.getType().isAssignableFrom(value.getClass());
	}

	private static Object copyToAssignableArray(Object array, Field to) {
		int length = Array.getLength(array);
		Object value = Array.newInstance(to.getType().getComponentType(), length);
		for (int i = 0; i < length; i++) {
			Array.set(value, i, Array.get(array, i));
		}
		return value;
	}

	public static void setField(Object o, Field to, Object value) {
		try {
			accessing(to).exec(f -> f.set(o, value));
		} catch (ReflectiveOperationException e) {
			throw new GenericObjectException("settings field " + to.getName() + " failed:", e);
		}
	}

	public static void copyArrayValues(Object from, Object to) {
		int fromLength = Array.getLength(from);
		int toLength = Array.getLength(to);
		if (fromLength != toLength) {
			throw new GenericObjectException("copying array failed:", new ArrayIndexOutOfBoundsException());
		}
		for (int i = 0; i < fromLength; i++) {
			Object value = Array.get(from, i);
			Array.set(to, i, value);
		}
	}

	public static void copyField(Field field, Object from, Object to) {
		try {
			accessing(field).exec(f -> {
				Object value = f.get(from);
				f.set(to, value);
			});
		} catch (ReflectiveOperationException e) {
			throw new GenericObjectException("copying field " + field.getName() + " failed:", e);
		}
	}

	public static Field findField(String name, Class clazz) {
		try {
			Optional field = getQualifiedField(clazz, name);
			if (field.isPresent()) {
				return field.get();
			}
			return getDeclaredField(clazz, name);
		} catch (NoSuchFieldException e) {
			throw new GenericObjectException("field " + name + " not found:", e);
		}
	}

	public List getGenericFields(Class clazz) {
		Field[] declaredFields = getClass().getDeclaredFields();
		return Stream.of(declaredFields)
			.filter(field -> isSerializable(clazz, field))
			.collect(toList());
	}

	private boolean isSerializable(Class clazz, Field field) {
		if (isUnhandledSynthetic(field) && !getQualifiedField(clazz, field.getName()).isPresent()) {
			return false;
		}
		return !isStatic(field)
			&& !isFinal(field);
	}

	public static Optional getQualifiedField(Class clazz, String name) {
		int nameSeparatorPos = name.lastIndexOf('$');
		if (nameSeparatorPos < 0) {
			return Optional.empty();
		}
		String className = name.substring(0, nameSeparatorPos).replace('$', '.');
		String fieldName = name.substring(nameSeparatorPos + 1);
		return Types.getDeclaredFields(clazz, fieldName).stream()
			.filter(field -> field.getDeclaringClass().getCanonicalName() != null)
			.filter(field -> field.getDeclaringClass().getCanonicalName().equals(className) || field.getDeclaringClass().getSimpleName().equals(className))
			.findFirst();
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy