package generators;
import java.util.ArrayList;
import java.util.List;
import ast.*;
import models.algebra.Type;
import models.dataConstraintModel.MapType;
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 final String idPostfix = "Id";
public static final String mapPostfix = "Map";
public static final String mapGet = "get";
public static final String mapPut = "put";
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) {
codebase = weavePrimitivePullDelta(codebase, (PrimitivePullDelta) primitiveDelta);
} else if (primitiveDelta instanceof PrimitivePullPushDelta) {
codebase = weavePrimitivePullPushDelta(codebase, (PrimitivePullPushDelta) primitiveDelta);
} else if (primitiveDelta instanceof PrimitivePushDelta) {
codebase = weavePrimitivePushDelta(codebase, (PrimitivePushDelta) primitiveDelta);
}
return codebase;
}
private static Codebase weavePrimitivePullDelta(Codebase codebase, PrimitivePullDelta primitivePullDelta) {
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, pullEdge1.getName(), type2); // class1 -> class2 (PULL)
FieldDeclaration field23 = null;
models.algebra.Type keyType = null;
if (!pullEdge2.toMany()) {
field23 = createField(class2, pullEdge2.getName(), type3); // class2 -> class3 (PULL)
} else {
MapType typeKeyToType3 = codebase.getMapType(pullEdge2.getKeyTypeName(), name3);
keyType = typeKeyToType3.getKeyType();
field23 = createField(class2, pullEdge2.getName() + mapPostfix, typeKeyToType3); // class2 -> {class3} (PULL)
}
FieldDeclaration field13 = createField(class1, pullEdge2.getName(), type3); // class1 -> class3 (create)
// Construct getter method
MethodDeclaration getter = createMethod(class2, getterPrefix + toComponentName(pullEdge2.getName()));
ReturnStatement return23 = new ReturnStatement();
if (!pullEdge2.toMany()) {
return23.setExpression(new FieldAccess(new ThisExpression(), field23.getName())); // return this.name3;
} else {
VariableDeclaration idVar = new VariableDeclaration(keyType, pullEdge2.getName() + idPostfix);
getter.addParameter(idVar);
FieldAccess field3 = new FieldAccess(field23.getName());
List<Expression> args = new ArrayList<>();
args.add(new Variable(idVar.getName()));
MethodInvocation callGetter = new MethodInvocation(field3, mapGet, args); // name3Map.get(name3Id)
return23.setExpression(callGetter); // return name3Map.get(name3Id);
}
getter.addStatement(return23);
getter.setReturnType(type3);
// Construct coordinator method
MethodDeclaration coordionator = createMethod(class1, updateMethodPrefix + toComponentName(pullEdge2.getName()));
FieldAccess field3 = new FieldAccess(new ThisExpression(), field13.getName()); // this.name3
FieldAccess field2 = new FieldAccess(field12.getName());
List<Expression> args = new ArrayList<>();
if (pullEdge2.toMany()) {
VariableDeclaration idVar = new VariableDeclaration(keyType, pullEdge2.getName() + idPostfix);
coordionator.addParameter(idVar);
args.add(new Variable(idVar.getName())); // name2.getName3(name3Id)
}
MethodInvocation callGetter = new MethodInvocation(field2, getter.getName(), args); // name2.getName3()
Assignment assignment = new Assignment(field3, callGetter); // this.name3 = name2.getName3();
ExpressionStatement assignmentStatement = new ExpressionStatement(assignment);
coordionator.addStatement(assignmentStatement);
return codebase;
}
private static Codebase weavePrimitivePullPushDelta(Codebase codebase, PrimitivePullPushDelta primitivePullPushDelta) {
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, pullEdge.getName(), type2); // class1 -> class2 (PULL)
FieldDeclaration field13 = null;
models.algebra.Type keyType = null;
if (!pushEdge.toMany()) {
field13 = createField(class1, pushEdge.getName(), type3); // class1 -> class3 (PUSH)
} else {
MapType typeKeyToType3 = codebase.getMapType(pushEdge.getKeyTypeName(), name3);
keyType = typeKeyToType3.getKeyType();
field13 = createField(class1, pushEdge.getName() + mapPostfix, typeKeyToType3); // class1 -> {class3} (PUSH)
}
FieldDeclaration field32 = createField(class3, pullEdge.getName(), type2); // class3 -> class2 (create)
// Construct setter method
MethodDeclaration setter = null;
if (!pushEdge.toMany()) {
setter = createMethod(class3, setterPrefix + toComponentName(name2));
} else {
setter = createConstructor(class3);
}
VariableDeclaration param2 = new VariableDeclaration(type2, toVariableName(name2));
setter.addParameter(param2);
FieldAccess field2 = new FieldAccess(new ThisExpression(), field32.getName()); // this.name2
Assignment assignment = new Assignment(field2, new Variable(toVariableName(name2))); // this.name2 = name2;
ExpressionStatement assignmentStatement = new ExpressionStatement(assignment);
setter.addStatement(assignmentStatement);
// Construct coordinator method
MethodDeclaration coordionator = createMethod(class1, updateMethodPrefix + toComponentName(name2));
List<Expression> args = new ArrayList<>();
args.add(new FieldAccess(field12.getName()));
FieldAccess field3 = new FieldAccess(field13.getName());
if (!pushEdge.toMany()) {
MethodInvocation callSetter = new MethodInvocation(field3, setter.getName(), args); // name3.setName2(name2)
coordionator.addStatement(new ExpressionStatement(callSetter));
} else {
VariableDeclaration idVar = new VariableDeclaration(keyType, pushEdge.getName() + idPostfix);
coordionator.addParameter(idVar);
ClassInstanceCreation callConstructor = new ClassInstanceCreation((SimpleType) type3.getImplementationType(), args); // new Name3(name2)
args = new ArrayList<>();
args.add(new Variable(idVar.getName()));
args.add(callConstructor);
coordionator.addStatement(new ExpressionStatement(new MethodInvocation(field3, mapPut, args))); // name3Map.put(name3Id, new new Name3(name2));
}
return codebase;
}
private static Codebase weavePrimitivePushDelta(Codebase codebase, PrimitivePushDelta primitivePushDelta) {
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, pushEdge1.getName(), type2); // class1 -> class2 (PUSH)
FieldDeclaration field23 = createField(class2, pushEdge2.getName(), 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(toVariableName(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, toVariableName(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 MethodDeclaration createConstructor(TypeDeclaration type) {
for (MethodDeclaration method: type.getMethods()) {
if (method.getName().equals(type.getTypeName())) return method;
}
MethodDeclaration method = new MethodDeclaration(type.getTypeName(), true); // constructor
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;
}
}