package generators; import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import algorithms.TypeInference; import code.ast.CodeUtil; import code.ast.CompilationUnit; import code.ast.MethodDeclaration; import code.ast.TypeDeclaration; import code.ast.VariableDeclaration; import models.Edge; import models.algebra.Constant; import models.algebra.Expression; import models.algebra.Field; import models.algebra.InvalidMessage; import models.algebra.Parameter; import models.algebra.ParameterizedIdentifierIsFutureWork; import models.algebra.Position; import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; import models.algebra.UnificationFailed; import models.algebra.ValueUndefined; import models.algebra.Variable; import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; import models.dataConstraintModel.JsonAccessor; import models.dataConstraintModel.JsonTerm; import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataConstraintModel.Selector; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.PushPullAttribute; import models.dataFlowModel.PushPullValue; import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; import models.dataFlowModel.ChannelNode; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; import models.dataFlowModel.StoreAttribute; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; public class JerseyMethodBodyGenerator { private static String baseURL = "http://localhost:8080"; public static ArrayList<CompilationUnit> doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList<CompilationUnit> codes) { // Create a map from type names (lower case) to their types. Map<String, TypeDeclaration> componentMap = new HashMap<>(); for (CompilationUnit code: codes) { for (TypeDeclaration component: code.types()) { componentMap.put(component.getTypeName(), component); } } // Generate the body of each update or getter method. try { Set<MethodDeclaration> chainedCalls = new HashSet<>(); Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); for (Edge e: graph.getEdges()) { DataFlowEdge resToCh = (DataFlowEdge) e; if (!resToCh.isChannelToResource()) { PushPullAttribute pushPull = (PushPullAttribute) resToCh.getAttribute(); ResourceNode src = (ResourceNode) resToCh.getSource(); for (Edge chToRes: resToCh.getDestination().getOutEdges()) { ResourceNode dst = (ResourceNode) chToRes.getDestination(); String srcResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy()); String dstResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy()); TypeDeclaration srcComponent = componentMap.get(srcResourceName); TypeDeclaration dstComponent = componentMap.get(dstResourceName); DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); for (ChannelMember out: ch.getOutputChannelMembers()) { if (dst.getInSideResources().contains(out.getResource())) { // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; ChannelMember in = null; for (ChannelMember cm: ch.getInputChannelMembers()) { if (src.getOutSideResources().contains(cm.getResource())) { in = cm; if (cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } } // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = out.isOutside(); if ((pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // for push data transfer MethodDeclaration update = null; if (dstComponent == null) { String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); dstComponent = componentMap.get(dstParentResourceName); update = getUpdateMethod(dstComponent, dstResourceName, srcResourceName); } else { update = getUpdateMethod(dstComponent, null, srcResourceName); } if (((StoreAttribute) dst.getAttribute()).isStored()) { // update stored state of dst side resource (when every incoming edge is in push style) Expression updateExp = null; if (ch.getReferenceChannelMembers().size() == 0) { updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor).getKey(); } else { // if there exists one or more reference channel member. HashMap<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>(); for (Edge chToRes2: dst.getInEdges()) { DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); for (Edge resToCh2: chToRes2.getSource().getInEdges()) { DataFlowEdge dIn = (DataFlowEdge) resToCh2; ChannelMember in2 = null; for (ChannelMember cm: ch2.getInputChannelMembers()) { if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { in2 = cm; break; } } inputResourceToStateAccessor.put(in2, JerseyCodeGenerator.pushAccessor); } } for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c, JerseyCodeGenerator.refAccessor); } updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor).getKey(); } // Replace Json constructor with a constructor of a descendant resource. ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { ResourceHierarchy descendantRes = outRes.getChildren().iterator().next(); Set<ResourceHierarchy> children; do { if (JerseyCodeGenerator.generatesComponent(descendantRes)) break; children = descendantRes.getChildren(); } while (children != null && children.size() == 1 && (descendantRes = children.iterator().next()) != null); Type descendantStateType = descendantRes.getResourceStateType(); String descendantComponentName = JerseyCodeGenerator.getComponentName(descendantRes); TypeDeclaration descendantComponent = componentMap.get(descendantComponentName); if (DataConstraintModel.typeJson.isAncestorOf(descendantStateType)) { replaceJsonTermWithConstructorInvocation(updateExp, descendantStateType, descendantComponentName, descendantComponent); } } // Replace the type of the state field. Type fieldType = JerseyCodeGenerator.getImplStateType(outRes); if (updateExp instanceof Term) { ((Term) updateExp).setType(fieldType); for (Map.Entry<Position, Variable> varEnt: ((Term) updateExp).getVariables().entrySet()) { if (varEnt.getValue().getName().equals("value")) { varEnt.getValue().setType(fieldType); } } } else if (updateExp instanceof Variable) { ((Variable) updateExp).setType(fieldType); } // Add statements to the update method. String[] sideEffects = new String[] {""}; String newState = updateExp.toImplementation(sideEffects); int numOfOutResourcesWithTheSameHierarchy = 0; for (ResourcePath outResPath: ch.getOutputResources()) { if (outResPath.getResourceHierarchy().equals(outRes)) { numOfOutResourcesWithTheSameHierarchy++; } } String updateStatement = ""; if (JerseyCodeGenerator.generatesComponent(outRes)) { if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; } else { updateStatement = sideEffects[0] + "this.value = " + newState + ";"; } } else { if (sideEffects[0] != null) { updateStatement = sideEffects[0]; updateStatement = updateStatement.replace(".value", "." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(outRes))); } if (DataConstraintModel.typeList.isAncestorOf(outRes.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.set); selector.addChild(new Field("value")); selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); selector.addChild(new Constant(newState)); String[] sideEffects2 = new String[] {""}; String newList = selector.toImplementation(sideEffects2); updateStatement += sideEffects2[0]; } else if (DataConstraintModel.typeMap.isAncestorOf(outRes.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.insert); selector.addChild(new Field("value")); selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); selector.addChild(new Constant(newState)); String[] sideEffects2 = new String[] {""}; String newMap = selector.toImplementation(sideEffects2); updateStatement += sideEffects2[0]; } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { updateStatement += "this." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(outRes)) + " = " + newState + ";"; } } // add an update statement of the state of dst side resource. if (numOfOutResourcesWithTheSameHierarchy == 1) { update.addFirstStatement(updateStatement); } else { Term conditions = null; int v = 1; Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); for (Expression pathParam: out.getResource().getPathParams()) { if (pathParam instanceof Variable) { String selfParamName = ((Variable) pathParam).getName(); Expression arg = null; for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); if (selVar.getName().equals(selfParamName)) { arg = selVar; break; } } } if (arg == null) { ResourcePath filledPath = resourcePaths.get(out).getKey(); arg = filledPath.getPathParams().get(v - 1); } Term condition = new Term(DataConstraintModel.eq, new Expression[] { new Parameter("self" + (v > 1 ? v : "")), arg}); if (conditions == null) { conditions = condition; } else { conditions = new Term(DataConstraintModel.and, new Expression[] { conditions, condition}); } } v++; } String ifStatement = "if (" + conditions.toImplementation(new String[] {""})+ ") {\n"; update.addFirstStatement(ifStatement + "\t" + updateStatement.replace("\n", "\n\t") + "\n}"); } } // Calculate in-degree of the destination resource. Set<ResourceHierarchy> inResources = new HashSet<>(); for (ResourceNode rn: graph.getResourceNodes(out.getResource().getResourceHierarchy())) { // ResourceNodes that have the same ResourceHierarchy. for (Edge chToRes2: rn.getInEdges()) { for (Edge resToCh2: chToRes2.getSource().getInEdges()) { inResources.add(((ResourceNode) resToCh2.getSource()).getResourceHierarchy()); } } } int inDegree = inResources.size(); if (inDegree > 1 || (inDegree == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // update a cache of src side resource (when incoming edges are multiple) String cacheStatement = "this." + JerseyCodeGenerator.toVariableName(srcResourceName) + " = " + JerseyCodeGenerator.toVariableName(srcResourceName) + ";"; if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { update.addStatement(cacheStatement); } } // For a post/put REST API. if (outsideOutputResource || (in.getResource().getCommonPrefix(out.getResource()) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { // Inter-services if (dst.getResourceHierarchy().getParent() != null) { // If not a root resource. TypeDeclaration rootComponent = componentMap.get(JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getRoot())); MethodDeclaration update2 = update; update = getMethod(rootComponent, update2.getName()); // get the accessor to the update method. // To make the accessor call the update method. Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), out.getResource().getRoot()); String args = ""; String delimiter = ""; if (resExp instanceof Term) { // to access the parent if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { args += delimiter + ((Variable)((Term) resExp).getChild(1)).getName(); delimiter = ", "; } resExp = ((Term) resExp).getChild(0); } String resourceAccess = resExp.toImplementation(new String[] {""}); int v = 0; for (VariableDeclaration var: update2.getParameters()) { if (v < out.getResource().getPathParams().size()) { args += delimiter + ((Variable) out.getResource().getPathParams().get(v)).getName(); } else { args += delimiter + var.getName(); } delimiter = ", "; v++; } update.addStatement(resourceAccess + "." + update2.getName() + "(" + args + ");"); } // to convert a json param to a tuple, pair or map object. for (VariableDeclaration param: update.getParameters()) { Type paramType = param.getType(); String paramName = param.getName(); String paramConverter = ""; if (DataConstraintModel.typeList.isAncestorOf(paramType) && paramType != DataConstraintModel.typeList) { Type compType = TypeInference.getListComponentType(paramType); if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { param.setType(DataConstraintModel.typeListStr); param.setName(paramName + "_json"); paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = new " + paramType.getImplementationTypeName() + "();\n"; paramConverter += "for (String str: " + param.getName() + ") {\n"; String mapTypeName = convertFromEntryToMapType(compType); paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n"; paramConverter += "}"; update.addThrow("JsonProcessingException"); } else if (DataConstraintModel.typePair.isAncestorOf(compType)) { param.setType(DataConstraintModel.typeListStr); param.setName(paramName + "_json"); paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = new " + paramType.getImplementationTypeName() + "();\n"; paramConverter += "for (String str: " + param.getName() + ") {\n"; String mapTypeName = convertFromEntryToMapType(compType); paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToPair(compType, "i") + ");\n"; paramConverter += "}"; update.addThrow("JsonProcessingException"); } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { param.setType(DataConstraintModel.typeListStr); // To do. } } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType)) { param.setType(DataConstraintModel.typeString); param.setName(paramName + "_json"); paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n"; paramConverter += "{\n"; String mapTypeName = convertFromEntryToMapType(paramType); paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToTuple(paramType, "i") + ";\n"; paramConverter += "}"; update.addThrow("JsonProcessingException"); } else if (DataConstraintModel.typePair.isAncestorOf(paramType)) { param.setType(DataConstraintModel.typeString); param.setName(paramName + "_json"); paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n"; paramConverter += "{\n"; String mapTypeName = convertFromEntryToMapType(paramType); paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToPair(paramType, "i") + ";\n"; paramConverter += "}"; update.addThrow("JsonProcessingException"); } else if (DataConstraintModel.typeMap.isAncestorOf(paramType)) { param.setType(DataConstraintModel.typeString); param.setName(paramName + "_json"); paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = " + "new " + paramType.getImplementationTypeName() + "();\n"; paramConverter += "{\n"; String mapTypeName = convertFromEntryToMapType(paramType); paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; paramConverter += "\t" + getCodeForConversionFromMapToMap(paramType, "i", paramName) + "\n"; paramConverter += "}"; update.addThrow("JsonProcessingException"); } if (paramConverter.length() > 0 && !update.getBody().getStatements().contains(paramConverter)) { update.addFirstStatement(paramConverter); } } } if (((StoreAttribute) dst.getAttribute()).isStored()) { // returns the state stored in a field. MethodDeclaration getter = null; if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { getter = getMethod(dstComponent, "getValue"); } else { getter = getGetterMethod(dstComponent, dstResourceName); } if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { if (dst.getResourceHierarchy().getNumParameters() == 0) { if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { // dst has a component. getter.addStatement("return value;"); } else { // dst has no component. String dstResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy())); getter.addStatement("return " + dstResName + ";"); } } else { if (DataConstraintModel.typeList.isAncestorOf(dst.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.get); selector.addChild(new Field("value")); selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); } else if (DataConstraintModel.typeMap.isAncestorOf(dst.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.lookup); selector.addChild(new Field("value")); selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); } } } } // src side (for a chain of update method invocations) String httpMethod = null; if (out.getStateTransition().isRightUnary()) { httpMethod = "put"; } else { httpMethod = "post"; } String srcName = null; if (srcComponent == null) { String srcParentResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy().getParent()); srcComponent = componentMap.get(srcParentResourceName); srcName = srcResourceName; } for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent, srcName)) { if (srcUpdate != null) { List<Map.Entry<Type, Map.Entry<String, String>>> params = new ArrayList<>(); ResourcePath dstRes = out.getResource(); // Values of channel parameters. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), new AbstractMap.SimpleEntry<>(selVar.getName(), selVar.getName()))); } } // Value of the source side (input side) resource. String srcFieldName = "this.value"; if (!JerseyCodeGenerator.generatesComponent(src.getResourceHierarchy())) { srcFieldName = JerseyCodeGenerator.toVariableName(srcResourceName); } params.add(new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(JerseyCodeGenerator.toVariableName(srcResourceName), srcFieldName))); Set<ResourcePath> referredSet = referredResources.get(srcUpdate); if (ch.getReferenceChannelMembers().size() > 0) { for (ChannelMember rc: ch.getReferenceChannelMembers()) { // For each reference channel member, get the current state of the reference side resource by pull data transfer. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(srcUpdate, referredSet); } if (!dst.getInSideResources().contains(ref)) { String refResourceName = ref.getLeafResourceName(); Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); String[] sideEffects = new String[] {""}; if (rc.isOutside()) { List<String> pathParams = new ArrayList<>(); for (Expression pathExp: ref.getPathParams()) { pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(srcUpdate, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); } else { ResourcePath srcRes = in.getResource(); if (!JerseyCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { srcRes = srcRes.getParent(); } Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); } } // Value of a reference side resource. params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); } } } if (outsideOutputResource || (in.getResource().getCommonPrefix(dstRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { // Inter-servces String[] sideEffects = new String[] {""}; List<String> pathParams = new ArrayList<>(); for (Expression pathExp: dstRes.getPathParams()) { pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } String srcResName = JerseyCodeGenerator.toVariableName(srcResourceName); if (inDegree <= 1) { srcResName = null; } if (!chainedCalls.contains(srcUpdate)) { // The first call to an update method in this method srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstRes.getResourceHierarchy().toResourcePath(pathParams), srcResName, httpMethod)); chainedCalls.add(srcUpdate); } else { // After the second time of call to update methods in this method srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstRes.getResourceHierarchy().toResourcePath(pathParams), srcResName, httpMethod)); } srcUpdate.addThrow("JsonProcessingException"); } else { // Inner-service String updateMethodName = null; if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { updateMethodName = "updateFrom" + srcResourceName; } else { updateMethodName = "update" + dstResourceName + "From" + srcResourceName; } String callParams = ""; String delimiter = ""; // Values of path parameters. for (Expression pathParam: dstRes.getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; callParams += delimiter + pathVar.getName(); delimiter = ", "; } } // Values of other parameters. for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) { callParams += delimiter + paramEnt.getValue().getValue(); delimiter = ", "; } if (srcComponent != dstComponent) { srcUpdate.addStatement("this." + JerseyCodeGenerator.toVariableName(dstResourceName) + "." + updateMethodName + "(" + callParams + ");"); } else { srcUpdate.addStatement("this." + updateMethodName + "(" + callParams + ");"); } if (update != null && update.getThrows() != null && update.getThrows().getExceptions().contains("JsonProcessingException")) { srcUpdate.addThrow("JsonProcessingException"); } } } } for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { List<Map.Entry<Type, Map.Entry<String, String>>> params = new ArrayList<>(); ResourcePath dstRes = out.getResource(); // Values of channel parameters. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), new AbstractMap.SimpleEntry<>(selVar.getName(), selVar.getName()))); } } // Value of the source side (input side) resource. String srcFieldName = "this.value"; if (!JerseyCodeGenerator.generatesComponent(src.getResourceHierarchy())) { srcFieldName = JerseyCodeGenerator.toVariableName(srcResourceName); } params.add(new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(JerseyCodeGenerator.toVariableName(srcResourceName), srcFieldName))); Set<ResourcePath> referredSet = referredResources.get(srcInput); for (ChannelMember rc: ch.getReferenceChannelMembers()) { // For each reference channel member, get the current state of the reference side resource by pull data transfer. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(srcInput, referredSet); } if (!dst.getInSideResources().contains(ref)) { String refResourceName = ref.getLeafResourceName(); Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); String[] sideEffects = new String[] {""}; if (rc.isOutside()) { List<String> pathParams = new ArrayList<>(); for (Expression pathExp: ref.getPathParams()) { pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(srcInput, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); } else { ResourcePath srcRes = in.getResource(); if (!JerseyCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { srcRes = srcRes.getParent(); } Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + ref.getLeafResourceName() + " = " + refExp + ";"); } } // Value of a reference side resource. params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); } } if (outsideOutputResource || (in.getResource().getCommonPrefix(dstRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { // Inter-services String[] sideEffects = new String[] {""}; List<String> pathParams = new ArrayList<>(); for (Expression pathExp: dstRes.getPathParams()) { pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } String srcResName = JerseyCodeGenerator.toVariableName(srcResourceName); if (inDegree <= 1) { srcResName = null; } if (!chainedCalls.contains(srcInput)) { // First call to an update method in this method srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstRes.getResourceHierarchy().toResourcePath(pathParams), srcResName, httpMethod)); chainedCalls.add(srcInput); } else { // After the second time of call to update methods in this method srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstRes.getResourceHierarchy().toResourcePath(pathParams), srcResName, httpMethod)); } srcInput.addThrow("JsonProcessingException"); } else { // Inner-service String updateMethodName = null; if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { updateMethodName = "updateFrom" + srcResourceName; } else { updateMethodName = "update" + dstResourceName + "From" + srcResourceName; } String callParams = ""; String delimiter = ""; // Values of path parameters. for (Expression pathParam: dstRes.getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; callParams += delimiter + pathVar.getName(); delimiter = ", "; } } // Values of other parameters. for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) { callParams += delimiter + paramEnt.getValue().getValue(); delimiter = ", "; } if (srcComponent != dstComponent) { srcInput.addStatement("this." + JerseyCodeGenerator.toVariableName(dstResourceName) + "." + updateMethodName + "(" + callParams + ");"); } else { srcInput.addStatement("this." + updateMethodName + "(" + callParams + ");"); } if (update != null && update.getThrows() != null && update.getThrows().getExceptions().contains("JsonProcessingException")) { srcInput.addThrow("JsonProcessingException"); } } } } else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // for pull (or push/pull) data transfer if (dstComponent == null) { String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); dstComponent = componentMap.get(dstParentResourceName); } MethodDeclaration getter = null; if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { getter = getMethod(dstComponent, "getValue"); } else { getter = getGetterMethod(dstComponent, dstResourceName); } if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { // generate a return statement. Expression curExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).getKey(); // no pull data transfer is included. Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor); String[] sideEffects = new String[] {""}; String curState = curExp.toImplementation(sideEffects); getter.addStatement(sideEffects[0] + "return " + curState + ";"); // For each reference channel member, get the current state of the reference side resource by pull data transfer. for (ChannelMember rc: ch.getReferenceChannelMembers()) { ResourcePath refRes = rc.getResource(); String refResourceName = refRes.getLeafResourceName(); Type refResourceType = refRes.getResourceStateType(); if (rc.isOutside()) { List<String> pathParams = new ArrayList<>(); for (Expression pathExp: refRes.getPathParams()) { pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(getter, refResourceName, refRes.getResourceHierarchy().toResourcePath(pathParams), refResourceType); } else { ResourcePath dstRes = out.getResource(); if (!JerseyCodeGenerator.generatesComponent(dstRes.getResourceHierarchy())) { dstRes = dstRes.getParent(); } Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(refRes, dstRes); String refExp = refGetter.toImplementation(sideEffects); String refTypeName = refResourceType.getInterfaceTypeName(); getter.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); } } for (Entry<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> pathEnt: resourcePaths.entrySet()) { ChannelMember cm = pathEnt.getKey(); ResourcePath src2 = pathEnt.getValue().getKey(); // get outside src resource state by pull data transfer. if (cm.isOutside() || src2.getCommonPrefix(dst.getInSideResource(ch)) == null) { Type srcResourceType = src2.getResourceStateType(); List<String> pathParams = new ArrayList<>(); for (Expression pathExp: src2.getPathParams()) { pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(getter, src2.getLeafResourceName(), src2.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); } } } // get src resource state by pull data transfer. if (src.getNumberOfParameters() == 0 && src.getOutSideResource(ch).getCommonPrefix(dst.getInSideResource(ch)) == null) { Type srcResourceType = src.getResourceStateType(); List<String> pathParams = new ArrayList<>(); generatePullDataTransfer(getter, src.getResourceName(), src.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); } } } } } } } // for source nodes for (ResourceHierarchy resource: model.getResourceHierarchies()) { String resourceName = JerseyCodeGenerator.getComponentName(resource); TypeDeclaration component = componentMap.get(resourceName); if (JavaCodeGenerator.generatesComponent(resource)) { if (component != null) { // state getter method Type resourceType = JerseyCodeGenerator.getImplStateType(resource); MethodDeclaration stateGetter = getMethod(component, "getValue"); if (stateGetter.getBody() == null || stateGetter.getBody().getStatements().size() == 0) { if (model.isPrimitiveType(resourceType)) { // primitive type stateGetter.addStatement("return value;"); } else { if (resource.getChildren() != null && resource.getChildren().size() == 1 && resource.getChildren().iterator().next().getNumParameters() > 0) { // list or map String implTypeName = resourceType.getImplementationTypeName(); // copy the current state to be returned as a 'value' stateGetter.addStatement("return new " + implTypeName + "(value);"); } else { if (resource.getChildren() == null || resource.getChildren().size() == 0) { // a leaf resource String implTypeName = resourceType.getImplementationTypeName(); stateGetter.addStatement("return new " + implTypeName + "(value);"); } else { Term composer = null; Term composerSub = new Constant(DataConstraintModel.nil); composerSub.setType(DataConstraintModel.typeMap); for (ResourceHierarchy child: resource.getChildren()) { String childTypeName = JerseyCodeGenerator.getComponentName(child); String fieldName = JerseyCodeGenerator.toVariableName(childTypeName); Term childGetter = null; if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { // the child is not a class childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD)); childGetter.addChild(new Constant("this")); } else { // the child is a class childGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); childGetter.addChild(new Field(fieldName, JerseyCodeGenerator.getImplStateType(child))); } composer = new Term(DataConstraintModel.insert); composer.addChild(composerSub); composer.addChild(new Constant(fieldName, DataConstraintModel.typeString)); // key composer.addChild(childGetter); // value composer.setType(DataConstraintModel.typeMap); composerSub = composer; } composer.setType(stateGetter.getReturnType()); String[] sideEffects = new String[] {null}; String returnValue = composer.toImplementation(sideEffects); if (sideEffects[0] != null) { stateGetter.addStatement(sideEffects[0] + "return " + returnValue+ ";"); } else { stateGetter.addStatement("return " + returnValue+ ";"); } } } } } // descendant getter method if (resource.getChildren().size() > 0) { for (ResourceHierarchy child: resource.getChildren()) { ResourceHierarchy parent = resource; ResourceHierarchy descendant = child; Set<ResourceHierarchy> children; Expression selector; int params = 0; if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) { selector = new Field("value"); params++; } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { selector = new Field("value"); params++; } else { String fieldName = JerseyCodeGenerator.getComponentName(descendant); selector = new Field(JerseyCodeGenerator.toVariableName(fieldName)); } do { String methodName = JerseyCodeGenerator.getComponentName(descendant); MethodDeclaration descendantGetter = null; for (MethodDeclaration getter: getGetterMethods(component, methodName)) { if ((getter.getParameters() == null && params == 0) || (getter.getParameters() != null && getter.getParameters().size() == params)) { descendantGetter = getter; } } if (descendantGetter != null) { if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) { Term newSelector = new Term(DataConstraintModel.get); newSelector.addChild(selector); newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName())); newSelector.setType(descendantGetter.getReturnType()); selector = newSelector; params++; } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { Term newSelector = new Term(DataConstraintModel.lookup); newSelector.addChild(selector); newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName())); newSelector.setType(descendantGetter.getReturnType()); selector = newSelector; params++; } if (descendantGetter != null && (descendantGetter.getBody() == null || descendantGetter.getBody().getStatements().size() == 0)) { String[] sideEffects = new String[] {null}; String returnValue = selector.toImplementation(sideEffects); if (sideEffects[0] != null) descendantGetter.addStatement(sideEffects[0]); descendantGetter.addStatement("return " + returnValue + ";"); } } if (JerseyCodeGenerator.generatesComponent(descendant)) { // If the descendant generates a component. break; } parent = descendant; children = descendant.getChildren(); } while (children != null && children.size() == 1 && (descendant = children.iterator().next()) != null); } } } } // methods for input events Map<DataTransferChannel, Set<ChannelMember>> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); for (Map.Entry<DataTransferChannel, Set<ChannelMember>> entry: ioChannelsAndMembers.entrySet()) { DataTransferChannel ch = entry.getKey(); Set<ChannelMember> outs = entry.getValue(); for (ChannelMember out: outs) { MethodDeclaration input = null; if (JerseyCodeGenerator.generatesComponent(resource)) { // A component is generated for this resource. input = getInputMethod(component, out, ch.getOutputChannelMembers().size()); } else { // No component is generated for this resource. ResourceHierarchy parent = resource.getParent(); if (parent != null) { TypeDeclaration parentType = componentMap.get(JerseyCodeGenerator.getComponentName(parent)); input = getInputMethod(parentType, out, ch.getOutputChannelMembers().size()); } } if (input != null) { // In each resource Set<ResourcePath> referredSet = referredResources.get(input); for (ChannelMember rc: ch.getReferenceChannelMembers()) { // For each reference channel member, get the current state of the reference side resource by pull data transfer. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(input, referredSet); } if (!out.getResource().equals(ref)) { String refResourceName = ref.getLeafResourceName(); Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); String[] sideEffects = new String[] {""}; if (rc.isOutside()) { List<String> pathParams = new ArrayList<>(); for (Expression pathExp: ref.getPathParams()) { pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(input, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); } else { ResourcePath dstRes = out.getResource(); if (!JerseyCodeGenerator.generatesComponent(dstRes.getResourceHierarchy())) { dstRes = dstRes.getParent(); } Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, dstRes); String refExp = refGetter.toImplementation(sideEffects); String refTypeName = refResourceType.getInterfaceTypeName(); input.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); } } } } Expression updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.refAccessor).getKey(); // Replace Json constructor with a constructor of a descendant resource. ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { ResourceHierarchy descendantRes = outRes.getChildren().iterator().next(); Set<ResourceHierarchy> children; do { if (JerseyCodeGenerator.generatesComponent(descendantRes)) break; children = descendantRes.getChildren(); } while (children != null && children.size() == 1 && (descendantRes = children.iterator().next()) != null); Type descendantStateType = descendantRes.getResourceStateType(); String descendantComponentName = JerseyCodeGenerator.getComponentName(descendantRes); TypeDeclaration descendantComponent = componentMap.get(descendantComponentName); if (DataConstraintModel.typeJson.isAncestorOf(descendantStateType)) { replaceJsonTermWithConstructorInvocation(updateExp, descendantStateType, descendantComponentName, descendantComponent); } } // Replace the type of the state field. Type fieldType = JerseyCodeGenerator.getImplStateType(outRes); if (updateExp instanceof Term) { ((Term) updateExp).setType(fieldType); for (Map.Entry<Position, Variable> varEnt: ((Term) updateExp).getVariables().entrySet()) { if (varEnt.getValue().getName().equals("value")) { varEnt.getValue().setType(fieldType); } } } else if (updateExp instanceof Variable) { ((Variable) updateExp).setType(fieldType); } // Add statements to the input method. String[] sideEffects = new String[] {""}; String newState = updateExp.toImplementation(sideEffects); if (JerseyCodeGenerator.generatesComponent(resource)) { String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; } else { updateStatement = sideEffects[0] + "this.value = " + newState + ";"; } if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { input.addStatement(updateStatement); } } else { String updateStatement = ""; if (sideEffects[0] != null) { updateStatement = sideEffects[0]; updateStatement = updateStatement.replace(".value", "." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource))); } if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.set); selector.addChild(new Field("value")); selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); selector.addChild(new Constant(newState)); String[] sideEffects2 = new String[] {""}; String newList = selector.toImplementation(sideEffects2); updateStatement += sideEffects2[0]; } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.insert); selector.addChild(new Field("value")); selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); selector.addChild(new Constant(newState)); String[] sideEffects2 = new String[] {""}; String newMap = selector.toImplementation(sideEffects2); updateStatement += sideEffects2[0]; } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { updateStatement += "this." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource)) + " = " + newState + ";"; } if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { input.addStatement(updateStatement); } } if (out.getResource().getParent() != null && out.getResource().getParent().getParent() != null) { // In the root resource Expression message = out.getStateTransition().getMessageExpression(); String inputAccessorName = input.getName(); if (message instanceof Term) { inputAccessorName = ((Term) message).getSymbol().getImplName(); } else if (message instanceof Variable) { inputAccessorName = ((Variable) message).getName(); } MethodDeclaration inputAccessor = getMethod(componentMap.get(JerseyCodeGenerator.getComponentName(resource.getRoot())), inputAccessorName); if (inputAccessor != null) { // The expression of the receiver (resource) of the input method. Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), out.getResource().getRoot()); String args = ""; String delimiter = ""; if (resExp instanceof Term) { // to access the parent if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { args += delimiter + ((Variable)((Term) resExp).getChild(1)).getName(); delimiter = ", "; } resExp = ((Term) resExp).getChild(0); } String resourceAccess = resExp.toImplementation(new String[] {""}); // Values of channel parameters. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); args += delimiter + selVar.getName(); delimiter = ", "; } } // Values of message parameters. if (message instanceof Term) { for (Variable mesVar: message.getVariables().values()) { args += delimiter + mesVar.getName(); delimiter = ", "; } } inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + args + ");"); if (input != null && input.getThrows() != null && input.getThrows().getExceptions().contains("JsonProcessingException")) { inputAccessor.addThrow("JsonProcessingException"); } } } } } } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e1) { e1.printStackTrace(); } return codes; } private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration descendantComponent) { Type descendantType = new Type(replacingClassName, replacingClassName); Map<Position, Term> subTerms = ((Term) exp).getSubTerms(Term.class); Iterator<Entry<Position, Term>> termEntItr = subTerms.entrySet().iterator(); while (termEntItr.hasNext()) { Entry<Position, Term> termEnt = termEntItr.next(); Term jsonTerm = termEnt.getValue(); if (jsonTerm.getType() != null) { if (jsonTerm.getType().equals(replacedJsonType)) { if (jsonTerm instanceof JsonTerm || jsonTerm.getSymbol().equals(DataConstraintModel.addMember)) { String constructorInvocation = "new " + replacingClassName + "("; MethodDeclaration descendantConstructor = getConstructor(descendantComponent); String delimiter = ""; for (VariableDeclaration var: descendantConstructor.getParameters()) { JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); jsonMember.addChild(jsonTerm); jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); Expression param = jsonMember.reduce(); if (param != null) { if (param instanceof Term) { if (((Term) param).getType() == null) { ((Term) param).setType(var.getType()); } } else if (param instanceof Variable) { if (((Variable) param).getType() == null) { ((Variable) param).setType(var.getType()); } } constructorInvocation = constructorInvocation + delimiter + param.toImplementation(null); } else { constructorInvocation = constructorInvocation + delimiter + var.getName(); } delimiter = ", "; } constructorInvocation += ")"; ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(constructorInvocation)); subTerms = ((Term) exp).getSubTerms(Term.class); termEntItr = subTerms.entrySet().iterator(); } else { jsonTerm.setType(descendantType); } } else { Type oldType = jsonTerm.getType(); Type newType = new Type(oldType.getTypeName(), oldType.getImplementationTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName), oldType.getInterfaceTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName)); for (Type parent: oldType.getParentTypes()) { newType.addParentType(parent); } jsonTerm.setType(newType); } } } } private static void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, String fromResourcePath, Type fromResourceType) { String varName = new String(fromResourceName); String respTypeName = fromResourceType.getInterfaceTypeName(); String respImplTypeName = fromResourceType.getImplementationTypeName(); String respConverter = ""; if (DataConstraintModel.typeList.isAncestorOf(fromResourceType) && fromResourceType != DataConstraintModel.typeList) { Type compType = TypeInference.getListComponentType(fromResourceType); if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { varName += "_json"; String mapTypeName = convertFromEntryToMapType(compType); respTypeName = "List<" + mapTypeName + ">"; respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = new " + fromResourceType.getImplementationTypeName() + "();\n"; respConverter += "for (" + mapTypeName + " i: " + varName + ") {\n"; respConverter += "\t" + fromResourceName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n"; respConverter += "}"; methodBody.addThrow("JsonProcessingException"); } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { // To do. } } else if (DataConstraintModel.typeTuple.isAncestorOf(fromResourceType)) { varName += "_json"; respTypeName = convertFromEntryToMapType(fromResourceType); respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = " + getCodeForConversionFromMapToTuple(fromResourceType, varName) + ";"; respImplTypeName = "HashMap"; } else if (DataConstraintModel.typePair.isAncestorOf(fromResourceType)) { varName += "_json"; respTypeName = convertFromEntryToMapType(fromResourceType); respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = " + getCodeForConversionFromMapToPair(fromResourceType, varName) + ";"; respImplTypeName = "HashMap"; } else if (DataConstraintModel.typeMap.isAncestorOf(fromResourceType)) { varName += "_json"; respTypeName = convertFromEntryToMapType(fromResourceType); respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = new " + fromResourceType.getImplementationTypeName() + "();\n"; respConverter += getCodeForConversionFromMapToMap(fromResourceType, varName, fromResourceName); respImplTypeName = "HashMap"; } if (respConverter.length() > 0) { methodBody.addFirstStatement(respConverter); } methodBody.addFirstStatement(respTypeName + " " + varName + " = " + getHttpMethodCallStatementWithResponse(baseURL, fromResourcePath, "get", respImplTypeName)); } private static String convertFromEntryToMapType(Type type) { String mapTypeName = null; if (DataConstraintModel.typePair.isAncestorOf(type)) { Type compType = TypeInference.getPairComponentType(type); String wrapperType = DataConstraintModel.getWrapperType(compType); if (wrapperType != null) { mapTypeName = "Map<String, " + wrapperType + ">"; } else { mapTypeName = "Map<String, " + compType.getInterfaceTypeName() + ">"; } } else if (DataConstraintModel.typeMap.isAncestorOf(type)) { List<Type> compTypes = TypeInference.getMapComponentTypes(type); String wrapperType = DataConstraintModel.getWrapperType(compTypes.get(1)); if (wrapperType != null) { mapTypeName = "Map<String, " + wrapperType + ">"; } else { mapTypeName = "Map<String, " + compTypes.get(1).getInterfaceTypeName() + ">"; } } else { mapTypeName = type.getInterfaceTypeName(); mapTypeName = mapTypeName.replace("Map.Entry", "Map"); for (int idx = mapTypeName.indexOf("<", 0); idx >= 0; idx = mapTypeName.indexOf("<", idx + 1)) { int to = mapTypeName.indexOf(",", idx); if (to > idx) { mapTypeName = mapTypeName.substring(0, idx + 1) + "String" + mapTypeName.substring(to); // All elements except for the last one have the string type. } } } return mapTypeName; } private static String getCodeForConversionFromMapToTuple(Type tupleType, String mapVar) { String decoded = "$x"; List<Type> elementsTypes = TypeInference.getTupleComponentTypes(tupleType); String elementBase = mapVar; for (Type elmType: elementsTypes.subList(0, elementsTypes.size() - 1)) { elementBase += ".entrySet().iterator().next()"; if (elmType == DataConstraintModel.typeBoolean || elmType == DataConstraintModel.typeInt || elmType == DataConstraintModel.typeLong || elmType == DataConstraintModel.typeFloat || elmType == DataConstraintModel.typeDouble) { String elmVal = CodeUtil.getToValueExp(elmType.getImplementationTypeName(), elementBase + ".getKey()"); decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elmVal + ", $x)"); } else if (elmType == DataConstraintModel.typeString) { decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elementBase + ".getKey(), $x)"); } else { // To do. } elementBase += ".getValue()"; } decoded = decoded.replace("$x", elementBase); return decoded; } private static String getCodeForConversionFromMapToPair(Type pairType, String mapVar) { String decoded = "$x"; decoded = decoded.replace("$x", "new Pair<>(" + mapVar + ".get(\"left\"), $x)"); decoded = decoded.replace("$x", mapVar + ".get(\"right\")"); return decoded; } private static String getCodeForConversionFromMapToMap(Type mapType, String mapVal, String mapVar) { List<Type> elementsTypes = TypeInference.getMapComponentTypes(mapType); Type keyType = elementsTypes.get(0); Type valType = elementsTypes.get(1); String keyVal = null; String decoded = ""; if (keyType == DataConstraintModel.typeBoolean || keyType == DataConstraintModel.typeInt || keyType == DataConstraintModel.typeLong || keyType == DataConstraintModel.typeFloat || keyType == DataConstraintModel.typeDouble) { decoded += "for (String k: " + mapVal + ".keySet()) {\n"; decoded += "\t" + mapVar + ".put("; keyVal = CodeUtil.getToValueExp(keyType.getImplementationTypeName(), "k"); decoded += keyVal + ", " + mapVal + ".get(" + keyVal + ")" + ");\n"; decoded += "}"; } else if (keyType == DataConstraintModel.typeString) { decoded += mapVar + " = " + mapVal + ";"; } return decoded; } private static String getHttpMethodParamsStatement(String callerResourceName, List<Map.Entry<Type, Map.Entry<String, String>>> params, boolean isFirstCall) { String statements = ""; if (isFirstCall) { statements += "Form "; } statements += "form = new Form();\n"; for (Map.Entry<Type, Map.Entry<String, String>> param: params) { Type paramType = param.getKey(); String paramName = param.getValue().getKey(); String value = param.getValue().getValue(); if (DataConstraintModel.typeList.isAncestorOf(paramType)) { Type compType = TypeInference.getListComponentType(paramType); String wrapperType = DataConstraintModel.getWrapperType(compType); if (wrapperType == null) { statements += "for (" + compType.getInterfaceTypeName() + " i: " + value + ") {\n"; } else { statements += "for (" + wrapperType + " i: " + value + ") {\n"; } if (DataConstraintModel.typeTuple.isAncestorOf(compType) || DataConstraintModel.typePair.isAncestorOf(paramType) || DataConstraintModel.typeList.isAncestorOf(compType) || DataConstraintModel.typeMap.isAncestorOf(paramType)) { statements += "\tform.param(\"" + paramName + "\", new ObjectMapper().writeValueAsString(i));\n"; // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0} } else { statements += "\tform.param(\"" + paramName + "\", i.toString());\n"; } statements += "}\n"; // return "Entity<String> entity = Entity.entity(" + paramName + ".toString(), MediaType.APPLICATION_JSON);"; } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType) || DataConstraintModel.typePair.isAncestorOf(paramType) || DataConstraintModel.typeMap.isAncestorOf(paramType)) { // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0} statements += "form.param(\"" + paramName + "\", new ObjectMapper().writeValueAsString(" + value + "));\n"; } else { statements += "form.param(\"" + paramName + "\", " + CodeUtil.getToStringExp(paramType.getImplementationTypeName(), value) + ");\n"; } } if (isFirstCall) { statements += "Entity<Form> "; } statements += "entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED);"; return statements; } 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 getHttpMethodCallStatementWithResponse(String baseURL, String resourceName, String httpMethod, String respImplName) { String responseShortTypeName = respImplName; if (respImplName.contains("<")) { responseShortTypeName = respImplName.substring(0, respImplName.indexOf("<")); } return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(" + responseShortTypeName + ".class);"; } private static MethodDeclaration getConstructor(TypeDeclaration component) { for (MethodDeclaration m: component.getMethods()) { if (m.isConstructor()) return m; } return null; } private static MethodDeclaration getUpdateMethod(TypeDeclaration component, String dstResName, String srcResName) { for (MethodDeclaration m: component.getMethods()) { if (dstResName == null) { if (m.getName().equals("updateFrom" + srcResName)) return m; } else { if (m.getName().equals("update" + dstResName + "From" + srcResName)) return m; } } return null; } private static List<MethodDeclaration> getUpdateMethods(TypeDeclaration component, String resName) { List<MethodDeclaration> updates = new ArrayList<>(); for (MethodDeclaration m: component.getMethods()) { if (resName == null) { if (m.getName().startsWith("updateFrom")) { updates.add(m); } } else { if (m.getName().startsWith("update" + resName + "From")) { updates.add(m); } } } return updates; } private static MethodDeclaration getGetterMethod(TypeDeclaration component, String resourceName) { for (MethodDeclaration m: component.getMethods()) { if (m.getName().startsWith("get" + resourceName)) return m; } return null; } private static List<MethodDeclaration> getGetterMethods(TypeDeclaration component, String resourceName) { List<MethodDeclaration> getters = new ArrayList<>(); for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals("get" + resourceName)) { getters.add(m); } } return getters; } private static Map<DataTransferChannel, Set<ChannelMember>> getIOChannelsAndMembers(ResourceHierarchy resource, DataTransferModel model) { Map<DataTransferChannel, Set<ChannelMember>> ioChannelsAndMembers = new HashMap<>(); for (Channel c: model.getInputChannels()) { DataTransferChannel ch = (DataTransferChannel) c; // I/O channel for (ChannelMember out: ch.getOutputChannelMembers()) { if (resource.equals(out.getResource().getResourceHierarchy())) { if (out.getStateTransition().getMessageExpression() instanceof Term || out.getStateTransition().getMessageExpression() instanceof Variable) { Set<ChannelMember> channelMembers = ioChannelsAndMembers.get(ch); if (channelMembers == null) { channelMembers = new HashSet<>(); ioChannelsAndMembers.put(ch, channelMembers); } channelMembers.add(out); } } } } return ioChannelsAndMembers; } private static List<MethodDeclaration> getInputMethods(TypeDeclaration component, ResourceNode resource, DataTransferModel model) { List<MethodDeclaration> inputs = new ArrayList<>(); for (Channel c: model.getInputChannels()) { DataTransferChannel channel = (DataTransferChannel) c; // I/O channel for (ChannelMember out: channel.getOutputChannelMembers()) { if (resource.getInSideResources().contains(out.getResource())) { MethodDeclaration input = getInputMethod(component, out, channel.getOutputChannelMembers().size()); inputs.add(input); } } } return inputs; } private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember cm, int outNumber) { String inputMethodName = null; if (cm.getStateTransition().getMessageExpression() instanceof Term) { Term message = (Term) cm.getStateTransition().getMessageExpression(); inputMethodName = message.getSymbol().getImplName(); } else if (cm.getStateTransition().getMessageExpression() instanceof Variable) { Variable message = (Variable) cm.getStateTransition().getMessageExpression(); inputMethodName = message.getName(); } if (outNumber > 1) { inputMethodName += "For" + JerseyCodeGenerator.getComponentName(cm.getResource().getResourceHierarchy()); } MethodDeclaration input = getMethod(component, inputMethodName); return input; } private static MethodDeclaration getMethod(TypeDeclaration component, String methodName) { for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; } }