package generators; import java.util.ArrayList; import java.util.Arrays; 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.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.Position; 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.ResourcePath; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; import models.dataFlowModel.PushPullAttribute; import models.dataFlowModel.PushPullValue; 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; } static public ArrayList<CompilationUnit> doGenerate(DataFlowGraph graph, DataTransferModel model) { ArrayList<CompilationUnit> codes = new ArrayList<>(); ArrayList<ResourceNode> resources = determineResourceOrder(graph); TypeDeclaration mainType = new TypeDeclaration(mainTypeName); CompilationUnit mainCU = new CompilationUnit(mainType); mainCU.addImport(new ImportDeclaration("java.util.*")); codes.add(mainCU); // Declare the constructor of the main type. MethodDeclaration mainConstructor = new MethodDeclaration(mainTypeName, true); mainType.addMethod(mainConstructor); // For each resource. for (ResourceNode rn: resources) { boolean f = false; String resourceName = rn.getResource().getResourceName().substring(0, 1).toUpperCase() + rn.getResource().getResourceName().substring(1); TypeDeclaration type = new TypeDeclaration(resourceName); // Declare the field to refer to each resource in the main type. String fieldInitializer = "new " + resourceName + "("; Set<ResourcePath> depends = new HashSet<>(); for (Edge e : rn.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) e; ResourcePath dstRes = ((ResourceNode) re.getDestination()).getResource(); String resName = dstRes.getResourceName().substring(0, 1).toUpperCase() + dstRes.getResourceName().substring(1); if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { depends.add(dstRes); fieldInitializer += resName.toLowerCase() + ","; f = true; } } for (Edge e : rn.getInEdges()) { DataFlowEdge re = (DataFlowEdge) e; ResourcePath srcRes = ((ResourceNode) re.getSource()).getResource(); String resName = srcRes.getResourceName().substring(0, 1).toUpperCase() + srcRes.getResourceName().substring(1); if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { depends.add(srcRes); fieldInitializer += resName.toLowerCase() + ","; f = true; } else { if (rn.getIndegree() > 1 || (rn.getIndegree() == 1 && re.getChannel().getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // Declare a field to cache the state of the source resource in the type of the destination resource. ResourcePath cashResId = ((ResourceNode) re.getSource()).getResource(); type.addField(new FieldDeclaration( cashResId.getResourceStateType(), ((ResourceNode) re.getSource()).getResource().getResourceName(), getInitializer(cashResId))); } } } Set<ResourcePath> refs = new HashSet<>(); for (Channel ch : model.getChannels()) { DataTransferChannel c = (DataTransferChannel) ch; if (c.getInputResources().contains(rn.getResource())) { for (ResourcePath res: c.getReferenceResources()) { if (!refs.contains(res) && !depends.contains(res)) { refs.add(res); String refResName = res.getResourceName(); fieldInitializer += refResName.toLowerCase() + ","; f = true; } } } } if (f) fieldInitializer = fieldInitializer.substring(0, fieldInitializer.length() - 1); fieldInitializer += ")"; FieldDeclaration field = new FieldDeclaration(new Type(resourceName, resourceName), rn.getResource().getResourceName()); mainType.addField(field); Block mainConstructorBody = mainConstructor.getBody(); if (mainConstructorBody == null) { mainConstructorBody = new Block(); mainConstructor.setBody(mainConstructorBody); } mainConstructorBody.addStatement(rn.getResource().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 e : rn.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) e; ResourcePath dstRes = ((ResourceNode) re.getDestination()).getResource(); String dstResName = dstRes.getResourceName().substring(0, 1).toUpperCase() + dstRes.getResourceName().substring(1); if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { // Declare a field to refer to the destination resource of push transfer. depends.add(dstRes); type.addField(new FieldDeclaration(new Type(dstResName, dstResName), dstRes.getResourceName())); constructor.addParameter(new VariableDeclaration(new Type(dstResName, dstResName), dstRes.getResourceName())); block.addStatement("this." + dstResName.toLowerCase() + " = " + dstResName.toLowerCase() + ";"); } } for (Edge e : rn.getInEdges()) { DataFlowEdge re = (DataFlowEdge) e; ResourcePath srcRes = ((ResourceNode) re.getSource()).getResource(); String srcResName = srcRes.getResourceName().substring(0, 1).toUpperCase() + srcRes.getResourceName().substring(1); if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { // Declare a field to refer to the source resource of pull transfer. depends.add(srcRes); type.addField(new FieldDeclaration(new Type(srcResName, srcResName), srcRes.getResourceName())); constructor.addParameter(new VariableDeclaration(new Type(srcResName, srcResName), srcRes.getResourceName())); block.addStatement("this." + srcResName.toLowerCase() + " = " + srcResName.toLowerCase() + ";"); } 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 = (DataTransferChannel) re.getChannel(); for (ResourcePath ref: c.getReferenceResources()) { if (!ref.equals(rn.getResource())) { vars.add(new VariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); } } type.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.getResource())) { for (ResourcePath res: c.getReferenceResources()) { if (!refs.contains(res) && !depends.contains(res)) { refs.add(res); String refResName = res.getResourceName(); refResName = refResName.substring(0, 1).toUpperCase() + refResName.substring(1); type.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) type.addMethod(constructor); // Declare input methods in resources and the main type. for (Channel ch : model.getIOChannels()) { for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { if (cm.getResource().equals(rn.getResource())) { Expression message = cm.getStateTransition().getMessageExpression(); if (message instanceof Term) { 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(new VariableDeclaration(var.getType(), refVarName)); } else { // var has not come from reference resource. params.add(new VariableDeclaration(var.getType(), var.getName())); } } MethodDeclaration input = new MethodDeclaration( ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), false, typeVoid, params); type.addMethod(input); String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); input = getMethod(mainType, str); if (input == null) { input = new MethodDeclaration(str, false, typeVoid, params); mainType.addMethod(input); } else { // Add type to a parameter without type. for (VariableDeclaration param: input.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) { MethodDeclaration input = new MethodDeclaration( ((Variable) cm.getStateTransition().getMessageExpression()).getName(), false, typeVoid, null); type.addMethod(input); String str = ((Variable) cm.getStateTransition().getMessageExpression()).getName(); input = getMethod(mainType, str); if (input == null) { input = new MethodDeclaration(str, false, typeVoid, null); mainType.addMethod(input); } } } } } // Declare the field to store the state in the type of each resource. if (((StoreAttribute) rn.getAttribute()).isStored()) { ResourcePath res = rn.getResource(); type.addField(new FieldDeclaration(res.getResourceStateType(), "value", getInitializer(res))); } // Declare the getter method to obtain the state in the type of each resource. type.addMethod(new MethodDeclaration("getValue", rn.getResource().getResourceStateType())); // Add compilation unit for each resource. CompilationUnit cu = new CompilationUnit(type); cu.addImport(new ImportDeclaration("java.util.*")); codes.add(cu); } // Declare the Pair class. boolean isCreatedPair = false; for(ResourceNode rn : resources) { if(isCreatedPair) continue; if(model.getType("Pair").isAncestorOf(rn.getResource().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; } } // Declare getter methods in the main type. for (Node n : graph.getNodes()) { ResourceNode rn = (ResourceNode) n; MethodDeclaration getter = new MethodDeclaration( "get" + rn.getResource().getResourceName().substring(0, 1).toUpperCase() + rn.getResource().getResourceName().substring(1), rn.getResource().getResourceStateType()); getter.setBody(new Block()); getter.getBody().addStatement( "return " + rn.getResource().getResourceName() + ".getValue();"); mainType.addMethod(getter); } 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 resId) { Type stateType = resId.getResourceStateType(); String initializer = null; if (resId.getInitialValue() != null) { initializer = resId.getInitialValue().toImplementation(new String[] {""}); } else { if (DataConstraintModel.typeList.isAncestorOf(stateType)) { initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()"; } else if (DataConstraintModel.typeMap.isAncestorOf(stateType)) { initializer = "new " + resId.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.getNodes()) { ResourceNode rn = (ResourceNode) n; topologicalSort(graph, rn, visited, resources); } return resources; } static private void topologicalSort(DataFlowGraph graph, ResourceNode curNode, Set<ResourceNode> visited, List<ResourceNode> orderedList) { if (visited.contains(curNode)) return; visited.add(curNode); for (Edge e : curNode.getInEdges()) { DataFlowEdge re = (DataFlowEdge) e; if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { topologicalSort(graph, (ResourceNode) re.getSource(), visited, orderedList); } } for (Edge e : curNode.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) e; if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { topologicalSort(graph, (ResourceNode) re.getDestination(), visited, orderedList); } } for (Node n: graph.getNodes()) { // for reference resources. ResourceNode rn = (ResourceNode) n; for (Edge e : rn.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) e; for (ChannelMember m: re.getChannel().getReferenceChannelMembers()) { if (m.getResource().equals(curNode.getResource())) { topologicalSort(graph, rn, visited, orderedList); } } } } orderedList.add(0, curNode); } private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { for (MethodDeclaration m: type.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; } static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { if (target.equals(from)) { return new Field("value", target.getResourceStateType() != null ? target.getResourceStateType() : DataConstraintModel.typeInt); } // use the cached value as the current state return new Field(target.getResourceName(), target.getResourceStateType() != null ? target.getResourceStateType() : DataConstraintModel.typeInt); } @Override public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { return new Parameter(target.getResourceName(), target.getResourceStateType() != null ? target.getResourceStateType() : DataConstraintModel.typeInt); } }; static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { @Override public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { if (target.equals(from)) { return new Field("value", target.getResourceStateType() != null ? target.getResourceStateType() : DataConstraintModel.typeInt); } // for reference channel member Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); return getter; } @Override public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); return getter; } }; static public IResourceStateAccessor refAccessor = new IResourceStateAccessor() { @Override public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { if (target.equals(from)) { return new Field("value", target.getResourceStateType() != null ? target.getResourceStateType() : DataConstraintModel.typeInt); } // for reference channel member return new Parameter(target.getResourceName(), target.getResourceStateType() != null ? target.getResourceStateType() : DataConstraintModel.typeInt); } @Override public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { return new Parameter(target.getResourceName(), target.getResourceStateType() != null ? target.getResourceStateType() : DataConstraintModel.typeInt); } }; }