package core.framework.impl.template.expression; import core.framework.api.util.Exceptions; import core.framework.api.util.Strings; import core.framework.impl.code.CodeBuilder; import core.framework.impl.code.CodeCompileException; import core.framework.impl.code.DynamicInstanceBuilder; import core.framework.impl.reflect.GenericTypes; import core.framework.impl.template.TemplateContext; import core.framework.impl.template.TemplateMetaContext; import java.lang.reflect.Method; import java.lang.reflect.Type; /** * @author neo */ public class ExpressionBuilder { private final String expressionSource; private final TemplateMetaContext context; private final String location; private final Token token; public ExpressionBuilder(String expressionSource, TemplateMetaContext context, String location) { this.expressionSource = expressionSource; this.context = context; this.location = location; token = new ExpressionParser().parse(expressionSource); } public ExpressionHolder build() { Expression expression = buildExpression(); Type returnType = returnType(token, context.rootClass); return new ExpressionHolder(expression, returnType, expressionSource, location); } private Expression buildExpression() { try { DynamicInstanceBuilder<Expression> builder = new DynamicInstanceBuilder<>(Expression.class, Expression.class.getCanonicalName()); builder.addMethod(buildEval()); return builder.build(); } catch (CodeCompileException e) { throw new Error(Strings.format("failed to compile expression, expression={}, location={}", expressionSource, location), e); } } private String buildEval() { CodeBuilder builder = new CodeBuilder(); builder.append("public Object eval({} context) {\n", TemplateContext.class.getCanonicalName()); builder.indent(1).append("{} $root = ({})context.root;\n", context.rootClass.getCanonicalName(), context.rootClass.getCanonicalName()); context.paramClasses.forEach((name, paramClass) -> builder.indent(1).append("{} {} = ({})context.context(\"{}\");\n", paramClass.getCanonicalName(), name, paramClass.getCanonicalName(), name)); String translatedExpression = new ExpressionTranslator(token, context).translate(); builder.indent(1).append("return {};\n", translatedExpression); builder.append("}"); return builder.build(); } private Type returnType(Token token, Class<?> modelClass) { if (token instanceof MethodToken) { String methodName = ((MethodToken) token).name; Type returnType = methodReturnType(modelClass, methodName); if (((MethodToken) token).next != null) { return returnType(((MethodToken) token).next, GenericTypes.rawClass(returnType)); } return returnType; } else if (token instanceof FieldToken) { Type fieldType = fieldType(modelClass, ((FieldToken) token).name); if (((FieldToken) token).next != null) { return returnType(((FieldToken) token).next, GenericTypes.rawClass(fieldType)); } return fieldType; } else { return ((ValueToken) token).type; } } private Type fieldType(Class<?> modelClass, String fieldName) { Class<?> fieldClass = context.paramClasses.get(fieldName); if (fieldClass != null) return fieldClass; try { return modelClass.getField(fieldName).getGenericType(); } catch (NoSuchFieldException e) { throw new Error(Strings.format("can not find field, class={}, field={}, expression={}, location={}", modelClass, fieldName, expressionSource, location), e); } } private Type methodReturnType(Class<?> modelClass, String methodName) { Method[] methods = modelClass.getMethods(); for (Method method : methods) { if (method.getName().equals(methodName)) return method.getGenericReturnType(); } throw Exceptions.error("can not find method, class={}, method={}, expression={}, location={}", modelClass, methodName, expressionSource, location); } }