diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 610356b..3ef1d7a 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,6 +1,7 @@ import java.util.ArrayList; import java.util.List; +import designPatternExtensions.*; import generators.ASTGenerator; import models.objectOrientedTransfer.*; @@ -27,7 +28,17 @@ DataTransferDesign design; try { design = new DataTransferDesign(context); - System.out.println(ASTGenerator.generate(design)); + + ObjectNode cm = new ObjectNode("CompanyMediator"); + MediatorInsertion mi = new MediatorInsertion(customer, company, cm); + design.addMediatorInsertion(mi); + + DependencyInversion di = new DependencyInversion(company, new InterfaceNode(company, "ICompany"), customer); + design.addDependencyInversion(di); + + + System.out.println(ASTGenerator.generate(Observer.create())); +// 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..ceda7a6 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); } @@ -38,6 +47,10 @@ methods.remove(method); } + public void removeField(FieldDeclaration field) { + fields.remove(field); + } + public List getFields() { return fields; } @@ -72,7 +85,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/Adapter.java b/src/main/java/designPatternExtensions/Adapter.java new file mode 100644 index 0000000..fe83a8c --- /dev/null +++ b/src/main/java/designPatternExtensions/Adapter.java @@ -0,0 +1,38 @@ +package designPatternExtensions; + +import models.objectOrientedTransfer.*; + +import java.util.ArrayList; +import java.util.List; + +public class Adapter { + + public static DataTransferDesign create() throws IllegalRelationException { + ObjectNode main = new ObjectNode("Main"); + ObjectNode fishingBoat = new ObjectNode("FishingBoat"); + ObjectNode captain = new ObjectNode("Captain"); + ObjectNode data = new ObjectNode("String"); + + List srcEdges = new ArrayList<>(); + List dstEdges = new ArrayList<>(); + ReferenceEdge srcEdge = new ReferenceEdge(main, fishingBoat, "fishingBoat", "String"); + ReferenceEdge dstEdge = new ReferenceEdge(main, captain, "captain", "String"); + srcEdges.add(srcEdge); + dstEdges.add(dstEdge); + + List relations = new ArrayList<>(); + Relation transferRelation = new Relation(srcEdges, dstEdges, MultiplicityValue.OneToOne); + DataTransferContext context = new DataTransferContext(relations, transferRelation, data, "data", PushPullValue.PULL); + + DataTransferDesign design = new DataTransferDesign(context); + + ObjectNode adapter = new ObjectNode("FishingBoatAdapter"); + MediatorInsertion mi = new MediatorInsertion(captain, fishingBoat, adapter); + design.addMediatorInsertion(mi); + + DependencyInversion di = new DependencyInversion(adapter, new InterfaceNode(adapter, "RowingBoat"), captain); + design.addDependencyInversion(di); + + return design; + } +} \ No newline at end of file diff --git a/src/main/java/designPatternExtensions/Bridge.java b/src/main/java/designPatternExtensions/Bridge.java new file mode 100644 index 0000000..4222324 --- /dev/null +++ b/src/main/java/designPatternExtensions/Bridge.java @@ -0,0 +1,35 @@ +package designPatternExtensions; + +import models.objectOrientedTransfer.*; + +import java.util.ArrayList; +import java.util.List; + +public class Bridge { + + public static DataTransferDesign create() throws IllegalRelationException { + ObjectNode main = new ObjectNode("Main"); + ObjectNode sword = new ObjectNode("Sword"); + ObjectNode soulEatingEnchantment = new ObjectNode("SoulEatingEnchantment"); + ObjectNode data = new ObjectNode("String"); + + List srcEdges = new ArrayList<>(); + List dstEdges = new ArrayList<>(); + srcEdges.add(new ReferenceEdge(main, soulEatingEnchantment, "soulEatingEnchantment", "String")); + dstEdges.add(new ReferenceEdge(main, sword, "sword", "String")); + + List relations = new ArrayList<>(); + Relation transferRelation = new Relation(srcEdges, dstEdges, MultiplicityValue.OneToOne); + DataTransferContext context = new DataTransferContext(relations, transferRelation, data, "data", PushPullValue.PULL); + + DataTransferDesign design = new DataTransferDesign(context); + + DependencyInversion di1 = new DependencyInversion(sword, new InterfaceNode(sword, "Weapon"), main); + design.addDependencyInversion(di1); + + DependencyInversion di2 = new DependencyInversion(soulEatingEnchantment, new InterfaceNode(soulEatingEnchantment, "Enchantment"), sword); + design.addDependencyInversion(di2); + + return design; + } +} \ No newline at end of file diff --git a/src/main/java/designPatternExtensions/DependencyInversion.java b/src/main/java/designPatternExtensions/DependencyInversion.java new file mode 100644 index 0000000..e59a1d5 --- /dev/null +++ b/src/main/java/designPatternExtensions/DependencyInversion.java @@ -0,0 +1,33 @@ +package designPatternExtensions; + +import models.objectOrientedTransfer.ObjectNode; + +public class DependencyInversion { + private ObjectNode dst; + private InterfaceNode interfaceNode; + private ObjectNode targetSrc; + + public DependencyInversion(ObjectNode dst, InterfaceNode interfaceNode) { + this.dst = dst; + this.interfaceNode = interfaceNode; + this.targetSrc = null; + } + + public DependencyInversion(ObjectNode dst, InterfaceNode interfaceNode, ObjectNode targetSrc) { + this.dst = dst; + this.interfaceNode = interfaceNode; + this.targetSrc = targetSrc; + } + + public ObjectNode getDst() { + return dst; + } + + public InterfaceNode getInterfaceNode() { + return interfaceNode; + } + + public ObjectNode getTargetSrc() { + return targetSrc; + } +} \ No newline at end of file diff --git a/src/main/java/designPatternExtensions/InterfaceNode.java b/src/main/java/designPatternExtensions/InterfaceNode.java new file mode 100644 index 0000000..cf8facd --- /dev/null +++ b/src/main/java/designPatternExtensions/InterfaceNode.java @@ -0,0 +1,38 @@ +package designPatternExtensions; + +import models.objectOrientedTransfer.ObjectNode; + +import java.util.HashSet; +import java.util.Set; + +public class InterfaceNode extends ObjectNode { + 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 void setImplementSideNode(ObjectNode implementSideNode) { + this.implementSideNode = implementSideNode; + } +} \ No newline at end of file diff --git a/src/main/java/designPatternExtensions/MediatorInsertion.java b/src/main/java/designPatternExtensions/MediatorInsertion.java new file mode 100644 index 0000000..552a090 --- /dev/null +++ b/src/main/java/designPatternExtensions/MediatorInsertion.java @@ -0,0 +1,40 @@ +package designPatternExtensions; + +import models.objectOrientedTransfer.ObjectNode; + +public class MediatorInsertion { + private ObjectNode src; + private ObjectNode dst; + private ObjectNode mediator; + private String mediatorFieldName; + + public MediatorInsertion(ObjectNode src, ObjectNode dst, ObjectNode mediator) { + this.src = src; + this.dst = dst; + this.mediator = mediator; + this.mediatorFieldName = mediator.getName().substring(0, 1).toLowerCase() + mediator.getName().substring(1); + } + + public MediatorInsertion(ObjectNode src, ObjectNode dst, ObjectNode mediator, String mediatorFieldName) { + this.src = src; + this.dst = dst; + this.mediator = mediator; + this.mediatorFieldName = mediatorFieldName; + } + + public ObjectNode getSrc() { + return src; + } + + public ObjectNode getDst() { + return dst; + } + + public ObjectNode getMediator() { + return mediator; + } + + public String getMediatorFieldName() { + return mediatorFieldName; + } +} \ No newline at end of file diff --git a/src/main/java/designPatternExtensions/Observer.java b/src/main/java/designPatternExtensions/Observer.java new file mode 100644 index 0000000..d1c1f1b --- /dev/null +++ b/src/main/java/designPatternExtensions/Observer.java @@ -0,0 +1,48 @@ +package designPatternExtensions; + +import models.objectOrientedTransfer.*; + +import java.util.ArrayList; +import java.util.List; + +public class Observer { + + public static DataTransferDesign create() throws IllegalRelationException { + ObjectNode main = new ObjectNode("Main"); + ObjectNode weather = new ObjectNode("Weather"); + ObjectNode orcs = new ObjectNode("Orcs"); + ObjectNode hobbits = new ObjectNode("Hobbits"); + ObjectNode data = new ObjectNode("WeatherType"); + + List srcEdges = new ArrayList<>(); + ReferenceEdge srcEdge = new ReferenceEdge(main, weather, "weather", "WeatherType"); + srcEdges.add(srcEdge); + + List dstEdges = new ArrayList<>(); + ReferenceEdge dstEdge = new ReferenceEdge(main, orcs, "orcs", "WeatherType"); + dstEdges.add(dstEdge); + + List relations = new ArrayList<>(); + Relation transferRelation = new Relation(srcEdges, dstEdges, MultiplicityValue.ManyToOne); + DataTransferContext context = new DataTransferContext( + relations, transferRelation, data, "currentWeather", PushPullValue.PUSH + ); + + DataTransferDesign design = new DataTransferDesign(context); + + DependencyInversion di1 = new DependencyInversion( + orcs, + new InterfaceNode(orcs, "WeatherObserver"), + weather + ); + DependencyInversion di2 = new DependencyInversion( + hobbits, + new InterfaceNode(hobbits, "WeatherObserver"), + weather + ); + design.addDependencyInversion(di1); + design.addDependencyInversion(di2); + + return design; + } +} \ No newline at end of file diff --git a/src/main/java/generators/ASTGenerator.java b/src/main/java/generators/ASTGenerator.java index 8ed2456..2eabbb0 100644 --- a/src/main/java/generators/ASTGenerator.java +++ b/src/main/java/generators/ASTGenerator.java @@ -4,6 +4,9 @@ import java.util.List; import ast.*; +import designPatternExtensions.DependencyInversion; +import designPatternExtensions.InterfaceNode; +import designPatternExtensions.MediatorInsertion; import models.algebra.Type; import models.dataConstraintModel.MapType; import models.objectOrientedTransfer.*; @@ -16,7 +19,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 +27,20 @@ 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); } + // do not change flow : MediatorInsertion -> DependencyInversion + for (MediatorInsertion mi: dataTransferDesign.getMediatorInsertions()) { + codebase = weaveMediatorInsertion(codebase, mi); + } + for (DependencyInversion di: dataTransferDesign.getDependencyInversions()) { + codebase = weaveDependencyInversion(codebase, di); + } return codebase; } @@ -43,7 +53,7 @@ return codebase; } } - + public static Codebase weavePrimitiveDelta(Codebase codebase, PrimitiveDelta primitiveDelta) { if (primitiveDelta instanceof PrimitivePullDelta) { codebase = weavePrimitivePullDelta(codebase, (PrimitivePullDelta) primitiveDelta); @@ -81,7 +91,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 +108,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 +152,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 +166,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 +205,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 +234,229 @@ coordionator.addStatement(new ExpressionStatement(callSetter)); return codebase; } - + + public static Codebase weaveMediatorInsertion(Codebase codebase, MediatorInsertion mediatorInsertion) { + ObjectNode srcNode = mediatorInsertion.getSrc(); + ObjectNode dstNode = mediatorInsertion.getDst(); + ObjectNode mediatorNode = mediatorInsertion.getMediator(); + String srcName = toComponentName(srcNode.getName()); + String dstName = toComponentName(dstNode.getName()); + String mediatorName = toComponentName(mediatorNode.getName()); + String mediatorFieldName = toVariableName(mediatorName); + String dstFieldName = toVariableName(dstName); + + models.algebra.Type dstType = codebase.getComponentType(dstName); + + // Create a Mediator class and add a field of type dst + TypeDeclaration mediatorClass = createClass(codebase, mediatorName); + models.algebra.Type mediatorType = codebase.getComponentType(mediatorName); + FieldDeclaration dstField = createField(mediatorClass, dstFieldName, dstType); + + // Add delegation methods to the Mediator that forward calls to dst's public methods + TypeDeclaration dstClass = createClass(codebase, dstName); + for (MethodDeclaration method: dstClass.getMethods()) { + if (method.isConstructor()) continue; + MethodDeclaration delegateMethod = createMethod(mediatorClass, method.getName()); + delegateMethod.setReturnType(method.getReturnType()); + List args = new ArrayList<>(); + if (method.getParameters() != null) { + for (VariableDeclaration param: method.getParameters()) { + delegateMethod.addParameter(param); + args.add(new Variable(param.getName())); + } + } + FieldAccess dstAccess = new FieldAccess(dstField.getName()); + MethodInvocation callDst = new MethodInvocation(dstAccess, method.getName(), args); + if (method.getReturnType() != null) { + ReturnStatement returnStatement = new ReturnStatement(); + returnStatement.setExpression(callDst); + delegateMethod.addStatement(returnStatement); + } else { + delegateMethod.addStatement(new ExpressionStatement(callDst)); + } + } + + // Replace the dst-type field in src with a Mediator-type field + TypeDeclaration srcClass = createClass(codebase, srcName); + for (FieldDeclaration field: srcClass.getFields()) { + if (field.getType() != null && field.getType().getTypeName().equals(dstType.getTypeName())) { + field.setType(mediatorType); + field.setName(mediatorFieldName); + } 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(), mediatorName)); + field.setName(mediatorFieldName); + } + } + } + + // Change the constructor parameter type in src from dst to Mediator, and rename the parameter accordingly + for (MethodDeclaration method: srcClass.getMethods()) { + if (method.isConstructor() && method.getParameters() != null) { + for (VariableDeclaration param: method.getParameters()) { + if (param.getType() != null && param.getType().getTypeName().equals(dstType.getTypeName())) { + param.setType(mediatorType); + param.setName(mediatorFieldName); + } + } + } + } + + // Replace all references to the dst field with references to the mediator field in the bodies of src's methods + for (MethodDeclaration method: srcClass.getMethods()) { + if (method.getBody() == null) continue; + for (Statement statement: method.getBody().getStatements2()) { + replaceFieldNameInStatement(statement, dstFieldName, mediatorFieldName); + } + } + + // Replace return type and MapType value type matching dst type with Mediator type in all classes + for (CompilationUnit cu: codebase.getCompilationUnits()) { + TypeDeclaration cls = cu.types().getFirst(); + for (MethodDeclaration method: cls.getMethods()) { + if (!method.isConstructor() && method.getReturnType() != null) { + if (method.getReturnType().getTypeName().equals(dstType.getTypeName())) { + method.setReturnType(mediatorType); + } + } + } + for (FieldDeclaration field: cls.getFields()) { + 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(), mediatorName)); + } + } + } + } + return codebase; + } + + private static void replaceFieldNameInStatement(Statement statement, String oldName, String newName) { + if (statement instanceof ExpressionStatement) { + replaceFieldNameInExpression(((ExpressionStatement) statement).getExpression(), oldName, newName); + } + } + + private static void replaceFieldNameInExpression(Expression expr, String oldName, String newName) { + if (expr instanceof MethodInvocation) { + MethodInvocation mi = (MethodInvocation) expr; + if (mi.getReceiver() instanceof FieldAccess) { + FieldAccess fa = (FieldAccess) mi.getReceiver(); + if (fa.getFieldName().equals(oldName)) { + fa.setFieldName(newName); + } + } + for (Expression arg: mi.getArguments()) { + replaceFieldNameInExpression(arg, oldName, newName); + } + } else if (expr instanceof Assignment) { + Assignment assignment = (Assignment) expr; + if (assignment.getLeft() instanceof FieldAccess) { + FieldAccess fa = (FieldAccess) assignment.getLeft(); + if (fa.getFieldName().equals(oldName)) { + fa.setFieldName(newName); + } + } + if (assignment.getRight() instanceof Variable) { + Variable var = (Variable) assignment.getRight(); + if (var.getName().equals(oldName)) { + assignment.setRight(new Variable(newName)); + } + } + replaceFieldNameInExpression(assignment.getLeft(), oldName, newName); + replaceFieldNameInExpression(assignment.getRight(), oldName, newName); + } + } + + 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()); + + //print -> public interface interfaceName { ... } + InterfaceDeclaration interfaceDeclaration = createInterface(codebase, interfaceName); + + TypeDeclaration dstClass = createClass(codebase, dstName); + for (MethodDeclaration method: dstClass.getMethods()) { + if (!method.isConstructor()) { + interfaceDeclaration.addMethod(method); + } + } + + // public class "dstClass" implements "interfaceName" + if (!dstClass.getImplementsInterfaces().contains(interfaceName)) { + dstClass.addImplementsInterface(interfaceName); + } + + models.algebra.Type interfaceType = codebase.getComponentType(interfaceName); + models.algebra.Type dstType = codebase.getComponentType(dstName); + + // // Change dst class method return types + replaceDepedencyInClass(codebase, dstClass, dstType, interfaceType, interfaceName); + + if (dependencyInversion.getTargetSrc() != null) { + // Traverse only the specified target class + String targetSrcName = toComponentName(dependencyInversion.getTargetSrc().getName()); + TypeDeclaration srcClass = createClass(codebase, targetSrcName); + replaceDepedencyInClass(codebase, srcClass, dstType, interfaceType, interfaceName); + } else { + // Traverse all dependent classes(srcClass) in Codebase + for (CompilationUnit cu: codebase.getCompilationUnits()) { + TypeDeclaration srcClass = cu.types().getFirst(); + if (srcClass.getTypeName().equals(dstName)) continue; + replaceDepedencyInClass(codebase, srcClass, dstType, interfaceType, interfaceName); + } + } + return codebase; + } + + private static void replaceDepedencyInClass(Codebase codebase, TypeDeclaration srcClass, models.algebra.Type dstType, models.algebra.Type interfaceType, String interfaceName) { + for (FieldDeclaration field: srcClass.getFields()) { + if (field.getType().equals(dstType)) { + // private Companies companies → private ICompanies companies + field.setType(interfaceType); + } else if (field.getType() instanceof MapType) { + MapType mapType = (MapType) field.getType(); + // Map → Map + if (mapType.getValueType() != null && mapType.getValueType().getTypeName().equals(dstType.getTypeName())) { + field.setType(codebase.getMapType(mapType.getKeyType().getTypeName(), interfaceName)); + } + } + } + + for (MethodDeclaration method: srcClass.getMethods()) { + // Change constructor parameter types + if (method.isConstructor() && method.getParameters() != null) { + for (VariableDeclaration param: method.getParameters()) { + if (param.getType() != null && param.getType().equals(dstType)) { + // Customer(Companies companies) → Customer(ICompanies companies) + param.setType(interfaceType); + } + } + } + // Change method return types + if (!method.isConstructor() && method.getReturnType() != null) { + if (method.getReturnType().getTypeName().equals(dstType.getTypeName())) { + // public Company getCompany() → public ICompany getCompany() + method.setReturnType(interfaceType); + } + } + } + } + + 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 +464,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 +473,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 +482,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 +491,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..b5231fc 100644 --- a/src/main/java/models/objectOrientedTransfer/DataTransferDesign.java +++ b/src/main/java/models/objectOrientedTransfer/DataTransferDesign.java @@ -1,5 +1,8 @@ package models.objectOrientedTransfer; +import designPatternExtensions.DependencyInversion; +import designPatternExtensions.MediatorInsertion; + import java.util.ArrayList; import java.util.List; import java.util.Stack; @@ -13,12 +16,30 @@ public class DataTransferDesign { private DataTransferContext transferContext; private DeltaComplex deltaComplex; - + private List dependencyInversions = new ArrayList<>(); + private List mediatorInsertions = 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 void addMediatorInsertion(MediatorInsertion mediatorInsertion) { + mediatorInsertions.add(mediatorInsertion); + } + + public List getMediatorInsertions() { + return mediatorInsertions; + } + public DeltaComplex createDefaultDeltaComlex(DataTransferContext transferContext) throws IllegalRelationException { if (transferContext.getTransferStyle() == PushPullValue.PUSH) { // PUSH transfer