diff --git a/models/designPattern/Mediator.Json b/models/designPattern/Mediator.Json index d2a1c4e..bb64029 100644 --- a/models/designPattern/Mediator.Json +++ b/models/designPattern/Mediator.Json @@ -4,19 +4,9 @@ { "relations": [], "transfer": { - "src": "Main", - "dst": "PartyImpl", - "data": { "type": "Action", "name": "action" }, - "style": "PUSH", - "multiplicity": "1:1" - } - }, - { - "relations": [], - "transfer": { - "src": "Main", - "dst": "Main.{ConcretePartyMember}", - "data": { "type": "Action", "name": "action" }, + "src": "Main.Action", + "dst": "Main.PartyImpl.{ConcretePartyMember}.Action", + "data": { "name": "action" }, "style": "PUSH", "multiplicity": "1:1" } @@ -25,9 +15,9 @@ "relations": [ ], "transfer": { - "src": "Main.{ConcretePartyMember}", - "dst": "Main.{ConcretePartyMember}", - "data": { "type": "Action", "name": "action" }, + "src": "Main.PartyImpl.{ConcretePartyMember}.Action", + "dst": "Main.PartyImpl.{ConcretePartyMember}.Action", + "data": { "name": "action" }, "style": "PUSH", "multiplicity": "1:*" } diff --git a/models/designPattern/Mediator2.Json b/models/designPattern/Mediator2.Json deleted file mode 100644 index bb64029..0000000 --- a/models/designPattern/Mediator2.Json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Mediator", - "contexts": [ - { - "relations": [], - "transfer": { - "src": "Main.Action", - "dst": "Main.PartyImpl.{ConcretePartyMember}.Action", - "data": { "name": "action" }, - "style": "PUSH", - "multiplicity": "1:1" - } - }, - { - "relations": [ - ], - "transfer": { - "src": "Main.PartyImpl.{ConcretePartyMember}.Action", - "dst": "Main.PartyImpl.{ConcretePartyMember}.Action", - "data": { "name": "action" }, - "style": "PUSH", - "multiplicity": "1:*" - } - } - ] -} \ No newline at end of file diff --git a/src/main/java/designPatternExtensions/designPattern/Mediator.java b/src/main/java/designPatternExtensions/designPattern/Mediator.java index 01a3697..c331a44 100644 --- a/src/main/java/designPatternExtensions/designPattern/Mediator.java +++ b/src/main/java/designPatternExtensions/designPattern/Mediator.java @@ -1,21 +1,27 @@ package designPatternExtensions.designPattern; -import designPatternExtensions.*; +import designPatternExtensions.DependencyInversion; +import designPatternExtensions.InterfaceNode; +import designPatternExtensions.MediatorInsertion; +import designPatternExtensions.ModelLoader; import models.deltaAlgebra.ObjectNode; import models.dataSynchronizationModel.*; //concretePartyMemberがActionをおこすと、PartyImplからほかのPartyMemberの処理を呼び出す //https://github.com/iluwatar/java-design-patterns/tree/master/mediator +//srcとdstが同じ型の転送の場合、weaveDeltaが生成したメソッドの中に、そのクラス固有の兄弟フィールドへの +//参照が入り込んでしまう(partyImpl→thisへの読み替え、action→パラメータ化)で対応できる +//MediatorInsertionで対応するべきかそれより前の問題か + public class Mediator implements DesignPattern { public DataSynchronizationDesign create() throws IllegalRelationException { DataSynchronizationDesign design = ModelLoader.load("models/designPattern/" + this.getClass().getSimpleName() + ".Json"); DataSynchronizationContext dtc = design.getSynchronizationContexts().getFirst(); ObjectNode main = dtc.getSrcTopObject(); - ObjectNode partyImpl = dtc.getDstTopObject(); - DataSynchronizationContext dtc2 = design.getSynchronizationContexts().getLast(); - ObjectNode concretePartyMember = (ObjectNode) dtc2.getDstObjectHierarchy().getEdge(0).getDestination(); + ObjectNode partyImpl = (ObjectNode) dtc.getDstObjectHierarchy().getEdge(0).getDestination(); + ObjectNode concretePartyMember = (ObjectNode) dtc.getDstObjectHierarchy().getEdge(1).getDestination(); InterfaceNode partyMemberBase = new InterfaceNode(concretePartyMember, "PartyMemberBase"); DependencyInversion di = new DependencyInversion(concretePartyMember, partyMemberBase); @@ -32,9 +38,6 @@ MediatorInsertion mi = new MediatorInsertion(concretePartyMember, concretePartyMember, partyImpl, PushPullValue.PUSH); design.addMediatorInsertion(mi); - MediatorInsertion mi2 = new MediatorInsertion(main, concretePartyMember, partyImpl, PushPullValue.PUSH); - design.addMediatorInsertion(mi2); - return design; } } diff --git a/src/main/java/designPatternExtensions/designPattern/Mediator2.java b/src/main/java/designPatternExtensions/designPattern/Mediator2.java deleted file mode 100644 index 28875a6..0000000 --- a/src/main/java/designPatternExtensions/designPattern/Mediator2.java +++ /dev/null @@ -1,66 +0,0 @@ -package designPatternExtensions.designPattern; - -import designPatternExtensions.DependencyInversion; -import designPatternExtensions.InterfaceNode; -import designPatternExtensions.MediatorInsertion; -import designPatternExtensions.ModelLoader; -import models.deltaAlgebra.ObjectNode; -import models.dataSynchronizationModel.*; - -//concretePartyMemberがActionをおこすと、PartyImplからほかのPartyMemberの処理を呼び出す -//https://github.com/iluwatar/java-design-patterns/tree/master/mediator - -//srcとdstが同じ型の転送の場合、weaveDeltaが生成したメソッドの中に、そのクラス固有の兄弟フィールドへの -//参照が入り込んでしまう(partyImpl→thisへの読み替え、action→パラメータ化)で対応できる -//MediatorInsertionで対応するべきかそれより前の問題か - -public class Mediator2 implements DesignPattern { - - public DataSynchronizationDesign create() throws IllegalRelationException { - DataSynchronizationDesign design = ModelLoader.load("models/designPattern/" + this.getClass().getSimpleName() + ".Json"); - DataSynchronizationContext dtc = design.getSynchronizationContexts().getFirst(); - ObjectNode main = dtc.getSrcTopObject(); - ObjectNode partyImpl = (ObjectNode) dtc.getDstObjectHierarchy().getEdge(0).getDestination(); - ObjectNode concretePartyMember = (ObjectNode) dtc.getDstObjectHierarchy().getEdge(1).getDestination(); - -// InterfaceNode partyMemberBase = new InterfaceNode(concretePartyMember, "PartyMemberBase"); -// DependencyInversion di = new DependencyInversion(concretePartyMember, partyMemberBase); -// design.addDependencyInversion(di); -// -// InterfaceNode partyMember = new InterfaceNode(partyMemberBase, "PartyMember"); -// DependencyInversion di2 = new DependencyInversion(partyMemberBase, partyMember); -// design.addDependencyInversion(di2); -// -// InterfaceNode party = new InterfaceNode(partyImpl, "Party"); -// DependencyInversion di3 = new DependencyInversion(partyImpl, party); -// design.addDependencyInversion(di3); - - ObjectNode imp2 = new ObjectNode("PartyImpl2"); - - MediatorInsertion mi = new MediatorInsertion(concretePartyMember, concretePartyMember, partyImpl, PushPullValue.PUSH); - design.addMediatorInsertion(mi); - -// MediatorInsertion mi2 = new MediatorInsertion(main, concretePartyMember, partyImpl, PushPullValue.PUSH); -// design.addMediatorInsertion(mi2); - - return design; - } -} -/* 極端に解釈すると - public abstract class PartyMemberBase{ - protected PartyImpl party; - } - - public class PartyImpl { - private final List members; - } - - Implements - PartyImpl <--- Party - PartyMemberBase <--- PartyMember - ConcretePartyMember <--- PartyMemberBase - - Main -> PartyImpl - Main -> ConcretePartyMember -> PartyImpl(Mediator?) -> List - 2つ目をどうする? - */ \ No newline at end of file diff --git a/src/main/java/generators/ASTGenerator.java b/src/main/java/generators/ASTGenerator.java index 294fc50..109e402 100644 --- a/src/main/java/generators/ASTGenerator.java +++ b/src/main/java/generators/ASTGenerator.java @@ -1,7 +1,9 @@ package generators; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import ast.*; import designPatternExtensions.DependencyInversion; @@ -531,6 +533,25 @@ } for (MethodDeclaration method: methodsToMove) { srcClass.removeMethod(method); + List danglingFields = new ArrayList<>(); + if (method.getBody() != null) { + for (Statement statement: method.getBody().getStatements2()) { + collectDanglingValueFields(statement, srcClass, mediatorClass, oldFieldName, danglingFields); + } + } + Set danglingFieldNames = new HashSet<>(); + for (FieldDeclaration extra: danglingFields) { + danglingFieldNames.add(extra.getName()); + if (!hasParameterNamed(method, extra.getName())) { + method.addParameter(new VariableDeclaration(extra.getType(), extra.getName())); + } + } + if (!danglingFieldNames.isEmpty() && method.getBody() != null) { + for (Statement statement: method.getBody().getStatements2()) { + rewriteDanglingFieldsAsParams(statement, danglingFieldNames); + } + } + MethodDeclaration existingOnMediator = null; for (MethodDeclaration existing: mediatorClass.getMethods()) { if (existing.getName().equals(method.getName())) { existingOnMediator = existing; break; } @@ -544,8 +565,12 @@ List args = new ArrayList<>(); if (method.getParameters() != null) { for (VariableDeclaration param: method.getParameters()) { - stub.addParameter(param); - args.add(new Variable(param.getName())); + if (danglingFieldNames.contains(param.getName())) { + args.add(new FieldAccess(param.getName())); + } else { + stub.addParameter(param); + args.add(new Variable(param.getName())); + } } } FieldAccess mediatorAccess = new FieldAccess(mediatorFieldName); @@ -773,6 +798,104 @@ return false; } + private static void collectDanglingValueFields(Statement statement, TypeDeclaration srcClass, + TypeDeclaration mediatorClass, String movedFieldName, List out) { + if (statement instanceof ExpressionStatement) { + walkExpressionForDanglingFields(((ExpressionStatement) statement).getExpression(), false, srcClass, mediatorClass, movedFieldName, out); + } else if (statement instanceof ReturnStatement) { + walkExpressionForDanglingFields(((ReturnStatement) statement).getExpression(), true, srcClass, mediatorClass, movedFieldName, out); + } else if (statement instanceof EnhancedForStatement) { + EnhancedForStatement forStatement = (EnhancedForStatement) statement; + walkExpressionForDanglingFields(forStatement.getExpression(), false, srcClass, mediatorClass, movedFieldName, out); + Statement body = forStatement.getBody(); + if (body instanceof Block) { + for (Statement inner: ((Block) body).getStatements2()) { + collectDanglingValueFields(inner, srcClass, mediatorClass, movedFieldName, out); + } + } else if (body != null) { + collectDanglingValueFields(body, srcClass, mediatorClass, movedFieldName, out); + } + } + } + + private static void walkExpressionForDanglingFields(Expression expr, boolean asValue, TypeDeclaration srcClass, + TypeDeclaration mediatorClass, String movedFieldName, List out) { + if (expr == null) return; + if (expr instanceof FieldAccess) { + if (!asValue) return; + String name = ((FieldAccess) expr).getFieldName(); + if (name.equals(movedFieldName)) return; + for (FieldDeclaration f: mediatorClass.getFields()) if (f.getName().equals(name)) return; + for (FieldDeclaration f: out) if (f.getName().equals(name)) return; + for (FieldDeclaration f: srcClass.getFields()) { + if (f.getName().equals(name)) { out.add(f); return; } + } + } else if (expr instanceof MethodInvocation) { + MethodInvocation mi = (MethodInvocation) expr; + walkExpressionForDanglingFields(mi.getReceiver(), false, srcClass, mediatorClass, movedFieldName, out); + if (mi.getArguments() != null) { + for (Expression arg: mi.getArguments()) { + walkExpressionForDanglingFields(arg, true, srcClass, mediatorClass, movedFieldName, out); + } + } + } else if (expr instanceof Assignment) { + Assignment assignment = (Assignment) expr; + walkExpressionForDanglingFields(assignment.getLeft(), false, srcClass, mediatorClass, movedFieldName, out); + walkExpressionForDanglingFields(assignment.getRight(), true, srcClass, mediatorClass, movedFieldName, out); + } + } + + private static void rewriteDanglingFieldsAsParams(Statement statement, Set fieldNames) { + if (statement instanceof ExpressionStatement) { + rewriteExpressionValues(((ExpressionStatement) statement).getExpression(), false, fieldNames); + } else if (statement instanceof ReturnStatement) { + ReturnStatement rs = (ReturnStatement) statement; + Expression expr = rs.getExpression(); + if (expr instanceof FieldAccess && fieldNames.contains(((FieldAccess) expr).getFieldName())) { + rs.setExpression(new Variable(((FieldAccess) expr).getFieldName())); + } else { + rewriteExpressionValues(expr, true, fieldNames); + } + } else if (statement instanceof EnhancedForStatement) { + EnhancedForStatement forStatement = (EnhancedForStatement) statement; + Statement body = forStatement.getBody(); + if (body instanceof Block) { + for (Statement inner: ((Block) body).getStatements2()) { + rewriteDanglingFieldsAsParams(inner, fieldNames); + } + } else if (body != null) { + rewriteDanglingFieldsAsParams(body, fieldNames); + } + } + } + + private static void rewriteExpressionValues(Expression expr, boolean asValue, Set fieldNames) { + if (expr instanceof MethodInvocation) { + MethodInvocation mi = (MethodInvocation) expr; + rewriteExpressionValues(mi.getReceiver(), false, fieldNames); + if (mi.getArguments() != null) { + List args = mi.getArguments(); + for (int i = 0; i < args.size(); i++) { + Expression arg = args.get(i); + if (arg instanceof FieldAccess && fieldNames.contains(((FieldAccess) arg).getFieldName())) { + args.set(i, new Variable(((FieldAccess) arg).getFieldName())); + } else { + rewriteExpressionValues(arg, true, fieldNames); + } + } + } + } else if (expr instanceof Assignment) { + Assignment assignment = (Assignment) expr; + rewriteExpressionValues(assignment.getLeft(), false, fieldNames); + Expression right = assignment.getRight(); + if (right instanceof FieldAccess && fieldNames.contains(((FieldAccess) right).getFieldName())) { + assignment.setRight(new Variable(((FieldAccess) right).getFieldName())); + } else { + rewriteExpressionValues(right, true, fieldNames); + } + } + } + private static boolean expressionReferencesField(Expression expr, String fieldName) { if (expr == null) return false; if (expr instanceof FieldAccess) { @@ -795,7 +918,6 @@ return false; } - public static Codebase weaveDependencyInversion(Codebase codebase, DependencyInversion dependencyInversion) { ObjectNode dstNode = dependencyInversion.getDst(); InterfaceNode interfaceNode = dependencyInversion.getInterfaceNode(); @@ -803,7 +925,7 @@ String interfaceName = toComponentName(interfaceNode.getName()); //print -> public interface interfaceName { ... } - InterfaceDeclaration interfaceDeclaration = createInterface(codebase, interfaceName); + InterfaceDeclaration interfaceDeclaration = DependencyInversionWeaver.createInterface(codebase, interfaceName); TypeDeclaration dstClass = createClass(codebase, dstName); for (MethodDeclaration method: dstClass.getMethods()) { @@ -825,75 +947,24 @@ models.algebra.Type dstType = codebase.getComponentType(dstName); // // Change dst class method return types - replaceDepedencyInClass(codebase, dstClass, dstType, interfaceType, interfaceName); + DependencyInversionWeaver.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); + DependencyInversionWeaver.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); + DependencyInversionWeaver.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); - } - } - } - if (!method.isConstructor() && method.getParameters() != null) { - for (VariableDeclaration param: method.getParameters()) { - if (param.getType() != null && param.getType().equals(dstType)) { - 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); diff --git a/src/main/java/generators/DependencyInversionWeaver.java b/src/main/java/generators/DependencyInversionWeaver.java new file mode 100644 index 0000000..1b36c7a --- /dev/null +++ b/src/main/java/generators/DependencyInversionWeaver.java @@ -0,0 +1,59 @@ +package generators; + +import ast.*; +import models.dataConstraintModel.MapType; + +public class DependencyInversionWeaver { + + 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); + } + } + } + if (!method.isConstructor() && method.getParameters() != null) { + for (VariableDeclaration param: method.getParameters()) { + if (param.getType() != null && param.getType().equals(dstType)) { + 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); + } + } + } + } + + public 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; + } + +} \ No newline at end of file diff --git a/src/test/java/parser/PatternTest.java b/src/test/java/parser/PatternTest.java index 1694cc3..27b46e4 100644 --- a/src/test/java/parser/PatternTest.java +++ b/src/test/java/parser/PatternTest.java @@ -19,7 +19,7 @@ @Test void flyweight() throws Exception { run(new Flyweight()); } @Test void interpreter() throws Exception { run(new Interpreter()); } @Test void iterator() throws Exception { run(new Iterator()); } - @Test void mediator() throws Exception { run(new Mediator2()); } + @Test void mediator() throws Exception { run(new Mediator()); } @Test void memento() throws Exception { run(new Memento()); } @Test void observer() throws Exception { run(new Observer()); } @Test void prototype() throws Exception { run(new Prototype()); }