ProxyHelpedLazyImplementation.java

package eu.javaexperience.pdw;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

import eu.javaexperience.asserts.AssertArgument;
import eu.javaexperience.generic.annotations.Ignore;
import eu.javaexperience.interfaces.simple.getBy.GetBy3;
import eu.javaexperience.reflect.Mirror;

public abstract class ProxyHelpedLazyImplementation<I, B extends I, R extends I> //I => ZkjeInterface, Base => ZookeeperPath
{
	protected final Set<String> backendImplMethodNames = new HashSet<>();
	
	protected Class<I> interfaceClass;
	protected B lazyImplObject;
	protected Class<R> rootInterface;
	protected R root;
	
	public ProxyHelpedLazyImplementation(Class<I> interfaceClass, B lazyImplObject, Class<R> rootObjectInterface) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
	{
		AssertArgument.assertTrue(interfaceClass.isInterface(), "interfaceClass must be and interface.");
		AssertArgument.assertNotNull(lazyImplObject, "lazyImplObject");
		AssertArgument.assertTrue(rootObjectInterface.isInterface(), "rootObjectInterface must be and interface.");
		this.interfaceClass = interfaceClass;
		this.rootInterface = rootObjectInterface;
		
		this.lazyImplObject = lazyImplObject;
		//for(Class i:Mirror.getClassData(lazyImplObject.getClass()).getAllSuperClassAndInterfaces())
		{
			for(Method m:lazyImplObject.getClass().getDeclaredMethods())
			{
				if(null == m.getAnnotation(Ignore.class))
				{
					backendImplMethodNames.add(m.getName());
				}
			}
		}
		
		root = wrapWithClass(rootInterface, lazyImplObject);
	}
	
	/**
	 * Relays the call to the "base object"
	 * 
	 * */
	protected final GetBy3<Object, B, Method, Object[]> mapper = (root, b, c)->
	{
		String methodName = b.getName();
		
		try
		{
			//top interface functions:
			if(backendImplMethodNames.contains(methodName))
			{
				return relayApiCall(root, b, c);
			}
			
			if("toString".equals(methodName))
			{
				return "Proxy generated ProxyHelpedLazyImplementation accessor for object: "+lazyImplObject+", with class: "+root.getClass().getSimpleName();
			}
			
			return handleInterfaceCall(root, b, c);
		}
		catch(Throwable e)
		{
			handleException(e);
			return null;
		}
	};

	public abstract Object handleInterfaceCall(B root, Method method, Object[] params) throws Throwable;

	protected void handleException(Throwable e)
	{
		Mirror.propagateAnyway(e);
	}

	public <T extends I> T createInstanceWithDefaultConstructor(Class<T> cls, B zkp) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
	{
		Constructor<?> constr = cls.getConstructor(lazyImplObject.getClass());
		if(null == constr)
		{
			throw new RuntimeException("No constructor "+cls.getSimpleName()+"("+lazyImplObject.getClass().getSimpleName()+" root) defined in the requested class ("+cls.getSimpleName()+").");
		}
		
		return (T) constr.newInstance(zkp);
	}
	
	public <T extends I> T wrapWithClass(Class<T> cls, B elem) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
	{
		if(cls.isInterface())
		{
			return ProxyDataWrapperTools.wrapAccessor(elem, cls, mapper);
		}
		else
		{
			return createInstanceWithDefaultConstructor(cls, elem);
		}
	}
	
	protected Object relayApiCall(B path, Method b, Object[] c) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
	{
		return lazyImplObject.getClass().getDeclaredMethod(b.getName(), b.getParameterTypes()).invoke(path, c);
	}
	
	public R getRoot()
	{
		return root;
	}
}