package generators; 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.Block; import code.ast.CompilationUnit; import code.ast.FieldDeclaration; import code.ast.MethodDeclaration; import code.ast.TypeDeclaration; import code.ast.VariableDeclaration; import models.Edge; import models.Node; import models.algebra.Expression; import models.algebra.InvalidMessage; import models.algebra.ParameterizedIdentifierIsFutureWork; import models.algebra.Position; 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.ResourcePath; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.IFlowGraph; import models.dataFlowModel.PushPullAttribute; import models.dataFlowModel.PushPullValue; import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; import models.dataFlowModel.ResourceNode; import models.dataFlowModel.StoreAttribute; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; public class CodeGeneratorFromDataFlowGraph extends CodeGenerator { public void generateCodeFromFlowGraph(DataTransferModel model, IFlowGraph flowGraph, ArrayList<Set<Node>> components, TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList<CompilationUnit> codes, ILanguageSpecific langSpec) { // For each of other components. for (Set<Node> componentNodeSet: components) { // Declare this resource. Node componentNode = componentNodeSet.iterator().next(); ResourceNode resourceNode = (ResourceNode) componentNode; String resourceName = langSpec.toComponentName(resourceNode.getResource().getResourceName()); TypeDeclaration component = langSpec.newTypeDeclaration(resourceName); // Declare the constructor and the fields to refer to other resources. List<ResourcePath> depends = new ArrayList<>(); MethodDeclaration constructor = declareConstructorAndFieldsToReferToResources(resourceNode, component, depends, langSpec); // Update the main component for this component. updateMainComponent(mainComponent, mainConstructor, componentNode, constructor, langSpec); ResourcePath res = resourceNode.getResource(); Type resStateType = res.getResourceStateType(); // Declare the field in this resource to store the state. if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, res.getInitialValue())); component.addField(stateField); } // Declare the getter method in this resource to obtain the state. MethodDeclaration getter = declareGetterMethod(resourceNode, component, resStateType, langSpec); // Declare the accessor method in the main component to call the getter method. declareAccessorInMainComponent(mainComponent, res, langSpec); // Declare the fields to refer to reference resources. declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); // Declare cache fields and update methods in this resource. List<MethodDeclaration> updates = declareCacheFieldsAndUpdateMethods(resourceNode, component, langSpec); // Declare input methods in this component and the main component. List<MethodDeclaration> inputs = declareInputMethodsInThisAndMainComponents(resourceNode, component, mainComponent, model, langSpec); if (constructor.getParameters() == null) { component.removeMethod(constructor); } // Add compilation unit for this component. CompilationUnit cu = langSpec.newCompilationUnit(component); codes.add(cu); } } private MethodDeclaration declareConstructorAndFieldsToReferToResources(ResourceNode resourceNode, TypeDeclaration component, List<ResourcePath> depends, ILanguageSpecific langSpec) { // Declare a constructor in each component. MethodDeclaration constructor = component.createConstructor(); Block block = new Block(); constructor.setBody(block); // Declare fields in each component. (for data-flow graph) for (Edge e: resourceNode.getOutEdges()) { if (((PushPullAttribute) ((DataFlowEdge) e).getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { // for PUSH transfer addReference(component, constructor, e.getDestination(), langSpec); ResourcePath dstId = ((ResourceNode) e.getDestination()).getResource(); if (!depends.contains(dstId)) depends.add(dstId); } } for (Edge e: resourceNode.getInEdges()) { if (((PushPullAttribute) ((DataFlowEdge) e).getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { // for PULL transfer addReference(component, constructor, e.getSource(), langSpec); ResourcePath srcId = ((ResourceNode) e.getSource()).getResource(); if (!depends.contains(srcId)) depends.add(srcId); } } return constructor; } private MethodDeclaration declareGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, ILanguageSpecific langSpec) { // Declare the getter method of the resource state. MethodDeclaration getter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); component.addMethod(getter); if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { fillGetterMethodToReturnStateField(getter, resStateType, langSpec); } else { // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. boolean isContainedPush = false; DataTransferChannel ch = null; HashMap<ResourcePath, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>(); for (Edge eIn: resourceNode.getInEdges()) { DataFlowEdge dIn = (DataFlowEdge) eIn; if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { // PUSH transfer isContainedPush = true; inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), getPushAccessor()); } else { // PULL transfer inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), getPullAccessor()); ch = dIn.getChannel(); } } // for reference channel members. for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c.getResource(), getPullAccessor()); // by pull data transfer } // generate a return statement. try { for (ChannelMember out: ch.getOutputChannelMembers()) { if (out.getResource().equals(resourceNode.getResource())) { String[] sideEffects = new String[] {""}; if (!isContainedPush) { // All incoming edges are in PULL-style. String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor()).toImplementation(sideEffects); getter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); } else { // At least one incoming edge is in PUSH-style. String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).toImplementation(sideEffects); getter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); } break; } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } } return getter; } private List<MethodDeclaration> declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, TypeDeclaration component, ILanguageSpecific langSpec) { // Declare cash fields and update methods in the component. String resComponentName = langSpec.toComponentName(resourceNode.getResource().getResourceName()); List<MethodDeclaration> updateMethods = new ArrayList<>(); for (Edge e: resourceNode.getInEdges()) { DataFlowEdge re = (DataFlowEdge) e; ResourcePath srcRes = ((ResourceNode) re.getSource()).getResource(); String srcResName = srcRes.getResourceName(); String srcResComponentName = langSpec.toComponentName(srcResName); if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { // for push data transfer // Declare an update method in the type of the destination resource. ArrayList<VariableDeclaration> vars = new ArrayList<>(); vars.add(langSpec.newVariableDeclaration(srcRes.getResourceStateType(), srcRes.getResourceName())); // For the refs. DataTransferChannel ch = (DataTransferChannel) re.getChannel(); for (ResourcePath ref: ch.getReferenceResources()) { if (!ref.equals(resourceNode.getResource())) { vars.add(langSpec.newVariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); } } MethodDeclaration update = langSpec.newMethodDeclaration(updateMethodName + srcResComponentName, false, null, vars); component.addMethod(update); updateMethods.add(update); // Add a statement to update the state field if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { try { for (ChannelMember out: ch.getOutputChannelMembers()) { if (out.getResource().equals(resourceNode.getResource())) { Expression updateExp = null; if (ch.getReferenceChannelMembers().size() == 0) { updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor()); } else { // if there exists one or more reference channel member. HashMap<ResourcePath, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>(); for (Edge eIn: resourceNode.getInEdges()) { DataFlowEdge dIn = (DataFlowEdge) eIn; inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), getPushAccessor()); } for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c.getResource(), getRefAccessor()); } updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor(), inputResourceToStateAccessor); } 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] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + curState + langSpec.getStatementDelimiter(); // this.value = ... } if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { update.addFirstStatement(updateStatement); } break; } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e1) { e1.printStackTrace(); } } // Declare the field to cache the state of the source resource in the type of the destination resource. if (resourceNode.getIndegree() > 1 || (resourceNode.getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // If incoming edges are multiple, or the current state of an input member is needed. if (langSpec.declareField()) { // Declare the cache field. FieldDeclaration cacheField = langSpec.newFieldDeclaration( srcRes.getResourceStateType(), srcRes.getResourceName(), langSpec.getFieldInitializer(srcRes.getResourceStateType(), srcRes.getInitialValue())); component.addField(cacheField); } // Update the cache field. String cacheStatement = langSpec.getFieldAccessor(srcResName) + langSpec.getAssignment() + srcResName + langSpec.getStatementDelimiter(); if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { update.addStatement(cacheStatement); } } // Add an invocation to another update method (for a chain of update method invocations). for (Edge eOut: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) eOut; if (((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { // PUSH transfer Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); List<String> params = new ArrayList<>(); params.add(langSpec.getFieldAccessor(fieldOfResourceState)); Set<ResourcePath> referredSet = referredResources.get(update); for (ChannelMember rc: dOut.getChannel().getReferenceChannelMembers()) { // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(update, referredSet); } if (!ref.equals(resourceNode.getResource())) { String refVarName = ref.getResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(ref, ((ResourceNode) dOut.getSource()).getResource()); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); update.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); } params.add(refVarName); } } update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(((ResourceNode) dOut.getDestination()).getResource().getResourceName()), updateMethodName + resComponentName, params) + langSpec.getStatementDelimiter()); // this.dst.updateSrc(value, refParams); } } } } return updateMethods; } private List<MethodDeclaration> declareInputMethodsInThisAndMainComponents(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration mainComponent, DataTransferModel model, ILanguageSpecific langSpec) { // Declare input methods. String resName = resourceNode.getResource().getResourceName(); String resComponentName = langSpec.toComponentName(resName); List<MethodDeclaration> inputMethods = new ArrayList<>(); for (Channel ch : model.getIOChannels()) { for (ChannelMember out : ((DataTransferChannel) ch).getOutputChannelMembers()) { if (out.getResource().equals(resourceNode.getResource())) { Expression message = out.getStateTransition().getMessageExpression(); MethodDeclaration input = null; MethodDeclaration mainInput = null; if (message instanceof Term) { // Declare an input method in this component. ArrayList<VariableDeclaration> params = new ArrayList<>(); for (Map.Entry<Position, Variable> varEnt: message.getVariables().entrySet()) { Variable var = varEnt.getValue(); String refVarName = null; for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) { Expression varExp = refCm.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); if (varExp != null && varExp instanceof Variable) { if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) { refVarName = refCm.getResource().getResourceName(); break; } } } if (refVarName != null) { // var has come from a reference resource. params.add(langSpec.newVariableDeclaration(var.getType(), refVarName)); } else { // var has not come from reference resource. params.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); } } input = langSpec.newMethodDeclaration(((Term) message).getSymbol().getImplName(), false, null, params); component.addMethod(input); inputMethods.add(input); // Declare the accessor in the main component to call the input method. String str = ((Term) message).getSymbol().getImplName(); mainInput = getMethod(mainComponent, str); if (mainInput == null) { mainInput = langSpec.newMethodDeclaration(str, false, null, params); mainComponent.addMethod(mainInput); } else { // Add type to a parameter without type. if (mainInput.getParameters() != null) { for (VariableDeclaration param: mainInput.getParameters()) { if (param.getType() == null) { for (VariableDeclaration p: params) { if (param.getName().equals(p.getName()) && p.getType() != null) { param.setType(p.getType()); } } } } } } } else if (message instanceof Variable) { // Declare an input method in this component. input = langSpec.newMethodDeclaration(((Variable) message).getName(), null); component.addMethod(input); inputMethods.add(input); String str = ((Variable) message).getName(); // Declare the accessor in the main component to call the input method. mainInput = getMethod(mainComponent, str); if (mainInput == null) { mainInput = langSpec.newMethodDeclaration(str, null); mainComponent.addMethod(mainInput); } } // Add an invocation to the accessor method. if (mainInput != null) { List<String> args = new ArrayList<>(); if (message instanceof Term) { for (Map.Entry<Position, Variable> varEnt: message.getVariables().entrySet()) { String refVarName = null; for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { Expression varExp = rc.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); if (varExp != null && rc.getStateTransition().getCurStateExpression().contains(varExp)) { refVarName = rc.getResource().getResourceName(); break; } } if (refVarName != null) { args.add(refVarName); } else { args.add(varEnt.getValue().getName()); } } } mainInput.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(resName), input.getName(), args) + langSpec.getStatementDelimiter()); } if (input != null) { // Add a statement to update the state field to the input method. try { String[] sideEffects = new String[] {""}; Expression updateExp; updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getRefAccessor()); String newState = updateExp.toImplementation(sideEffects); String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; } else { updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); } input.addFirstStatement(updateStatement); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } // Add an invocation to an update method (for a chain of update method invocations). for (Edge eOut: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) eOut; if (((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { // PUSH transfer Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); List<String> params = new ArrayList<>(); params.add(langSpec.getFieldAccessor(fieldOfResourceState)); Set<ResourcePath> referredSet = referredResources.get(input); for (ChannelMember rc: dOut.getChannel().getReferenceChannelMembers()) { // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(input, referredSet); } if (!ref.equals(resourceNode.getResource())) { String refVarName = ref.getResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(ref, ((ResourceNode) dOut.getSource()).getResource()); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); input.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); } params.add(refVarName); } } input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(((ResourceNode) dOut.getDestination()).getResource().getResourceName()), updateMethodName + resComponentName, params) + langSpec.getStatementDelimiter()); // this.dst.updateSrc(value, refParams); } } } } } } return inputMethods; } }