Newer
Older
org.ntlab.pushPullRefactoring / src / org / ntlab / pushPullRefactoring / SourceRewriter.java
Shinji on 25 Mar 2023 16 KB コメントの追加
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;
	}

}