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.Expression;
import models.algebra.InvalidMessage;
import models.algebra.ParameterizedIdentifierIsFutureWork;
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().substring(0,1).toLowerCase() + type.getTypeName().substring(1), 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 = src.getResourceName();
						String dstResourceName = dst.getResourceName();
						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()) {
										if (dst.getNumberOfParameters() == 0) {
											// returns the current state stored in a field.
											MethodDeclaration getter = getGetterMethod(dstType, null);
											if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) {
												Type resourceType = dst.getResourceStateType();
												if (model.isPrimitiveType(resourceType)) {
													getter.addStatement("return value;");
												} else {
													// copy the current state to be returned as a 'value'
													String implTypeName = resourceType.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;
//													}
													getter.addStatement("return new " + implTypeName + "(value);");
												}
											}
										}
									}
									// 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, null);
										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
			String mainTypeName = JavaCodeGenerator.mainTypeName.substring(0,1).toLowerCase() + JavaCodeGenerator.mainTypeName.substring(1);
			TypeDeclaration mainType = typeMap.get(mainTypeName);
			for (ResourceHierarchy resource: model.getResourceHierarchies()) {
//				ResourceNode resource = (ResourceNode) n;
				String resourceName = resource.getResourceName();
				TypeDeclaration type = typeMap.get(resourceName);
				if (type != null) {
					// getter method
					if (resource.getTotalNumParameters() == 0) {
						MethodDeclaration getter = getGetterMethod(type, null);
						if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) {
							Type resourceType = resource.getResourceStateType();
							if (model.isPrimitiveType(resourceType)) {
								getter.addStatement("return value;");							
							} else {
								// copy the current state to be returned as a 'value'
								String implTypeName = resourceType.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 + "<" + generics + ">";
//								} else {
//									concreteTypeName = implTypeName;
//								}
								getter.addStatement("return new " + implTypeName + "(value);");
							}
						}
					}
					// 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 the resource
								if (resource.getTotalNumParameters() == 0) {
									String[] sideEffects = new String[] {""};
									Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor);
									String newState = updateExp.toImplementation(sideEffects);
									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);
									}
								}
								
								// In the main type
								if (mainType != null) {
									MethodDeclaration mainInput = getMethod(mainType, input.getName());
									if (mainInput != null) {
										String args = "";
										String delimiter = "";
										for (Selector selector: entry.getKey().getAllSelectors()) {
											if (selector.getExpression() instanceof Variable) {
												Variable var = (Variable) selector.getExpression();
												args += delimiter + var.getName();
												delimiter = ", ";
											}
										}
										if (out.getStateTransition().getMessageExpression() instanceof Term) {
											Term message = (Term) out.getStateTransition().getMessageExpression();
											for (Variable var: message.getVariables().values()) {
												args += delimiter + var.getName();
												delimiter = ", ";
											}
										}
										mainInput.addStatement("this." + resourceName + "." + 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, List<VariableDeclaration> params) {
		for (MethodDeclaration m: type.getMethods()) {
			if (m.getName().startsWith("get")) {
				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;
	}
	
	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;
	}
}