Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code replacement with an annotation processor

Tags:

I'm trying to write an annotation processor to insert methods and fields on a class... and the documentation is so sparse. I'm not getting far and I don't know if I'm approaching it correctly.

The processing environment provides a Filer object which has handy methods for creating new source and class files. Those work fine but then I tried to figure out how read the existing source files, and all it provides is "getResource". So in my Processor implementation I've done this:

@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {     try {         for (TypeElement te : annotations) {             for (Element element : roundEnv.getElementsAnnotatedWith(te)) {                 FileObject in_file = processingEnv.getFiler().getResource(                     StandardLocation.SOURCE_PATH, "",                     element.asType().toString().replace(".", "/") + ".java");                  FileObject out_file = processingEnv.getFiler().getResource(                     StandardLocation.SOURCE_OUTPUT, "",                     element.asType().toString().replace(".", "/") + ".java");                  //if (out_file.getLastModified() >= in_file.getLastModified()) continue;                  CharSequence data = in_file.getCharContent(false);                  data = transform(data); // run the macro processor                  JavaFileObject out_file2 = processingEnv.getFiler().createSourceFile(                     element.asType().toString(), element);                 Writer w = out_file2.openWriter();                 w.append(data);                 w.close();             }         }     } catch (Exception e) {         e.printStackTrace();         processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());     }     return true; } 

My first quandary is I can't help feeling that element.asType().toString().replace(".", "/") + ".java" (to get the qualified type name and convert it into a package and source file path) is not a nice way to approach the problem. The rest of the API is so over-engineered but there doesn't seem to be a handy method for retrieving the original source code.

The real problem is that then the compiler gets spontaneously upset by the second source file in the output directory ("error: duplicate class") and now I'm stuck.

I've already written the rest of this -- a macro lexer and parser and whatnot for calculating some data and inserting the field values and methods -- but it operates as a initial step outside the compiler. Except for the fact that the original files cannot have a .java extension (to prevent the compiler seeing them), this works nicely. Then I heard that annotations can do code generation, which I assume will be more proper and convenient, but I can't find much guidance on it.

like image 338
Boann Avatar asked Dec 03 '12 19:12

Boann


People also ask

What is an annotation processor?

Annotation processing is a tool built into javac for scanning and processing annotations at compile time. It can create new source files; however, it can't modify existing ones. It's done in rounds. The first round starts when the compilation reaches the pre-compile phase.

Why do we need annotation processor?

Annotations provide information to a program at compile time or at runtime based on which the program can take further action. An annotation processor processes these annotations at compile time or runtime to provide functionality such as code generation, error checking, etc.

What is an annotation code?

In the Java computer programming language, an annotation is a form of syntactic metadata that can be added to Java source code. Classes, methods, variables, parameters and Java packages may be annotated. Like Javadoc tags, Java annotations can be read from source files.

How does annotation processing work?

Annotation processing takes place at compile time (compile time = the time when the java compiler compiles your java source code). Annotation processing is a tool build in javac for scanning and processing annotations at compile time. You can register your own annotation processor for certain annotations.


1 Answers

The intention behind the annotation processor is to allow a developer to add new classes, not replace existing classes. That being said, there is a bug that allows you to add code to existing classes. Project Lombok has leveraged this to add getter and setter (among other things) to your compiled java classes.

The approach I have taken to 'replace' methods/fields is either extend from or delegate to the input class. This allows you to override/divert calls to the target class.

So if this is your input class:

InputImpl.java:

public class InputImpl implements Input{     public void foo(){         System.out.println("foo");     }     public void bar(){         System.out.println("bar");     } } 

You could generate the following to "replace" it:

InputReplacementImpl.java:

public class InputReplacementImpl implements Input{      private Input delegate;      //setup delegate....      public void foo(){         System.out.println("foo replacement");     }     public void bar(){         delegate.bar();     } } 

This begs the question, how do you reference InputReplacementImpl instead of InputImpl. You can either generate some more code to perform the wrapping or simply call the constructor of the code expected to be generated.

I'm not really sure what your question is, but I hope this sheds some light on your issues.

like image 186
John Ericksen Avatar answered Oct 15 '22 18:10

John Ericksen