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

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 code.ast.Block;
import code.ast.CompilationUnit;
import code.ast.FieldDeclaration;
import code.ast.MethodDeclaration;
import code.ast.TypeDeclaration;
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.ChannelGenerator;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.IdentifierTemplate;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataTransferChannelGenerator;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.IFlowGraph;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.DataTransferChannelGenerator.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<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<Node> components,
			TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList<CompilationUnit> codes, ILanguageSpecific langSpec);
	
	private static ArrayList<Node> determineComponentOrder(IFlowGraph graph) {
		ArrayList<Node> objects = new ArrayList<>();
		Set<Node> visited = new HashSet<>();
		Set<Node> allNodes = graph.getAllNodes();
		for (Node n: allNodes) {
			if (!(n instanceof EntryPointObjectNode)) {
				topologicalSort(allNodes, n, visited, objects);
			}
		}
		return objects;
	}
	
	private static void topologicalSort(Set<Node> allNodes, Node curNode, Set<Node> visited, List<Node> orderedList) {
		if (visited.contains(curNode)) return;
		visited.add(curNode);
		// a caller is before the callee
		for (Edge e: curNode.getInEdges()) {
			if (!(e.getSource() instanceof EntryPointObjectNode)) {
				if (!(e instanceof DataFlowEdge) || ((PushPullAttribute)((DataFlowEdge) e).getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
					topologicalSort(allNodes, e.getSource(), visited, orderedList);
				}
			}
		}
		if (curNode instanceof ResourceNode) {
			for (Edge e: curNode.getOutEdges()) {
				DataFlowEdge de = (DataFlowEdge) e;
				if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) {
					topologicalSort(allNodes, e.getDestination(), visited, orderedList);
				}
			}
		}
		// For reference resources.
		ResourceNode cn = null;
		if (curNode instanceof ResourceNode) {
			cn = (ResourceNode) curNode;
		} else if (curNode instanceof StatefulObjectNode) {
			cn = ((StatefulObjectNode) curNode).getResource();
		}
		if (cn != null) {
			for (Node n: allNodes) {
				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()) {
						DataTransferChannelGenerator ch = ((DataFlowEdge) e).getChannelGenerator();
						for (ChannelMember m: ch.getReferenceChannelMembers()) {
							if (m.getIdentifierTemplate() == cn.getIdentifierTemplate()) {
								topologicalSort(allNodes, n, visited, orderedList);
							}
						}
					}
				}
			}
		}
		orderedList.add(0, curNode);
	}

	protected void updateMainComponent(DataTransferModel model, TypeDeclaration mainType, MethodDeclaration mainConstructor, Node componentNode, 
			final List<IdentifierTemplate> depends, 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.getIdentifierTemplate().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<>();
		for (IdentifierTemplate id: depends) {
			// For the callee objects (the destination resource of push transfer or the source resource of pull transfer).
			parameters.add(id.getResourceName());
		}
		// For the refs. 
		if (resNode != null) {
			Set<IdentifierTemplate> refs = new HashSet<>();
			for (ChannelGenerator cg : model.getChannelGenerators()) {
				DataTransferChannelGenerator ch = (DataTransferChannelGenerator) cg;
				if (ch.getInputIdentifierTemplates().contains(resNode.getIdentifierTemplate())) {
					for (IdentifierTemplate id: ch.getReferenceIdentifierTemplates()) {
						if (!refs.contains(id) && !depends.contains(id)) {
							refs.add(id);
							String refResName = id.getResourceName();
							parameters.add(refResName);
						}
					}
				}
			}
		}

		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).getIdentifierTemplate().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, IdentifierTemplate accessResId, ILanguageSpecific langSpec) {
		MethodDeclaration getter = new MethodDeclaration("get" + langSpec.toComponentName(accessResId.getResourceName()), accessResId.getResourceStateType());
		Block block = new Block();
		block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessResId.getResourceName(), getterOfResourceState)) + langSpec.getStatementDelimiter());
		getter.setBody(block);
		mainComponent.addMethod(getter);
	}

	protected void declareFieldsToReferenceResources(DataTransferModel model, ResourceNode resourceNode, TypeDeclaration component, MethodDeclaration constructor,
			final List<IdentifierTemplate> depends, ILanguageSpecific langSpec) {
		Set<IdentifierTemplate> refs = new HashSet<>();
		for (ChannelGenerator ch : model.getChannelGenerators()) {
			DataTransferChannelGenerator c = (DataTransferChannelGenerator) ch;
			if (c.getInputIdentifierTemplates().contains(resourceNode.getIdentifierTemplate())) {
				for (IdentifierTemplate id: c.getReferenceIdentifierTemplates()) {
					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) {
			IdentifierTemplate rId = rn.getIdentifierTemplate();
			methodName += langSpec.toComponentName(rId.getResourceName());
		}
		return getMethod(component, methodName);
	}

	protected MethodDeclaration getInputMethod(ResourceNode resourceNode, DataTransferChannelGenerator ch, TypeDeclaration component) {
		MethodDeclaration input = null;
		for (ChannelMember out : ch.getOutputChannelMembers()) {
			if (out.getIdentifierTemplate().equals(resourceNode.getIdentifierTemplate())) {
				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(IdentifierTemplate target, IdentifierTemplate 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(IdentifierTemplate target, IdentifierTemplate from) {
				return new Parameter(target.getResourceName(),
						target.getResourceStateType() != null ? target.getResourceStateType()
								: DataConstraintModel.typeInt);
			}
		};
	}

	protected IResourceStateAccessor getPullAccessor() {
		return new IResourceStateAccessor() {
			@Override
			public Expression getCurrentStateAccessorFor(IdentifierTemplate target, IdentifierTemplate 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(IdentifierTemplate target, IdentifierTemplate from) {
				Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
				getter.addChild(new Field(target.getResourceName(), target.getResourceStateType()));
				return getter;
			}
		};
	}
}