mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-10 20:30:11 +08:00
generate correct setter and getter
This commit is contained in:
parent
bc49fda272
commit
ce296710d9
@ -2,20 +2,20 @@ package com.alibaba.testable.generator;
|
|||||||
|
|
||||||
import com.alibaba.testable.generator.model.Statement;
|
import com.alibaba.testable.generator.model.Statement;
|
||||||
import com.alibaba.testable.generator.statement.CallSuperMethodStatementGenerator;
|
import com.alibaba.testable.generator.statement.CallSuperMethodStatementGenerator;
|
||||||
import com.alibaba.testable.generator.statement.FieldGetterStatementGenerator;
|
|
||||||
import com.alibaba.testable.generator.statement.FieldSetterStatementGenerator;
|
|
||||||
import com.alibaba.testable.generator.statement.FieldStatementGenerator;
|
|
||||||
import com.alibaba.testable.translator.TestableClassDevRoleTranslator;
|
import com.alibaba.testable.translator.TestableClassDevRoleTranslator;
|
||||||
import com.alibaba.testable.util.ConstPool;
|
import com.alibaba.testable.util.ConstPool;
|
||||||
import com.alibaba.testable.util.StringUtil;
|
import com.alibaba.testable.util.StringUtil;
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import com.sun.tools.javac.api.JavacTrees;
|
import com.sun.tools.javac.api.JavacTrees;
|
||||||
|
import com.sun.tools.javac.code.Symbol;
|
||||||
import com.sun.tools.javac.code.Type;
|
import com.sun.tools.javac.code.Type;
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
import com.sun.tools.javac.tree.TreeMaker;
|
import com.sun.tools.javac.tree.TreeMaker;
|
||||||
|
import com.sun.tools.javac.util.Names;
|
||||||
|
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -30,15 +30,17 @@ public class TestableClassDevRoleGenerator {
|
|||||||
|
|
||||||
private final JavacTrees trees;
|
private final JavacTrees trees;
|
||||||
private final TreeMaker treeMaker;
|
private final TreeMaker treeMaker;
|
||||||
|
private final Names names;
|
||||||
|
|
||||||
public TestableClassDevRoleGenerator(JavacTrees trees, TreeMaker treeMaker) {
|
public TestableClassDevRoleGenerator(JavacTrees trees, TreeMaker treeMaker, Names names) {
|
||||||
this.trees = trees;
|
this.trees = trees;
|
||||||
this.treeMaker = treeMaker;
|
this.treeMaker = treeMaker;
|
||||||
|
this.names = names;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String fetch(Element clazz, String packageName, String className) {
|
public String fetch(Symbol.ClassSymbol clazz, String packageName, String className) {
|
||||||
JCTree tree = trees.getTree(clazz);
|
JCTree tree = trees.getTree(clazz);
|
||||||
TestableClassDevRoleTranslator translator = new TestableClassDevRoleTranslator(treeMaker);
|
TestableClassDevRoleTranslator translator = new TestableClassDevRoleTranslator(treeMaker, names);
|
||||||
tree.accept(translator);
|
tree.accept(translator);
|
||||||
|
|
||||||
List<MethodSpec> methodSpecs = new ArrayList<>();
|
List<MethodSpec> methodSpecs = new ArrayList<>();
|
||||||
@ -81,27 +83,36 @@ public class TestableClassDevRoleGenerator {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodSpec buildFieldGetter(Element classElement, JCTree.JCVariableDecl field) {
|
private MethodSpec buildFieldGetter(Symbol.ClassSymbol classElement, JCTree.JCVariableDecl field) {
|
||||||
return buildFieldAccessor(classElement, field, "TestableGet",
|
String fieldName = field.name.toString();
|
||||||
TypeName.get(field.vartype.type), new FieldGetterStatementGenerator());
|
return MethodSpec.methodBuilder(fieldName + ConstPool.TESTABLE_GET_METHOD_PREFIX)
|
||||||
}
|
|
||||||
|
|
||||||
private MethodSpec buildFieldSetter(Element classElement, JCTree.JCVariableDecl field) {
|
|
||||||
return buildFieldAccessor(classElement, field, "TestableSet",
|
|
||||||
TypeName.VOID, new FieldSetterStatementGenerator());
|
|
||||||
}
|
|
||||||
|
|
||||||
private MethodSpec buildFieldAccessor(Element classElement, JCTree.JCVariableDecl field, String prefix,
|
|
||||||
TypeName returnType, FieldStatementGenerator generator) {
|
|
||||||
MethodSpec.Builder builder = MethodSpec.methodBuilder(field.name.toString() + prefix)
|
|
||||||
.addModifiers(Modifier.PUBLIC)
|
.addModifiers(Modifier.PUBLIC)
|
||||||
.returns(returnType);
|
.returns(TypeName.get(field.vartype.type))
|
||||||
String className = classElement.getSimpleName().toString();
|
.beginControlFlow("try")
|
||||||
Statement[] statements = generator.fetch(className, field);
|
.addStatement("$T field = $T.class.getDeclaredField(\"$N\")", Field.class, classElement.type, fieldName)
|
||||||
for (Statement s : statements) {
|
.addStatement("field.setAccessible(true)")
|
||||||
builder.addStatement(s.getLine(), s.getParams());
|
.addStatement("return ($T)field.get(this)", field.vartype.type)
|
||||||
|
.nextControlFlow("catch ($T e)", Exception.class)
|
||||||
|
.addStatement("e.printStackTrace()")
|
||||||
|
.addStatement("return null")
|
||||||
|
.endControlFlow()
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
return builder.build();
|
|
||||||
|
private MethodSpec buildFieldSetter(Symbol.ClassSymbol classElement, JCTree.JCVariableDecl field) {
|
||||||
|
String fieldName = field.name.toString();
|
||||||
|
return MethodSpec.methodBuilder(fieldName + ConstPool.TESTABLE_SET_METHOD_PREFIX)
|
||||||
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
.addParameter(getParameterSpec(field))
|
||||||
|
.returns(TypeName.VOID)
|
||||||
|
.beginControlFlow("try")
|
||||||
|
.addStatement("$T field = $T.class.getDeclaredField(\"$N\")", Field.class, classElement.type, fieldName)
|
||||||
|
.addStatement("field.setAccessible(true)")
|
||||||
|
.addStatement("field.set(this, $N)", fieldName)
|
||||||
|
.nextControlFlow("catch ($T e)", Exception.class)
|
||||||
|
.addStatement("e.printStackTrace()")
|
||||||
|
.endControlFlow()
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodSpec buildMemberMethod(Element classElement, JCTree.JCMethodDecl method) {
|
private MethodSpec buildMemberMethod(Element classElement, JCTree.JCMethodDecl method) {
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
package com.alibaba.testable.generator.statement;
|
|
||||||
|
|
||||||
import com.alibaba.testable.generator.model.Statement;
|
|
||||||
import com.alibaba.testable.util.ConstPool;
|
|
||||||
import com.alibaba.testable.util.StringUtil;
|
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
|
||||||
|
|
||||||
import javax.lang.model.element.Modifier;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate field getter method statement
|
|
||||||
*
|
|
||||||
* @author flin
|
|
||||||
*/
|
|
||||||
public class FieldGetterStatementGenerator implements FieldStatementGenerator {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement[] fetch(String className, JCTree.JCVariableDecl field) {
|
|
||||||
return new Statement[] {new Statement("return null", new Object[] {})};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package com.alibaba.testable.generator.statement;
|
|
||||||
|
|
||||||
import com.alibaba.testable.generator.model.Statement;
|
|
||||||
import com.alibaba.testable.util.ConstPool;
|
|
||||||
import com.alibaba.testable.util.StringUtil;
|
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
|
||||||
|
|
||||||
import javax.lang.model.element.Modifier;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate field setter method statement
|
|
||||||
*
|
|
||||||
* @author flin
|
|
||||||
*/
|
|
||||||
public class FieldSetterStatementGenerator implements FieldStatementGenerator {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement[] fetch(String className, JCTree.JCVariableDecl field) {
|
|
||||||
return new Statement[]{};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package com.alibaba.testable.generator.statement;
|
|
||||||
|
|
||||||
import com.alibaba.testable.generator.model.Statement;
|
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author flin
|
|
||||||
*/
|
|
||||||
public interface FieldStatementGenerator {
|
|
||||||
/**
|
|
||||||
* Generate field access code statement
|
|
||||||
*/
|
|
||||||
Statement[] fetch(String className, JCTree.JCVariableDecl field);
|
|
||||||
}
|
|
@ -86,7 +86,7 @@ public class TestableProcessor extends BaseProcessor {
|
|||||||
String fullQualityTypeName = packageName + "." + testableTypeName;
|
String fullQualityTypeName = packageName + "." + testableTypeName;
|
||||||
try {
|
try {
|
||||||
writeSourceFile(fullQualityTypeName,
|
writeSourceFile(fullQualityTypeName,
|
||||||
new TestableClassDevRoleGenerator(trees, treeMaker).fetch(clazz, packageName, testableTypeName));
|
new TestableClassDevRoleGenerator(trees, treeMaker, names).fetch(clazz, packageName, testableTypeName));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ public class TestableProcessor extends BaseProcessor {
|
|||||||
|
|
||||||
private void processTestRoleClassElement(Symbol.ClassSymbol clazz) {
|
private void processTestRoleClassElement(Symbol.ClassSymbol clazz) {
|
||||||
JCTree tree = trees.getTree(clazz);
|
JCTree tree = trees.getTree(clazz);
|
||||||
tree.accept(new TestableClassTestRoleTranslator(getPkgName(clazz), getOriginClassName(clazz), treeMaker));
|
tree.accept(new TestableClassTestRoleTranslator(getPkgName(clazz), getOriginClassName(clazz), treeMaker, names));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPkgName(Symbol.ClassSymbol clazz) {
|
private String getPkgName(Symbol.ClassSymbol clazz) {
|
||||||
|
@ -10,6 +10,7 @@ import com.sun.tools.javac.tree.TreeTranslator;
|
|||||||
import com.sun.tools.javac.util.List;
|
import com.sun.tools.javac.util.List;
|
||||||
import com.sun.tools.javac.util.ListBuffer;
|
import com.sun.tools.javac.util.ListBuffer;
|
||||||
import com.sun.tools.javac.util.Name;
|
import com.sun.tools.javac.util.Name;
|
||||||
|
import com.sun.tools.javac.util.Names;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ import java.lang.reflect.Modifier;
|
|||||||
public class TestableClassDevRoleTranslator extends TreeTranslator {
|
public class TestableClassDevRoleTranslator extends TreeTranslator {
|
||||||
|
|
||||||
private final TreeMaker treeMaker;
|
private final TreeMaker treeMaker;
|
||||||
|
private final Names names;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Methods to inject
|
* Methods to inject
|
||||||
@ -40,8 +42,9 @@ public class TestableClassDevRoleTranslator extends TreeTranslator {
|
|||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestableClassDevRoleTranslator(TreeMaker treeMaker) {
|
public TestableClassDevRoleTranslator(TreeMaker treeMaker, Names names) {
|
||||||
this.treeMaker = treeMaker;
|
this.treeMaker = treeMaker;
|
||||||
|
this.names = names;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -135,9 +138,8 @@ public class TestableClassDevRoleTranslator extends TreeTranslator {
|
|||||||
if (isNewOperation(expr)) {
|
if (isNewOperation(expr)) {
|
||||||
JCTree.JCNewClass newClassExpr = (JCTree.JCNewClass)expr;
|
JCTree.JCNewClass newClassExpr = (JCTree.JCNewClass)expr;
|
||||||
Name className = ((JCTree.JCIdent)newClassExpr.clazz).name;
|
Name className = ((JCTree.JCIdent)newClassExpr.clazz).name;
|
||||||
Name.Table nameTable = className.table;
|
|
||||||
try {
|
try {
|
||||||
return getStaticNewCall(newClassExpr, className, nameTable);
|
return getStaticNewCall(newClassExpr, className);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -149,14 +151,13 @@ public class TestableClassDevRoleTranslator extends TreeTranslator {
|
|||||||
return expr != null && expr.getClass().equals(JCTree.JCNewClass.class);
|
return expr != null && expr.getClass().equals(JCTree.JCNewClass.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestableMethodInvocation getStaticNewCall(JCTree.JCNewClass newClassExpr, Name className,
|
private TestableMethodInvocation getStaticNewCall(JCTree.JCNewClass newClassExpr, Name className) {
|
||||||
Name.Table nameTable) {
|
TestableFieldAccess snClass = new TestableFieldAccess(treeMaker.Ident(names.fromString(ConstPool.SN_PKG)),
|
||||||
TestableFieldAccess snClass = new TestableFieldAccess(treeMaker.Ident(nameTable.fromString(ConstPool.SN_PKG)),
|
names.fromString(ConstPool.SN_CLS), null);
|
||||||
nameTable.fromString(ConstPool.SN_CLS), null);
|
|
||||||
TestableFieldAccess snMethod = new TestableFieldAccess(snClass,
|
TestableFieldAccess snMethod = new TestableFieldAccess(snClass,
|
||||||
nameTable.fromString(ConstPool.SN_METHOD), null);
|
names.fromString(ConstPool.SN_METHOD), null);
|
||||||
JCTree.JCExpression classType = new TestableFieldAccess(treeMaker.Ident(className),
|
JCTree.JCExpression classType = new TestableFieldAccess(treeMaker.Ident(className),
|
||||||
nameTable.fromString("class"), null);
|
names.fromString("class"), null);
|
||||||
ListBuffer<JCTree.JCExpression> args = ListBuffer.of(classType);
|
ListBuffer<JCTree.JCExpression> args = ListBuffer.of(classType);
|
||||||
args.addAll(newClassExpr.args);
|
args.addAll(newClassExpr.args);
|
||||||
return new TestableMethodInvocation(null, snMethod, args.toList());
|
return new TestableMethodInvocation(null, snMethod, args.toList());
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package com.alibaba.testable.translator;
|
package com.alibaba.testable.translator;
|
||||||
|
|
||||||
|
import com.alibaba.testable.translator.tree.TestableFieldAccess;
|
||||||
|
import com.alibaba.testable.translator.tree.TestableMethodInvocation;
|
||||||
import com.alibaba.testable.util.ConstPool;
|
import com.alibaba.testable.util.ConstPool;
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
import com.sun.tools.javac.tree.TreeMaker;
|
import com.sun.tools.javac.tree.TreeMaker;
|
||||||
import com.sun.tools.javac.tree.TreeTranslator;
|
import com.sun.tools.javac.tree.TreeTranslator;
|
||||||
import com.sun.tools.javac.util.Name;
|
import com.sun.tools.javac.util.Name;
|
||||||
|
import com.sun.tools.javac.util.Names;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -18,13 +21,15 @@ import java.util.List;
|
|||||||
public class TestableClassTestRoleTranslator extends TreeTranslator {
|
public class TestableClassTestRoleTranslator extends TreeTranslator {
|
||||||
|
|
||||||
private TreeMaker treeMaker;
|
private TreeMaker treeMaker;
|
||||||
|
private Names names;
|
||||||
private String sourceClassName;
|
private String sourceClassName;
|
||||||
private List<Name> sourceClassIns = new ArrayList<>();
|
private List<Name> sourceClassIns = new ArrayList<>();
|
||||||
private List<String> stubbornFields = new ArrayList<>();
|
private List<String> stubbornFields = new ArrayList<>();
|
||||||
|
|
||||||
public TestableClassTestRoleTranslator(String pkgName, String className, TreeMaker treeMaker) {
|
public TestableClassTestRoleTranslator(String pkgName, String className, TreeMaker treeMaker, Names names) {
|
||||||
this.sourceClassName = className;
|
this.sourceClassName = className;
|
||||||
this.treeMaker = treeMaker;
|
this.treeMaker = treeMaker;
|
||||||
|
this.names = names;
|
||||||
try {
|
try {
|
||||||
stubbornFields = Arrays.asList(
|
stubbornFields = Arrays.asList(
|
||||||
(String[])Class.forName(pkgName + "." + className + ConstPool.TESTABLE)
|
(String[])Class.forName(pkgName + "." + className + ConstPool.TESTABLE)
|
||||||
@ -72,11 +77,20 @@ public class TestableClassTestRoleTranslator extends TreeTranslator {
|
|||||||
public void visitExec(JCTree.JCExpressionStatement jcExpressionStatement) {
|
public void visitExec(JCTree.JCExpressionStatement jcExpressionStatement) {
|
||||||
if (jcExpressionStatement.expr.getClass().equals(JCTree.JCAssign.class) &&
|
if (jcExpressionStatement.expr.getClass().equals(JCTree.JCAssign.class) &&
|
||||||
isAssignStubbornField((JCTree.JCAssign)jcExpressionStatement.expr)) {
|
isAssignStubbornField((JCTree.JCAssign)jcExpressionStatement.expr)) {
|
||||||
//jcExpressionStatement.expr =
|
JCTree.JCAssign assign = (JCTree.JCAssign)jcExpressionStatement.expr;
|
||||||
|
TestableFieldAccess stubbornSetter = new TestableFieldAccess(((JCTree.JCFieldAccess)assign.lhs).selected,
|
||||||
|
getStubbornSetterMethodName(assign), null);
|
||||||
|
jcExpressionStatement.expr = new TestableMethodInvocation(null, stubbornSetter,
|
||||||
|
com.sun.tools.javac.util.List.of(assign.rhs));
|
||||||
}
|
}
|
||||||
super.visitExec(jcExpressionStatement);
|
super.visitExec(jcExpressionStatement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Name getStubbornSetterMethodName(JCTree.JCAssign assign) {
|
||||||
|
String name = ((JCTree.JCFieldAccess)assign.lhs).name.toString() + ConstPool.TESTABLE_SET_METHOD_PREFIX;
|
||||||
|
return names.fromString(name);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isAssignStubbornField(JCTree.JCAssign expr) {
|
private boolean isAssignStubbornField(JCTree.JCAssign expr) {
|
||||||
return expr.lhs.getClass().equals(JCTree.JCFieldAccess.class) &&
|
return expr.lhs.getClass().equals(JCTree.JCFieldAccess.class) &&
|
||||||
sourceClassIns.contains(((JCTree.JCIdent)((JCTree.JCFieldAccess)(expr).lhs).selected).name) &&
|
sourceClassIns.contains(((JCTree.JCIdent)((JCTree.JCFieldAccess)(expr).lhs).selected).name) &&
|
||||||
@ -85,7 +99,7 @@ public class TestableClassTestRoleTranslator extends TreeTranslator {
|
|||||||
|
|
||||||
private JCTree.JCIdent getTestableClassIdent(JCTree.JCExpression clazz) {
|
private JCTree.JCIdent getTestableClassIdent(JCTree.JCExpression clazz) {
|
||||||
Name className = ((JCTree.JCIdent)clazz).name;
|
Name className = ((JCTree.JCIdent)clazz).name;
|
||||||
return treeMaker.Ident(className.table.fromString(className + ConstPool.TESTABLE));
|
return treeMaker.Ident(names.fromString(className + ConstPool.TESTABLE));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,5 +13,8 @@ public final class ConstPool {
|
|||||||
public static final String SN_METHOD = "w";
|
public static final String SN_METHOD = "w";
|
||||||
public static final String SN_PKG_CLS = "n.e";
|
public static final String SN_PKG_CLS = "n.e";
|
||||||
public static final String STUBBORN_FIELD_METHOD = "stubbornField";
|
public static final String STUBBORN_FIELD_METHOD = "stubbornField";
|
||||||
|
public static final String TESTABLE_GET_METHOD_PREFIX = "TestableGet";
|
||||||
|
public static final String TESTABLE_SET_METHOD_PREFIX = "TestableSet";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user