package generators; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import code.ast.Block; import code.ast.CompilationUnit; import code.ast.FieldDeclaration; import code.ast.ImportDeclaration; 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.Field; import models.algebra.Parameter; import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; 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.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; import models.dataFlowModel.StoreAttribute; /** * Generator for plain Java prototypes * * @author Nitta * */ public class JavaCodeGenerator { public static final Type typeVoid = new Type("Void", "void"); private static String defaultMainTypeName = "Main"; static String mainTypeName = defaultMainTypeName; public static String getMainTypeName() { return mainTypeName; } public static void setMainTypeName(String mainTypeName) { JavaCodeGenerator.mainTypeName = mainTypeName; } public static void resetMainTypeName() { JavaCodeGenerator.mainTypeName = defaultMainTypeName; } public static String getComponentName(ResourceHierarchy res) { String name = res.getResourceName(); if (res.getNumParameters() > 0) { if (name.length() > 3 && name.endsWith("ies")) { name = name.substring(0, name.length() - 3) + "y"; } else if (name.length() > 1 && name.endsWith("s")) { name = name.substring(0, name.length() - 1); } else { name += "Element"; } } return name.substring(0, 1).toUpperCase() + name.substring(1); } public static String toVariableName(String name) { return name.substring(0, 1).toLowerCase() + name.substring(1); } public static Type getImplStateType(ResourceHierarchy res) { Set<ResourceHierarchy> children = res.getChildren(); if (children == null || children.size() == 0) { // leaf resource. return res.getResourceStateType(); } else { ResourceHierarchy child = children.iterator().next(); if (children.size() == 1 && child.getNumParameters() > 0) { // map or list. if (DataConstraintModel.typeList.isAncestorOf(res.getResourceStateType())) { // list. if (generatesComponent(child)) { return new Type("List", "ArrayList<>", "List<" + getComponentName(child) + ">", DataConstraintModel.typeList); } else { return new Type("List", "ArrayList<>", "List<" + getImplStateType(child).getImplementationTypeName() + ">", DataConstraintModel.typeList); } } else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) { // map. if (generatesComponent(child)) { return new Type("Map", "HashMap<>", "Map<String, " + getComponentName(child) + ">", DataConstraintModel.typeMap); } else { return new Type("Map", "HashMap<>", "Map<String, " + getImplStateType(child).getImplementationTypeName() + ">", DataConstraintModel.typeMap); } } return null; } else { // class return res.getResourceStateType(); } } } public static boolean generatesComponent(ResourceHierarchy res) { return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); // Type resType = res.getResourceStateType(); // return DataConstraintModel.typeJson.isAncestorOf(resType) || DataConstraintModel.typeList.isAncestorOf(resType) || DataConstraintModel.typeMap.isAncestorOf(resType); } static public ArrayList<CompilationUnit> doGenerate(DataFlowGraph graph, DataTransferModel model) { ArrayList<CompilationUnit> codes = new ArrayList<>(); ArrayList<ResourceNode> resources = determineResourceOrder(graph); Map<ResourceHierarchy, TypeDeclaration> resourceComponents = new HashMap<>(); List<Map.Entry<ResourceHierarchy, MethodDeclaration>> getters = new ArrayList<>(); List<Map.Entry<ResourceHierarchy, MethodDeclaration>> inputs = new ArrayList<>(); List<Map.Entry<ResourceHierarchy, FieldDeclaration>> fields = new ArrayList<>(); TypeDeclaration mainComponent = new TypeDeclaration(mainTypeName); CompilationUnit mainCU = new CompilationUnit(mainComponent); mainCU.addImport(new ImportDeclaration("java.util.*")); codes.add(mainCU); // Declare the constructor of the main type. MethodDeclaration mainConstructor = new MethodDeclaration(mainTypeName, true); mainComponent.addMethod(mainConstructor); // For each resource node. for (ResourceNode rn: resources) { TypeDeclaration component = null; if (generatesComponent(rn.getResourceHierarchy())) { boolean f = false; String resourceName = getComponentName(rn.getResourceHierarchy()); component = resourceComponents.get(rn.getResourceHierarchy()); if (component == null) { // Add compilation unit for each resource. component = new TypeDeclaration(resourceName); resourceComponents.put(rn.getResourceHierarchy(), component); CompilationUnit cu = new CompilationUnit(component); cu.addImport(new ImportDeclaration("java.util.*")); codes.add(cu); // Declare the field to refer to each resource in the main type. Set<ResourcePath> depends = new HashSet<>(); Set<ResourcePath> refs = new HashSet<>(); if (rn.getResourceHierarchy().getParent() == null) { // For a root resource String fieldInitializer = "new " + resourceName + "("; for (Edge resToCh: rn.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { for (Edge chToRes: re.getDestination().getOutEdges()) { ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); String resName = getComponentName(dstRes.getResourceHierarchy()); depends.add(dstRes); fieldInitializer += toVariableName(resName) + ","; f = true; } } } for (Edge chToRes : rn.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); String resName = getComponentName(srcRes.getResourceHierarchy()); if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { depends.add(srcRes); fieldInitializer += toVariableName(resName) + ","; f = true; } else { if (re.getDestination().getIndegree() > 1) { // Declare a field to cash the state of the source resource in the type of the destination resource. ResourcePath cashRes = ((ResourceNode) re.getSource()).getOutSideResource(); component.addField(new FieldDeclaration( cashRes.getResourceStateType(), ((ResourceNode) re.getSource()).getOutSideResource().getResourceName(), getInitializer(cashRes))); } } } } for (Channel ch : model.getChannels()) { DataTransferChannel c = (DataTransferChannel) ch; if (c.getInputResources().contains(rn.getOutSideResource())) { for (ResourcePath res: c.getReferenceResources()) { if (!refs.contains(res) && !depends.contains(res)) { refs.add(res); String refResName = res.getResourceName(); fieldInitializer += toVariableName(refResName) + ","; f = true; } } } } if (f) fieldInitializer = fieldInitializer.substring(0, fieldInitializer.length() - 1); fieldInitializer += ")"; FieldDeclaration field = new FieldDeclaration(new Type(resourceName, resourceName), rn.getResourceName()); mainComponent.addField(field); Block mainConstructorBody = mainConstructor.getBody(); if (mainConstructorBody == null) { mainConstructorBody = new Block(); mainConstructor.setBody(mainConstructorBody); } mainConstructorBody.addStatement(rn.getResourceName() + " = " + fieldInitializer + ";"); } // Declare a constructor, fields and update methods in the type of each resource. MethodDeclaration constructor = new MethodDeclaration(resourceName, true); Block block = new Block(); depends = new HashSet<>(); for (Edge resToCh : rn.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; for (ChannelMember cm: ch.getInputChannelMembers()) { if (cm.getResource().getResourceHierarchy().equals(rn.getResourceHierarchy()) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = false; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. break; } } if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { // Declare a field to refer to the destination resource of push transfer. for (Edge chToRes: re.getDestination().getOutEdges()) { ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); if (!generatesComponent(dstRes.getResourceHierarchy())) { dstRes = dstRes.getParent(); } String dstResName = getComponentName(dstRes.getResourceHierarchy()); depends.add(dstRes); component.addField(new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName))); constructor.addParameter(new VariableDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName))); block.addStatement("this." + toVariableName(dstResName) + " = " + toVariableName(dstResName) + ";"); if (outsideOutputResource) { if (dstRes.getParent() != null) { // Reference to root resource. ResourcePath dstRootRes = dstRes.getRoot(); String dstRootResName = getComponentName(dstRootRes.getResourceHierarchy()); component.addField(new FieldDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName))); constructor.addParameter(new VariableDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName))); block.addStatement("this." + toVariableName(dstRootResName) + " = " + toVariableName(dstRootResName) + ";"); } } } } } for (Edge chToRes : rn.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; for (ChannelMember cm: ch.getInputChannelMembers()) { if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = false; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. break; } } String srcResName = null; ResourcePath srcRes2 = srcRes; if (generatesComponent(srcRes.getResourceHierarchy())) { srcResName = getComponentName(srcRes.getResourceHierarchy()); } else { srcRes2 = srcRes.getParent(); srcResName = getComponentName(srcRes2.getResourceHierarchy()); } if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // Declare a field to refer to the source resource of pull transfer. depends.add(srcRes2); component.addField(new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName))); constructor.addParameter(new VariableDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName))); block.addStatement("this." + toVariableName(srcResName) + " = " + toVariableName(srcResName) + ";"); if (outsideInputResource) { if (srcRes2.getParent() != null) { // Reference to root resource. ResourcePath srcRootRes = srcRes2.getRoot(); String srcRootResName = getComponentName(srcRootRes.getResourceHierarchy()); component.addField(new FieldDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName))); constructor.addParameter(new VariableDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName))); block.addStatement("this." + toVariableName(srcRootResName) + " = " + toVariableName(srcRootResName) + ";"); } } } else { // Declare an update method in the type of the destination resource. ArrayList<VariableDeclaration> vars = new ArrayList<>(); vars.add(new VariableDeclaration(srcRes.getResourceStateType(), srcRes.getResourceName())); DataTransferChannel c = ((ChannelNode) resToCh.getDestination()).getChannel(); for (ResourcePath ref: c.getReferenceResources()) { if (!ref.equals(rn.getOutSideResource())) { vars.add(new VariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); } } component.addMethod(new MethodDeclaration("update" + srcResName, false, typeVoid, vars)); } } } // Declare a field to refer to the reference resource. refs = new HashSet<>(); for (Channel ch : model.getChannels()) { DataTransferChannel c = (DataTransferChannel) ch; if (c.getInputResources().contains(rn.getOutSideResource())) { for (ResourcePath res: c.getReferenceResources()) { if (!refs.contains(res) && !depends.contains(res)) { refs.add(res); String refResName = getComponentName(res.getResourceHierarchy()); component.addField(new FieldDeclaration(new Type(refResName, refResName), res.getResourceName())); constructor.addParameter(new VariableDeclaration(new Type(refResName, refResName), res.getResourceName())); block.addStatement("this." + res.getResourceName() + " = " + res.getResourceName() + ";"); } } } } constructor.setBody(block); if (constructor.getParameters() != null) component.addMethod(constructor); } // Declare the field to store the state in the type of each resource. if (((StoreAttribute) rn.getAttribute()).isStored()) { ResourcePath res = rn.getOutSideResource(); Set<ResourceHierarchy> children = rn.getResourceHierarchy().getChildren(); if (children == null || children.size() == 0) { // leaf resource. component.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res))); } else { ResourceHierarchy child = children.iterator().next(); if (children.size() == 1 && child.getNumParameters() > 0) { // map or list. component.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res))); } else { // class for (ResourceHierarchy c: children) { String childTypeName = getComponentName(c); Type childType = null; if (generatesComponent(c)) { // The child has a component. childType = new Type(childTypeName, childTypeName); String fieldName = toVariableName(childTypeName); component.addField(new FieldDeclaration(childType, fieldName, getInitializer(res))); } } } } } // Declare the getter methods to obtain the children resources. for (ResourceNode child: rn.getChildren()) { if (generatesComponent(child.getResourceHierarchy())) { // A component for the child is generated. List<VariableDeclaration> params = new ArrayList<>(); int v = 1; for (Selector param: child.getSelectors()) { if (param.getExpression() instanceof Variable) { Variable var = (Variable) param.getExpression(); params.add(new VariableDeclaration(var.getType(), var.getName())); } else if (param.getExpression() instanceof Term) { Term var = (Term) param.getExpression(); params.add(new VariableDeclaration(var.getType(), "v" + v)); } v++; } String childCompName = getComponentName(child.getResourceHierarchy()); Type childType = new Type(childCompName, childCompName); MethodDeclaration childGetter = null; if (params.size() == 0) { childGetter = new MethodDeclaration("get" + childCompName, childType); } else { childGetter = new MethodDeclaration("get" + childCompName, false, childType, params); } component.addMethod(childGetter); } } } // Declare the state field and reference fields in the parent component. if (component == null) { // Declare reference fields for push/pull data transfer. boolean noPullTransfer = true; for (Edge resToCh : rn.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); for (Edge chToRes: re.getDestination().getOutEdges()) { ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); boolean outsideOutputResource = false; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (cm.getResource().getResourceHierarchy().equals(dstRes.getResourceHierarchy()) && cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. break; } } if (outsideOutputResource) { // This logic may be incorrect. (ToDo) // Declare a field in the parent component to refer to the destination resource of push transfer. if (!generatesComponent(dstRes.getResourceHierarchy())) { dstRes = dstRes.getParent(); } String dstResName = getComponentName(dstRes.getResourceHierarchy()); FieldDeclaration refFieldForPush = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), refFieldForPush)); if (dstRes.getParent() != null) { // Reference to root resource. ResourcePath dstRootRes = dstRes.getRoot(); String dstRootResName = getComponentName(dstRootRes.getResourceHierarchy()); FieldDeclaration refRootFieldForPush = new FieldDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName)); fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), refRootFieldForPush)); } } } } for (Edge chToRes : rn.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); boolean outsideInputResource = false; for (ChannelMember cm: ch.getInputChannelMembers()) { if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } if (outsideInputResource) { // This logic may be incorrect. (ToDo) // Declare a field in the parent component to refer to the source resource of pull transfer. if (!generatesComponent(srcRes.getResourceHierarchy())) { srcRes = srcRes.getParent(); } String srcResName = getComponentName(srcRes.getResourceHierarchy()); FieldDeclaration refFieldForPull = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), refFieldForPull)); if (srcRes.getParent() != null) { // Reference to root resource. ResourcePath srcRootRes = srcRes.getRoot(); String srcRootResName = getComponentName(srcRootRes.getResourceHierarchy()); FieldDeclaration refRootFieldForPull = new FieldDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName)); fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), refRootFieldForPull)); } noPullTransfer = false; } } } // Declare the state field in the parent component. ResourceHierarchy res = rn.getOutSideResource().getResourceHierarchy(); if (((StoreAttribute) rn.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { String resName = getComponentName(res); FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName)); fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), stateField)); } } // Declare the getter method in this component to obtain the resource state. if (component != null) { // A component is created for this resource. MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(rn.getResourceHierarchy())); component.addMethod(stateGetter); } else { // No component is created for this resource. List<VariableDeclaration> getterParams = new ArrayList<>(); int v = 1; for (Selector param: rn.getSelectors()) { if (param.getExpression() instanceof Variable) { Variable var = (Variable) param.getExpression(); getterParams.add(new VariableDeclaration(var.getType(), var.getName())); } else if (param.getExpression() instanceof Term) { Term var = (Term) param.getExpression(); getterParams.add(new VariableDeclaration(var.getType(), "v" + v)); } v++; } String resCompName = getComponentName(rn.getResourceHierarchy()); Type resType = getImplStateType(rn.getResourceHierarchy()); MethodDeclaration stateGetter = null; if (getterParams.size() == 0) { stateGetter = new MethodDeclaration("get" + resCompName, resType); } else { stateGetter = new MethodDeclaration("get" + resCompName, false, resType, getterParams); } getters.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), stateGetter)); } // Declare the accessor method in the main type to call the getter method. MethodDeclaration accessor = null; List<VariableDeclaration> mainGetterParams = new ArrayList<>(); int v = 1; for (Selector param: rn.getAllSelectors()) { if (param.getExpression() instanceof Variable) { Variable var = (Variable) param.getExpression(); mainGetterParams.add(new VariableDeclaration(var.getType(), var.getName())); } else if (param.getExpression() instanceof Term) { Term var = (Term) param.getExpression(); mainGetterParams.add(new VariableDeclaration(var.getType(), "v" + v)); } v++; } if (mainGetterParams.size() > 0) { accessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()), false, getImplStateType(rn.getResourceHierarchy()), mainGetterParams); } else { accessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()), getImplStateType(rn.getResourceHierarchy())); } accessor.setBody(new Block()); Expression getState = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(rn.getOutSideResource(), null); accessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); mainComponent.addMethod(accessor); // Declare the input method in this component and the main component. for (Channel ch : model.getIOChannels()) { for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { if (rn.getInSideResources().contains(cm.getResource())) { Expression message = cm.getStateTransition().getMessageExpression(); if (message instanceof Term) { // In each resource. ArrayList<VariableDeclaration> resInputParams = new ArrayList<>(); ArrayList<VariableDeclaration> mainInputParams = new ArrayList<>(); v = 1; for (Selector selector: rn.getSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); } else if (selector.getExpression() instanceof Term) { Term var = (Term) selector.getExpression(); resInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); } v++; } if (rn.getParent() != null) { for (Selector selector: rn.getParent().getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); } else if (selector.getExpression() instanceof Term) { Term var = (Term) selector.getExpression(); mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); } v++; } } for (Variable var: message.getVariables().values()) { resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); } MethodDeclaration input = new MethodDeclaration( ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), false, typeVoid, resInputParams); if (component != null) { // A component is created for this resource. component.addMethod(input); } else { // No component is created for this resource. inputs.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), input)); } // In the main type. String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); input = getMethod(mainComponent, str, mainInputParams); if (input == null) { input = new MethodDeclaration(str, false, typeVoid, mainInputParams); mainComponent.addMethod(input); } else { // Add type to a parameter without type. for (VariableDeclaration param: input.getParameters()) { if (param.getType() == null) { for (VariableDeclaration p: mainInputParams) { if (param.getName().equals(p.getName()) && p.getType() != null) { param.setType(p.getType()); } } } } } } else if (message instanceof Variable) { // In each resource. ArrayList<VariableDeclaration> resInputParams = new ArrayList<>(); ArrayList<VariableDeclaration> mainInputParams = new ArrayList<>(); v = 1; for (Selector selector: rn.getSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); } else if (selector.getExpression() instanceof Term) { Term var = (Term) selector.getExpression(); resInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); } v++; } if (rn.getResourceHierarchy().getParent() != null) { for (Selector selector: rn.getParent().getSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); } else if (selector.getExpression() instanceof Term) { Term var = (Term) selector.getExpression(); mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); } v++; } } MethodDeclaration input = null; if (resInputParams.size() > 0) { input = new MethodDeclaration( ((Variable) cm.getStateTransition().getMessageExpression()).getName(), false, typeVoid, resInputParams); } else { input = new MethodDeclaration( ((Variable) cm.getStateTransition().getMessageExpression()).getName(), false, typeVoid, null); } if (component != null) { // A component is created for this resource. component.addMethod(input); } else { // No component is created for this resource. inputs.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), input)); } // In the main type. String str = ((Variable) cm.getStateTransition().getMessageExpression()).getName(); input = getMethod(mainComponent, str, mainInputParams); if (input == null) { if (mainInputParams.size() > 0) { input = new MethodDeclaration(str, false, typeVoid, mainInputParams); } else { input = new MethodDeclaration(str, false, typeVoid, null); } mainComponent.addMethod(input); } } } } } } // Add leaf getter methods to the parent components. for (Map.Entry<ResourceHierarchy, MethodDeclaration> entry: getters) { resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); } // Add leaf input methods to the parent components. for (Map.Entry<ResourceHierarchy, MethodDeclaration> entry: inputs) { resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); } // Add leaf reference fields to the parent components. for (Map.Entry<ResourceHierarchy, FieldDeclaration> entry: fields) { resourceComponents.get(entry.getKey()).addField(entry.getValue()); } // Declare the Pair class. boolean isCreatedPair = false; for (ResourceNode rn : resources) { if (isCreatedPair) continue; if (rn.getResourceStateType() != null && model.getType("Pair").isAncestorOf(rn.getResourceStateType())) { TypeDeclaration type = new TypeDeclaration("Pair<T>"); type.addField(new FieldDeclaration(new Type("Double", "T"), "left")); type.addField(new FieldDeclaration(new Type("Double", "T"), "right")); MethodDeclaration constructor = new MethodDeclaration("Pair", true); constructor.addParameter(new VariableDeclaration(new Type("Double", "T"), "left")); constructor.addParameter(new VariableDeclaration(new Type("Double", "T"), "right")); Block block = new Block(); block.addStatement("this.left = left;"); block.addStatement("this.right = right;"); constructor.setBody(block); type.addMethod(constructor); for(FieldDeclaration field : type.getFields()) { MethodDeclaration getter = new MethodDeclaration( "get" + field.getName().substring(0,1).toUpperCase() + field.getName().substring(1), new Type("Double","T")); getter.setBody(new Block()); getter.getBody().addStatement("return " + field.getName() + ";"); type.addMethod(getter); } CompilationUnit cu = new CompilationUnit(type); cu.addImport(new ImportDeclaration("java.util.*")); codes.add(cu); isCreatedPair = true; } } // HashSet<String> tmps = new HashSet<>(); // HashSet<String> cont = new HashSet<>(); // for (MethodDeclaration method : mainType.getMethods()) { // if (!tmps.contains(method.getName())) // tmps.add(method.getName()); // else // cont.add(method.getName()); // } // for (MethodDeclaration method : mainType.getMethods()) { // if (cont.contains(method.getName())) { // method.setName(method.getName() + method.getParameters().get(0).getName().substring(0, 1).toUpperCase() // + method.getParameters().get(0).getName().substring(1)); // } // } return codes; } private static String getInitializer(ResourcePath res) { Type stateType = res.getResourceStateType(); String initializer = null; if (res.getResourceHierarchy().getInitialValue() != null) { initializer = res.getResourceHierarchy().getInitialValue().toImplementation(new String[] {""}); } else if (stateType != null) { if (DataConstraintModel.typeList.isAncestorOf(stateType)) { initializer = "new " + res.getResourceStateType().getImplementationTypeName() + "()"; } else if (DataConstraintModel.typeMap.isAncestorOf(stateType)) { initializer = "new " + res.getResourceStateType().getImplementationTypeName() + "()"; } } return initializer; } static public ArrayList<String> getCodes(ArrayList<TypeDeclaration> codeTree) { ArrayList<String> codes = new ArrayList<>(); for (TypeDeclaration type : codeTree) { codes.add("public class " + type.getTypeName() + "{"); for (FieldDeclaration field : type.getFields()) { if (type.getTypeName() != mainTypeName) { String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " " + field.getName(); if (DataConstraintModel.isListType(field.getType())) cons += " = new ArrayList<>()"; cons += ";"; codes.add(cons); } else { String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " " + field.getName() + " = new " + field.getType().getTypeName() + "("; cons += ");"; codes.add(cons); } } codes.add(""); for (MethodDeclaration method : type.getMethods()) { String varstr = "\t" + "public " + method.getReturnType().getInterfaceTypeName() + " " + method.getName() + "("; if (method.getParameters() != null) { for (VariableDeclaration var : method.getParameters()) { varstr += var.getType().getInterfaceTypeName() + " " + var.getName() + ","; } if (!method.getParameters().isEmpty()) varstr = varstr.substring(0, varstr.length() - 1); } if (method.getBody() != null) { for (String str : method.getBody().getStatements()) { codes.add("\t\t" + str + ";"); } } codes.add(varstr + ")" + "{"); codes.add("\t" + "}"); codes.add(""); } codes.add("}"); codes.add(""); } return codes; } static private ArrayList<ResourceNode> determineResourceOrder(DataFlowGraph graph) { ArrayList<ResourceNode> resources = new ArrayList<>(); Set<ResourceNode> visited = new HashSet<>(); for (Node n : graph.getResourceNodes()) { ResourceNode resNode = (ResourceNode) n; topologicalSort(graph, resNode, visited, resources); } return resources; } static private void topologicalSort(DataFlowGraph graph, ResourceNode curResNode, Set<ResourceNode> visited, List<ResourceNode> orderedList) { if (visited.contains(curResNode)) return; visited.add(curResNode); for (Edge chToRes: curResNode.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { topologicalSort(graph, (ResourceNode) re.getSource(), visited, orderedList); } } } for (Edge resToCh: curResNode.getOutEdges()) { DataFlowEdge de = (DataFlowEdge) resToCh; if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { for (Edge chToRes : resToCh.getDestination().getOutEdges()) { topologicalSort(graph, (ResourceNode) chToRes.getDestination(), visited, orderedList); } } } for (Node n: graph.getResourceNodes()) { // for reference resources. ResourceNode resNode = (ResourceNode) n; for (Edge resToCh : resNode.getOutEdges()) { ChannelNode chNode = (ChannelNode) resToCh.getDestination(); for (ChannelMember m: chNode.getChannel().getReferenceChannelMembers()) { if (m.getResource().equals(curResNode.getOutSideResource())) { topologicalSort(graph, resNode, visited, orderedList); } } } } orderedList.add(0, curResNode); } private static MethodDeclaration getMethod(TypeDeclaration type, String methodName, List<VariableDeclaration> params) { for (MethodDeclaration m: type.getMethods()) { if (m.getName().equals(methodName)) { if (m.getParameters() == null && (params == null || params.size() == 0)) return m; if (m.getParameters() != null && params != null && m.getParameters().size() == params.size()) { boolean matchParams = true; for (int i = 0; i < m.getParameters().size(); i++) { if (!m.getParameters().get(i).getType().equals(params.get(i).getType())) { matchParams = false; break; } } if (matchParams) return m; } } } return null; } static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { ResourcePath targetRes = target.getResource(); ResourcePath fromRes = from.getResource(); if (targetRes.equals(fromRes)) { return new Field("value", targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } // for reference channel member return new Parameter(targetRes.getResourceName(), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { ResourcePath targetRes = target.getResource(); return new Parameter(targetRes.getResourceName(), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { if (fromRes != null && targetRes.equals(fromRes)) { return new Field("value", targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } // for reference channel member return new Parameter(targetRes.getResourceName(), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } }; static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { @Override public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { ResourcePath targetRes = target.getResource(); if (from != null) { ResourcePath fromRes = from.getResource(); if (!target.isOutside()) { return getDirectStateAccessorFor(targetRes, fromRes); } Term getter = null; String targetComponentName = getComponentName(targetRes.getResourceHierarchy()); if (generatesComponent(targetRes.getResourceHierarchy())) { getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); getter.addChild(new Field(toVariableName(targetComponentName), targetRes.getResourceStateType())); } else { String parentName = toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent())); Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); getter = new Term(new Symbol("get" + targetComponentName, 1, Symbol.Type.METHOD)); getter.addChild(new Field(parentName, parentType)); } return getter; } else { return getDirectStateAccessorFor(targetRes, null); } } @Override public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { ResourcePath targetRes = target.getResource(); if (from != null) { ResourcePath fromRes = from.getResource(); if (!target.isOutside()) { return getDirectStateAccessorFor(targetRes, fromRes); } Term getter = null; String targetComponentName = getComponentName(targetRes.getResourceHierarchy()); if (generatesComponent(targetRes.getResourceHierarchy())) { getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); getter.addChild(new Field(toVariableName(targetComponentName), targetRes.getResourceStateType())); } else { String parentName = toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent())); Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); getter = new Term(new Symbol("get" + targetComponentName, 1, Symbol.Type.METHOD)); getter.addChild(new Field(parentName, parentType)); } return getter; } else { return getDirectStateAccessorFor(targetRes, null); } } @Override public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { if (fromRes != null) { if (targetRes.equals(fromRes)) { return new Field("value", targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } // for reference channel member Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); getter.addChild(new Field(targetRes.getResourceName(), targetRes.getResourceStateType())); return getter; } else { // access from the outside of the hierarchy Stack<ResourcePath> pathStack = new Stack<>(); ResourcePath curPath = targetRes; do { pathStack.push(curPath); curPath = curPath.getParent(); } while (curPath != null); // iterate from the root resource Term getter = null; int v = 1; while (!pathStack.empty()) { curPath = pathStack.pop(); String typeName = getComponentName(curPath.getResourceHierarchy()); if (getter == null) { // root resource String fieldName = toVariableName(typeName); getter = new Field(fieldName, new Type(typeName, typeName)); } else { Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); newGetter.addChild(getter); if (curPath.getResourceHierarchy().getNumParameters() > 0) { Variable var = null; Expression param = curPath.getLastParam(); if (param instanceof Variable) { var = (Variable) param; } else if (param instanceof Term) { var = new Variable("v" + v, ((Term) param).getType()); } if (var != null) { newGetter.addChild(var); newGetter.getSymbol().setArity(2); } v++; } getter = newGetter; } } if (generatesComponent(targetRes.getResourceHierarchy())) { Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); newGetter.addChild(getter); getter = newGetter; } return getter; } } }; }