diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 4815170..3fbd121 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -3,6 +3,7 @@ import designPatternExtensions.DependencyInversion; import designPatternExtensions.InterfaceNode; +import designPatternExtensions.MediatorInsertion; import generators.ASTGenerator; import models.objectOrientedTransfer.*; @@ -30,8 +31,11 @@ try { design = new DataTransferDesign(context); - InterfaceNode ICompany = new InterfaceNode(company, "ICompany"); - DependencyInversion di = new DependencyInversion(company, ICompany); + ObjectNode cm = new ObjectNode("CompaniesMediator"); + MediatorInsertion mi = new MediatorInsertion(customer, companies, cm); + design.addMediatorInsertion(mi); + + DependencyInversion di = new DependencyInversion(company, new InterfaceNode(company, "ICompany")); design.addDependencyInversion(di); System.out.println(ASTGenerator.generate(design)); diff --git a/src/main/java/ast/TypeDeclaration.java b/src/main/java/ast/TypeDeclaration.java index 37916ab..ceda7a6 100644 --- a/src/main/java/ast/TypeDeclaration.java +++ b/src/main/java/ast/TypeDeclaration.java @@ -47,6 +47,10 @@ methods.remove(method); } + public void removeField(FieldDeclaration field) { + fields.remove(field); + } + public List getFields() { return fields; } diff --git a/src/main/java/designPatternExtensions/DependencyInversion.java b/src/main/java/designPatternExtensions/DependencyInversion.java index 73510ec..e59a1d5 100644 --- a/src/main/java/designPatternExtensions/DependencyInversion.java +++ b/src/main/java/designPatternExtensions/DependencyInversion.java @@ -5,10 +5,18 @@ 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() { @@ -18,4 +26,8 @@ public InterfaceNode getInterfaceNode() { return interfaceNode; } + + public ObjectNode getTargetSrc() { + return targetSrc; + } } \ No newline at end of file diff --git a/src/main/java/designPatternExtensions/MediatorInsertion.java b/src/main/java/designPatternExtensions/MediatorInsertion.java index e694356..552a090 100644 --- a/src/main/java/designPatternExtensions/MediatorInsertion.java +++ b/src/main/java/designPatternExtensions/MediatorInsertion.java @@ -6,4 +6,35 @@ 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/generators/ASTGenerator.java b/src/main/java/generators/ASTGenerator.java index 35fee75..7ae3eae 100644 --- a/src/main/java/generators/ASTGenerator.java +++ b/src/main/java/generators/ASTGenerator.java @@ -6,6 +6,7 @@ import ast.*; import designPatternExtensions.DependencyInversion; import designPatternExtensions.InterfaceNode; +import designPatternExtensions.MediatorInsertion; import models.algebra.Type; import models.dataConstraintModel.MapType; import models.objectOrientedTransfer.*; @@ -33,6 +34,10 @@ 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); } @@ -230,12 +235,128 @@ 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); + } + } + 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); @@ -245,40 +366,67 @@ } } + // 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); - 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)); - } - } - } + // // Change dst class method return types + replaceDepedencyInClass(codebase, dstClass, dstType, interfaceType, 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); - } - } - } + 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(); diff --git a/src/main/java/models/objectOrientedTransfer/DataTransferDesign.java b/src/main/java/models/objectOrientedTransfer/DataTransferDesign.java index f212d8a..b5231fc 100644 --- a/src/main/java/models/objectOrientedTransfer/DataTransferDesign.java +++ b/src/main/java/models/objectOrientedTransfer/DataTransferDesign.java @@ -1,6 +1,7 @@ package models.objectOrientedTransfer; import designPatternExtensions.DependencyInversion; +import designPatternExtensions.MediatorInsertion; import java.util.ArrayList; import java.util.List; @@ -16,6 +17,7 @@ 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; @@ -30,6 +32,14 @@ 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