Newer
Older
org.ntlab.pushPullRefactoring / src / org / ntlab / pushPullRefactoring / Push2PullRewriter.java
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 Push2PullRewriter extends SourceRewriter{

	public Push2PullRewriter(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;
	}

}