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

import java.util.AbstractMap;
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.CompilationUnit;
import code.ast.MethodDeclaration;
import code.ast.TypeDeclaration;
import code.ast.VariableDeclaration;
import models.Edge;
import models.Node;
import models.algebra.Constant;
import models.algebra.Expression;
import models.algebra.Field;
import models.algebra.InvalidMessage;
import models.algebra.ParameterizedIdentifierIsFutureWork;
import models.algebra.Symbol;
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.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.ResolvingMultipleDefinitionIsFutureWork;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.StoreAttribute;

public class JavaMethodBodyGenerator {
	public static ArrayList<CompilationUnit> doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList<CompilationUnit> codes) {
		// Create a map from type names (lower case) to their types.
		Map<String, TypeDeclaration> typeMap = new HashMap<>();
		for (CompilationUnit code: codes) {
			for (TypeDeclaration type: code.types()) {
				typeMap.put(type.getTypeName(), type);
			}
		}
		
		// Generate the body of each update or getter method.
		try {
			Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); 
			for (Edge e: graph.getEdges()) {
				DataFlowEdge resToCh = (DataFlowEdge) e;
				if (!resToCh.isChannelToResource()) {
					PushPullAttribute pushPull = (PushPullAttribute) resToCh.getAttribute();
					ResourceNode src = (ResourceNode) resToCh.getSource();
					for (Edge chToRes: resToCh.getDestination().getOutEdges()) {
						ResourceNode dst = (ResourceNode) chToRes.getDestination();
						String srcResourceName = JavaCodeGenerator.getComponentName(src.getResourceHierarchy());
						String dstResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy());
						TypeDeclaration srcType = typeMap.get(srcResourceName);
						TypeDeclaration dstType = typeMap.get(dstResourceName);
						for (ChannelMember out: ((ChannelNode) resToCh.getDestination()).getChannel().getOutputChannelMembers()) {
							if (dst.getInSideResources().contains(out.getResource())) {
								if (pushPull.getOptions().get(0) == PushPullValue.PUSH && srcType != null) {
									// for push data transfer
									MethodDeclaration update = getUpdateMethod(dstType, srcType);
									if (((StoreAttribute) dst.getAttribute()).isStored()) {
										// update stored state of dst side resource (when every incoming edge is in push style)
										Expression updateExp = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor);
										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] + "value = " + curState + ";";
										}
										if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) {
											update.addFirstStatement(updateStatement);
										}
									}
									if (resToCh.getDestination().getIndegree() > 1) {
										// update a cash of src side resource (when incoming edges are multiple)
										String cashStatement = "this." + srcResourceName + " = " + srcResourceName + ";";
										if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) {
											update.addFirstStatement(cashStatement);
										}								
									}
									if (((StoreAttribute) dst.getAttribute()).isStored()) {
										// returns the current state stored in a field.
										MethodDeclaration getter = getGetterMethod(dstType, dst.getResourceStateType());
										if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) {
											Type resourceType = dst.getResourceStateType();
											if (dst.getResourceHierarchy().getNumParameters() == 0) {
												if (model.isPrimitiveType(resourceType)) {
													getter.addStatement("return value;");
												} else {
													// copy the current state to be returned as a 'value'
													String implTypeName = resourceType.getImplementationTypeName();
													getter.addStatement("return new " + implTypeName + "(value);");
												}
											} else {
												if (DataConstraintModel.typeList.isAncestorOf(dst.getParent().getResourceStateType())) {
													Term selector = new Term(DataConstraintModel.get);
													selector.addChild(new Field("value"));
													selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression());
													getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";");
												} else if (DataConstraintModel.typeMap.isAncestorOf(dst.getParent().getResourceStateType())) {
													Term selector = new Term(DataConstraintModel.lookup);
													selector.addChild(new Field("value"));
													selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression());
													getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";");
												}
											}
										}
									}
									// src side (for a chain of update method invocations)
									for (MethodDeclaration srcUpdate: getUpdateMethods(srcType)) {
										String refParams = "";
										Set<ResourcePath> referredSet = referredResources.get(srcUpdate);
										for (ChannelMember rc: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) {
											// to get the value of reference member.
											ResourcePath ref = rc.getResource();
											if (referredSet == null) {
												referredSet = new HashSet<>();
												referredResources.put(srcUpdate, referredSet);
											}
											if (!dst.getInSideResources().contains(ref)) {
												String refVarName = ref.getResourceName();
												if (!referredSet.contains(ref)) {
													referredSet.add(ref);
													Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(ref, src.getOutSideResource());
													String[] sideEffects = new String[] {""};
													String refExp = refGetter.toImplementation(sideEffects);
													String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
													srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";");
												}
												refParams += ", " + refVarName;
											}
										}
										srcUpdate.addStatement("this." + dstResourceName + ".update" + srcType.getTypeName() + "(value" + refParams + ");");
									}
									for (MethodDeclaration srcInput: getInputMethods(srcType, src, model)) {
										String refParams = "";
										Set<ResourcePath> referredSet = referredResources.get(srcInput);
										for (ChannelMember rc: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) {
											// to get the value of reference member.
											ResourcePath ref = rc.getResource();
											if (referredSet == null) {
												referredSet = new HashSet<>();
												referredResources.put(srcInput, referredSet);
											}
											if (!dst.getInSideResources().contains(ref)) {
												String refVarName = ref.getResourceName();
												if (!referredSet.contains(ref)) {
													referredSet.add(ref);
													Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(ref, src.getOutSideResource());
													String[] sideEffects = new String[] {""};
													String refExp = refGetter.toImplementation(sideEffects);
													String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
													srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";");
												}
												refParams += ", " + refVarName;
											}
										}
										srcInput.addStatement("this." + dstResourceName + ".update" + srcType.getTypeName() + "(value" + refParams + ");");
									}
								} else {
									// for pull (or push/pull) data transfer
									if (dst.getNumberOfParameters() == 0) {
										MethodDeclaration getter = getGetterMethod(dstType, dst.getResourceStateType());
										if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) {
											boolean isContainedPush = false;
											HashMap<ResourcePath, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>();
											for (Edge chToRes2: dst.getInEdges()) {
												for (Edge resToCh2: chToRes2.getSource().getInEdges()) {
													DataFlowEdge dIn = (DataFlowEdge) resToCh2;
													if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
														isContainedPush = true;
														inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getOutSideResource(), JavaCodeGenerator.pushAccessor);
													} else {
														inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getOutSideResource(), JavaCodeGenerator.pullAccessor);
													}
												}
											}
											// for reference channel members
											for (ChannelMember c: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) {
												inputResourceToStateAccessor.put(c.getResource(), JavaCodeGenerator.pullAccessor);			// by pull data transfer
											}
											String[] sideEffects = new String[] {""};
											// generate a return statement.
											if (!isContainedPush) {
												// All incoming edges are in PULL style.
												String curState = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects);
												getter.addStatement(sideEffects[0] + "return " + curState + ";");
											} else {
												// At least one incoming edge is in PUSH style.
												String curState = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).toImplementation(sideEffects);
												getter.addStatement(sideEffects[0] + "return " + curState + ";");
											}
										}
									}
								} 
							}
						}
					}
				}
			}
			// for source nodes
			TypeDeclaration mainType = typeMap.get(JavaCodeGenerator.mainTypeName);
			for (ResourceHierarchy resource: model.getResourceHierarchies()) {
//				ResourceNode resource = (ResourceNode) n;
				String resourceName = JavaCodeGenerator.getComponentName(resource);
				TypeDeclaration type = typeMap.get(resourceName);
				if (type != null) {
					// state getter method
					Type resourceType = JavaCodeGenerator.getImplStateType(resource);
					MethodDeclaration stateGetter = getGetterMethod(type, resourceType);
					if (stateGetter != null && (stateGetter.getBody() == null || stateGetter.getBody().getStatements().size() == 0)) {
						if (model.isPrimitiveType(resourceType)) {
							// primitive type
							stateGetter.addStatement("return value;");							
						} else {
							if (resource.getChildren() != null && resource.getChildren().size() == 1 && resource.getChildren().iterator().next().getNumParameters() > 0) {
								// list or map
								String implTypeName = resourceType.getImplementationTypeName();
								// copy the current state to be returned as a 'value'
								stateGetter.addStatement("return new " + implTypeName + "(value);");
							} else {
								if (resource.getChildren() == null || resource.getChildren().size() == 0) {
									// a leaf resource
									String implTypeName = resourceType.getImplementationTypeName();
									stateGetter.addStatement("return new " + implTypeName + "(value);");
								} else {
									Term composer = null; 
									Term composerSub = new Constant(DataConstraintModel.nil);
									composerSub.setType(DataConstraintModel.typeMap);
									for (ResourceHierarchy child: resource.getChildren()) {
										String childTypeName = JavaCodeGenerator.getComponentName(child);
										String fieldName = childTypeName.substring(0, 1).toLowerCase() + childTypeName.substring(1);
										Term childGetter = null; 
										if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) {
											// the child is not a class
											childGetter = new Field(fieldName, JavaCodeGenerator.getImplStateType(child));
										} else {
											// the child is a class
											childGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
											childGetter.addChild(new Field(fieldName, JavaCodeGenerator.getImplStateType(child)));
										}
										composer = new Term(DataConstraintModel.insert);
										composer.addChild(composerSub);
										composer.addChild(new Constant(fieldName, DataConstraintModel.typeString));		// key
										composer.addChild(childGetter);													// value
										composer.setType(DataConstraintModel.typeMap);
										composerSub = composer;
									}
									composer.setType(stateGetter.getReturnType());
									String[] sideEffects = new String[] {null};
									String returnValue = composer.toImplementation(sideEffects);
									if (sideEffects[0] != null) stateGetter.addStatement(sideEffects[0]);
									stateGetter.addStatement("return " + returnValue+ ";");
								}
							}
						}
					}
					
					// child getter method
					if (resource.getChildren().size() > 0) {
						for (ResourceHierarchy child: resource.getChildren()) {
							String methodName = "get" + JavaCodeGenerator.getComponentName(child);
							MethodDeclaration childGetter = getMethod(type, methodName);
							if (childGetter != null && (childGetter.getBody() == null || childGetter.getBody().getStatements().size() == 0)) {
								if (DataConstraintModel.typeList.isAncestorOf(resource.getResourceStateType())) {
									Term selector = new Term(DataConstraintModel.get);
									selector.addChild(new Field("value"));
									selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName()));
									selector.setType(childGetter.getReturnType());
									String[] sideEffects = new String[] {null};
									String returnValue = selector.toImplementation(sideEffects);
									if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]);
									childGetter.addStatement("return " + returnValue + ";");
								} else if (DataConstraintModel.typeMap.isAncestorOf(resource.getResourceStateType())) {
									Term selector = new Term(DataConstraintModel.lookup);
									selector.addChild(new Field("value"));
									selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName()));
									selector.setType(childGetter.getReturnType());
									String[] sideEffects = new String[] {null};
									String returnValue = selector.toImplementation(sideEffects);
									if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]);
									childGetter.addStatement("return " + returnValue+ ";");
								} else {
									String fieldName = JavaCodeGenerator.getComponentName(child);
									String returnValue = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
									childGetter.addStatement("return this." + returnValue + ";");
								}
							}
						}
					}
					
					// methods for input events
					Map<DataTransferChannel, Set<ChannelMember>> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model);
					for (Map.Entry<DataTransferChannel, Set<ChannelMember>> entry: ioChannelsAndMembers.entrySet()) {
						Set<ChannelMember> outs = entry.getValue();
						for (ChannelMember out: outs) {
							MethodDeclaration input = getInputMethod(type, out);
							if (input != null) {
								// In each resource
								String[] sideEffects = new String[] {""};
								Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor);
								String newState = updateExp.toImplementation(sideEffects);
								if (resource.getNumParameters() == 0 || resource.getChildren() == null || resource.getChildren().size() == 0) {
									String updateStatement;
									if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) {
										updateStatement = sideEffects[0];	
									} else {
										updateStatement = sideEffects[0] + "this.value = " + newState + ";";
									}
									if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) {
										input.addFirstStatement(updateStatement);
									}
								} else {
									String updateStatement = null;
									if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) {
										// ToDo.
										updateStatement = sideEffects[0];	
									} else {
										if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) {
											Term selector = new Term(DataConstraintModel.set);
											selector.addChild(new Field("value"));
											selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName()));
											selector.addChild(new Constant(newState));
											String[] sideEffects2 = new String[] {""};
											String newList = selector.toImplementation(sideEffects2);
											updateStatement = sideEffects[0] + sideEffects2[0];
										} else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) {
											Term selector = new Term(DataConstraintModel.set);
											selector.addChild(new Field("value"));
											selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName()));
											selector.addChild(new Constant(newState));
											String[] sideEffects2 = new String[] {""};
											String newList = selector.toImplementation(sideEffects2);
											updateStatement = sideEffects[0] + sideEffects2[0];
										}
										if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) {
											input.addFirstStatement(updateStatement);
										}
									}
								}
								
								// In the main type
								if (mainType != null) {
									MethodDeclaration inputAccessor = getMethod(mainType, input.getName());
									if (inputAccessor != null) {
										Expression resExp = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(out.getResource(), null);
										if (resExp instanceof Term) {
											// remove '.getValue()'
											resExp = ((Term) resExp).getChild(0);
										}
										String resourceAccess = resExp.toImplementation(new String[] {null});
										String args = "";
										String delimiter = "";
										if (out.getStateTransition().getMessageExpression() instanceof Term) {
											Term message = (Term) out.getStateTransition().getMessageExpression();
											for (Variable var: message.getVariables().values()) {
												args += delimiter + var.getName();
												delimiter = ", ";
											}
										}
										inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + args + ");");
									}
								}
							}
						}
					}
				}
			}
		} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
				| InvalidMessage | UnificationFailed | ValueUndefined e1) {
			e1.printStackTrace();
		}
		return codes;
	}

	private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) {
		for (MethodDeclaration m: type.getMethods()) {
			if (m.getName().equals("update" + from.getTypeName())) return m;
		}
		return null;
	}

	private static List<MethodDeclaration> getUpdateMethods(TypeDeclaration type) {
		List<MethodDeclaration> updates = new ArrayList<>();
		for (MethodDeclaration m: type.getMethods()) {
			if (m.getName().startsWith("update")) {
				updates.add(m);
			}
		}
		return updates;
	}
	
	private static MethodDeclaration getGetterMethod(TypeDeclaration type, Type returnType) {
		for (MethodDeclaration m: type.getMethods()) {
			if (m.getName().startsWith("get")) {
				if (m.getReturnType().getInterfaceTypeName().equals(returnType.getInterfaceTypeName())) return m;
			}
		}
		return null;
	}
	
	private static Map<DataTransferChannel, Set<ChannelMember>> getIOChannelsAndMembers(ResourceHierarchy resource, DataTransferModel model) {
		Map<DataTransferChannel, Set<ChannelMember>> ioChannelsAndMembers = new HashMap<>();
		for (Channel c: model.getIOChannels()) {
			DataTransferChannel ch = (DataTransferChannel) c;
			// I/O channel
			for (ChannelMember out: ch.getOutputChannelMembers()) {
				if (resource.equals(out.getResource().getResourceHierarchy())) {
					if (out.getStateTransition().getMessageExpression() instanceof Term || out.getStateTransition().getMessageExpression() instanceof Variable) {
						Set<ChannelMember> channelMembers = ioChannelsAndMembers.get(ch);
						if (channelMembers == null) {
							channelMembers = new HashSet<>();
							ioChannelsAndMembers.put(ch, channelMembers);
						}
						channelMembers.add(out);
					}
				}
			}
		}
		return ioChannelsAndMembers;
	}

	private static List<MethodDeclaration> getInputMethods(TypeDeclaration type, ResourceNode resource, DataTransferModel model) {
		List<MethodDeclaration> inputs = new ArrayList<>();
		for (Channel c: model.getIOChannels()) {
			DataTransferChannel channel = (DataTransferChannel) c;
			// I/O channel
			for (ChannelMember out: channel.getOutputChannelMembers()) {
				if (resource.getInSideResources().contains(out.getResource())) {
					MethodDeclaration input = getInputMethod(type, out);
					inputs.add(input);
				}
			}
		}
		return inputs;
	}

	private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) {
		MethodDeclaration input = null;
		if (out.getStateTransition().getMessageExpression() instanceof Term) {
			Term message = (Term) out.getStateTransition().getMessageExpression();
			input = getMethod(type, message.getSymbol().getImplName());
		} else if (out.getStateTransition().getMessageExpression() instanceof Variable) {
			Variable message = (Variable) out.getStateTransition().getMessageExpression();
			input = getMethod(type, message.getName());
		}
		return input;
	}

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