square / javapoet

A Java API for generating .java source files.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Getting a CodeBlock from an AnnotationValue for an enum constant

cushon opened this issue · comments

JDK-8164819 changed the toString() implementation of some AnnotationValue implementations to match core reflection.

For AnnotationValues representing an enum constant, the toString() is now the simple name instead of a fully qualified name. I encountered some code that using CodeBlock.of("$L", annotationValue), which ends up just calling toString(), which now results in an unqualified simple name of the enum constant.

Is there already a better way to convert an AnnotationValue to a CodeBlock? I couldn't find one. There's some logic in AnnotationSpec that's close to what I want, but the only entry point takes an entire AnnotationMirror:

@Override public Builder visitEnumConstant(VariableElement c, String name) {
return builder.addMember(name, "$T.$L", c.asType(), c.getSimpleName());
}

Demo:

import com.squareup.javapoet.CodeBlock;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedAnnotationTypes("*")
public class P extends AbstractProcessor {

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
  }

  enum E {
    ONE;
  }

  @interface A {

    E value() default E.ONE;
  }

  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    TypeElement typeElement = processingEnv.getElementUtils()
        .getTypeElement("P.A");
    for (Element member : typeElement.getEnclosedElements()) {
      if (member.getSimpleName().contentEquals("value")) {
        AnnotationValue av = ((ExecutableElement) member).getDefaultValue();
        processingEnv.getMessager().printMessage(Kind.NOTE, CodeBlock.of("$L", av).toString());
      }
    }
    return false;
  }
}

With JDK 11, CodeBlock.of("$L", av) is the qualified name P.E.ONE:

$ javac -version  -processorpath .:javapoet-1.13.0.jar -processor P -cp javapoet-1.13.0.jar P.java
javac 11.0.11
Note: P.E.ONE
Note: P.E.ONE

With JDK 17, it's just ONE:

$ javac -version  -processorpath .:javapoet-1.13.0.jar -processor P -cp javapoet-1.13.0.jar P.java
javac 17-ea
Note: ONE
Note: ONE

A few updates

  • I ran into this in some more processors, and the incompatible change in javac is now being tracked by JDK-8268729
  • I'm looking at adding a helpers to auto-common to convert AnnotationValue to a more useful string
  • It might still be nice to have support in JavaPoet for converting AnnotationValues into source

I'm working on this issue and will try to fix it.