利用java的插入式注解处理器, 处理掉所有的builder类型的set方法和add方法
import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Names;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;
@SupportedAnnotationTypes("protobuf.advice.NullReturn")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class NullReturnProcessor extends AbstractProcessor {
/**
* Messager 主要是用来在编译期打log用的
* JavacTrees 提供了待处理的抽象语法树
* TreeMaker 封装了创建AST节点的一些方法
* Names 提供了创建标识符的方法
*/
private Messager messager;
private JavacTrees trees;
private TreeMaker treeMaker;
private Names names;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.messager = processingEnv.getMessager();
this.trees = JavacTrees.instance(processingEnv);
Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
this.treeMaker = TreeMaker.instance(context);
this.names = Names.instance(context);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(NullReturn.class);
set.forEach(element -> {
JCTree jcTree = trees.getTree(element);
jcTree.accept(new TreeTranslator() {
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
visitAllBuilder(jcClassDecl);
}
});
});
return true;
}
public void visitAllBuilder(JCTree.JCClassDecl jcClassDecl) {
final JCTree.JCExpression extending = jcClassDecl.extending;
if (extending != null) {
// check是否是builder
final String extendClazz = extending.toString();
messager.printMessage(Diagnostic.Kind.NOTE, extendClazz);
if ("com.google.protobuf.GeneratedMessage.Builder<Builder>".equals(extendClazz)) {
processNull(jcClassDecl);
return;
}
}
for (JCTree tree : jcClassDecl.defs) {
if (tree.getKind().equals(Tree.Kind.CLASS)) {
visitAllBuilder(((JCTree.JCClassDecl) tree));
}
}
}
private void processNull(JCTree.JCClassDecl jcClassDecl) {
for (JCTree tree : jcClassDecl.defs) {
if (tree.getKind().equals(Tree.Kind.METHOD)) {
if ("<init>".equals(((JCTree.JCMethodDecl) tree).name.toString())) {
continue;
}
JCTree.JCMethodDecl jcMethodDecl = (JCTree.JCMethodDecl) tree;
final List<JCTree.JCVariableDecl> parameters = jcMethodDecl.getParameters();
final String methodName = jcMethodDecl.getName().toString();
// set方法只有一个参数
if (parameters.length() != 1) {
continue;
}
// 参数是原始类型
final JCTree.JCVariableDecl parameter = parameters.get(0);
if (parameter.vartype.type.getKind().isPrimitive()) {
continue;
}
// set add方法
if (!methodName.startsWith("set") && !methodName.startsWith("add")) {
continue;
}
messager.printMessage(Diagnostic.Kind.NOTE, methodName + " has been processed");
// if (param == null) return;
final JCTree.JCIf ifNullReturn = treeMaker.If(
treeMaker.Binary(
JCTree.Tag.EQ,
treeMaker.Ident(parameter),
treeMaker.Literal(TypeTag.BOT, null)
),
treeMaker.Return(treeMaker.Ident(names.fromString("this"))),
null
);
jcMethodDecl.body = treeMaker.Block(0, List.of(
ifNullReturn,
jcMethodDecl.body
));
}
}
}
}