0

I have a working annotation processor that gathers information of the annotated classes. Everything is there during compilation. But I would like to have access to those results during runtime.

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class TestProcessor extends AbstractProcessor {

    private final static List<TestInfo> tests = new ArrayList<>();

    @Override
    public Set getSupportedAnnotationTypes() {
        return new LinkedHashSet() {
            {
                add(Annotation.class.getCanonicalName());
            }
        };
    }

    @Override
    public boolean process(final Set<? extends TypeElement> annotations,
            final RoundEnvironment env) {
        System.out.println("Processing!");
        if (!env.processingOver()) {
            Set<? extends Element> rootE = env.getRootElements();
            for (Element e : rootE) {
                if (e.getKind() == ElementKind.CLASS) {
                    TestInfo t = new TestInfo(e.asType().toString());
                    for (Element se : e.getEnclosedElements()) {
                        if (se.getKind() == ElementKind.METHOD) {
                            t.addMethod(se.getSimpleName().toString());
                        }
                    }
                    getTests().add(t);
                }
            }
            getTests().forEach(ti -> {
                System.out.println(ti);
            });
        }
        return false;
    }

    public static TypeElement findEnclosingTypeElement(Element e) {

        while (e != null && !(e instanceof TypeElement)) {
            e = e.getEnclosingElement();
        }

        return TypeElement.class.cast(e);
    }

    /**
     * @return the tests
     */
    public static List<TestInfo> getTests() {
        return tests;
    }
}

Is there a way to retrieve the results at runtime? TestProcessor.getTests returns an empty list.

Here's the TestInfo class fyi:

public class TestInfo {

    private final String name;
    private final List<String> methods = new ArrayList<>();

    public TestInfo(String name) {
        this.name = name;
    }

    public void addMethod(String m) {
        getMethods().add(m);
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @return the methods
     */
    public List<String> getMethods() {
        return methods;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(name).append(methods.toString());
        return sb.toString();
    }
}

Update: The annotation is marked with retention runtime.

2 Answers 2

1

The annotation-processing is in compile time. So you can't get the information in the runtime.

A direct way is to write the information as a resource file in compile time and read it at runtime.

Here is my example:

The annotation:

@Retention(SOURCE)
@Target(TYPE)
public @interface Anno {
}

The processor:

  @Override
  public boolean processActual(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (roundEnv.processingOver()) {
      return false;
    }
    try {
      write(roundEnv);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return false;
  }

  private void write(RoundEnvironment roundEnv) throws IOException, UnsupportedEncodingException {
    Filer filer = processingEnv.getFiler();
    FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT, "", "TestInfo");
    OutputStream output = resource.openOutputStream();
    PrintStream writer = new PrintStream(output, false, "UTF-8");
    roundEnv.getElementsAnnotatedWith(Anno.class)
        .stream()
        .filter(e -> e.getKind() == ElementKind.CLASS)
        .map(e -> e.asType().toString())
        .forEach(writer::println);
    writer.flush();
  }

And the user code:

@Anno
public class Q48177784 {
  public static final List<Class<?>> CLASSES;
  static {
    try {
      URL resource = Q48177784.class.getClassLoader().getResource("TestInfo");
      CLASSES = Files.readAllLines(Paths.get(resource.toURI()))
          .stream()
          .map(s -> {
            try {
              return Class.forName(s);
            } catch (ClassNotFoundException e) {
              throw new Error(e);
            }
          })
          .collect(Collectors.toList());
    } catch (Exception e) {
      throw new Error(e);
    }
  }

  public static void main(String[] args) {
    System.out.println(CLASSES);
  }
}

After build with processor, run the main method:

[class xdean.stackoverflow.Q48177784]

For your case, the only thing you should do is serialize/deserialize your TestInfo

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

1 Comment

Excellent! This was exactly a valid option for me. I didn't have control over the annotation so changing it to source was not an option. I was able to use the write to file approach as a workaround.
1

Check out @RetentionPolicy. I think you want to set it to RUNTIME.

2 Comments

The annotation is set up for RUNTIME. Forgot to add that.
RUNTIME is not necessary for this question. The principle of RetentionPolicy is like AcessLevel(public/private), to use the lowest level as you can.

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.