📜 ⬆️ ⬇️

Custom Annotation Preprocessor - the creation of Android-based applications and configuration in IntelliJ IDEA

Hello!

Recently, I had the task of writing my own custom annotations and processing them during compilation. The first question I asked myself: where to start? After analyzing, I decided to share with you the answer to this question.
I think that telling what annotations in java are and with what they are eaten does not make sense, since every young programmer is familiar with this (and who he doesn’t know can read it himself). In addition, there is an interesting introductory article on this phenomenon in Habré.
But today I want to talk about custom annotations in the Android application, which are processed in the process of compiling the project by your own handler and on the autogeneration of classes based on them. And also, along the way, I will tell you how quickly everything is set up in IDEA (I myself use version 12.1, maybe others have differences).

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

Step 1

Create a new library project in which we describe our annotation. An annotated class looks like this:
')
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) 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 this point we will process it).

Step 2

Create our own annotation processor directly. It is a class that extends AbstractProcessor. We tell him that he will process all the annotations and indicate the supported version of the source files as follows:

 @SupportedAnnotationTypes({"*"}) @SupportedSourceVersion(SourceVersion.RELEASE_6) public class CustomProcessor extends AbstractProcessor { } 

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

 @Override public boolean process(Set<? extends TypeElement> 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"); processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "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(" }"); pw.println("}"); pw.flush(); } finally { w.close(); } } catch (IOException x) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, x.toString()); } } return true; } 

At this stage, you can turn on the 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, from this project you need to generate a jar file. 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 happy that our annotations are now available to us. In any class, write our annotation, for example:
 public class MyActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @CustomAnnotation(className = "String", type = 1) public void annotatedMethod(String value) { } } 

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

Step 4

Now let's say Idea use our annotation handler and the magic will start working! To do this, go to Settings in the Compiler section -> Annotation Processors . We tick the Enable annotation processing checkbox, in the Processor path field we indicate 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. The filled window should look something like this.



Step 5

Build -> Rebuild project and enjoy the results. The generated folder should appear in the project tree in which the new files will be located.
That's what happened with me:

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


Happy codding!

Source: https://habr.com/ru/post/200354/


All Articles