Search code examples
javaannotations

How to allow an annotation on a method annotated by other specific annotation?


I have created following 2 annotations.

  • @TestSuite
  • @TestCase

I want to allow users to use @TestCase annotation only on the methods which are annotated with @Test. What @Target should I use on TestCase?

@Test
@TestCase(id="") ---> This should be allowed only with @Test
public void testAnnotation() {}

Solution

  • What you want is not possible, not in that way. The @Target annotations specify only what kind of element you can put the annotation on; this system isn't 'pluggable' nor programmable. It's just the enum options. You can't expand on that.

    What you can do, is write an AnnotationProcessor that runs along with your compilation process, triggers on any occurrence of @TestCase, check if that method is also annotated with @Test. If not, emit a compilation error. That you can do.

    The IDE will not know anything is wrong until it runs the AP. That means:

    • The IDE will suggest @TestCase in e.g. autocomplete dialogs even when it wouldn't be legal. Because it doesn't know it wouldn't be legal.
    • The IDE won't red-wavy-underline it until the AP runs, and that might take a while, especially on non-eclipse IDEs which aren't nearly as good at just-in-time building as of writing; they pretty much just run the build system eventually and defer to it to produce these errors. That means you definitely won't see red wavy underlines as you type it, and often not even when you save it.

    Writing APs is not trivial; you'd have to search the web for a tutorial and go through it.

    Note that these days you will have to edit your build; in general the build turns into a complex beast (you'd first have to build the annotation processor as separate artefact, then compile the main project with the annotation processor, which very very very strongly want to be in jar form, not as raw source or as class files). In standard maven dir structure parlance, the annotation processor would not be in src/main nor in src/test; maybe src/testProcessor or whatnot. If you aren't an expert in build systems you may just want to forego all this and make it an entirely separate internal project that you pull in as dependency in your main project.