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;
}
}