Skip to content

Instantly share code, notes, and snippets.

@pietrocaselani
Last active October 20, 2023 02:44
Show Gist options
  • Save pietrocaselani/8624554 to your computer and use it in GitHub Desktop.
Save pietrocaselani/8624554 to your computer and use it in GitHub Desktop.
Generate main method using Annotation Processor.
package com.pc.hello;
import com.sun.source.util.Trees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.TypeTags;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import static com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import static com.sun.tools.javac.tree.JCTree.JCBlock;
import static com.sun.tools.javac.tree.JCTree.JCClassDecl;
import static com.sun.tools.javac.tree.JCTree.JCExpression;
import static com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import static com.sun.tools.javac.tree.JCTree.JCIdent;
import static com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import static com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import static com.sun.tools.javac.tree.JCTree.JCModifiers;
import static com.sun.tools.javac.tree.JCTree.JCNewClass;
import static com.sun.tools.javac.tree.JCTree.JCStatement;
import static com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import static com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import static com.sun.tools.javac.util.Name.Table;
import static javax.lang.model.SourceVersion.RELEASE_6;
/**
* Created by Pietro Caselani
* On 25/01/14
* Hello
*/
@SupportedAnnotationTypes("com.pc.hello.MakeHello")
@SupportedSourceVersion(RELEASE_6)
public class HelloProcessor extends AbstractProcessor {
//region Fields
private Trees mTrees;
private TreeMaker mTreeMaker;
//endregion
//region Processor
@Override public synchronized void init(ProcessingEnvironment processingEnv) {
mTreeMaker = TreeMaker.instance(((JavacProcessingEnvironment) processingEnv).getContext());
mTrees = Trees.instance(processingEnv);
super.init(processingEnv);
}
@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
Set<? extends Element> elements = env.getElementsAnnotatedWith(MakeHello.class);
if (elements.size() > 0) {
for (Element element : elements) {
JCClassDecl classDecl = (JCClassDecl) mTrees.getTree(element);
addMainMethod(classDecl, element.toString());
addHelloMethod(classDecl);
}
}
return true;
}
//endregion
//region Private
private void addMainMethod(JCClassDecl classDecl, String className) {
JCModifiers modifiers = mTreeMaker.Modifiers(Flags.PUBLIC | Flags.STATIC);
JCExpression returnType = mTreeMaker.TypeIdent(TypeTags.VOID);
List<JCVariableDecl> parameters = makeMainParameters();
List<JCTypeParameter> generics = List.nil();
List<JCExpression> throwz = List.nil();
JCBlock methodBody = makeMainBody(className);
Name methodName = getName("main");
JCMethodDecl mainMethodDecl =
mTreeMaker.MethodDef(modifiers, methodName, returnType, generics, parameters, throwz,
methodBody, null);
classDecl.defs = classDecl.defs.append(mainMethodDecl);
}
private List<JCVariableDecl> makeMainParameters() {
JCIdent paramType = mTreeMaker.Ident(getName("String"));
JCArrayTypeTree paramArray = mTreeMaker.TypeArray(paramType);
JCVariableDecl paramDecl =
mTreeMaker.VarDef(mTreeMaker.Modifiers(Flags.PARAMETER), getName("args"), paramArray, null);
return List.from(new JCVariableDecl[]{paramDecl});
}
private JCBlock makeMainBody(String className) {
String[] strings = className.split("\\.");
JCExpression classNameIdent = mTreeMaker.Ident(getName(strings[0]));
for (int i = 1; i < strings.length; i++) {
classNameIdent = mTreeMaker.Select(classNameIdent, getName(strings[i]));
}
JCNewClass classObj = mTreeMaker.NewClass(null, List.<JCExpression>nil(), classNameIdent,
List.<JCExpression>nil(), null);
JCFieldAccess printHello = mTreeMaker.Select(classObj, getName("printHello"));
JCMethodInvocation printHelloInv =
mTreeMaker.Apply(List.<JCExpression>nil(), printHello, List.<JCExpression>nil());
JCStatement exec = mTreeMaker.Exec(printHelloInv);
List<JCStatement> statements = List.of(exec);
return mTreeMaker.Block(0, statements);
}
private void addHelloMethod(JCClassDecl classDecl) {
JCModifiers modifiers = mTreeMaker.Modifiers(Flags.PRIVATE | Flags.FINAL);
JCExpression returnType = mTreeMaker.TypeIdent(TypeTags.VOID);
List<JCVariableDecl> parameters = List.nil();
List<JCTypeParameter> generics = List.nil();
Name methodName = getName("printHello");
List<JCExpression> throwz = List.nil();
JCBlock methodBody = makeHelloBody();
JCMethodDecl helloMethodDecl =
mTreeMaker.MethodDef(modifiers, methodName, returnType, generics, parameters, throwz,
methodBody, null);
classDecl.defs = classDecl.defs.append(helloMethodDecl);
}
private JCBlock makeHelloBody() {
JCExpression printExpression = mTreeMaker.Ident(getName("System"));
printExpression = mTreeMaker.Select(printExpression, getName("out"));
printExpression = mTreeMaker.Select(printExpression, getName("println"));
List<JCExpression> printArgs = List.from(new JCExpression[] {mTreeMaker.Literal("Hello from HelloProcessor!")});
printExpression = mTreeMaker.Apply(List.<JCExpression>nil(), printExpression, printArgs);
JCStatement call = mTreeMaker.Exec(printExpression);
List<JCStatement> statements = List.from(new JCStatement[]{call});
return mTreeMaker.Block(0, statements);
}
private Name getName(String string) {
Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
return Name.fromString(Table.instance(context), string);
}
//endregion
}
package com.pc.helloimpl;
import com.pc.hello.MakeHello;
/**
* Created by Pietro Caselani
* On 25/01/14
* HelloImpl
*/
@MakeHello
public final class Main {}
/*
package com.pc.helloimpl;
import com.pc.hello.MakeHello;
import java.io.PrintStream;
@MakeHello
public final class Main
{
public static void main(String[] args)
{
new Main().printHello();
}
private final void printHello() {
System.out.println("Hello from HelloProcessor!");
}
}
*/
package com.pc.hello;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.CLASS;
/**
* Created by Pietro Caselani
* On 25/01/14
* Hello
*/
@Retention(CLASS)
@Target(TYPE)
public @interface MakeHello {}
@yaroslav-prokipchyn
Copy link

Thanks this is really help

@Taymindis
Copy link

hi @pietrocaselani,

Name.fromString seemed not working for me. It is a java version compatibility issue

@KnIfER
Copy link

KnIfER commented Jul 22, 2021

How to modify( delete ) some imports of the class?

@KnIfER
Copy link

KnIfER commented Jul 22, 2021

@gvsem
Copy link

gvsem commented Oct 21, 2022

hi @pietrocaselani,

Name.fromString seemed not working for me. It is a java version compatibility issue

Though this API is marked as one which can change without notice, the following implementation does the job
return Names.instance(context).fromString(string);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment