Java annotation processing with source code manipulation
Asked Answered
F

3

18

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

Filial answered 12/2, 2013 at 7:32 Comment(1)
This annotation library modifies the source code by initializing the value of a string field based on the comment above it. Maybe you can use that as an example.Tabasco
B
5

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"

Brufsky answered 18/2, 2013 at 19:13 Comment(2)
web.archive.org/web/20100410203408/http://blogs.sun.com/darcy/…Guddle
"The standard annotation processing API does not support direct modification of source code." +1Seemaseeming
O
3

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.

Oneway answered 26/9, 2014 at 18:58 Comment(1)
It's not either "Editing the compiler tree" or "Supplying the tree to compiler(or AST)" at all, also project Lombok does code exploit to Token Arrays and resupplying to Parser for modifying the AST.Seemaseeming
A
2

I would suggest you copy all of the source code to a separate directory, modify the code there and build from the temporary path.

Acknowledge answered 18/2, 2013 at 19:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.