package org.ntlab.pushPullRefactoring; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.stream.Collectors; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; public class Pull2PushRewriter extends SourceRewriter { public Pull2PushRewriter(PushPullDescriptor descriptor) { super(descriptor); // TODO Auto-generated constructor stub } @Override public void rewrite(IProgressMonitor pm) { var srcUnit = descriptor.getSourceClass(); var dstUnit = descriptor.getDistinationClass(); var srcDec = new TypeDeclaration[1]; var messages = searchMethodDeclarations(srcUnit, "Message"); descriptor.getSourceClass().accept(new ASTVisitor() { @Override public boolean visit(TypeDeclaration node) { srcDec[0] = node; return false; } }); ASTRewrite srcUnitRewrite = ASTRewrite.create(srcDec[0].getAST()); String srcValue = annotationValueCase(descriptor.getSourceClassName()); String dstValue = annotationValueCase(descriptor.getDistinationClassName()); // @PushReferenceが付与されたフィールドを追加 String pushrefCode = "@PushReference(\"" + dstValue + "\")" + System.getProperty("line.separator") + "@Pullable(\"direct\")" + System.getProperty("line.separator") + "String " + dstValue + "= " + "\"/" + dstValue + "\";"; addStatement(srcUnitRewrite, srcUnit, srcDec[0], pushrefCode); String importPushRef = "import pushPullRefactor.PushReference;" + System.getProperty("line.separator") + "import pushPullRefactor.Pullable;" + System.getProperty("line.separator") + System.getProperty("line.separator") ; addStatement(srcUnitRewrite, srcUnit, srcDec[0],importPushRef,TypeDeclaration.MODIFIERS2_PROPERTY); // 転送元クラスのupdateメソッドの最後に,転送先クラスのupdateメソッドを //フィールド value を引数として渡して呼び出す行を追加する. Type srcType = searchFieldDeclaration(srcUnit, "State").getType(); for (var update : messages) { var invocations = getPutMethodInvocations(update); String code = ""; if (invocations.size() == 0) code += "Form "; code += "form = new Form();" + System.getProperty("line.separator"); if (srcType instanceof ParameterizedType) { var ptype = (ParameterizedType) srcType; if (ptype.getType().toString().equals("List")) { var typeArg = (Type) ptype.typeArguments().get(0); code += "for (" + typeArg + " i: this.value) {" + System.getProperty("line.separator"); code += "\t form.param(\"" + srcValue + "\", new ObjectMapper().writeValueAsString(i));" + System.getProperty("line.separator"); code += "}" + System.getProperty("line.separator"); } if (ptype.getType().toString().equals("Map.Entry")) { code += "form.param(\"" + srcValue + "\", new ObjectMapper().writeValueAsString(this.value));" + System.getProperty("line.separator"); } } else { code += "form.param(\"" + srcValue + "\", new ObjectMapper().writeValueAsString(this.value));" + System.getProperty("line.separator"); } if (invocations.size() == 0) code += "Entity<Form> "; code += "entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED);" + System.getProperty("line.separator"); if (invocations.size() == 0) code += "String "; code += "result = client.target(\"http://localhost:8080\").path(" + dstValue + ").request().put(entity, String.class);" + System.getProperty("line.separator"); addStatementLast(srcUnitRewrite, srcUnit, update.getBody(), code, Block.STATEMENTS_PROPERTY); } var dstDec = new TypeDeclaration[1]; descriptor.getDistinationClass().accept(new ASTVisitor() { @Override public boolean visit(TypeDeclaration node) { dstDec[0] = node; return false; } }); ASTRewrite dstUnitRewrite = ASTRewrite.create(dstDec[0].getAST()); //転送先クラスのPullReferenceが付与されたフィールドを削除する. deleteASTNode(dstUnitRewrite, dstUnit, searchFieldDeclaration(dstUnit, "PullReference", srcValue)); // 転送先クラスに状態を保持するフィールドvalue を追加する. Type dstType = searchMethodDeclaration(dstUnit, "Getter").getReturnType2(); String stateCode = "@State" + System.getProperty("line.separator") + dstType.toString() + " value;"; addStatement(dstUnitRewrite, dstUnit, dstDec[0], stateCode); // 転送先クラスにupdateメソッドを追加する. // 転送先クラスのgetメソッド内で行っていた,戻り値を計算する処理を // updateメソッドに移動し,更新後の値をvalueに保存するようにする. String updateCode = generatePutMethod(dstType, new Type[] { srcType }, new String[] { srcValue }); addStatement(dstUnitRewrite, dstUnit, dstDec[0], updateCode); // Shipping クラスの getValue() メソッドを value フィールドの値を返すように書き換える. Statement statement = (Statement) dstUnitRewrite.createStringPlaceholder("{ return this.value; }", ASTNode.VARIABLE_DECLARATION_STATEMENT); replaceASTNode(dstUnitRewrite, dstUnit, searchMethodDeclaration(dstUnit, "Getter").getBody(), statement); //変更を適用する try { applyRewrite(srcUnit, srcUnitRewrite, pm); applyRewrite(dstUnit, dstUnitRewrite, pm); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } /** * PUTメソッドを作成する. * @param 転送先リソースの型 * @param 転送元リソースの型 * @param 転送元のリソース名 * @return */ public String generatePutMethod(Type updatedValueType, Type[] paramTypes, String[] srcNames) { String paramType = "String"; if (paramTypes[0] instanceof ParameterizedType) { //転送元の型がリストだった場合, PUTメソッドが受けとるパラメーターの型をList<String>にする var ptype = (ParameterizedType) paramTypes[0]; if (ptype.getType().toString().equals("List")) { paramType = "List<String>"; } } var args = "@FormParam(\"" + srcNames[0] + "\") "+paramType+" " + srcNames[0] + "_json"; String srcs = Arrays.stream(srcNames).map(x -> "\"" + x + "\"").collect(Collectors.joining(", ")); String result = "@PUT" + System.getProperty("line.separator") + "@Message({" + srcs + "})" + System.getProperty("line.separator") + "public void update" + "(" + args + ") throws JsonProcessingException {" + System.getProperty("line.separator"); // 型宣言 for (int i = 0; i < paramTypes.length; i++) { String initializer = ""; if (paramTypes[i] instanceof ParameterizedType) { var ptype = (ParameterizedType) paramTypes[i]; if (ptype.getType().toString().equals("List")) { initializer = " = new ArrayList<>()"; } } result += paramTypes[i].toString() + " " + srcNames[i] + initializer + ";" + System.getProperty("line.separator"); } for (int i = 0; i < paramTypes.length; i++) { result += "{" + System.getProperty("line.separator") + "\t"; if (paramTypes[i] instanceof ParameterizedType) { var ptype = (ParameterizedType) paramTypes[i]; if (ptype.getType().toString().equals("List")) { var argType = (Type)ptype.typeArguments().get(0); String initializer = ""; if(argType instanceof ParameterizedType) { if (((ParameterizedType)argType).getType().toString().equals("Map.Entry")) { initializer = " new ObjectMapper().readValue(str, HashMap.class);"; } //((ParameterizedType)argType).getType(); }else { if(argType.toString().equals("Integer")) { initializer = " Integer.parseInt(str);"; } } result += "for(String str: "+srcNames[i]+"_json ){ " + System.getProperty("line.separator"); result += convertType(argType) +" i =" + initializer + System.getProperty("line.separator"); result += srcNames[i].toString()+".add("; // String value, String tmpValue, Type type result += geterateParameterizedTypeStatements("i", "i", argType); result += ");" + System.getProperty("line.separator"); result += "}" + System.getProperty("line.separator"); } if (ptype.getType().toString().equals("Map.Entry")) { result += convertType(ptype) + " i = new ObjectMapper().readValue(" + srcNames[i] + "_json, HashMap.class);" + System.getProperty("line.separator") + "\t"; var mapValue = "i"; var typeArg = ptype.typeArguments().get(1); if(typeArg instanceof ParameterizedType) { if(( (ParameterizedType) ptype.typeArguments().get(1)).getType().toString().equals("Map.Entry")){ mapValue += ".entrySet().iterator().next().getValue()"; } } result += srcNames[i] + " = new AbstractMap.SimpleEntry<>(i.entrySet().iterator().next().getKey(), " + geterateParameterizedTypeStatements("i", mapValue, (Type) ptype.typeArguments().get(1)) + ");" + System.getProperty("line.separator"); } } result += "}" + System.getProperty("line.separator"); } { var returnStatement = new ReturnStatement[1]; searchMethodDeclaration(descriptor.getDistinationClass(), "Getter").accept(new ASTVisitor() { @Override public boolean visit(ReturnStatement node) { returnStatement[0] = node; return super.visit(node); } }); ; result += "this.value = " + returnStatement[0].getExpression() + ";" + System.getProperty("line.separator"); } result += "}" + System.getProperty("line.separator"); return result; } }