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

1
  • 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. Commented Feb 20, 2014 at 21:31

3 Answers 3

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"

Sign up to request clarification or add additional context in comments.

2 Comments

"The standard annotation processing API does not support direct modification of source code." +1
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.

1 Comment

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.
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.