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.Set; import java.util.Map.Entry; 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.DataTransferChannel.IResourceStateAccessor; import models.dataFlowModel.PushPullAttribute; import models.dataFlowModel.PushPullValue; import models.dataFlowModel.ChannelNode; import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; import models.dataFlowModel.StoreAttribute; public class JavaMethodBodyGenerator { 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 { 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 = JavaCodeGenerator.getComponentName(src.getResourceHierarchy()); String dstResourceName = JavaCodeGenerator.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; for (ChannelMember cm: ch.getInputChannelMembers()) { if (src.getOutSideResources().contains(cm.getResource()) && 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 = JavaCodeGenerator.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, JavaCodeGenerator.pushAccessor).getKey(); } else { // if there exists one or more reference channel member. HashMap<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>(); for (ChannelMember in: ch.getInputChannelMembers()) { inputResourceToStateAccessor.put(in, JavaCodeGenerator.pushAccessor); } for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c, JavaCodeGenerator.refAccessor); } updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.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 (JavaCodeGenerator.generatesComponent(descendantRes)) break; children = descendantRes.getChildren(); } while (children != null && children.size() == 1 && (descendantRes = children.iterator().next()) != null); Type descendantStateType = descendantRes.getResourceStateType(); String descendantComponentName = JavaCodeGenerator.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 = JavaCodeGenerator.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 (JavaCodeGenerator.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", "." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.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." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outRes)) + " = " + newState + ";"; } } 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}"); } } if (resToCh.getDestination().getIndegree() > 1 || (resToCh.getDestination().getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // update a cache of src side resource (when incoming edges are multiple) String cacheStatement = "this." + JavaCodeGenerator.toVariableName(srcResourceName) + " = " + JavaCodeGenerator.toVariableName(srcResourceName) + ";"; if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { update.addStatement(cacheStatement); } } if (((StoreAttribute) dst.getAttribute()).isStored()) { // returns the current state stored in a field. MethodDeclaration getter = null; if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { getter = getMethod(dstComponent, "getValue"); } else { getter = getGetterMethod(dstComponent, dstResourceName); } if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { Type resourceType = dst.getResourceStateType(); if (dst.getResourceHierarchy().getNumParameters() == 0) { if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { // dst has a component. if (model.isPrimitiveType(resourceType)) { getter.addStatement("return value;"); } else { // copy the current state to be returned as a 'value' String implTypeName = resourceType.getImplementationTypeName(); getter.addStatement("return new " + implTypeName + "(value);"); } } else { // dst has no component. String dstResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(dst.getResourceHierarchy())); if (model.isPrimitiveType(resourceType)) { getter.addStatement("return " + dstResName + ";"); } else { // copy the current state to be returned as a 'value' String implTypeName = resourceType.getImplementationTypeName(); getter.addStatement("return new " + implTypeName + "(" + dstResName + ");"); } } } else { String[] sideEffects = new String[] {""}; 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()); String curState = selector.toImplementation(sideEffects); getter.addStatement(sideEffects[0] + "return " + curState + ";"); } 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()); String curState = selector.toImplementation(sideEffects); getter.addStatement(sideEffects[0] + "return " + curState + ";"); } } } } // src side (for a chain of update method invocations) ChannelMember in = null; for (ChannelMember cm: ch.getInputChannelMembers()) { if (src.getOutSideResources().contains(cm.getResource())) { in = cm; break; } } String srcResName = null; if (srcComponent == null) { String srcParentResourceName = JavaCodeGenerator.getComponentName(src.getResourceHierarchy().getParent()); srcComponent = componentMap.get(srcParentResourceName); srcResName = srcResourceName; } for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent, srcResName)) { ResourcePath dstRes = out.getResource(); // Values of path parameters. String pathParams = ""; for (Expression pathParam: dstRes.getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; pathParams += pathVar.getName() + ", "; } } // Values of channel parameters. String chParams = ""; for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); chParams += selVar.getName() + ", "; } } String refParams = ""; Set<ResourcePath> referredSet = referredResources.get(srcUpdate); for (ChannelMember rc: ch.getReferenceChannelMembers()) { // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(srcUpdate, referredSet); } if (!dst.getInSideResources().contains(ref)) { String refVarName = ref.getLeafResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); } refParams += ", " + refVarName; } } String updateMethodName = null; if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { updateMethodName = "updateFrom" + srcResourceName; } else { updateMethodName = "update" + dstResourceName + "From" + srcResourceName; } // Value of the source side (input side) resource. String srcFieldName = "value"; if (!JavaCodeGenerator.generatesComponent(src.getResourceHierarchy())) { srcFieldName = JavaCodeGenerator.toVariableName(srcResourceName); } if (!outsideOutputResource) { // The destination resource is not outside. if (srcComponent != dstComponent) { srcUpdate.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); } else { srcUpdate.addStatement("this." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); } } else { // Use the reference field to refer to outside destination resource. srcUpdate.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); } } for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { ResourcePath dstRes = out.getResource(); // Values of path parameters. String pathParams = ""; for (Expression pathParam: dstRes.getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; pathParams += pathVar.getName() + ", "; } } // Values of channel parameters. String chParams = ""; for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); chParams += selVar.getName() + ", "; } } String refParams = ""; Set<ResourcePath> referredSet = referredResources.get(srcInput); for (ChannelMember rc: ch.getReferenceChannelMembers()) { // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(srcInput, referredSet); } if (!dst.getInSideResources().contains(ref)) { String refVarName = ref.getLeafResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); } refParams += ", " + refVarName; } } String updateMethodName = null; if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { updateMethodName = "updateFrom" + srcResourceName; } else { updateMethodName = "update" + dstResourceName + "From" + srcResourceName; } String srcFieldName = "value"; if (!JavaCodeGenerator.generatesComponent(src.getResourceHierarchy())) { srcFieldName = JavaCodeGenerator.toVariableName(srcResourceName); } if (!outsideOutputResource) { // The destination resource is not outside. if (srcComponent != dstComponent) { srcInput.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); } else { srcInput.addStatement("this." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); } } else { // Use the reference field to refer to outside destination resource. srcInput.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); } } } else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // for pull (or push/pull) data transfer if (dstComponent == null) { String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); dstComponent = componentMap.get(dstParentResourceName); } MethodDeclaration getter = null; if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { getter = getMethod(dstComponent, "getValue"); } else { getter = getGetterMethod(dstComponent, dstResourceName); } if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { boolean isContainedPush = false; Map<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 in = null; for (ChannelMember cm: ch2.getInputChannelMembers()) { if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { in = cm; break; } } if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { isContainedPush = true; inputResourceToStateAccessor.put(in, JavaCodeGenerator.pushAccessor); } else { inputResourceToStateAccessor.put(in, JavaCodeGenerator.pullAccessor); } } } // for reference channel members for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c, JavaCodeGenerator.pullAccessor); // by pull data transfer } String[] sideEffects = new String[] {""}; // generate a return statement. // An input resource is outside. if (!isContainedPush) { // All incoming edges are in PULL style. String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).getKey().toImplementation(sideEffects); getter.addStatement(sideEffects[0] + "return " + curState + ";"); } else { // At least one incoming edge is in PUSH style. String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).getKey().toImplementation(sideEffects); getter.addStatement(sideEffects[0] + "return " + curState + ";"); } } if (outsideInputResource) { // Update fields to refer to outside resources. Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor); if (resourcePaths != null && resourcePaths.size() > 0) { for (ChannelMember outsideMember: resourcePaths.keySet()) { ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { outsidePath = outsidePath.getParent(); } String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); Set<ChannelMember> dependingMembers = resourcePaths.get(outsideMember).getValue(); for (ChannelMember dependingMember: dependingMembers) { ResourcePath dependingRes = dependingMember.getResource(); ResourceNode dependingNode = null; PushPullAttribute pushPull2 = null; for (Edge resToCh2: resToCh.getDestination().getInEdges()) { if (((ResourceNode) resToCh2.getSource()).getOutSideResources().contains(dependingRes)) { dependingNode = (ResourceNode) resToCh2.getSource(); pushPull2 = (PushPullAttribute) resToCh.getAttribute(); } } TypeDeclaration dependingComponent = null; if (JavaCodeGenerator.generatesComponent(dependingRes.getResourceHierarchy())) { String dependingResourceName = JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()); dependingComponent = componentMap.get(dependingResourceName); } else { String dependingParentResourceName = JavaCodeGenerator.getComponentName(dependingRes.getParent().getResourceHierarchy()); dependingComponent = componentMap.get(dependingParentResourceName); } Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { outsideExp = ((Term) outsideExp).getChild(0); } Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); if (nextExp != null && outsideExp instanceof Term) { if (nextExp instanceof Variable) { outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())))); } else { // ToDo. } } if (dstComponent == dependingComponent) { // In the common parent. if (dependingNode != null) { // Inspect further dependency. for (Edge chToRes2: dependingNode.getInEdges()) { DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); if (ch2.getInputChannelMembers().size() == 0) { // In an input method of the parent component. Set<ChannelMember> outs = ch2.getOutputChannelMembers(); MethodDeclaration input = getInputMethod(dependingComponent, outs.iterator().next(), outs.size()); String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); input.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. // Update constructor. MethodDeclaration constructor = getConstructor(dependingComponent); constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. } else { boolean isPush = true; for (Edge resToCh2: chToRes2.getSource().getInEdges()) { if (((PushPullAttribute) resToCh2.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { isPush = false; break; } } if (isPush) { String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); for (Edge resToCh2: chToRes2.getSource().getInEdges()) { // In an update method of the parent component. ResourceNode dependingResSrc = (ResourceNode) resToCh2.getSource(); MethodDeclaration update = null; if (JavaCodeGenerator.generatesComponent(dependingRes.getResourceHierarchy())) { update = getUpdateMethod(dependingComponent, null, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); } else { String dependingResName = JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()); update = getUpdateMethod(dependingComponent, dependingResName, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); } update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. } // Update constructor. MethodDeclaration constructor = getConstructor(dependingComponent); constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. } } } } } else { if (pushPull2.getOptions().get(0) == PushPullValue.PUSH) { // In an update method of the destination component. MethodDeclaration update = null; if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { update = getUpdateMethod(dstComponent, null, JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())); } else { String dstResName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy()); update = getUpdateMethod(dstComponent, dstResName, JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())); } String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. // Update constructor. MethodDeclaration constructor = getConstructor(dstComponent); constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. } } } } } } } } } } } } // for source nodes TypeDeclaration mainComponent = componentMap.get(JavaCodeGenerator.mainTypeName); for (ResourceHierarchy resource: model.getResourceHierarchies()) { String resourceName = JavaCodeGenerator.getComponentName(resource); TypeDeclaration component = componentMap.get(resourceName); if (JavaCodeGenerator.generatesComponent(resource)) { if (component != null) { // state getter method Type resourceType = JavaCodeGenerator.getImplStateType(resource); MethodDeclaration stateGetter = getMethod(component, "getValue"); if (stateGetter != null && (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 = JavaCodeGenerator.getComponentName(child); String fieldName = JavaCodeGenerator.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, JavaCodeGenerator.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 = JavaCodeGenerator.getComponentName(descendant); selector = new Field(JavaCodeGenerator.toVariableName(fieldName)); } do { String methodName = JavaCodeGenerator.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 (JavaCodeGenerator.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()) { Set<ChannelMember> outs = entry.getValue(); DataTransferChannel ch = entry.getKey(); for (ChannelMember out: outs) { MethodDeclaration input = null; if (JavaCodeGenerator.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(JavaCodeGenerator.getComponentName(parent)); input = getInputMethod(parentType, out, ch.getOutputChannelMembers().size()); } } if (input != null) { // In each resource Expression updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.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 (JavaCodeGenerator.generatesComponent(descendantRes)) break; children = descendantRes.getChildren(); } while (children != null && children.size() == 1 && (descendantRes = children.iterator().next()) != null); Type descendantStateType = descendantRes.getResourceStateType(); String descendantComponentName = JavaCodeGenerator.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 = JavaCodeGenerator.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 (JavaCodeGenerator.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.addFirstStatement(updateStatement); } } else { String updateStatement = ""; if (sideEffects[0] != null) { updateStatement = sideEffects[0]; updateStatement = updateStatement.replace(".value", "." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.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." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(resource)) + " = " + newState + ";"; } if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { input.addFirstStatement(updateStatement); } } // In the main type if (mainComponent != null) { 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(mainComponent, inputAccessorName); if (inputAccessor != null) { 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 refVarName = ref.getLeafResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); Expression refGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, null); sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); inputAccessor.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); } } } Expression resExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), null); List<String> args = new ArrayList<>(); if (resExp instanceof Term) { // to access the parent if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { args.add(((Variable)((Term) resExp).getChild(1)).getName()); } resExp = ((Term) resExp).getChild(0); } String resourceAccess = resExp.toImplementation(new String[] {null}); // Values of channel parameters. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); if (!args.contains(selVar.getName())) { args.add(selVar.getName()); } } } // Values of message parameters. if (message instanceof Term) { for (Map.Entry<Position, Variable> varEnt: message.getVariables().entrySet()) { String refVarName = null; for (ChannelMember rc: ch.getReferenceChannelMembers()) { Expression varExp = rc.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); if (varExp != null && rc.getStateTransition().getCurStateExpression().contains(varExp)) { refVarName = rc.getResource().getLeafResourceName(); break; } } if (refVarName != null) { if (!args.contains(refVarName)) { args.add(refVarName); } } else { if (!args.contains(varEnt.getValue().getName())) { args.add(varEnt.getValue().getName()); } } } } String argsStr = ""; String delimiter = ""; for (String arg: args) { argsStr += delimiter + arg; delimiter = ", "; } inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + argsStr + ");"); } } } } } } } 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, descendantType)); 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 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 List<MethodDeclaration> getInputMethods(TypeDeclaration component, ResourceHierarchy 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.equals(out.getResource().getResourceHierarchy())) { 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" + JavaCodeGenerator.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; } }