package codegen.codetemplates.templatecompletion.replacementrule; import java.lang.reflect.Method; import java.util.regex.Matcher; import java.util.regex.Pattern; import codegen.codetemplates.CodeTemplate; import static CIAPI.Java.logging.Log.debug; import static CIAPI.Java.logging.Log.trace; import static CIAPI.Java.logging.Log.error; import static CIAPI.Java.logging.Log.warn; /** * Class that handles parsing a string and using reflection to create actual method calls and * resulting return values. * * @author Justin Nelson * */ public abstract class Replacement { protected String templateValue; protected String objectValue; protected String objectName; /** * Creates a new Replacement with the given values. * * @param templateValue * the name of the value in the template to replace * @param objectValue * the methods to call on the object to retrieve the desired result * @param objectName * the name of the variable we are calling methods on */ public Replacement(String templateValue, String objectValue, String objectName) { this.templateValue = templateValue; this.objectValue = objectValue; this.objectName = objectName; } /** * The name of the template value we are replacing * * @return */ public String getTemplateName() { return templateValue; } /** * Given some object, call the method chain and create a result. * * @param obj * the object to mutate * @return the resulting object */ protected Object resolveValue(Object obj) { trace("Beginning process of resolving value from object"); Object result = obj; if (objectValue.startsWith("@")) { // Here a string literal was given to us return objectValue.substring(1); } else { // otherwise we are using reflection to call the specified method // break the statement into the different method calls String[] methods = objectValue.split("\\."); for (String method : methods) { if (!method.contains("(")) { // this is the case for the name of the variable the methods are being called on // we just ignore it continue; } // Now we parse the method parts. Find the method name and the method arguments. String name = method.substring(0, method.indexOf('(')); String methodArgs = method.substring(method.indexOf("(") + 1, method.indexOf(')')); try { if (methodArgs.trim().length() == 0) { // No arguments to the method Method method2 = result.getClass().getMethod(name); method2.setAccessible(true); trace("Invoking method '" + method2.getName() + "' on object '" + result + "'"); result = method2.invoke(result); } else { // We have method args. We need to determine the type Class<?> argType = inferType(methodArgs); Object value = getValue(methodArgs); Method method2 = result.getClass().getMethod(name, argType); method2.setAccessible(true); trace("Invoking method '" + method2.getName() + "' on object '" + result + "' with parameter '" + value + "'"); result = method2.invoke(result, value); } } catch (Exception e) { warn(e.getMessage(), e); // Whoops, let's just keep going and see if we can partially recover later return null; } } } return result; } private Object getValue(String methodArgs) { if (methodArgs.startsWith("#")) { return Integer.parseInt(methodArgs.substring(1)); } warn("Could not get value fron value: " + methodArgs); return null; } /** * Used to parse the type of an incoming value. Right now we only support strings and ints. * * @param methodArgs * the method arguments that were passed into one of the methods * @return the type of the parameter passed into the method */ private Class<?> inferType(String methodArgs) { if (methodArgs.startsWith("$")) { return String.class; } else if (methodArgs.startsWith("#")) { return int.class; } warn("Could not infer type fron value: " + methodArgs); return null; } /** * Fills a template given the rules of this replacement object * * @param obj * the model object used to populate the template * @param template * the code template to fill */ public abstract void fillTemplateHole(Object obj, CodeTemplate template); }