package algorithms; import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import code.ast.CodeUtil; import code.ast.CompilationUnit; import code.ast.MethodDeclaration; import code.ast.TypeDeclaration; import models.Edge; import models.Node; import models.algebra.Expression; import models.algebra.InvalidMessage; import models.algebra.ParameterizedIdentifierIsFutureWork; import models.algebra.Term; import models.algebra.Type; import models.algebra.UnificationFailed; import models.algebra.ValueUndefined; import models.dataConstraintModel.ChannelGenerator; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; import models.dataFlowModel.DataFlowModel; import models.dataFlowModel.DataflowChannelGenerator; import models.dataFlowModel.PushPullAttribute; import models.dataFlowModel.PushPullValue; import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; import models.dataFlowModel.ResourceDependency; import models.dataFlowModel.ResourceDependencyGraph; import models.dataFlowModel.ResourceNode; import models.dataFlowModel.StoreAttribute; public class JerseyMethodBodyGenerator { private static String baseURL = "http://localhost:8080"; public static ArrayList<CompilationUnit> doGenerate(ResourceDependencyGraph graph, DataFlowModel model, ArrayList<CompilationUnit> codes) { // Create a map from type names (lower case) to their types. Map<String, TypeDeclaration> typeMap = new HashMap<>(); for (CompilationUnit code: codes) { for (TypeDeclaration type: code.types()) { typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); } } // Generate the body of each update or getter method. try { Set<MethodDeclaration> chainedCalls = new HashSet<>(); for (Edge e: graph.getEdges()) { ResourceDependency d = (ResourceDependency) e; PushPullAttribute pushPull = (PushPullAttribute) d.getAttribute(); ResourceNode src = (ResourceNode) d.getSource(); ResourceNode dst = (ResourceNode) d.getDestination(); String srcResourceName = src.getIdentifierTemplate().getResourceName(); String dstResourceName = dst.getIdentifierTemplate().getResourceName(); TypeDeclaration srcType = typeMap.get(srcResourceName); TypeDeclaration dstType = typeMap.get(dstResourceName); for (ChannelMember out: d.getChannelGenerator().getOutputChannelMembers()) { if (out.getIdentifierTemplate() == dst.getIdentifierTemplate()) { if (pushPull.getOptions().get(0) == PushPullValue.PUSH && srcType != null) { // for push data transfer MethodDeclaration update = getUpdateMethod(dstType, srcType); if (((StoreAttribute) dst.getAttribute()).isStored()) { // update stored state of dst resource (when every incoming edge is in push style) Expression updateExp = d.getChannelGenerator().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); String[] sideEffects = new String[] {""}; String curState = updateExp.toImplementation(sideEffects); String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; } else { updateStatement = sideEffects[0] + dstResourceName + " = " + curState + ";"; } if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { update.addFirstStatement(updateStatement); } } if (dst.getIndegree() > 1) { // update a cash of src resource (when incoming edges are multiple) String cashStatement = "this." + srcResourceName + " = " + srcResourceName + ";"; if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { update.addFirstStatement(cashStatement); } } MethodDeclaration getter = getGetterMethod(dstType); if (((StoreAttribute) dst.getAttribute()).isStored()) { // returns the state stored in a field. if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { getter.addStatement("return " + dstResourceName + ";"); } } // src side (for a chain of update method invocations) String httpMethod = null; if (((StoreAttribute) dst.getAttribute()).isNeeded()) { httpMethod = "post"; } else { httpMethod = "put"; } for (MethodDeclaration srcUpdate: getUpdateMethods(srcType)) { if (srcUpdate != null) { String srcResName = null; if (dst.getIndegree() > 1) { srcResName = srcResourceName; } if (!chainedCalls.contains(srcUpdate)) { srcUpdate.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), src.getIdentifierTemplate().getResourceStateType(), srcResourceName)); srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); chainedCalls.add(srcUpdate); } else { srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); } } } MethodDeclaration srcInput = getInputMethod(srcType, src, model); if (srcInput != null) { String srcResName = null; if (dst.getIndegree() > 1) { srcResName = srcResourceName; } if (!chainedCalls.contains(srcInput)) { srcInput.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), src.getIdentifierTemplate().getResourceStateType(), srcResourceName)); srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); chainedCalls.add(srcInput); } else { srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); } } } else { // for pull (or push/pull) data transfer MethodDeclaration getter = getGetterMethod(dstType); if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { String[] sideEffects = new String[] {""}; String curState = d.getChannelGenerator().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).toImplementation(sideEffects); getter.addStatement(sideEffects[0] + "return " + curState + ";"); } getter.addFirstStatement(getHttpMethodCallStatement(baseURL, srcResourceName, "get", src.getIdentifierTemplate().getResourceStateType())); } } } } // for source nodes for (Node n: graph.getNodes()) { ResourceNode resource = (ResourceNode) n; String resourceName = resource.getIdentifierTemplate().getResourceName(); TypeDeclaration type = typeMap.get(resourceName); if (type != null) { // getter method MethodDeclaration getter = getGetterMethod(type); if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { getter.addStatement("return " + resource.getIdentifierTemplate().getResourceName() + ";"); } // methods for input events Map.Entry<DataflowChannelGenerator, ChannelMember> ioChannelAndMember = getIOChannelAndMember(resource, model); if (ioChannelAndMember != null) { ChannelMember out = ioChannelAndMember.getValue(); Term message = (Term) out.getStateTransition().getMessageExpression(); MethodDeclaration input = getMethod(type, message.getSymbol().getName()); if (input != null) { Expression updateExp = ioChannelAndMember.getKey().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); String[] sideEffects = new String[] {""}; String newState = updateExp.toImplementation(sideEffects); String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; } else { updateStatement = sideEffects[0] + "this." + resourceName + " = " + newState + ";"; } if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { input.addFirstStatement(updateStatement); } } } } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e1) { e1.printStackTrace(); } return codes; } private static String getHttpMethodParamsStatement(String callerResourceName, Type paramType, String paramName) { if (DataConstraintModel.typeList.isAncestorOf(paramType)) { String compType = "Integer"; String interfaceType = paramType.getInterfaceTypeName(); if (interfaceType.contains("<")) { compType = interfaceType.substring(interfaceType.indexOf("<") + 1, interfaceType.lastIndexOf(">")); } String statements = "Form form = new Form();\n"; statements += "for (" + compType + " i: " + paramName + ") {\n"; statements += "\tform.param(\"" + paramName + "\", i.toString());\n"; statements += "}\n"; statements += "Entity<Form> entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED);"; return statements; // return "Entity<String> entity = Entity.entity(" + paramName + ".toString(), MediaType.APPLICATION_JSON);"; } return "Entity<Form> entity = Entity.entity(new Form().param(\"" + paramName + "\", " + CodeUtil.getToStringExp(paramType.getImplementationTypeName(), paramName) + "), MediaType.APPLICATION_FORM_URLENCODED_TYPE);"; } private static String getHttpMethodCallStatement(String baseURL, String resourceName, String srcResName, String httpMethod) { if (srcResName == null) { return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(entity, String.class);"; } else { // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "/" + srcResName + "\").request()." + httpMethod + "(entity, String.class);"; } } private static String getHttpMethodCallStatement(String baseURL, String resourceName, String httpMethod, Type responseType) { String responseTypeName = responseType.getImplementationTypeName(); String responseShortTypeName = responseTypeName; if (responseTypeName.contains("<")) { responseShortTypeName = responseTypeName.substring(0, responseTypeName.indexOf("<")); } return responseType.getInterfaceTypeName() + " " + resourceName + " = client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(" + responseShortTypeName + ".class);"; } private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { for (MethodDeclaration m: type.getMethods()) { if (m.getName().equals("update" + from.getTypeName())) return m; } return null; } private static List<MethodDeclaration> getUpdateMethods(TypeDeclaration type) { List<MethodDeclaration> updates = new ArrayList<>(); for (MethodDeclaration m: type.getMethods()) { if (m.getName().startsWith("update")) { updates.add(m); } } return updates; } private static MethodDeclaration getGetterMethod(TypeDeclaration type) { for (MethodDeclaration m: type.getMethods()) { if (m.getName().startsWith("get")) return m; } return null; } private static Map.Entry<DataflowChannelGenerator, ChannelMember> getIOChannelAndMember(ResourceNode resource, DataFlowModel model) { for (ChannelGenerator c: model.getIOChannelGenerators()) { DataflowChannelGenerator channel = (DataflowChannelGenerator) c; // I/O channel for (ChannelMember out: channel.getOutputChannelMembers()) { if (out.getIdentifierTemplate().equals(resource.getIdentifierTemplate())) { if (out.getStateTransition().getMessageExpression() instanceof Term) { // not an identity element return new AbstractMap.SimpleEntry<>(channel, out); } } } } return null; } private static MethodDeclaration getInputMethod(TypeDeclaration type, ResourceNode resource, DataFlowModel model) { for (ChannelGenerator c: model.getIOChannelGenerators()) { DataflowChannelGenerator channel = (DataflowChannelGenerator) c; // I/O channel for (ChannelMember out: channel.getOutputChannelMembers()) { if (out.getIdentifierTemplate().equals(resource.getIdentifierTemplate())) { if (out.getStateTransition().getMessageExpression() instanceof Term) { // not an identity element Term message = (Term) out.getStateTransition().getMessageExpression(); MethodDeclaration input = getMethod(type, message.getSymbol().getName()); return input; } } } } return null; } private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { for (MethodDeclaration m: type.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; } }