package play.utils.crud;

import java.lang.reflect.Method;

import org.springframework.util.ReflectionUtils;

import play.Logger;
import play.Logger.ALogger;
import play.twirl.api.Html;
import play.twirl.api.Content;
import play.mvc.Controller;
import play.mvc.Result;

public class TemplateController extends Controller {

	protected final ALogger log = Logger.of(getClass());
	
	private ClassLoader classLoader;
	protected String templatePackageName = "views.html."; //derived classes can change this. MUST end in "."

	public TemplateController(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	public TemplateController() {
	}

	protected Result ok(String template, Parameters params) {
		return ok(render(template, params));
	}

	protected Result badRequest(String template, Parameters params) {
		return badRequest(render(template, params));
	}
	
	protected Content render(String template, Parameters params) {
		Content content;
		try {
			content = call(templatePackageName + template, "render", params);
		} catch (ClassNotFoundException | MethodNotFoundException e) {
			if (log.isDebugEnabled())
				log.debug("template not found : '" + template + "'");
			return templateNotFound(template, params);
		}
		return content;
	}

	protected Content templateNotFound(String template, Parameters params) {
		StringBuilder sb = new StringBuilder("Template ");
		sb.append(template).append("(");
		Class<?>[] types = params.types();
		for (int i = 0; i < types.length; i++) {
			Class<?> type = types[i];
			if (i != 0)
				sb.append(", ");
			sb.append(type.getSimpleName());
		}
		sb.append(") is not found");
		return Html.apply(sb.toString());
	}

	protected <T> Parameters with() {
		return new Parameters();
	}

	protected <T> Parameters with(Class<?> paramType, T paramValue) {
		return new Parameters(paramType, paramValue);
	}

	@SuppressWarnings("unchecked")
	protected <R> R call(String className, String methodName, Parameters params)
			throws ClassNotFoundException, MethodNotFoundException {
		if (log.isDebugEnabled())
			log.debug("call <-");
		if (log.isDebugEnabled())
			log.debug("className : " + className);
		if (log.isDebugEnabled())
			log.debug("methodName : " + methodName);
		if (log.isDebugEnabled())
			log.debug("params : " + params);
		
		ClassLoader cl = classLoader();
		Object result = null;
		Class<?> clazz = cl.loadClass(className);
		if (log.isDebugEnabled())
			log.debug("clazz : " + clazz);

		Class<?>[] paramTypes = params.types();
		Object[] paramValues = params.values();
		Method method = ReflectionUtils.findMethod(clazz, methodName,
				paramTypes);
		if (log.isDebugEnabled())
			log.debug("method : " + method);

		if (method == null) {
			throw new MethodNotFoundException(className, methodName, paramTypes);
		}
		result = ReflectionUtils.invokeMethod(method, null, paramValues);
		return (R) result;
	}

	private ClassLoader classLoader() {
		//create class loader if one does not exist
		if (classLoader == null) {
			classLoader = getClass().getClassLoader();
		}
		return classLoader;
	}

	public class MethodNotFoundException extends Exception {

		private static final long serialVersionUID = 1L;
		private String className;
		private String methodName;
		private Class<?>[] paramTypes;

		public MethodNotFoundException(String className, String methodName,
				Class<?>[] paramTypes) {
			this.className = className;
			this.methodName = methodName;
			this.paramTypes = paramTypes;
		}

		public String toString() {
			StringBuffer sb = new StringBuffer(className);
			sb.append(".").append(methodName).append("(");
			for (int i = 0; i < paramTypes.length; i++) {
				Class<?> t = paramTypes[i];
				sb.append(t.getSimpleName());
				if (i < paramTypes.length - 1)
					sb.append(", ");
			}
			sb.append(")");
			return sb.toString();
		}

	}
}

@@ -495,16 +495,51 @@
 nge this
+ by reassigning it in a constructor
 . MUST e

Update TemplateController.java