move static new method to global class

This commit is contained in:
金戟 2020-05-14 18:33:26 +08:00
parent 066107372b
commit 3e90422cb6
4 changed files with 84 additions and 29 deletions

View File

@ -0,0 +1,48 @@
package com.alibaba.testable.generator;
import com.alibaba.testable.util.ConstPool;
import com.squareup.javapoet.*;
import javax.lang.model.element.Modifier;
import java.util.ArrayList;
import java.util.List;
/**
* Generate global n.e class code
*
* @author flin
*/
public class StaticNewClassGenerator {
public String fetch() {
return JavaFile.builder(ConstPool.SN_PKG,
TypeSpec.classBuilder(ConstPool.SN_CLS)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(buildStaticNewMethod())
.build())
.build().toString();
}
private MethodSpec buildStaticNewMethod() {
TypeVariableName typeVariable = TypeVariableName.get("T");
MethodSpec.Builder builder = MethodSpec.methodBuilder(ConstPool.SN_METHOD)
.addModifiers(Modifier.PUBLIC).addModifiers(Modifier.STATIC)
.addException(Exception.class)
.addTypeVariable(typeVariable)
.varargs(true)
.addParameter(ParameterizedTypeName.get(ClassName.get(Class.class), typeVariable), "type")
.addParameter(ArrayTypeName.of(Object.class), "args")
.returns(typeVariable);
addStaticNewMethodStatement(builder);
return builder.build();
}
private void addStaticNewMethodStatement(MethodSpec.Builder builder) {
builder.addStatement("$T<$T> pts = new $T<>()", List.class, Class.class, ArrayList.class)
.beginControlFlow("for (Object o : args)")
.addStatement("pts.add(o.getClass())")
.endControlFlow()
.addStatement("return type.getConstructor(pts.toArray(new Class[0])).newInstance(args)");
}
}

View File

@ -7,6 +7,7 @@ import com.squareup.javapoet.*;
import com.sun.tools.javac.api.JavacTrees; import com.sun.tools.javac.api.JavacTrees;
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 javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
@ -23,18 +24,19 @@ import java.util.Set;
public class TestableClassGenerator { public class TestableClassGenerator {
private final JavacTrees trees; private final JavacTrees trees;
private final TreeMaker treeMaker;
public TestableClassGenerator(JavacTrees trees) { public TestableClassGenerator(JavacTrees trees, TreeMaker treeMaker) {
this.trees = trees; this.trees = trees;
this.treeMaker = treeMaker;
} }
public String fetch(Element clazz, String packageName, String className) { public String fetch(Element clazz, String packageName, String className) {
JCTree tree = trees.getTree(clazz); JCTree tree = trees.getTree(clazz);
TestableClassTranslator translator = new TestableClassTranslator(); TestableClassTranslator translator = new TestableClassTranslator(treeMaker);
tree.accept(translator); tree.accept(translator);
List<MethodSpec> methodSpecs = new ArrayList<>(); List<MethodSpec> methodSpecs = new ArrayList<>();
methodSpecs.add(buildStaticNewMethod(clazz));
for (JCTree.JCMethodDecl method : translator.getMethods()) { for (JCTree.JCMethodDecl method : translator.getMethods()) {
if (isNoncallableMethod(method)) { if (isNoncallableMethod(method)) {
continue; continue;
@ -57,28 +59,6 @@ public class TestableClassGenerator {
return javaFile.toString(); return javaFile.toString();
} }
private MethodSpec buildStaticNewMethod(Element clazz) {
TypeVariableName typeVariable = TypeVariableName.get("T");
MethodSpec.Builder builder = MethodSpec.methodBuilder("New")
.addModifiers(Modifier.PUBLIC).addModifiers(Modifier.STATIC)
.addException(Exception.class)
.addTypeVariable(typeVariable)
.varargs(true)
.addParameter(ParameterizedTypeName.get(ClassName.get(Class.class), typeVariable), "type")
.addParameter(ArrayTypeName.of(Object.class), "args")
.returns(typeVariable);
addStaticNewMethodStatement(builder);
return builder.build();
}
private void addStaticNewMethodStatement(MethodSpec.Builder builder) {
builder.addStatement("$T<$T> pts = new $T<>()", List.class, Class.class, ArrayList.class)
.beginControlFlow("for (Object o : args)")
.addStatement("pts.add(o.getClass())")
.endControlFlow()
.addStatement("return type.getConstructor(pts.toArray(new Class[0])).newInstance(args)");
}
private MethodSpec buildMemberMethod(Element classElement, JCTree.JCMethodDecl method) { private MethodSpec buildMemberMethod(Element classElement, JCTree.JCMethodDecl method) {
MethodSpec.Builder builder = MethodSpec.methodBuilder(method.name.toString()) MethodSpec.Builder builder = MethodSpec.methodBuilder(method.name.toString())
.addModifiers(toPublicFlags(method.getModifiers())) .addModifiers(toPublicFlags(method.getModifiers()))

View File

@ -1,6 +1,7 @@
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.StaticNewClassGenerator;
import com.alibaba.testable.generator.TestableClassGenerator; import com.alibaba.testable.generator.TestableClassGenerator;
import com.alibaba.testable.translator.TestableFieldTranslator; import com.alibaba.testable.translator.TestableFieldTranslator;
import com.alibaba.testable.util.ConstPool; import com.alibaba.testable.util.ConstPool;
@ -13,11 +14,14 @@ import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.Name; import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.tools.FileObject;
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.Set; import java.util.Set;
import static javax.tools.StandardLocation.SOURCE_OUTPUT;
/** /**
* @author flin * @author flin
*/ */
@ -25,9 +29,13 @@ import java.util.Set;
@SupportedSourceVersion(SourceVersion.RELEASE_7) @SupportedSourceVersion(SourceVersion.RELEASE_7)
public class TestableProcessor extends BaseProcessor { public class TestableProcessor extends BaseProcessor {
private static final String JAVA_POSTFIX = ".java";
private static final String GENERATED_TEST_SOURCES = "generated-test-sources";
@Override @Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Testable.class); Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Testable.class);
createStaticNewClass();
for (Element element : elements) { for (Element element : elements) {
if (element.getKind().isClass()) { if (element.getKind().isClass()) {
processClassElement(element); processClassElement(element);
@ -38,6 +46,17 @@ public class TestableProcessor extends BaseProcessor {
return true; return true;
} }
private void createStaticNewClass() {
try {
FileObject staticNewClassFile = filter.getResource(SOURCE_OUTPUT, ConstPool.SN_PKG, ConstPool.SN_CLS + JAVA_POSTFIX);
if (!staticNewClassFile.getName().contains(GENERATED_TEST_SOURCES) && staticNewClassFile.getLastModified() == 0) {
writeSourceFile(ConstPool.SN_PKG + "." + ConstPool.SN_CLS, new StaticNewClassGenerator().fetch());
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void processFieldElement(Element field) { private void processFieldElement(Element field) {
JCTree tree = trees.getTree(field); JCTree tree = trees.getTree(field);
tree.accept(new TestableFieldTranslator(treeMaker)); tree.accept(new TestableFieldTranslator(treeMaker));
@ -48,15 +67,20 @@ public class TestableProcessor extends BaseProcessor {
String testableTypeName = getTestableClassName(clazz.getSimpleName()); String testableTypeName = getTestableClassName(clazz.getSimpleName());
String fullQualityTypeName = packageName + "." + testableTypeName; String fullQualityTypeName = packageName + "." + testableTypeName;
try { try {
JavaFileObject jfo = filter.createSourceFile(fullQualityTypeName); writeSourceFile(fullQualityTypeName,
Writer writer = jfo.openWriter(); new TestableClassGenerator(trees, treeMaker).fetch(clazz, packageName, testableTypeName));
writer.write(new TestableClassGenerator(trees).fetch(clazz, packageName, testableTypeName));
writer.close();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private void writeSourceFile(String fullQualityTypeName, String content) throws IOException {
JavaFileObject jfo = filter.createSourceFile(fullQualityTypeName);
Writer writer = jfo.openWriter();
writer.write(content);
writer.close();
}
private String getTestableClassName(Name className) { private String getTestableClassName(Name className) {
return className.toString().replace(".", "_") + ConstPool.TESTABLE; return className.toString().replace(".", "_") + ConstPool.TESTABLE;
} }

View File

@ -8,5 +8,8 @@ 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 TYPE_VOID = "void";
public static final String TESTABLE = "Testable"; public static final String TESTABLE = "Testable";
public static final String SN_PKG = "n";
public static final String SN_CLS = "e";
public static final String SN_METHOD = "w";
} }