Annotation Type AnnotationCollector


@Documented @Retention(RUNTIME) @Target({ANNOTATION_TYPE,TYPE}) public @interface AnnotationCollector
The AnnotationCollector can be used to define aliases for annotations and attributes. The alias needs to be a class or annotation annotated with AnnotationCollector, otherwise nothing is required. The alias will be replaced on the AST level and will never appear in later. Any members of the class or annotation will be ignored, but could be used by a custom processor. Annotation arguments are mapped to the aliased annotations if existing. Should the default processor not be able to map one of the arguments and error will be given. Is this not wished or if you want a different mapping a custom processor has to be used. There are two ways of using the alias. The first way is by providing the annotations as list/array:
     import groovy.transform.*
     @AnnotationCollector([EqualsAndHashCode, ToString, Immutable])
     @interface Alias { }

     @Alias(excludes=["a"])
     class Foo {
         Integer a, b
     }
     assert Foo.class.annotations.size() == 1 // KnownImmutable
     assert new Foo(a: 1, b: 2).toString() == "Foo(2)"
 
In the example above we have Alias as the alias annotation and an argument excludes which will be mapped to EqualsAndHashCode and ToString. Immutable doesn't have excludes, thus nothing will be done there.
The other way is to add annotations to the alias:
     import groovy.transform.*
     @ToString(excludes=["a"])
     @EqualsAndHashCode
     @Immutable
     @AnnotationCollector
     @interface Alias { }

     @Alias
     class Foo {
         Integer a, b
     }
     assert Foo.class.annotations.size() == 1 // KnownImmutable
     assert new Foo(a: 1, b: 2).toString() == "Foo(2)"
 
In the example above we have again Alias as the alias annotation, but this time the argument is part of the alias. Instead of mapping excludes to ToString as well as EqualsAndHashCode, only ToString will have the excludes. Again the alias can have an argument excludes, which would overwrite the excludes given in from the definition and be mapped to ToString as well as EqualsAndHashCode. If both ways are combined, then the list overwrites annotation usage. NOTE: The aliasing does not support aliasing of aliased annotations.

More examples:

     import groovy.transform.*
     @AnnotationCollector([EqualsAndHashCode, ToString])
     @interface Simple { }

     @Simple
     class User {
         String username
         int age
     }

     // no runtime annotations:
     assert User.class.annotations.size() == 0

     def user = new User(username: 'mrhaki', age: 39)
     assert user.toString() == 'User(mrhaki, 39)'

     // We can use the attributes from the grouped annotations.
     @Simple(excludes = 'street')
     class Address {
         String street, town
     }

     def address = new Address(street: 'Evergreen Terrace', town: 'Springfield')
     assert address.toString() == 'Address(Springfield)'
 
     // Use a custom processor to handle attributes.
     import org.codehaus.groovy.transform.*
     import org.codehaus.groovy.ast.*
     import org.codehaus.groovy.control.*

     class SimpleProcessor extends AnnotationCollectorTransform {
         public List<AnnotationNode> visit(AnnotationNode collector,
                                           AnnotationNode aliasAnnotationUsage,
                                           AnnotatedNode aliasAnnotated,
                                           SourceUnit source) {
             // Get attributes and attribute value for dontUse.
             def attributes = aliasAnnotationUsage.getMembers()
             def dontUse = attributes.get('dontUse')
             attributes.remove('dontUse')

             if (dontUse) {
                 // Assign value of dontUse to excludes attributes.
                 aliasAnnotationUsage.addMember("excludes", dontUse)
             }

             super.visit(collector, aliasAnnotationUsage, aliasAnnotated, source)
         }
     }

     new GroovyShell(this.class.classLoader).evaluate '''
     import groovy.transform.*

     @AnnotationCollector(value = [EqualsAndHashCode, ToString], processor = 'SimpleProcessor')
     @interface Simple { }

     @Simple(dontUse = 'age')
     class User {
         String username
         int age
     }

     def user = new User(username: 'mrhaki', age: 39)
     assert user.toString() == 'User(mrhaki)'
     '''
 
     // Use AnnotationCollector last to group the previous annotations.
     import groovy.transform.*
     @EqualsAndHashCode
     @ToString
     @AnnotationCollector
     @interface Simple {}

     @Simple
     class User {
         String username
     }

     def user = new User(username: 'mrhaki')
     assert user.toString() == 'User(mrhaki)'
 
Since:
2.1.0
See Also:
  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    When the collector annotation is replaced, whether to check for duplicates between the replacement annotations and existing explicit annotations.
    Processor used for computing custom logic or the list of annotations, or both.
    Used internally - the default value is a marker value indicating that the attribute hasn't been set.
    List of aliased annotations.
  • Element Details

    • processor

      String processor
      Processor used for computing custom logic or the list of annotations, or both. The default is org.codehaus.groovy.transform.AnnotationCollectorTransform. Custom processors need to extend that class.
      Default:
      "org.codehaus.groovy.transform.AnnotationCollectorTransform"
    • mode

      When the collector annotation is replaced, whether to check for duplicates between the replacement annotations and existing explicit annotations. If you use a custom processor, it is up to that processor whether it honors or ignores this parameter. The default processor honors the parameter.
      Default:
      DUPLICATE
    • value

      Class[] value
      List of aliased annotations.
      Default:
      {}
    • serializeClass

      Class serializeClass
      Used internally - the default value is a marker value indicating that the attribute hasn't been set. Normally set automatically during annotation processing to an automatically created nested helper class which holds serialization information used in pre-compiled scenarios. If set to the collector annotation, re-purposes the annotation itself to become the helper class (legacy approach used in Groovy 2.5 up to 2.5.2).
      Default:
      groovy.transform.Undefined.CLASS.class