package org.ntlab.pushPullRefactoring; import java.util.ArrayList; 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.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.filebuffers.ITextFileBufferManager; import org.eclipse.core.filebuffers.LocationKind; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.ArrayInitializer; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.MarkerAnnotation; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.PackageDeclaration; import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; 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; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; import org.eclipse.jface.text.IDocument; import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.text.edits.UndoEdit; public abstract class SourceRewriter { protected PushPullDescriptor descriptor = null; public SourceRewriter(PushPullDescriptor descriptor){ this.descriptor = descriptor; } abstract public void rewrite(IProgressMonitor pm) ; protected void applyRewrite(CompilationUnit unit, ASTRewrite unitRewrite, IProgressMonitor pm) throws Exception { IPath path = unit.getJavaElement().getPath(); ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager(); bufferManager.connect(path, LocationKind.IFILE, pm); ITextFileBuffer textFileBuffer = bufferManager.getTextFileBuffer(path, LocationKind.IFILE); IDocument document = textFileBuffer.getDocument(); TextEdit edit = unitRewrite.rewriteAST(document, null); UndoEdit undo = edit.apply(document); textFileBuffer.commit(null /* ProgressMonitor */, true/* Overwrite */); } public MethodDeclaration[] searchMethodDeclarations(CompilationUnit cu, String annotationName, String... values) { List<MethodDeclaration> result = new ArrayList<MethodDeclaration>(); cu.accept(new ASTVisitor() { @Override public boolean visit(MethodDeclaration node) { if (hasMatchAnnotation(node, node.modifiers(), annotationName, values)) { result.add(node); } return super.visit(node); } }); return result.toArray(new MethodDeclaration[] {}); } protected String generateGetStatement(String assignedValue, String path, Type type) { // 返すボディ全体のソースコード String result = ""; // 転送元の値を代入する変数名 String tmp = assignedValue; // GETメソッドの引数で渡す型名 String simpleType = type.toString(); if (type instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType) type; if (ptype.getType().toString().equals("List")) { simpleType = "ArrayList"; tmp = assignedValue + "_json"; result += "List<" + convertType((Type) ptype.typeArguments().get(0)) + "> "; result += tmp + " = client.target(\"http://localhost:8080\").path(this."+path+").request().get(" + simpleType + ".class);" + System.getProperty("line.separator"); result += "List<" + ptype.typeArguments().get(0) + "> " + assignedValue + " = new ArrayList<>();" + System.getProperty("line.separator"); result += geterateParameterizedTypeStatements(assignedValue, tmp, type); } if (ptype.getType().toString().equals("Map.Entry")) { simpleType = "HashMap"; tmp = assignedValue + "_json"; result += convertType((Type) ptype) + " "; result += tmp + " = client.target(\"http://localhost:8080\").path(this."+path+").request().get(" + simpleType + ".class);" + System.getProperty("line.separator"); result += (Type) ptype + " " + assignedValue + " = new AbstractMap.SimpleEntry<>(" + tmp + ".entrySet().iterator().next().getKey(), " + geterateParameterizedTypeStatements(assignedValue, tmp + ".entrySet().iterator().next().getValue()", (Type) ptype.typeArguments().get(1)) + ");" + System.getProperty("line.separator"); // result += convertType((Type) ptype.typeArguments().get(0)); } } return result; } /** * * @param astRewrite * @param cu * @param astNode * @param replasement */ protected void replaceASTNode(ASTRewrite astRewrite, CompilationUnit cu, ASTNode astNode, ASTNode replasement) { TextEditGroup dstEditGroup = new TextEditGroup("new class edit group"); astRewrite.replace(astNode, replasement, dstEditGroup); } protected void addStatement(ASTRewrite astRewrite, CompilationUnit cu, ASTNode astNode, String statementCode) { ListRewrite dstUnitRewriteList = astRewrite.getListRewrite(astNode, TypeDeclaration.BODY_DECLARATIONS_PROPERTY); PackageDeclaration dstPackageDeclaration = cu.getPackage(); Statement statement = (Statement) astRewrite.createStringPlaceholder(statementCode, ASTNode.VARIABLE_DECLARATION_STATEMENT); astRewrite.set(cu, CompilationUnit.PACKAGE_PROPERTY, dstPackageDeclaration, null); TextEditGroup dstEditGroup = new TextEditGroup("new class edit group"); dstUnitRewriteList.insertFirst(statement, dstEditGroup); } /** * * @param astRewrite 書き換え対象のASTRewrite * @param cu 書き換え対象のCompilationUnit * @param astNode cu内に存在する, 書き換え対象のAST * @param statementCode astNodeに追加するステートメント文 * @param childListPropertyDescriptor astNodeのChildListPropertyDescriptor */ protected void addStatement(ASTRewrite astRewrite, CompilationUnit cu, ASTNode astNode, String statementCode, ChildListPropertyDescriptor childListPropertyDescriptor) { ListRewrite dstUnitRewriteList = astRewrite.getListRewrite(astNode, childListPropertyDescriptor); PackageDeclaration dstPackageDeclaration = cu.getPackage(); Statement statement = (Statement) astRewrite.createStringPlaceholder(statementCode, ASTNode.VARIABLE_DECLARATION_STATEMENT); astRewrite.set(cu, CompilationUnit.PACKAGE_PROPERTY, dstPackageDeclaration, null); TextEditGroup dstEditGroup = new TextEditGroup("new class edit group"); dstUnitRewriteList.insertFirst(statement, dstEditGroup); } protected void addStatementBefore(ASTRewrite astRewrite, CompilationUnit cu, ASTNode astNode, ASTNode nextNode, String statementCode, ChildListPropertyDescriptor childListPropertyDescriptor) { ListRewrite dstUnitRewriteList = astRewrite.getListRewrite(astNode, childListPropertyDescriptor); PackageDeclaration dstPackageDeclaration = cu.getPackage(); Statement statement = (Statement) astRewrite.createStringPlaceholder(statementCode, ASTNode.VARIABLE_DECLARATION_STATEMENT); astRewrite.set(cu, CompilationUnit.PACKAGE_PROPERTY, dstPackageDeclaration, null); TextEditGroup dstEditGroup = new TextEditGroup("new class edit group"); dstUnitRewriteList.insertBefore(statement, nextNode, dstEditGroup); } protected void addStatementLast(ASTRewrite astRewrite, CompilationUnit cu, ASTNode astNode, String statementCode, ChildListPropertyDescriptor childListPropertyDescriptor) { ListRewrite dstUnitRewriteList = astRewrite.getListRewrite(astNode, childListPropertyDescriptor); PackageDeclaration dstPackageDeclaration = cu.getPackage(); Statement statement = (Statement) astRewrite.createStringPlaceholder(statementCode, ASTNode.VARIABLE_DECLARATION_STATEMENT); astRewrite.set(cu, CompilationUnit.PACKAGE_PROPERTY, dstPackageDeclaration, null); TextEditGroup dstEditGroup = new TextEditGroup("new class edit group"); dstUnitRewriteList.insertLast(statement, dstEditGroup); } protected void deleteASTNode(ASTRewrite astRewrite, CompilationUnit cu, ASTNode astNode) { TextEditGroup dstEditGroup = new TextEditGroup("new class edit group"); astRewrite.remove(astNode, dstEditGroup); } protected boolean hasClient(ASTNode astNode) { boolean[] result = {false} ; astNode.accept(new ASTVisitor() { public boolean visit(FieldDeclaration node) { if(node.getType().toString().equals("Client")) { result[0] = true; } return false; } }); return result[0]; } /** * 文字列の先端と末尾にあるダブルクォーテーションを削除するメソッド * * @param str * @return */ protected String annotationValueCase(String str) { return Character.toLowerCase(str.charAt(0)) + (str.length() > 1 ? str.substring(1) : ""); } /** * Map.Entry<Object, Object>をMap<String, Object>に変換するメソッド * * @param type * @return */ protected String convertType(Type type) { if ((type instanceof ParameterizedType && ((ParameterizedType) type).getType().toString().equals("Map.Entry"))) { return "Map<String, " + convertType((Type) ((ParameterizedType) type).typeArguments().get(1)) + ">"; } return type.toString(); } /** * クラスファイルを対象に,アノテーション名と値から,それが付与されているフィールドを返すメソッド. アノテーションの値も検索条件に含める処理は未完成 * * @param cu 対象のクラスファイル * @param annotationName アノテーション名 * @param value アノテーションの値 * @return アノテーションが付与されたフィールドの宣言部 */ protected FieldDeclaration searchFieldDeclaration(CompilationUnit cu, String annotationName, String... values) { FieldDeclaration[] result = new FieldDeclaration[1]; cu.accept(new ASTVisitor() { @Override public boolean visit(FieldDeclaration node) { if (hasMatchAnnotation(node, node.modifiers(), annotationName, values)) { result[0] = node; } return super.visit(node); } }); return result[0]; } /** * クラスファイルを対象に,アノテーション名と値から,それが付与されているフィールドを返すメソッド. アノテーションの値も検索条件に含める処理は未完成 * * @param cu 対象のクラスファイル * @param annotationName アノテーション名 * @param value アノテーションの値 * @return アノテーションが付与されたフィールドの宣言部 */ protected MethodDeclaration searchMethodDeclaration(CompilationUnit cu, String annotationName, String... values) { MethodDeclaration[] result = new MethodDeclaration[1]; cu.accept(new ASTVisitor() { @Override public boolean visit(MethodDeclaration node) { if (hasMatchAnnotation(node, node.modifiers(), annotationName, values)) { result[0] = node; } return super.visit(node); } }); return result[0]; } protected String geterateParameterizedTypeStatements(String value, String tmpValue, Type type) { if (type instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType) type; if (ptype.getType().toString().equals("List")) { String str = "for (" + convertType((Type) ptype.typeArguments().get(0)) + " i: " + tmpValue + ") {" + System.getProperty("line.separator"); var argment = ((Type) ptype.typeArguments().get(0)); str += value + ".add(" + geterateParameterizedTypeStatements(value, "i", argment) + ");"; str += System.getProperty("line.separator") + "}"; return str; } if (ptype.getType().toString().equals("Map.Entry")) { String parsedKey = ""; if (ptype.typeArguments().get(0).toString().equals("Integer")) { parsedKey += ptype.typeArguments().get(0) + ".parseInt(" + tmpValue + ".entrySet().iterator().next().getKey())"; } else { parsedKey += tmpValue + ".entrySet().iterator().next().getKey()"; } String str = "new AbstractMap.SimpleEntry<>(" + parsedKey + ", " + tmpValue + ".entrySet().iterator().next().getValue())"; return str; } for (var arg : ((ParameterizedType) type).typeArguments()) { } } return ""; } /** * 引数として渡されたMethodDeclarationのメソッドボディ内にあるPUTメソッドの呼び出しを取得するメソッド * * @param md * @return */ protected LinkedHashMap<String, List<Statement>> getPutMethodInvocations(MethodDeclaration md) { LinkedHashMap<String, List<Statement>> invocations = new LinkedHashMap<>(); List<Statement> statements = new ArrayList<>(); boolean[] isRegistering = new boolean[1]; for (var st : md.getBody().statements()) { Statement statement = (Statement) st; statement.accept(new ASTVisitor() { @Override public boolean visit(VariableDeclarationStatement node) { if (node.getType().toString().equals("Form")) { startRegister(); } if (node.getType().toString().equals("String")) { if (((VariableDeclarationFragment) node.fragments().get(0)).getName().toString() .equals("result")) { MethodInvocation right = (MethodInvocation) ((VariableDeclarationFragment) node.fragments() .get(0)).getInitializer(); String resourceName = ((MethodInvocation) ((MethodInvocation) right.getExpression()) .getExpression()).arguments().get(0).toString(); stopRegister(resourceName); } } return super.visit(node); } @Override public boolean visit(Assignment node) { if (node.getLeftHandSide().toString().equals("form")) { startRegister(); } if (node.getLeftHandSide().toString().equals("result")) { MethodInvocation right = (MethodInvocation) node.getRightHandSide(); String resourceName = ((MethodInvocation) ((MethodInvocation) right.getExpression()) .getExpression()).arguments().get(0).toString(); stopRegister(resourceName); } return super.visit(node); } private void startRegister() { isRegistering[0] = true; statements.clear(); } private void stopRegister(String resourceName) { statements.add(statement); invocations.put(resourceName, List.copyOf(statements)); isRegistering[0] = false; } }); if (isRegistering[0]) { statements.add(statement); } } return invocations; } protected boolean hasMatchAnnotation(ASTNode node, List modifiers, String annotationName, String... values) { for (var annotation : modifiers) { // annotationがアノテーションじゃない時,次のループへ if (!(annotation instanceof Annotation)) continue; String annoName = ((Annotation) annotation).getTypeName().toString(); // nodeに付与されたアノテーション名とannotationNameが一致する場合 if (annotationName.equals(annoName)) { // annotationが値の無いアノテーションだった場合 if (annotation instanceof MarkerAnnotation) { return true; } // annotationが値が1つのアノテーションだった場合 if (annotation instanceof SingleMemberAnnotation) { //引数で値の指定が特にされていないときはアノテーション名が一致した段階でtrueを返す if (values.length == 0) return true; SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) annotation; Expression tmp = singleMemberAnnotation.getValue(); IType itype = (IType) tmp.resolveTypeBinding().getJavaElement(); String typename = itype.getTypeQualifiedName(); if (tmp instanceof ArrayInitializer) { String value = singleMemberAnnotation.getValue().toString().substring(1, singleMemberAnnotation.getValue().toString().length() - 1); String[] arr = value.split(","); for(int i=0; i<arr.length; i++)arr[i] = arr[i].substring(1, arr[i].length() - 1); if (arr[0].equals(values[0])) return true; } // if(tmp instanceof )) { // } // アノテーションから取得した値から先頭と末尾のダブルクォーテーションを削除した文字列 String value = singleMemberAnnotation.getValue().toString().substring(1, singleMemberAnnotation.getValue().toString().length() - 1); if (value.equals(values[0])) return true; } // annotationが値を複数もつアノテーションだった場合 if (annotation instanceof NormalAnnotation) { NormalAnnotation normalAnnotation = (NormalAnnotation) annotation; // 未完成(まだ値の判定ができていない) return true; // if(normalAnnotation.values())result[0] = node; } } } return false; } }