Newer
Older
DesignCraft / src / main / java / generators / ASTGenerator.java
package generators;

import java.util.ArrayList;
import java.util.List;

import ast.*;
import models.objectOrientedTransfer.*;

public class ASTGenerator {
	public static final String getterPrefix = "get";
	public static final String setterPrefix = "set";
	public static final String updateMethodPrefix = "update";
	
	public static String toComponentName(String name) {
		return name.substring(0, 1).toUpperCase() + name.substring(1);
	}

	public static String toVariableName(String name) {
		return name.substring(0, 1).toLowerCase() + name.substring(1);
	}
	
	public static Codebase generate(DataTransferDesign dataTransferDesign) {
		Codebase codebase = new Codebase();
		DeltaComplex deltaComplex = dataTransferDesign.getDeltaComplex();
		for (Delta delta: deltaComplex.split()) {
			codebase = weaveDelta(codebase, delta);
		}
		return codebase;
	}

	public static Codebase weaveDelta(Codebase codebase, Delta delta) {
		List<PrimitiveDelta> primitiveDeltas = delta.split();
		if (primitiveDeltas.size() == 1) {
			return weavePrimitiveDelta(codebase, primitiveDeltas.getFirst());
		} else {
			// To do.
			return codebase;
		}
	}
	
	public static Codebase weavePrimitiveDelta(Codebase codebase, PrimitiveDelta primitiveDelta) {
		if (primitiveDelta instanceof PrimitivePullDelta) {
			PrimitivePullDelta primitivePullDelta = (PrimitivePullDelta) primitiveDelta;
			ReferenceEdge pullEdge1 = primitivePullDelta.getPullEdge1();
			ReferenceEdge pullEdge2 = primitivePullDelta.getPullEdge2();
			ObjectNode obj1 = (ObjectNode) pullEdge1.getSource();
			ObjectNode obj2 = (ObjectNode) pullEdge1.getDestination();
			ObjectNode obj3 = (ObjectNode) pullEdge2.getDestination();
			String name1 = toComponentName(obj1.getName());
			String name2 = toComponentName(obj2.getName());
			String name3 = toComponentName(obj3.getName());
			TypeDeclaration class1 = createClass(codebase, name1);
			TypeDeclaration class2 = createClass(codebase, name2);
			TypeDeclaration class3 = createClass(codebase, name3);
			models.algebra.Type type1 = codebase.getComponentType(name1);
			models.algebra.Type type2 = codebase.getComponentType(name2);
			models.algebra.Type type3 = codebase.getComponentType(name3);
			FieldDeclaration field12 = createField(class1, toVariableName(name2), type2);				// class1 -> class2 (PULL)
			FieldDeclaration field23 = createField(class2, toVariableName(name3), type3);				// class2 -> class3 (PULL)
			FieldDeclaration field13 = createField(class1, toVariableName(name3), type3);				// class1 -> class3 (create)
			
			// Construct getter method
			MethodDeclaration getter = createMethod(class2, getterPrefix + toComponentName(name3));
			ReturnStatement return23 = new ReturnStatement();
			return23.setExpression(new FieldAccess(new ThisExpression(), field23.getName()));			// return this.name3;
			getter.addStatement(return23);
			
			// Construct coordinator method
			MethodDeclaration coordionator = createMethod(class1, getterPrefix + toComponentName(name3));
			FieldAccess field3 = new FieldAccess(new ThisExpression(), field13.getName());				// this.name3
			FieldAccess field2 = new FieldAccess(field12.getName());
			MethodInvocation callGetter = new MethodInvocation(field2, getter.getName());				// name2.getName3()
			Assignment assignment = new Assignment(field3, callGetter);									// this.name3 = name2.getName3();
			ExpressionStatement assignmentStatement = new ExpressionStatement(assignment);
			coordionator.addStatement(assignmentStatement);
		} else if (primitiveDelta instanceof PrimitivePullPushDelta) {
			PrimitivePullPushDelta primitivePullPushDelta = (PrimitivePullPushDelta) primitiveDelta;			
			ReferenceEdge pullEdge = primitivePullPushDelta.getPullEdge();
			ReferenceEdge pushEdge = primitivePullPushDelta.getPushEdge();
			ObjectNode obj1 = (ObjectNode) pullEdge.getSource();
			ObjectNode obj2 = (ObjectNode) pullEdge.getDestination();
			ObjectNode obj3 = (ObjectNode) pushEdge.getDestination();
			String name1 = toComponentName(obj1.getName());
			String name2 = toComponentName(obj2.getName());
			String name3 = toComponentName(obj3.getName());
			TypeDeclaration class1 = createClass(codebase, name1);
			TypeDeclaration class2 = createClass(codebase, name2);
			TypeDeclaration class3 = createClass(codebase, name3);
			models.algebra.Type type1 = codebase.getComponentType(name1);
			models.algebra.Type type2 = codebase.getComponentType(name2);
			models.algebra.Type type3 = codebase.getComponentType(name3);
			FieldDeclaration field12 = createField(class1, toVariableName(name2), type2);				// class1 -> class2 (PULL)
			FieldDeclaration field13 = createField(class1, toVariableName(name3), type3);				// class1 -> class3 (PUSH)
			FieldDeclaration field32 = createField(class3, toVariableName(name2), type2);				// class3 -> class2 (create)
			
			// Construct setter method
			MethodDeclaration setter = createMethod(class3, setterPrefix + toComponentName(name2));
			VariableDeclaration param2 = new VariableDeclaration(type2, name2);
			setter.addParameter(param2);
			FieldAccess field2 = new FieldAccess(new ThisExpression(), field32.getName());				// this.name2
			Assignment assignment = new Assignment(field2, new Variable(name2));						// this.name2 = name2;
			ExpressionStatement assignmentStatement = new ExpressionStatement(assignment);
			setter.addStatement(assignmentStatement);
			
			// Construct coordinator method
			MethodDeclaration coordionator = createMethod(class1, updateMethodPrefix + toComponentName(name2));
			FieldAccess field3 = new FieldAccess(field13.getName());
			List<Expression> args = new ArrayList<>();
			args.add(new FieldAccess(field12.getName()));
			MethodInvocation callSetter = new MethodInvocation(field3, setter.getName(), args);			// name3.setName2(name2)
			coordionator.addStatement(new ExpressionStatement(callSetter));
		} else if (primitiveDelta instanceof PrimitivePushDelta) {
			PrimitivePushDelta primitivePushDelta = (PrimitivePushDelta) primitiveDelta;
			ReferenceEdge pushEdge1 = primitivePushDelta.getPushEdge1();
			ReferenceEdge pushEdge2 = primitivePushDelta.getPushEdge2();
			ObjectNode obj1 = (ObjectNode) pushEdge1.getSource();
			ObjectNode obj2 = (ObjectNode) pushEdge1.getDestination();
			ObjectNode obj3 = (ObjectNode) pushEdge2.getDestination();
			String name1 = toComponentName(obj1.getName());
			String name2 = toComponentName(obj2.getName());
			String name3 = toComponentName(obj3.getName());
			TypeDeclaration class1 = createClass(codebase, name1);
			TypeDeclaration class2 = createClass(codebase, name2);
			TypeDeclaration class3 = createClass(codebase, name3);
			models.algebra.Type type1 = codebase.getComponentType(name1);
			models.algebra.Type type2 = codebase.getComponentType(name2);
			models.algebra.Type type3 = codebase.getComponentType(name3);
			FieldDeclaration field12 = createField(class1, toVariableName(name2), type2);				// class1 -> class2 (PUSH)
			FieldDeclaration field23 = createField(class2, toVariableName(name3), type3);				// class2 -> class3 (PUSH)
			FieldDeclaration field31 = createField(class3, toVariableName(name1), type1);				// class3 -> class1 (create)
			
			// Construct setter in class3
			MethodDeclaration setter3 = createMethod(class3, setterPrefix + toComponentName(name1));
			VariableDeclaration param1 = new VariableDeclaration(type1, name1);
			setter3.addParameter(param1);
			FieldAccess field1 = new FieldAccess(new ThisExpression(), field31.getName());				// this.name1
			Assignment assignment = new Assignment(field1, new Variable(name1));						// this.name1 = name1;
			ExpressionStatement assignmentStatement = new ExpressionStatement(assignment);
			setter3.addStatement(assignmentStatement);

			// Construct setter in class2
			MethodDeclaration setter2 = createMethod(class2, setterPrefix + toComponentName(name1));
			param1 = new VariableDeclaration(type1, name1);
			setter2.addParameter(param1);
			FieldAccess field3 = new FieldAccess(field23.getName());
			List<Expression> args = new ArrayList<>();
			args.add(new Variable(param1.getName()));
			MethodInvocation callSetter = new MethodInvocation(field3, setter3.getName(), args);			// name3.setName1(name1)
			setter2.addStatement(new ExpressionStatement(callSetter));

			// Construct coordinator method
			MethodDeclaration coordionator = createMethod(class1, updateMethodPrefix + toComponentName(name1));
			FieldAccess field2 = new FieldAccess(field12.getName());
			args = new ArrayList<>();
			args.add(new ThisExpression());
			callSetter = new MethodInvocation(field2, setter2.getName(), args);			// name2.setName1(this)
			coordionator.addStatement(new ExpressionStatement(callSetter));
		}
		return codebase;
	}
	
	private static TypeDeclaration createClass(Codebase codebase, String name) {
		if (codebase.getCompilationUnit(name) != null) return codebase.getCompilationUnit(name).types().getFirst();
		TypeDeclaration type = new TypeDeclaration(name);
		CompilationUnit compilationUnit = new CompilationUnit(type);
		codebase.addCompilationUnit(name, compilationUnit);
		return type;
	}
	
	private static MethodDeclaration createMethod(TypeDeclaration type, String name) {
		for (MethodDeclaration method: type.getMethods()) {
			if (method.getName().equals(name)) return method;
		}
		MethodDeclaration method = new MethodDeclaration(name);
		type.addMethod(method);
		return method;
	}
	
	private static FieldDeclaration createField(TypeDeclaration type, String fieldName, models.algebra.Type fieldType) {
		for (FieldDeclaration field: type.getFields()) {
			if (field.getName().equals(fieldName)) return field;
		}
		FieldDeclaration field = new FieldDeclaration(fieldType, fieldName);
		type.addField(field);
		return field;
	}
}