Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java annotation processing with source code manipulation

I have been looking for a solution for below requirement -

  • Source files are written with Custom Annotation on a method
  • Method body needs a little variation based on the annotation.
  • Source file should not be changed, but input to compiler should be modified source file

I have looked at below APIs -

  • javax.annotation.processing - Annotation processing.
  • javax.lang.model.* - Language model used in annotation processing and Compiler Tree API
  • com.sun.source.* - Compiler Tree API.

I thought of designing this by following :

  1. Write an annotation processor
  2. Generate the compiler tree
  3. Edit the compiler tree at runtime without affecting origional source file
  4. Supply the tree to compiler

Compiler Tree API appears to be promissing where it gives access to com.sun.source.tree.MethodTree

However compiler Tree API appears to be Read Only. I can not figure out how to acomplish the steps 3 & 4

Is there any API for this which I can adopt to acomplish the task

NOTE: I am looking for only Source Code manipulation technique. No runtime byte code manipulation / AOP

Environment: Java 6

like image 743
user1128134 Avatar asked Feb 12 '13 07:02

user1128134


2 Answers

The standard annotation processing API does not support direct modification of source code. However, some of the effects of modifying source code can be had by generating either the superclass or subclass(es) of the annotated type. The blog entry below shows an example of this technique:

"Properties via Annotation Processing"

like image 123
Joe Darcy Avatar answered Oct 23 '22 14:10

Joe Darcy


You can do this as something below which will let you accomplish 3) and 4).

Example taken from java annotation processor example

@SupportedAnnotationTypes( "com.javacodegeeks.advanced.processor.Immutable" )
@SupportedSourceVersion( SourceVersion.RELEASE_7 )
public class SimpleAnnotationProcessor extends AbstractProcessor {
  @Override
  public boolean process(final Set< ? extends TypeElement > annotations, 
      final RoundEnvironment roundEnv) {

    for( final Element element: roundEnv.getElementsAnnotatedWith( Immutable.class ) ) {
      if( element instanceof TypeElement ) {
        final TypeElement typeElement = ( TypeElement )element;

        for( final Element eclosedElement: typeElement.getEnclosedElements() ) {
       if( eclosedElement instanceof VariableElement ) {
           final VariableElement variableElement = ( VariableElement )eclosedElement;

           if( !variableElement.getModifiers().contains( Modifier.FINAL ) ) {
             processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR,
               String.format( "Class '%s' is annotated as @Immutable, 
                 but field '%s' is not declared as final", 
                 typeElement.getSimpleName(), variableElement.getSimpleName()            
               ) 
             );                     
           }
         }
       }
    }

    // Claiming that annotations have been processed by this processor 
    return true;
  }
}

Another way using projectlombok with custom handler.

Example built in handler from GitHub Project Lombok. This annotation adds try catch block

public class SneakyThrowsExample implements Runnable {
    @SneakyThrows(UnsupportedEncodingException.class)
    public String utf8ToString(byte[] bytes) {
        return new String(bytes, "UTF-8");
    }

    @SneakyThrows
    public void run() {
        throw new Throwable();
    }
}

This gets processed to

public String utf8ToString(byte[] bytes) {
    try {
        return new String(bytes, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        throw Lombok.sneakyThrow(e);
    }
}

public void run() {
    try {
        throw new Throwable();
    } catch (Throwable t) {
        throw Lombok.sneakyThrow(t);
    }
}

You can find the Handler code on the same Github/lombok site.

like image 3
bhantol Avatar answered Oct 23 '22 16:10

bhantol