Saturday, 12 November 2016

This week 18/2016

When I was at Devoxx 2016 conference, I chose one of DuyHai DOAN's presentation. I was late and I went at 15 minutes. It seemed that he was talking about some DSL framework so I was disappointed and bored. Lucky I tried to watch the presentation one more time and now I know that I didn't understand the context, the first 10 minutes was the most interesting.

However, to the point. Inspirited by DuyHai DOAN's presentation, I have interested in Annotation Processing. I found a good and clear article about this subject and all became easy.
But to the point. what is the Annotation Processing?
It is a phase of Java code compilation to byte code by javac. As you can see at picture bellow


http://openjdk.java.net/groups/compiler/doc/compilation-overview/javac-flow.png
at the beginning of compilation, the code is parsed and there is created a syntax's dictionary. Then there is executed the Annotation Processor which can read Java code or create new one. (There is assumption, the Annotation Processor can't modify existing code but there is some possibility. I will write about it later).
If the Annotation Processor creates new code, all cycle is repeated. If not, all code is analysed and translated to byte code. 

To study the Annotation Processor, I created 3 projects:
  • my annotation 
  • my implementation of the Annotation Processor 
  • some code in which I used my annotation.

Annotation: 
Notice a Retention Policy type.
@Retention(RetentionPolicy.SOURCE)
@Target({ ElementType.TYPE })
public @interface Getter {
}


Annotation Processor:
I created a file "javax.annotation.processing.Processor" in path "META-INF/services" and I put into it a path to implementation of my Annotation Processor 


@SupportedAnnotationTypes("test.annotationprocessor.annotation.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class GetterProcessor extends AbstractProcessor {

    public GetterProcessor() {
        super();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        createClassWithVelocityTemplate(roundEnv);
        return true;
    }

    public void createClassWithVelocityTemplate(RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Getter.class);
        for (Element element : elements) {
            try {
                FileObject in_file = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", element.asType().toString().replace(".", "/") + ".java");

                CharSequence data = in_file.getCharContent(false);
                String data1 = addAFewMethods(data).toString();

                FileObject out_file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", element.asType().toString().replace(".", "/") + ".java");
                Writer w = out_file.openWriter();
                w.append(data1);

                velocityGeneration(w);
                w.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
....
    }
}
It reads a source of class with my annotation and create new class with additional code.


To be drawing subject to the end, it is good to mention in a nutshell: 

  1. Annotation Processing is supported by IDEs like Eclipse or IntelliJ. It is required to check this option in project settings.
  2. As I mentioned above, the Annotation Processor can read and create new code, however authors of Lombok library have found a bypass to this limit. Their library works as it could change a code. They modifies a syntax's dictionary and inject their code. 
  3. Javac doesn't use JVM but it is possible to debugging code in your IDE. When a debug option is added to execute command, javac shares an interface like JVM for remote connections.

No comments:

Post a Comment