package org.ntlab.pushPullRefactoring; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map.Entry; 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.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; 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.rewrite.ASTRewrite; public class Push2PullRewrite extends SourceRewriter{ public Push2PullRewrite(PushPullDescriptor descriptor) { super(descriptor); } // TODO Auto-generated constructor stub @Override public void rewrite(IProgressMonitor pm) { // TODO Auto-generated method stub var srcUnit = descriptor.getSourceClass(); var dstUnit = descriptor.getDistinationClass(); var dstDec = new TypeDeclaration[1]; var messages = searchMethodDeclarations(srcUnit, "Message"); var md = searchMethodDeclaration(dstUnit, "Message", annotationValueCase(descriptor.getSourceClassName())); descriptor.getDistinationClass().accept(new ASTVisitor() { @Override public boolean visit(TypeDeclaration node) { dstDec[0] = node; return false; } }); ASTRewrite dstUnitRewrite = ASTRewrite.create(dstDec[0].getAST()); //ASTRewrite dstUnitRewrite = ASTRewrite.create(dstUnit.getAST()); String srcValue = annotationValueCase(descriptor.getSourceClassName()); String dstValue = annotationValueCase(descriptor.getDistinationClassName()); String pullrefCode = "@PullReference(\"" + srcValue + "\")" + System.getProperty("line.separator") + "@Pushable" + System.getProperty("line.separator") + "String " + srcValue + "= " + "\"/" + srcValue + "\";"; // @PullReferenceが付与されたフィールドを追加 addStatement(dstUnitRewrite, dstUnit, dstDec[0], pullrefCode); String importPullRef = "import pushPullRefactor.PullReference;" + System.getProperty("line.separator") + "import pushPullRefactor.Pushable;" + System.getProperty("line.separator") + System.getProperty("line.separator") ; addStatement(dstUnitRewrite, dstUnit, dstDec[0],importPullRef,TypeDeclaration.MODIFIERS2_PROPERTY); if(!hasClient(dstUnit)) { addStatement(dstUnitRewrite, dstUnit, dstDec[0],"import javax.ws.rs.client.*;" + System.getProperty("line.separator"),TypeDeclaration.MODIFIERS2_PROPERTY); addStatement(dstUnitRewrite, dstUnit, dstDec[0], "private Client client = ClientBuilder.newClient();"+ System.getProperty("line.separator")); } // @Stateが付与されたフィールドを削除 deleteASTNode(dstUnitRewrite, dstUnit, searchFieldDeclaration(descriptor.getDistinationClass(), "State")); // @Messageが付与されたメソッドを削除 // deleteASTNode(dstUnitRewrite, dstUnit, // searchMethodDeclaration(dstUnit, "Message", annotationValueCase(descriptor.getSourceClassName()))); deleteASTNode(dstUnitRewrite, dstUnit, searchMethodDeclaration(dstUnit, "Message", annotationValueCase(descriptor.getSourceClassName()))); // @Getterが付与されたメソッドの返り値をその場で計算するように変更 var srcState = searchFieldDeclaration(srcUnit, "State"); Type srcType = srcState.getType(); var getterStatements = generateGetStatement(srcValue, srcValue, srcType); var transition = new Expression[1]; md.accept(new ASTVisitor() { @Override public boolean visit(Assignment node) { var left = node.getLeftHandSide(); if(left.toString().equals("this.value")); transition[0] = node.getRightHandSide(); return true; } }); var getter = searchMethodDeclaration(dstUnit, "Getter"); var returnStatement = new ReturnStatement[1]; getter.accept(new ASTVisitor() { @Override public boolean visit(ReturnStatement node) { returnStatement[0] = node; return true; } }); addStatement(dstUnitRewrite, dstUnit, getter.getBody(), getterStatements, Block.STATEMENTS_PROPERTY); replaceASTNode(dstUnitRewrite, dstUnit, returnStatement[0].getExpression(), transition[0]); var srcDec = new TypeDeclaration[1]; descriptor.getSourceClass().accept(new ASTVisitor() { @Override public boolean visit(TypeDeclaration node) { srcDec[0] = node; return false; } }); ASTRewrite srcUnitRewrite = ASTRewrite.create(srcDec[0].getAST()); // 転送元の変更 deletePutMethodInvocation(srcUnitRewrite, srcUnit, dstValue); // @PushReferenceが付与されたフィールドを削除 deleteASTNode(srcUnitRewrite, srcUnit, searchFieldDeclaration(descriptor.getSourceClass(), "PushReference", dstValue)); System.out.println(dstUnitRewrite.toString()); try { applyRewrite(srcUnit, srcUnitRewrite, pm); applyRewrite(dstUnit, dstUnitRewrite, pm); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } protected void deletePutMethodInvocation(ASTRewrite astRewrite, CompilationUnit srcUnit, String resourceName) { var updateMethods = searchMethodDeclarations(srcUnit, "Message"); for (MethodDeclaration md : updateMethods) { LinkedHashMap<String, List<Statement>> invocations = getPutMethodInvocations(md); System.out.println(invocations); // メソッドの呼び出し群が2つ以上でかつ, // invocationsから対応リソースへのメソッド呼び出し群と, // invocationsの先頭にあるメソッド呼び出し群が一致するかを判定 Iterator<Entry<String, List<Statement>>> invocationsIterator = invocations.entrySet().iterator(); var g = invocations.get(resourceName); var i = (invocationsIterator.next()).getKey().equals(resourceName); if (invocations.size() > 1 && i) { for (Statement st : (List<Statement>) invocationsIterator.next().getValue()) { ASTNode[] n = new ASTNode[1]; st.accept(new ASTVisitor() { @Override public boolean visit(Assignment node) { if (node.getLeftHandSide().toString().equals("form")) { n[0] = node; addDecStatement(node, "Form "); } if (node.getLeftHandSide().toString().equals("entity")) { n[0] = node; addDecStatement(node, "Entity<Form> "); } if (node.getLeftHandSide().toString().equals("result")) { MethodInvocation right = (MethodInvocation) node.getRightHandSide(); n[0] = node; addDecStatement(node, "String "); } return super.visit(node); } public void addDecStatement(Assignment node, String typeName) { // addStatement(astRewrite, srcUnit, node, typeName, Assignment ); addStatementBefore(astRewrite, srcUnit, md.getBody(), node.getParent(), typeName, Block.STATEMENTS_PROPERTY); } }); } } for (Statement st : invocations.get(resourceName)) { deleteASTNode(astRewrite, srcUnit, st); } } } 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; } }