/* * Copyright 2013 Guidewire Software, Inc. */ package gw.lang.ir.builder.expression; import gw.lang.UnstableAPI; import gw.lang.ir.IJavaClassIRType; import gw.lang.ir.IRExpression; import gw.lang.ir.IRSymbol; import gw.lang.ir.IRType; import gw.lang.ir.IRTypeConstants; import gw.lang.ir.builder.IRArgConverter; import gw.lang.ir.builder.IRBuilderContext; import gw.lang.ir.builder.IRExpressionBuilder; import gw.lang.ir.expression.IRIdentifier; import gw.lang.ir.expression.IRMethodCallExpression; import gw.lang.ir.statement.IRMethodStatement; import gw.lang.reflect.java.IJavaClassConstructor; import gw.lang.reflect.java.IJavaClassMethod; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @UnstableAPI public class IRMethodCallExpressionBuilder extends IRExpressionBuilder { private BuilderImpl _builderImpl; public static IRMethodCallExpressionBuilder callSuperInit(List<IRExpressionBuilder> args) { return new IRMethodCallExpressionBuilder(new SuperInitBuilder(args)); } private IRMethodCallExpressionBuilder(BuilderImpl builderImpl) { _builderImpl = builderImpl; } public IRMethodCallExpressionBuilder(IRExpressionBuilder root, String name, List<IRExpressionBuilder> args) { _builderImpl = new RootAndNameAndArgsBuilder(root, name, args); } public IRMethodCallExpressionBuilder(IRExpressionBuilder root, IJavaClassMethod method, List<IRExpressionBuilder> args) { _builderImpl = new MethodAndArgsBuilder(root, method, args); } public IRMethodCallExpressionBuilder(IRExpressionBuilder root, Method method, List<IRExpressionBuilder> args) { _builderImpl = new JavaMethodAndArgsBuilder(root, method, args); } @Override protected IRExpression buildImpl(IRBuilderContext context) { return _builderImpl.buildImpl(context); } private static IRMethodCallExpression buildCall(IRBuilderContext context, String name, IRExpression root, List<IRExpressionBuilder> argBuilders, MethodInfo methodInfo) { List<IRType> parameterTypes = methodInfo.getParamTypes(); // Convert arguments as needed List<IRExpression> args = new ArrayList<IRExpression>(); for (int i = 0; i < argBuilders.size(); i++) { args.add(IRArgConverter.castOrConvertIfNecessary( parameterTypes.get(i), argBuilders.get(i).build(context))); } IRMethodCallExpression methodCall = new IRMethodCallExpression(name, methodInfo.getOwningType(), methodInfo.isInterface(), methodInfo.getReturnType(), parameterTypes, root, args); methodCall.setSpecial(methodInfo.isSpecial()); return methodCall; } private static interface BuilderImpl { IRMethodCallExpression buildImpl(IRBuilderContext context); } private static final class RootAndNameAndArgsBuilder implements BuilderImpl { private IRExpressionBuilder _root; private String _name; private List<IRExpressionBuilder> _args; private RootAndNameAndArgsBuilder(IRExpressionBuilder root, String name, List<IRExpressionBuilder> args) { _root = root; _name = name; _args = args; } @Override public IRMethodCallExpression buildImpl(IRBuilderContext context) { IRExpression root = _root.build(context); IRType rootType = root.getType(); MethodInfo methodInfo = findMethod(_name, _args.size(), rootType, context); return buildCall( context, _name, root, _args, methodInfo ); } } private static final class MethodAndArgsBuilder implements BuilderImpl { private IRExpressionBuilder _root; private IJavaClassMethod _method; private List<IRExpressionBuilder> _args; private MethodAndArgsBuilder(IRExpressionBuilder root, IJavaClassMethod method, List<IRExpressionBuilder> args) { _root = root; _method = method; _args = args; } @Override public IRMethodCallExpression buildImpl(IRBuilderContext context) { IRExpression root = _root == null ? null : _root.build(context); MethodInfo methodInfo = new MethodInfo( _method ); return buildCall( context, _method.getName(), root, _args, methodInfo ); } } private static final class JavaMethodAndArgsBuilder implements BuilderImpl { private IRExpressionBuilder _root; private Method _method; private List<IRExpressionBuilder> _args; private JavaMethodAndArgsBuilder(IRExpressionBuilder root, Method method, List<IRExpressionBuilder> args) { _root = root; _method = method; _args = args; } @Override public IRMethodCallExpression buildImpl(IRBuilderContext context) { IRExpression root = _root == null ? null : _root.build(context); MethodInfo methodInfo = new MethodInfo( _method ); return buildCall( context, _method.getName(), root, _args, methodInfo ); } } private static final class SuperInitBuilder implements BuilderImpl { private List<IRExpressionBuilder> _args; private SuperInitBuilder(List<IRExpressionBuilder> args) { _args = args; } @Override public IRMethodCallExpression buildImpl(IRBuilderContext context) { IRType type = context.currentClassSuperType(); if (type instanceof IJavaClassIRType) { IJavaClassConstructor cons = findConstructor(((IJavaClassIRType)type).getJavaClassInfo(), _args.size()); MethodInfo methodInfo = new MethodInfo(type, getIRTypes(cons.getParameterTypes()), IRTypeConstants.pVOID(), true); return buildCall(context, "<init>", new IRIdentifier(new IRSymbol("this", context.owningType(), false)), _args, methodInfo); } else { throw new IllegalArgumentException(); } } } private static class MethodInfo { private IRType _owningType; private List<IRType> _paramTypes; private IRType _returnType; private boolean _special; private MethodInfo(IJavaClassMethod method) { this(getIRType(method.getEnclosingClass()), getIRTypes(method.getParameterTypes()), getIRType(method.getReturnClassInfo()), false); } private MethodInfo(Method method) { this(getIRType(method.getDeclaringClass()), getIRTypes(method.getParameterTypes()), getIRType(method.getReturnType()), false); } private MethodInfo(IRType owningType, List<IRType> paramTypes, IRType returnType, boolean special) { _owningType = owningType; _paramTypes = paramTypes; _returnType = returnType; _special = special; } public IRType getOwningType() { return _owningType; } public List<IRType> getParamTypes() { return _paramTypes; } public IRType getReturnType() { return _returnType; } public boolean isInterface() { return _owningType.isInterface(); } public boolean isSpecial() { return _special; } } private static MethodInfo findMethod(String name, int numArgs, IRType rootType, IRBuilderContext context) { if (rootType instanceof IJavaClassIRType) { IJavaClassMethod method = findMethod(((IJavaClassIRType)rootType).getJavaClassInfo(), name, numArgs); return new MethodInfo(method); } else if (rootType.equals(context.owningType())) { IRMethodStatement methodDecl = context.findMethod(name, numArgs); if (methodDecl != null) { List<IRType> paramTypes = new ArrayList<IRType>(); for (IRSymbol paramSymbol : methodDecl.getParameters()) { paramTypes.add(paramSymbol.getType()); } return new MethodInfo(context.owningType(), paramTypes, methodDecl.getReturnType(), false); } else { return findMethod(name, numArgs, context.currentClassSuperType(), context); } } else { throw new IllegalArgumentException("Cannot reference a method only by name on a root expression that's not an IJavaClassIRType"); } } }