Custom Annotation Preprocessor - creating an Android-based application and configuration in IntelliJ IDEA

  • Tutorial

Recently, I was faced with the task of writing my own custom annotations and processing them during compilation. The first question I asked myself: where to start? After the analysis, I decided to share with you the answer to this question.
I think it makes no sense to tell what annotations are in java and what they are eaten with, as every young programmer is familiar with this (and who is not familiar with it, he can read it on his own). In addition, there is an interesting fact-finding article on this phenomenon.
But today I want to talk specifically about custom annotations in an Android application that are processed during the compilation of the project by your own handler and about auto-generation of classes based on them. And also, along the way, I’ll tell you how to quickly configure everything in IDEA (I myself use version 12.1, maybe there are differences in others).

For implementation, we need 2 projects: in the first, we will describe our custom annotations and their handler, from it we will generate a jar file, which we will connect to the second test project, which will use our annotations.

Step 1

We create a new library project in which we describe our annotation. An annotated class looks something like this:

public @interface CustomAnnotation {
    String className();
    String value() default "Hello";
    int type() default 0;

@Retention says that our annotation will be present only in the source code and discarded by the compiler (and until that moment we will process it).

Step 2

We create our annotation processor directly. It is a class that extends AbstractProcessor. We tell him that he will process all annotations and indicate the supported version of the source files in this way:

public class CustomProcessor extends AbstractProcessor { 

Next, we redefine the process method, in which we prescribe the logic for generating a new class. My method looks like this:

    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        for (Element e : roundEnv.getElementsAnnotatedWith(CustomAnnotation.class)) {
            CustomAnnotation ca = e.getAnnotation(CustomAnnotation.class);
            String name = e.getSimpleName().toString();
            char[] c = name.toCharArray();
            c[0] = Character.toUpperCase(c[0]);
            name = new String(name);
            TypeElement clazz = (TypeElement) e.getEnclosingElement();
            try {
                JavaFileObject f = processingEnv.getFiler().
                        createSourceFile(clazz.getQualifiedName() + "Autogenerate");
                        "Creating " + f.toUri());
                Writer w = f.openWriter();
                try {
                    String pack = clazz.getQualifiedName().toString();
                    PrintWriter pw = new PrintWriter(w);
                    pw.println("package "
                            + pack.substring(0, pack.lastIndexOf('.')) + ";");
                    pw.println("\npublic class "
                            + clazz.getSimpleName() + "Autogenerate {");
                    TypeMirror type = e.asType();
                    pw.println("\n    public " + ca.className() + " result = \"" + ca.value() + "\";");
                    pw.println("    public int type = " + ca.type() + ";");
                    pw.println("\n    protected " + clazz.getSimpleName()
                            + "Autogenerate() {}");
                    pw.println("\n    /** Handle something. */");
                    pw.println("    protected final void handle" + name
                            + "(" + ca.className() + " value" + ") {");
                    pw.println("\n//" + e);
                    pw.println("//" + ca);
                    pw.println("\n        System.out.println(value);");
                    pw.println("    }");
                } finally {
            } catch (IOException x) {
        return true;

At this stage, you can turn on fantasy and write what your heart desires (well, or what is required =). After you have finished with the annotation processor and described all your custom annotations, you need to generate a jar file from this project. In Idea 12, this is done quite simply: Project Settings -> Artifacts -> Add -> Jar -> From modules ... Next, do the Build -> Rebuild Project and find our generated jar file in the Output directory of the project.

Step 3

We create a test project in which we will use our custom annotations. We connect the jar file generated at the last step to the project and are glad that our annotations are now available to us. In any class, write our annotation, for example like this:
public class MyActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
    @CustomAnnotation(className = "String", type = 1)
    public void annotatedMethod(String value) {

Remember that we indicated our annotation @Target(ElementType.METHOD), which means that we can register it only before the method.

Step 4

Now let's say Idea use our annotation processor and the magic will start working! To do this, go to Settings in the Compiler -> Annotation Processors section . We tick Enable annotation processing , in the Processor path field specify the path to our generated jar file. Also in the Processor FQ name window, enter the full name of the class that is responsible for processing. In our case, this is a CustomProcessor. A filled window should look something like this.

Step 5

We do the Build -> Rebuild project and enjoy the results. The generated folder should appear in the project tree, in which new files will lie.
Here's what I got:

public class MyActivityAutogenerate {
    public String result = "Hello";
    public int type = 1;
    protected MyActivityAutogenerate() {}
    /** Handle something. */
    protected final void handleannotatedMethod(String value) {
//@com.example.AnnotationsProcessor.CustomAnnotation(type=1, value=Hello, className=String)

Happy codding!

Also popular now: