diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 610356b..4815170 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,6 +1,8 @@ import java.util.ArrayList; import java.util.List; +import designPatternExtensions.DependencyInversion; +import designPatternExtensions.InterfaceNode; import generators.ASTGenerator; import models.objectOrientedTransfer.*; @@ -27,6 +29,11 @@ DataTransferDesign design; try { design = new DataTransferDesign(context); + + InterfaceNode ICompany = new InterfaceNode(company, "ICompany"); + DependencyInversion di = new DependencyInversion(company, ICompany); + design.addDependencyInversion(di); + System.out.println(ASTGenerator.generate(design)); } catch (IllegalRelationException e) { e.printStackTrace(); diff --git a/src/main/java/ast/CompilationUnit.java b/src/main/java/ast/CompilationUnit.java index 68403a1..f4af142 100644 --- a/src/main/java/ast/CompilationUnit.java +++ b/src/main/java/ast/CompilationUnit.java @@ -7,15 +7,21 @@ private String fileName = null; private List imports = new ArrayList<>(); private List types = new ArrayList<>(); - + private List interfaces = new ArrayList<>(); + public CompilationUnit(TypeDeclaration type) { types.add(type); - if(type.getTypeName().contains("<")) + if(type.getTypeName().contains("<")) fileName = type.getTypeName().split("<")[0] + ".java"; else fileName = type.getTypeName() + ".java"; } - + + public CompilationUnit(InterfaceDeclaration interfaceDeclaration) { + interfaces.add(interfaceDeclaration); + fileName = interfaceDeclaration.getTypeName() + ".java"; + } + public List imports() { return imports; } @@ -32,6 +38,14 @@ types.add(type); } + public List interfaces() { + return interfaces; + } + + public void addInterface(InterfaceDeclaration interfaceDeclaration) { + interfaces.add(interfaceDeclaration); + } + public String getFileName() { return fileName; } @@ -45,6 +59,9 @@ for (TypeDeclaration type: types) { result += type.toString(); } + for (InterfaceDeclaration iDec: interfaces) { + result += iDec.toString(); + } return result; } -} +} \ No newline at end of file diff --git a/src/main/java/ast/InterfaceDeclaration.java b/src/main/java/ast/InterfaceDeclaration.java new file mode 100644 index 0000000..61de92d --- /dev/null +++ b/src/main/java/ast/InterfaceDeclaration.java @@ -0,0 +1,71 @@ +package ast; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +public class InterfaceDeclaration extends AbstractTypeDeclaration implements IAnnotatable { + private List methods = new ArrayList<>(); + private Map annotations = new HashMap<>(); + + public InterfaceDeclaration(String typeName) { + this.typeName = typeName; + } + + public void addMethod(MethodDeclaration method) { + methods.add(method); + } + + public void removeMethod(MethodDeclaration method) { + methods.remove(method); + } + + public List getMethods() { + return methods; + } + + @Override + public Annotation getAnnotation(String name) { + return annotations.get(name); + } + + @Override + public Collection getAnnotations() { + return annotations.values(); + } + + @Override + public void addAnnotation(Annotation annotation) { + annotations.put(annotation.getElementName(), annotation); + } + + public String toString() { + String code = ""; + for (Annotation annotation : getAnnotations()) { + code += annotation.toString() + "\n"; + } + code += "public interface " + typeName + " {\n"; + for (MethodDeclaration m : methods) { + String signature = "\t"; + if (m.getReturnType() == null) { + signature += "void "; + } else { + signature += m.getReturnType().getInterfaceTypeName() + " "; + } + signature += m.getName() + "("; + if (m.getParameters() != null) { + for (int i = 0; i < m.getParameters().size(); i++) { + signature += m.getParameters().get(i).toString(); + if (i < m.getParameters().size() - 1) signature += ", "; + } + } + signature += ");\n"; + code += signature; + } + code += "}"; + return code; + } +} \ No newline at end of file diff --git a/src/main/java/ast/TypeDeclaration.java b/src/main/java/ast/TypeDeclaration.java index 4d4a3e5..37916ab 100644 --- a/src/main/java/ast/TypeDeclaration.java +++ b/src/main/java/ast/TypeDeclaration.java @@ -10,7 +10,8 @@ private List fields = new ArrayList<>(); private List methods = new ArrayList<>(); private Map annotations = new HashMap<>(); - + private List implementsInterfaces = new ArrayList<>(); + public TypeDeclaration(String typeName) { this.typeName = typeName; } @@ -25,7 +26,15 @@ this.fields = fields; this.methods = methods; } - + + public void addImplementsInterface(String interfaceName) { + implementsInterfaces.add(interfaceName); + } + + public List getImplementsInterfaces() { + return implementsInterfaces; + } + public void addField(FieldDeclaration field) { fields.add(field); } @@ -72,7 +81,11 @@ for (Annotation annotation: getAnnotations()) { code += annotation.toString() + "\n"; } - code += "public class " + typeName + " {\n"; + code += "public class " + typeName; + if (!implementsInterfaces.isEmpty()) { + code += " implements " + String.join(", ", implementsInterfaces); + } + code += " {\n"; for (FieldDeclaration f: fields) { code += "\t" + f.toString(); } diff --git a/src/main/java/designPatternExtensions/DependencyInversion.java b/src/main/java/designPatternExtensions/DependencyInversion.java index 767d7dd..73510ec 100644 --- a/src/main/java/designPatternExtensions/DependencyInversion.java +++ b/src/main/java/designPatternExtensions/DependencyInversion.java @@ -3,13 +3,19 @@ import models.objectOrientedTransfer.ObjectNode; public class DependencyInversion { - private ObjectNode src; private ObjectNode dst; private InterfaceNode interfaceNode; - public DependencyInversion(ObjectNode src, ObjectNode dst, InterfaceNode interfaceNode) { - this.src = src; + public DependencyInversion(ObjectNode dst, InterfaceNode interfaceNode) { this.dst = dst; this.interfaceNode = interfaceNode; } + + public ObjectNode getDst() { + return dst; + } + + public InterfaceNode getInterfaceNode() { + return interfaceNode; + } } \ No newline at end of file diff --git a/src/main/java/designPatternExtensions/InterfaceNode.java b/src/main/java/designPatternExtensions/InterfaceNode.java index 7fbd6de..cf8facd 100644 --- a/src/main/java/designPatternExtensions/InterfaceNode.java +++ b/src/main/java/designPatternExtensions/InterfaceNode.java @@ -9,13 +9,30 @@ private Set dependencySideNodes = new HashSet<>(); private ObjectNode implementSideNode = null; + public InterfaceNode(ObjectNode implementSideNode, String name) { + super(name); + this.implementSideNode = implementSideNode; + } + public InterfaceNode(ObjectNode dependencySideNode, ObjectNode implementSideNode, String name) { super(name); this.dependencySideNodes.add(dependencySideNode); this.implementSideNode = implementSideNode; } - public Set getDependencySideNodes() { return dependencySideNodes; } - public void addDependencySideNode(ObjectNode node) { dependencySideNodes.add(node); } - public ObjectNode getImplementSideNode() { return implementSideNode; } -} + public Set getDependencySideNodes() { + return dependencySideNodes; + } + + public void addDependencySideNode(ObjectNode node) { + dependencySideNodes.add(node); + } + + public ObjectNode getImplementSideNode() { + return implementSideNode; + } + + public void setImplementSideNode(ObjectNode implementSideNode) { + this.implementSideNode = implementSideNode; + } +} \ No newline at end of file diff --git a/src/main/java/generators/ASTGenerator.java b/src/main/java/generators/ASTGenerator.java index 8ed2456..35fee75 100644 --- a/src/main/java/generators/ASTGenerator.java +++ b/src/main/java/generators/ASTGenerator.java @@ -4,6 +4,8 @@ import java.util.List; import ast.*; +import designPatternExtensions.DependencyInversion; +import designPatternExtensions.InterfaceNode; import models.algebra.Type; import models.dataConstraintModel.MapType; import models.objectOrientedTransfer.*; @@ -16,7 +18,7 @@ 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); } @@ -24,13 +26,16 @@ 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); } + for (DependencyInversion di: dataTransferDesign.getDependencyInversions()) { + codebase = weaveDependencyInversion(codebase, di); + } return codebase; } @@ -43,7 +48,7 @@ return codebase; } } - + public static Codebase weavePrimitiveDelta(Codebase codebase, PrimitiveDelta primitiveDelta) { if (primitiveDelta instanceof PrimitivePullDelta) { codebase = weavePrimitivePullDelta(codebase, (PrimitivePullDelta) primitiveDelta); @@ -81,7 +86,7 @@ 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(); @@ -98,7 +103,7 @@ } 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 @@ -142,7 +147,7 @@ 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()) { @@ -156,7 +161,7 @@ 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 args = new ArrayList<>(); @@ -195,7 +200,7 @@ 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); @@ -224,7 +229,66 @@ coordionator.addStatement(new ExpressionStatement(callSetter)); return codebase; } - + + public static Codebase weaveDependencyInversion(Codebase codebase, DependencyInversion dependencyInversion) { + ObjectNode dstNode = dependencyInversion.getDst(); + InterfaceNode interfaceNode = dependencyInversion.getInterfaceNode(); + String dstName = toComponentName(dstNode.getName()); + String interfaceName = toComponentName(interfaceNode.getName()); + + InterfaceDeclaration interfaceDeclaration = createInterface(codebase, interfaceName); + + TypeDeclaration dstClass = createClass(codebase, dstName); + for (MethodDeclaration method: dstClass.getMethods()) { + if (!method.isConstructor()) { + interfaceDeclaration.addMethod(method); + } + } + + if (!dstClass.getImplementsInterfaces().contains(interfaceName)) { + dstClass.addImplementsInterface(interfaceName); + } + + models.algebra.Type interfaceType = codebase.getComponentType(interfaceName); + models.algebra.Type dstType = codebase.getComponentType(dstName); + for (CompilationUnit cu: codebase.getCompilationUnits()) { + TypeDeclaration srcClass = cu.types().getFirst(); + if (srcClass.getTypeName().equals(dstName)) continue; + + for (FieldDeclaration field: srcClass.getFields()) { + if (field.getType().equals(dstType)) { + field.setType(interfaceType); + } else if (field.getType() instanceof MapType) { + MapType mapType = (MapType) field.getType(); + if (mapType.getValueType() != null && mapType.getValueType().getTypeName().equals(dstType.getTypeName())) { + field.setType(codebase.getMapType(mapType.getKeyType().getTypeName(), interfaceName)); + } + } + } + + for (MethodDeclaration method: srcClass.getMethods()) { + if (method.isConstructor() && method.getParameters() != null) { + for (VariableDeclaration param: method.getParameters()) { + if (param.getType() != null && param.getType().equals(dstType)) { + param.setType(interfaceType); + } + } + } + } + } + return codebase; + } + + private static InterfaceDeclaration createInterface(Codebase codebase, String name) { + if (codebase.getInterfaceCompilationUnit(name) != null) { + return codebase.getInterfaceCompilationUnit(name).interfaces().getFirst(); + } + InterfaceDeclaration interfaceDeclaration = new InterfaceDeclaration(name); + CompilationUnit compilationUnit = new CompilationUnit(interfaceDeclaration); + codebase.addInterfaceCompilationUnit(name, compilationUnit); + return interfaceDeclaration; + } + private static TypeDeclaration createClass(Codebase codebase, String name) { if (codebase.getCompilationUnit(name) != null) return codebase.getCompilationUnit(name).types().getFirst(); TypeDeclaration type = new TypeDeclaration(name); @@ -232,7 +296,7 @@ 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; @@ -241,7 +305,7 @@ type.addMethod(method); return method; } - + private static MethodDeclaration createConstructor(TypeDeclaration type) { for (MethodDeclaration method: type.getMethods()) { if (method.getName().equals(type.getTypeName())) return method; @@ -250,7 +314,7 @@ 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; @@ -259,4 +323,4 @@ type.addField(field); return field; } -} +} \ No newline at end of file diff --git a/src/main/java/generators/Codebase.java b/src/main/java/generators/Codebase.java index 2b5ae0f..a7f229f 100644 --- a/src/main/java/generators/Codebase.java +++ b/src/main/java/generators/Codebase.java @@ -11,6 +11,7 @@ public class Codebase { private Map classes = new HashMap<>(); + private Map interfaces = new HashMap<>(); private Map componentTypes = new HashMap<>(); private Map standardTypes = new HashMap<>(); @@ -52,12 +53,25 @@ models.algebra.Type newType = new models.algebra.Type(name, new SimpleType(name)); componentTypes.put(name, newType); } - + + public CompilationUnit getInterfaceCompilationUnit(String name) { + return interfaces.get(name); + } + + public void addInterfaceCompilationUnit(String name, CompilationUnit compilationUnit) { + interfaces.put(name, compilationUnit); + models.algebra.Type newType = new models.algebra.Type(name, new SimpleType(name)); + componentTypes.put(name, newType); + } + public String toString() { String code = ""; for (CompilationUnit cu: classes.values()) { code += cu.toString(); } + for (CompilationUnit cu: interfaces.values()) { + code += cu.toString(); + } return code; } } diff --git a/src/main/java/models/objectOrientedTransfer/DataTransferDesign.java b/src/main/java/models/objectOrientedTransfer/DataTransferDesign.java index a650dac..f212d8a 100644 --- a/src/main/java/models/objectOrientedTransfer/DataTransferDesign.java +++ b/src/main/java/models/objectOrientedTransfer/DataTransferDesign.java @@ -1,5 +1,7 @@ package models.objectOrientedTransfer; +import designPatternExtensions.DependencyInversion; + import java.util.ArrayList; import java.util.List; import java.util.Stack; @@ -13,12 +15,21 @@ public class DataTransferDesign { private DataTransferContext transferContext; private DeltaComplex deltaComplex; - + private List dependencyInversions = new ArrayList<>(); + public DataTransferDesign(DataTransferContext transferContext) throws IllegalRelationException { this.transferContext = transferContext; this.deltaComplex = createDefaultDeltaComlex(transferContext); } + public void addDependencyInversion(DependencyInversion dependencyInversion) { + dependencyInversions.add(dependencyInversion); + } + + public List getDependencyInversions() { + return dependencyInversions; + } + public DeltaComplex createDefaultDeltaComlex(DataTransferContext transferContext) throws IllegalRelationException { if (transferContext.getTransferStyle() == PushPullValue.PUSH) { // PUSH transfer