mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-02-13 21:31:08 +08:00
refactor and extract generators
This commit is contained in:
parent
e6f450dff4
commit
8a13e3d595
@ -14,25 +14,17 @@ import java.util.List;
|
|||||||
*
|
*
|
||||||
* @author flin
|
* @author flin
|
||||||
*/
|
*/
|
||||||
public class CallSuperMethod {
|
public class CallSuperMethodStatementGenerator {
|
||||||
|
|
||||||
private final String className;
|
public Statement[] fetch(String className, JCTree.JCMethodDecl method) {
|
||||||
private final JCTree.JCMethodDecl method;
|
|
||||||
|
|
||||||
public CallSuperMethod(String className, JCTree.JCMethodDecl method) {
|
|
||||||
this.className = className;
|
|
||||||
this.method = method;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Statement[] invoke() {
|
|
||||||
if (method.getModifiers().getFlags().contains(Modifier.PRIVATE)) {
|
if (method.getModifiers().getFlags().contains(Modifier.PRIVATE)) {
|
||||||
return reflectCall();
|
return reflectCall(className, method);
|
||||||
} else {
|
} else {
|
||||||
return commonCall();
|
return commonCall(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Statement[] commonCall() {
|
private Statement[] commonCall(JCTree.JCMethodDecl method) {
|
||||||
List<Object> args = new ArrayList<>();
|
List<Object> args = new ArrayList<>();
|
||||||
StringBuilder code = new StringBuilder();
|
StringBuilder code = new StringBuilder();
|
||||||
List<String> placeholders = new ArrayList<>();
|
List<String> placeholders = new ArrayList<>();
|
||||||
@ -45,25 +37,25 @@ public class CallSuperMethod {
|
|||||||
code.append(".").append(method.name);
|
code.append(".").append(method.name);
|
||||||
}
|
}
|
||||||
code.append("(").append(StringUtil.join(placeholders, ", ")).append(")");
|
code.append("(").append(StringUtil.join(placeholders, ", ")).append(")");
|
||||||
return new Statement[] { returnStatement(new Statement(code.toString(), args.toArray())) };
|
return new Statement[] { returnStatement(method, new Statement(code.toString(), args.toArray())) };
|
||||||
}
|
}
|
||||||
|
|
||||||
private Statement[] reflectCall() {
|
private Statement[] reflectCall(String className, JCTree.JCMethodDecl method) {
|
||||||
List<Statement> statements = new ArrayList<>();
|
List<Statement> statements = new ArrayList<>();
|
||||||
statements.add(getMethodStatement());
|
statements.add(getMethodStatement(className, method));
|
||||||
statements.add(setAccessibleStatement());
|
statements.add(setAccessibleStatement());
|
||||||
statements.add(returnStatement(invokeStatement()));
|
statements.add(returnStatement(method, invokeStatement(method)));
|
||||||
return statements.toArray(new Statement[0]);
|
return statements.toArray(new Statement[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Statement returnStatement(Statement statement) {
|
private Statement returnStatement(JCTree.JCMethodDecl method, Statement statement) {
|
||||||
if (method.restype != null && !method.restype.toString().equals(ConstPool.CONSTRUCTOR_VOID)) {
|
if (method.restype != null && !method.restype.toString().equals(ConstPool.TYPE_VOID)) {
|
||||||
statement.setLine("return " + statement.getLine());
|
statement.setLine("return " + statement.getLine());
|
||||||
}
|
}
|
||||||
return statement;
|
return statement;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Statement getMethodStatement() {
|
private Statement getMethodStatement(String className, JCTree.JCMethodDecl method) {
|
||||||
List<Object> args = new ArrayList<>();
|
List<Object> args = new ArrayList<>();
|
||||||
StringBuilder code = new StringBuilder();
|
StringBuilder code = new StringBuilder();
|
||||||
code.append("$T m = ");
|
code.append("$T m = ");
|
||||||
@ -81,9 +73,9 @@ public class CallSuperMethod {
|
|||||||
return new Statement("m.setAccessible(true)", new Object[0]);
|
return new Statement("m.setAccessible(true)", new Object[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Statement invokeStatement() {
|
private Statement invokeStatement(JCTree.JCMethodDecl method) {
|
||||||
StringBuilder code = new StringBuilder();
|
StringBuilder code = new StringBuilder();
|
||||||
if (!method.restype.toString().equals(ConstPool.CONSTRUCTOR_VOID)) {
|
if (!method.restype.toString().equals(ConstPool.TYPE_VOID)) {
|
||||||
code.append("(").append(method.restype).append(")");
|
code.append("(").append(method.restype).append(")");
|
||||||
}
|
}
|
||||||
code.append("m.invoke(this");
|
code.append("m.invoke(this");
|
@ -0,0 +1,116 @@
|
|||||||
|
package com.alibaba.testable.generator;
|
||||||
|
|
||||||
|
import com.alibaba.testable.generator.model.Statement;
|
||||||
|
import com.alibaba.testable.translator.TestableClassTranslator;
|
||||||
|
import com.alibaba.testable.util.ConstPool;
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import com.sun.tools.javac.api.JavacTrees;
|
||||||
|
import com.sun.tools.javac.code.Type;
|
||||||
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
|
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate testable class code
|
||||||
|
*
|
||||||
|
* @author flin
|
||||||
|
*/
|
||||||
|
public class TestableClassGenerator {
|
||||||
|
|
||||||
|
private final JavacTrees trees;
|
||||||
|
|
||||||
|
public TestableClassGenerator(JavacTrees trees) {
|
||||||
|
this.trees = trees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String fetch(Element clazz, String packageName, String className) {
|
||||||
|
JCTree tree = trees.getTree(clazz);
|
||||||
|
TestableClassTranslator translator = new TestableClassTranslator();
|
||||||
|
tree.accept(translator);
|
||||||
|
|
||||||
|
List<MethodSpec> methodSpecs = new ArrayList<>();
|
||||||
|
for (JCTree.JCMethodDecl method : translator.getMethods()) {
|
||||||
|
if (isNoncallableMethod(method)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isConstructorMethod(method)) {
|
||||||
|
methodSpecs.add(buildConstructorMethod(clazz, method));
|
||||||
|
} else {
|
||||||
|
methodSpecs.add(buildMemberMethod(clazz, method));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSpec.Builder builder = TypeSpec.classBuilder(className)
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
||||||
|
.superclass(clazz.asType());
|
||||||
|
for (MethodSpec m : methodSpecs) {
|
||||||
|
builder.addMethod(m);
|
||||||
|
}
|
||||||
|
TypeSpec testableClass = builder.build();
|
||||||
|
JavaFile javaFile = JavaFile.builder(packageName, testableClass).build();
|
||||||
|
return javaFile.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodSpec buildMemberMethod(Element classElement, JCTree.JCMethodDecl method) {
|
||||||
|
MethodSpec.Builder builder = MethodSpec.methodBuilder(method.name.toString())
|
||||||
|
.addModifiers(toPublicFlags(method.getModifiers()))
|
||||||
|
.returns(TypeName.get(((Type.MethodType)method.sym.type).restype));
|
||||||
|
for (JCTree.JCVariableDecl p : method.getParameters()) {
|
||||||
|
builder.addParameter(getParameterSpec(p));
|
||||||
|
}
|
||||||
|
if (method.getModifiers().getFlags().contains(Modifier.PRIVATE)) {
|
||||||
|
builder.addException(Exception.class);
|
||||||
|
} else {
|
||||||
|
builder.addAnnotation(Override.class);
|
||||||
|
for (JCTree.JCExpression exception : method.getThrows()) {
|
||||||
|
builder.addException(TypeName.get(exception.type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addStatements(builder, classElement, method);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodSpec buildConstructorMethod(Element classElement, JCTree.JCMethodDecl method) {
|
||||||
|
MethodSpec.Builder builder = MethodSpec.constructorBuilder()
|
||||||
|
.addModifiers(Modifier.PUBLIC);
|
||||||
|
for (JCTree.JCVariableDecl p : method.getParameters()) {
|
||||||
|
builder.addParameter(getParameterSpec(p));
|
||||||
|
}
|
||||||
|
addStatements(builder, classElement, method);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStatements(MethodSpec.Builder builder, Element classElement, JCTree.JCMethodDecl method) {
|
||||||
|
String className = classElement.getSimpleName().toString();
|
||||||
|
Statement[] statements = new CallSuperMethodStatementGenerator().fetch(className, method);
|
||||||
|
for (Statement s : statements) {
|
||||||
|
builder.addStatement(s.getLine(), s.getParams());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isConstructorMethod(JCTree.JCMethodDecl method) {
|
||||||
|
return method.name.toString().equals(ConstPool.CONSTRUCTOR_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNoncallableMethod(JCTree.JCMethodDecl method) {
|
||||||
|
return method.getModifiers().getFlags().contains(Modifier.ABSTRACT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Modifier> toPublicFlags(JCTree.JCModifiers modifiers) {
|
||||||
|
Set<Modifier> flags = new HashSet<>(modifiers.getFlags());
|
||||||
|
flags.remove(Modifier.PRIVATE);
|
||||||
|
flags.remove(Modifier.PROTECTED);
|
||||||
|
flags.add(Modifier.PUBLIC);
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParameterSpec getParameterSpec(JCTree.JCVariableDecl type) {
|
||||||
|
return ParameterSpec.builder(TypeName.get(type.sym.type), type.name.toString()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,13 +1,9 @@
|
|||||||
package com.alibaba.testable.processor;
|
package com.alibaba.testable.processor;
|
||||||
|
|
||||||
import com.alibaba.testable.annotation.Testable;
|
import com.alibaba.testable.annotation.Testable;
|
||||||
import com.alibaba.testable.generator.CallSuperMethod;
|
import com.alibaba.testable.generator.TestableClassGenerator;
|
||||||
import com.alibaba.testable.generator.model.Statement;
|
|
||||||
import com.alibaba.testable.translator.TestableClassTranslator;
|
|
||||||
import com.alibaba.testable.translator.TestableFieldTranslator;
|
import com.alibaba.testable.translator.TestableFieldTranslator;
|
||||||
import com.alibaba.testable.util.ConstPool;
|
import com.alibaba.testable.util.ConstPool;
|
||||||
import com.squareup.javapoet.*;
|
|
||||||
import com.sun.tools.javac.code.Type;
|
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
|
|
||||||
import javax.annotation.processing.RoundEnvironment;
|
import javax.annotation.processing.RoundEnvironment;
|
||||||
@ -15,14 +11,11 @@ import javax.annotation.processing.SupportedAnnotationTypes;
|
|||||||
import javax.annotation.processing.SupportedSourceVersion;
|
import javax.annotation.processing.SupportedSourceVersion;
|
||||||
import javax.lang.model.SourceVersion;
|
import javax.lang.model.SourceVersion;
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Name;
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,101 +45,20 @@ public class TestableProcessor extends BaseProcessor {
|
|||||||
|
|
||||||
private void processClassElement(Element clazz) {
|
private void processClassElement(Element clazz) {
|
||||||
String packageName = elementUtils.getPackageOf(clazz).getQualifiedName().toString();
|
String packageName = elementUtils.getPackageOf(clazz).getQualifiedName().toString();
|
||||||
String testableTypeName = clazz.getSimpleName().toString().replace(".", "_") + "Testable";
|
String testableTypeName = getTestableClassName(clazz.getSimpleName());
|
||||||
String fullQualityTypeName = packageName + "." + testableTypeName;
|
String fullQualityTypeName = packageName + "." + testableTypeName;
|
||||||
try {
|
try {
|
||||||
JavaFileObject jfo = filter.createSourceFile(fullQualityTypeName);
|
JavaFileObject jfo = filter.createSourceFile(fullQualityTypeName);
|
||||||
Writer writer = jfo.openWriter();
|
Writer writer = jfo.openWriter();
|
||||||
writer.write(createTestableClass(clazz, packageName, testableTypeName));
|
writer.write(new TestableClassGenerator(trees).fetch(clazz, packageName, testableTypeName));
|
||||||
writer.close();
|
writer.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createTestableClass(Element clazz, String packageName, String className) {
|
private String getTestableClassName(Name className) {
|
||||||
JCTree tree = trees.getTree(clazz);
|
return className.toString().replace(".", "_") + ConstPool.TESTABLE;
|
||||||
TestableClassTranslator translator = new TestableClassTranslator();
|
|
||||||
tree.accept(translator);
|
|
||||||
|
|
||||||
List<MethodSpec> methodSpecs = new ArrayList<>();
|
|
||||||
for (JCTree.JCMethodDecl method : translator.getMethods()) {
|
|
||||||
if (isNoncallableMethod(method)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isConstructorMethod(method)) {
|
|
||||||
buildConstructorMethod(clazz, methodSpecs, method);
|
|
||||||
} else {
|
|
||||||
buildMemberMethod(clazz, methodSpecs, method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeSpec.Builder builder = TypeSpec.classBuilder(className)
|
|
||||||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
|
||||||
.superclass(clazz.asType());
|
|
||||||
for (MethodSpec m : methodSpecs) {
|
|
||||||
builder.addMethod(m);
|
|
||||||
}
|
|
||||||
TypeSpec testableClass = builder.build();
|
|
||||||
JavaFile javaFile = JavaFile.builder(packageName, testableClass).build();
|
|
||||||
return javaFile.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildMemberMethod(Element classElement, List<MethodSpec> methodSpecs, JCTree.JCMethodDecl method) {
|
|
||||||
MethodSpec.Builder builder = MethodSpec.methodBuilder(method.name.toString())
|
|
||||||
.addModifiers(toPublicFlags(method.getModifiers()))
|
|
||||||
.returns(TypeName.get(((Type.MethodType)method.sym.type).restype));
|
|
||||||
for (JCTree.JCVariableDecl p : method.getParameters()) {
|
|
||||||
builder.addParameter(getParameterSpec(p));
|
|
||||||
}
|
|
||||||
if (method.getModifiers().getFlags().contains(Modifier.PRIVATE)) {
|
|
||||||
builder.addException(Exception.class);
|
|
||||||
} else {
|
|
||||||
builder.addAnnotation(Override.class);
|
|
||||||
for (JCTree.JCExpression exception : method.getThrows()) {
|
|
||||||
builder.addException(TypeName.get(exception.type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addStatements(builder, classElement, method);
|
|
||||||
methodSpecs.add(builder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildConstructorMethod(Element classElement, List<MethodSpec> methodSpecs,
|
|
||||||
JCTree.JCMethodDecl method) {
|
|
||||||
MethodSpec.Builder builder = MethodSpec.constructorBuilder()
|
|
||||||
.addModifiers(Modifier.PUBLIC);
|
|
||||||
for (JCTree.JCVariableDecl p : method.getParameters()) {
|
|
||||||
builder.addParameter(getParameterSpec(p));
|
|
||||||
}
|
|
||||||
addStatements(builder, classElement, method);
|
|
||||||
methodSpecs.add(builder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addStatements(MethodSpec.Builder builder, Element classElement, JCTree.JCMethodDecl method) {
|
|
||||||
Statement[] statements = new CallSuperMethod(classElement.getSimpleName().toString(), method).invoke();
|
|
||||||
for (Statement s : statements) {
|
|
||||||
builder.addStatement(s.getLine(), s.getParams());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isConstructorMethod(JCTree.JCMethodDecl method) {
|
|
||||||
return method.name.toString().equals(ConstPool.CONSTRUCTOR_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isNoncallableMethod(JCTree.JCMethodDecl method) {
|
|
||||||
return method.getModifiers().getFlags().contains(Modifier.ABSTRACT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<Modifier> toPublicFlags(JCTree.JCModifiers modifiers) {
|
|
||||||
Set<Modifier> flags = new HashSet<>(modifiers.getFlags());
|
|
||||||
flags.remove(Modifier.PRIVATE);
|
|
||||||
flags.remove(Modifier.PROTECTED);
|
|
||||||
flags.add(Modifier.PUBLIC);
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ParameterSpec getParameterSpec(JCTree.JCVariableDecl type) {
|
|
||||||
return ParameterSpec.builder(TypeName.get(type.sym.type), type.name.toString()).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.alibaba.testable.translator;
|
package com.alibaba.testable.translator;
|
||||||
|
|
||||||
|
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;
|
||||||
@ -30,7 +31,7 @@ public class TestableFieldTranslator 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 + "Testable"));
|
return treeMaker.Ident(className.table.fromString(className + ConstPool.TESTABLE));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,6 @@ import com.sun.tools.javac.tree.JCTree;
|
|||||||
public final class ConstPool {
|
public final class ConstPool {
|
||||||
|
|
||||||
public static final String CONSTRUCTOR_NAME = "<init>";
|
public static final String CONSTRUCTOR_NAME = "<init>";
|
||||||
|
public static final String TYPE_VOID = "void";
|
||||||
public static final String CONSTRUCTOR_VOID = "void";
|
public static final String TESTABLE = "Testable";
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user