Newer
Older
AlgebraicDataflowArchitectureModel / AlgebraicDataflowArchitectureModel / src / generators / CodeGenerator.java
package generators;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
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.Field;
import models.algebra.Parameter;
import models.algebra.Symbol;
import models.algebra.Term;
import models.algebra.Type;
import models.algebra.Variable;
import models.controlFlowModel.EntryPointObjectNode;
import models.controlFlowModel.ObjectNode;
import models.controlFlowModel.StatefulObjectNode;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
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.ResourceNode;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;

/**
 * Common generator for prototypes
 * 
 * @author Nitta
 *
 */
public abstract class CodeGenerator {
	public static final String fieldOfResourceState = "value";
	public static final String getterOfResourceState = "getValue";
	public static final String updateMethodName = "update";
	private static String mainTypeName = null;
		
	public static String getMainTypeName() {
		return mainTypeName;
	}

	public static void setMainTypeName(String mainTypeName) {
		CodeGenerator.mainTypeName = mainTypeName;
	}
	
	public static void resetMainTypeName() {
		CodeGenerator.mainTypeName = null;
	}

	/**
	 * Generate source codes in specified language from data-flow/control-flow graph.
	 * 
	 * @param model architecture model
	 * @param flowGraph data-flow or control-flow graph
	 * @param langSpec specified language
	 * @return source codes
	 */
	public ArrayList<CompilationUnit> generateCode(DataTransferModel model, IFlowGraph flowGraph, ILanguageSpecific langSpec) {
		ArrayList<CompilationUnit> codes = new ArrayList<>();
		
		// Sort the all components.
		ArrayList<Set<Node>> components = determineComponentOrder(flowGraph);
		
		// Add the main component.
		if (mainTypeName == null) {
			mainTypeName = langSpec.getMainComponentName();
		}
		TypeDeclaration mainComponent = langSpec.newTypeDeclaration(mainTypeName);
		MethodDeclaration mainConstructor = mainComponent.createConstructor();
		CompilationUnit mainCU = langSpec.newCompilationUnit(mainComponent);
		codes.add(mainCU);
		
		// Generate the other components.
		generateCodeFromFlowGraph(model, flowGraph, components, mainComponent, mainConstructor, codes, langSpec);
		
		return codes;
	}
	
	public abstract void generateCodeFromFlowGraph(DataTransferModel model, IFlowGraph flowGraph, ArrayList<Set<Node>> components,
			TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList<CompilationUnit> codes, ILanguageSpecific langSpec);
	
	private static ArrayList<Set<Node>> determineComponentOrder(IFlowGraph graph) {
		ArrayList<Set<Node>> objects = new ArrayList<>();
		Set<Set<Node>> visited = new HashSet<>();
		Map<Node, Set<Node>> allNodeSets = graph.getAllNodes();
		for (Set<Node> nodeSet: allNodeSets.values()) {
			if (!(nodeSet.iterator().next() instanceof EntryPointObjectNode)) {
				topologicalSort(allNodeSets, nodeSet, visited, objects);
			}
		}
		return objects;
	}
	
	private static void topologicalSort(Map<Node, Set<Node>> allNodeSets, Set<Node> curNodeSet, Set<Set<Node>> visited, List<Set<Node>> orderedList) {
		if (visited.contains(curNodeSet)) return;
		visited.add(curNodeSet);
		// a caller is before the callee
		for (Node curNode: curNodeSet) {
			for (Edge e: curNode.getInEdges()) {
				if (!(e.getSource() instanceof EntryPointObjectNode)) {
					if (!(e instanceof DataFlowEdge) || ((PushPullAttribute)((DataFlowEdge) e).getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
						// for a call edge or PUSH data-flow edge
						topologicalSort(allNodeSets, allNodeSets.get(e.getSource()), visited, orderedList);
					}
				}
			}
		}
//		if (curNode instanceof StatefulObjectNode) {
//			ResourceNode resNode = ((StatefulObjectNode) curNode).getResource();
//			for (Node n: allNodes) {
//				if (n != curNode && n instanceof StatefulObjectNode && !visited.contains(n)) {
//					if (resNode.equals(((StatefulObjectNode) n).getResource())) {
//						// n and curNode are identical (one in PUSH call graph and another in PULL call graph).
//						visited.add(n);
//						for (Edge e: n.getInEdges()) {
//							if (!(e.getSource() instanceof EntryPointObjectNode)) {
//								if (!(e instanceof DataFlowEdge)) {
//									// for a call edge
//									topologicalSort(allNodes, e.getSource(), visited, orderedList);
//								}
//							}
//						}
//					}
//				}
//			}
//		}
		if (curNodeSet.iterator().next() instanceof ResourceNode) {
			for (Node curNode: curNodeSet) {
				for (Edge e: curNode.getOutEdges()) {
					DataFlowEdge de = (DataFlowEdge) e;
					if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) {
						// for a PULL data-flow edge
						topologicalSort(allNodeSets, allNodeSets.get(e.getDestination()), visited, orderedList);
					}
				}
			}
		}
		// For reference resources.
		ResourceNode cn = null;
		Node curNode = curNodeSet.iterator().next();
		if (curNode instanceof ResourceNode) {
			cn = (ResourceNode) curNode;
		} else if (curNode instanceof StatefulObjectNode) {
			cn = ((StatefulObjectNode) curNode).getResource();
		}
		if (cn != null) {
			for (Node n: allNodeSets.keySet()) {
				ResourceNode rn = null;
				if (n instanceof ResourceNode) {
					rn = (ResourceNode) n;
				} else if (n instanceof StatefulObjectNode) {
					rn = ((StatefulObjectNode) n).getResource();
				}
				if (rn != null) {
					for (Edge e: rn.getOutEdges()) {
						DataTransferChannel ch = ((DataFlowEdge) e).getChannel();
						for (ChannelMember m: ch.getReferenceChannelMembers()) {
							if (m.getResource() == cn.getResource()) {
								topologicalSort(allNodeSets, allNodeSets.get(n), visited, orderedList);
							}
						}
					}
				}
			}
		}
		orderedList.add(0, curNodeSet);
	}

	protected void updateMainComponent(TypeDeclaration mainType, MethodDeclaration mainConstructor, Node componentNode, 
			MethodDeclaration constructor, ILanguageSpecific langSpec) {
		// Declare the field to refer to each object in the main type.
		ResourceNode resNode = null;
		String nodeName = null;
		if (componentNode instanceof ResourceNode) {
			resNode = (ResourceNode) componentNode;
			nodeName = resNode.getResource().getResourceName();
		} else if (componentNode instanceof ObjectNode) {
			nodeName = ((ObjectNode) componentNode).getName();
			if (componentNode instanceof StatefulObjectNode) {
				resNode = ((StatefulObjectNode) componentNode).getResource();
			}
		}
		String componentName = langSpec.toComponentName(nodeName);
		// Declare a field to refer each object.
		if (langSpec.declareField()) {
			FieldDeclaration refField = langSpec.newFieldDeclaration(new Type(componentName, componentName), nodeName);
			mainType.addField(refField);
		}
		// Add a statement to instantiate each object to the main constructor.
		List<String> parameters = new ArrayList<>();
		if (constructor.getParameters() != null) {
			for (VariableDeclaration var: constructor.getParameters()) {
				parameters.add(var.getName());
			}
		}

		Block mainConstructorBody = mainConstructor.getBody();
		if (mainConstructorBody == null) {
			mainConstructorBody = new Block();
			mainConstructor.setBody(mainConstructorBody);
		}
		mainConstructorBody.addStatement(langSpec.getFieldAccessor(nodeName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(componentName, parameters) + langSpec.getStatementDelimiter());
	}

	protected void addReference(TypeDeclaration component, MethodDeclaration constructor, Node dstNode, ILanguageSpecific langSpec) {
		String dstNodeName = null;
		if (dstNode instanceof ResourceNode) {
			dstNodeName = ((ResourceNode) dstNode).getResource().getResourceName();
		} else if (dstNode instanceof ObjectNode) {
			dstNodeName = ((ObjectNode) dstNode).getName();
		}
		String dstComponentName = langSpec.toComponentName(dstNodeName);
		if (langSpec.declareField()) {
			// Declare a field to refer to another component.
			component.addField(langSpec.newFieldDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName));
		}
		// Initialize the field to refer to another component.
		constructor.addParameter(langSpec.newVariableDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName));
		constructor.getBody().addStatement(langSpec.getFieldAccessor(dstNodeName) + langSpec.getAssignment() + dstNodeName + langSpec.getStatementDelimiter());
	}

	protected void fillGetterMethodToReturnStateField(MethodDeclaration getter, Type resStateType, ILanguageSpecific langSpec) {
		// returns the state field when all incoming data-flow edges are PUSH-style.
		if (langSpec.isValueType(resStateType)) {
			getter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter());		// return value;
		} else {
			// copy the current state to be returned as a 'value'
			String implTypeName = resStateType.getImplementationTypeName();
//					String interfaceTypeName = resourceType.getInterfaceTypeName();
//					String concreteTypeName;
//					if (interfaceTypeName.contains("<")) {
//						String typeName = implTypeName.substring(0, implTypeName.indexOf("<"));
////						String generics = interfaceTypeName.substring(interfaceTypeName.indexOf("<") + 1, interfaceTypeName.lastIndexOf(">"));
//						concreteTypeName = typeName + "<>";
//					} else {
//						concreteTypeName = implTypeName;
//					}
			List<String> parameters = new ArrayList<>();
			parameters.add(langSpec.getFieldAccessor(fieldOfResourceState));
			getter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter());	// return new Resource(value);
		}
	}

	protected void declareAccessorInMainComponent(TypeDeclaration mainComponent, ResourcePath accessRes, ILanguageSpecific langSpec) {
		MethodDeclaration getter = langSpec.newMethodDeclaration("get" + langSpec.toComponentName(accessRes.getResourceName()), accessRes.getResourceStateType());
		Block block = new Block();
		block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), getterOfResourceState)) + langSpec.getStatementDelimiter());
		getter.setBody(block);
		mainComponent.addMethod(getter);
	}

	protected void declareFieldsToReferenceResources(DataTransferModel model, ResourceNode resourceNode, TypeDeclaration component, MethodDeclaration constructor,
			final List<ResourcePath> depends, ILanguageSpecific langSpec) {
		Set<ResourcePath> refs = new HashSet<>();
		for (Channel ch : model.getChannels()) {
			DataTransferChannel c = (DataTransferChannel) ch;
			if (c.getInputResources().contains(resourceNode.getResource())) {
				for (ResourcePath id: c.getReferenceResources()) {
					if (!refs.contains(id) && !depends.contains(id)) {
						refs.add(id);
						String refResName = langSpec.toComponentName(id.getResourceName());
						component.addField(langSpec.newFieldDeclaration(new Type(refResName, refResName), id.getResourceName()));
						constructor.addParameter(langSpec.newVariableDeclaration(new Type(refResName, refResName), id.getResourceName()));						
						constructor.getBody().addStatement(langSpec.getFieldAccessor(id.getResourceName()) + langSpec.getAssignment() + id.getResourceName() + langSpec.getStatementDelimiter());
					}
				}
			}
		}
	}
	
	protected MethodDeclaration getUpdateMethod(Edge inEdge, TypeDeclaration component,
			Map<Edge, Map<PushPullValue, List<ResourceNode>>> dataFlowInform, ILanguageSpecific langSpec) {
		List<ResourceNode> passedResoueces = dataFlowInform.get(inEdge).get(PushPullValue.PUSH);
		String methodName = updateMethodName;
		for (ResourceNode rn: passedResoueces) {
			ResourcePath rId = rn.getResource();
			methodName += langSpec.toComponentName(rId.getResourceName());
		}
		return getMethod(component, methodName);
	}

	protected MethodDeclaration getInputMethod(ResourceNode resourceNode, DataTransferChannel ch, TypeDeclaration component) {
		MethodDeclaration input = null;
		for (ChannelMember out : ch.getOutputChannelMembers()) {
			if (out.getResource().equals(resourceNode.getResource())) {
				Expression message = out.getStateTransition().getMessageExpression();
				if (message instanceof Term) {
					input = getMethod(component, ((Term) message).getSymbol().getImplName());
				} else if (message instanceof Variable) {
					// Declare an input method in this component.
					input = getMethod(component, ((Variable) message).getName());
				}
				break;
			}
		}
		return input;
	}

	protected MethodDeclaration getMethod(TypeDeclaration component, String methodName) {
		for (MethodDeclaration m: component.getMethods()) {
			if (m.getName().equals(methodName)) return m;
		}
		return null;
	}

	protected IResourceStateAccessor getPushAccessor() {
		return new IResourceStateAccessor() {
			@Override
			public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) {
				if (target.equals(from)) {
					return new Field(fieldOfResourceState,
							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);
			}
		};
	}

	protected IResourceStateAccessor getPullAccessor() {
		return new IResourceStateAccessor() {
			@Override
			public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) {
				if (target.equals(from)) {
					return new Field(fieldOfResourceState,
							target.getResourceStateType() != null ? target.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
				// for reference channel member
				Term getter = new Term(new Symbol(getterOfResourceState, 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(getterOfResourceState, 1, Symbol.Type.METHOD));
				getter.addChild(new Field(target.getResourceName(), target.getResourceStateType()));
				return getter;
			}
		};
	}
}