package generators; import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import code.ast.CompilationUnit; import code.ast.MethodDeclaration; import code.ast.TypeDeclaration; import code.ast.VariableDeclaration; import models.Edge; import models.Node; import models.algebra.Constant; import models.algebra.Expression; import models.algebra.Field; import models.algebra.InvalidMessage; import models.algebra.ParameterizedIdentifierIsFutureWork; 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.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> typeMap = new HashMap<>(); for (CompilationUnit code: codes) { for (TypeDeclaration type: code.types()) { typeMap.put(type.getTypeName(), type); } } // 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 srcType = typeMap.get(srcResourceName); TypeDeclaration dstType = typeMap.get(dstResourceName); for (ChannelMember out: ((ChannelNode) resToCh.getDestination()).getChannel().getOutputChannelMembers()) { if (dst.getInSideResources().contains(out.getResource())) { if (pushPull.getOptions().get(0) == PushPullValue.PUSH && srcType != null) { // for push data transfer MethodDeclaration update = getUpdateMethod(dstType, srcType); if (((StoreAttribute) dst.getAttribute()).isStored()) { // update stored state of dst side resource (when every incoming edge is in push style) Expression updateExp = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); String[] sideEffects = new String[] {""}; String curState = updateExp.toImplementation(sideEffects); String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; } else { updateStatement = sideEffects[0] + "value = " + curState + ";"; } if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { update.addFirstStatement(updateStatement); } } if (resToCh.getDestination().getIndegree() > 1) { // update a cash of src side resource (when incoming edges are multiple) String cashStatement = "this." + srcResourceName + " = " + srcResourceName + ";"; if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { update.addFirstStatement(cashStatement); } } if (((StoreAttribute) dst.getAttribute()).isStored()) { // returns the current state stored in a field. MethodDeclaration getter = getGetterMethod(dstType, dst.getResourceStateType()); if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { Type resourceType = dst.getResourceStateType(); if (dst.getResourceHierarchy().getNumParameters() == 0) { 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 { 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) for (MethodDeclaration srcUpdate: getUpdateMethods(srcType)) { String refParams = ""; Set<ResourcePath> referredSet = referredResources.get(srcUpdate); for (ChannelMember rc: ((ChannelNode) resToCh.getDestination()).getChannel().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.getResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(ref, src.getOutSideResource()); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); } refParams += ", " + refVarName; } } srcUpdate.addStatement("this." + dstResourceName + ".update" + srcType.getTypeName() + "(value" + refParams + ");"); } for (MethodDeclaration srcInput: getInputMethods(srcType, src, model)) { String refParams = ""; Set<ResourcePath> referredSet = referredResources.get(srcInput); for (ChannelMember rc: ((ChannelNode) resToCh.getDestination()).getChannel().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.getResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(ref, src.getOutSideResource()); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); } refParams += ", " + refVarName; } } srcInput.addStatement("this." + dstResourceName + ".update" + srcType.getTypeName() + "(value" + refParams + ");"); } } else { // for pull (or push/pull) data transfer if (dst.getNumberOfParameters() == 0) { MethodDeclaration getter = getGetterMethod(dstType, dst.getResourceStateType()); if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { boolean isContainedPush = false; HashMap<ResourcePath, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>(); for (Edge chToRes2: dst.getInEdges()) { for (Edge resToCh2: chToRes2.getSource().getInEdges()) { DataFlowEdge dIn = (DataFlowEdge) resToCh2; if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { isContainedPush = true; inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getOutSideResource(), JavaCodeGenerator.pushAccessor); } else { inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getOutSideResource(), JavaCodeGenerator.pullAccessor); } } } // for reference channel members for (ChannelMember c: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c.getResource(), JavaCodeGenerator.pullAccessor); // by pull data transfer } String[] sideEffects = new String[] {""}; // generate a return statement. if (!isContainedPush) { // All incoming edges are in PULL style. String curState = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects); getter.addStatement(sideEffects[0] + "return " + curState + ";"); } else { // At least one incoming edge is in PUSH style. String curState = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).toImplementation(sideEffects); getter.addStatement(sideEffects[0] + "return " + curState + ";"); } } } } } } } } } // for source nodes TypeDeclaration mainType = typeMap.get(JavaCodeGenerator.mainTypeName); for (ResourceHierarchy resource: model.getResourceHierarchies()) { // ResourceNode resource = (ResourceNode) n; String resourceName = JavaCodeGenerator.getComponentName(resource); TypeDeclaration type = typeMap.get(resourceName); if (type != null) { // state getter method Type resourceType = JavaCodeGenerator.getImplStateType(resource); MethodDeclaration stateGetter = getGetterMethod(type, resourceType); 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 = childTypeName.substring(0, 1).toLowerCase() + childTypeName.substring(1); Term childGetter = null; if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { // the child is not a class childGetter = new Field(fieldName, JavaCodeGenerator.getImplStateType(child)); } 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]); stateGetter.addStatement("return " + returnValue+ ";"); } } } } // child getter method if (resource.getChildren().size() > 0) { for (ResourceHierarchy child: resource.getChildren()) { String methodName = "get" + JavaCodeGenerator.getComponentName(child); MethodDeclaration childGetter = getMethod(type, methodName); if (childGetter != null && (childGetter.getBody() == null || childGetter.getBody().getStatements().size() == 0)) { if (DataConstraintModel.typeList.isAncestorOf(resource.getResourceStateType())) { Term selector = new Term(DataConstraintModel.get); selector.addChild(new Field("value")); selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); selector.setType(childGetter.getReturnType()); String[] sideEffects = new String[] {null}; String returnValue = selector.toImplementation(sideEffects); if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); childGetter.addStatement("return " + returnValue + ";"); } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getResourceStateType())) { Term selector = new Term(DataConstraintModel.lookup); selector.addChild(new Field("value")); selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); selector.setType(childGetter.getReturnType()); String[] sideEffects = new String[] {null}; String returnValue = selector.toImplementation(sideEffects); if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); childGetter.addStatement("return " + returnValue+ ";"); } else { String fieldName = JavaCodeGenerator.getComponentName(child); String returnValue = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); childGetter.addStatement("return this." + returnValue + ";"); } } } } // 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(); for (ChannelMember out: outs) { MethodDeclaration input = getInputMethod(type, out); if (input != null) { // In each resource String[] sideEffects = new String[] {""}; Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); String newState = updateExp.toImplementation(sideEffects); if (resource.getNumParameters() == 0 || resource.getChildren() == null || resource.getChildren().size() == 0) { 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 = null; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { // ToDo. updateStatement = sideEffects[0]; } else { 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 = sideEffects[0] + sideEffects2[0]; } else if (DataConstraintModel.typeMap.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 = sideEffects[0] + sideEffects2[0]; } if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { input.addFirstStatement(updateStatement); } } } // In the main type if (mainType != null) { MethodDeclaration inputAccessor = getMethod(mainType, input.getName()); if (inputAccessor != null) { Expression resExp = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(out.getResource(), null); if (resExp instanceof Term) { // remove '.getValue()' resExp = ((Term) resExp).getChild(0); } String resourceAccess = resExp.toImplementation(new String[] {null}); String args = ""; String delimiter = ""; if (out.getStateTransition().getMessageExpression() instanceof Term) { Term message = (Term) out.getStateTransition().getMessageExpression(); for (Variable var: message.getVariables().values()) { args += delimiter + var.getName(); delimiter = ", "; } } inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + args + ");"); } } } } } } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e1) { e1.printStackTrace(); } return codes; } private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { for (MethodDeclaration m: type.getMethods()) { if (m.getName().equals("update" + from.getTypeName())) return m; } return null; } private static List<MethodDeclaration> getUpdateMethods(TypeDeclaration type) { List<MethodDeclaration> updates = new ArrayList<>(); for (MethodDeclaration m: type.getMethods()) { if (m.getName().startsWith("update")) { updates.add(m); } } return updates; } private static MethodDeclaration getGetterMethod(TypeDeclaration type, Type returnType) { for (MethodDeclaration m: type.getMethods()) { if (m.getName().startsWith("get")) { if (m.getReturnType().getInterfaceTypeName().equals(returnType.getInterfaceTypeName())) return m; } } return null; } private static Map<DataTransferChannel, Set<ChannelMember>> getIOChannelsAndMembers(ResourceHierarchy resource, DataTransferModel model) { Map<DataTransferChannel, Set<ChannelMember>> ioChannelsAndMembers = new HashMap<>(); for (Channel c: model.getIOChannels()) { 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 type, ResourceNode resource, DataTransferModel model) { List<MethodDeclaration> inputs = new ArrayList<>(); for (Channel c: model.getIOChannels()) { DataTransferChannel channel = (DataTransferChannel) c; // I/O channel for (ChannelMember out: channel.getOutputChannelMembers()) { if (resource.getInSideResources().contains(out.getResource())) { MethodDeclaration input = getInputMethod(type, out); inputs.add(input); } } } return inputs; } private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { MethodDeclaration input = null; if (out.getStateTransition().getMessageExpression() instanceof Term) { Term message = (Term) out.getStateTransition().getMessageExpression(); input = getMethod(type, message.getSymbol().getImplName()); } else if (out.getStateTransition().getMessageExpression() instanceof Variable) { Variable message = (Variable) out.getStateTransition().getMessageExpression(); input = getMethod(type, message.getName()); } return input; } private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { for (MethodDeclaration m: type.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; } }