Class JavaAnnotation<OWNER extends HasDescription>

  • Type Parameters:
    OWNER - The type of the closest "parent" of this annotation. If this annotation is annotated on a class or member, it is that class or member. If this annotation is a member of another annotation, it is that annotation.
    All Implemented Interfaces:
    HasDescription, HasOwner<OWNER>, HasType

    public final class JavaAnnotation<OWNER extends HasDescription>
    extends java.lang.Object
    implements HasType, HasOwner<OWNER>, HasDescription
    Represents an imported annotation on an annotated object like a class or a method. To be independent of the classpath, all properties of this annotation are just stored as a simple key value pairs. I.e. if you consider
    
      @MyAnnotation(name = "some name", anAttribute = 7)
      class MyClass {}
     
    this annotation will be imported storing the association
    
       name → "some name"
       anAttribute → 7
     
    Properties will be made available via get(String), e.g.
    
       myAnnotation.get("anAttribute")
     
    will return the value 7. Since this behavior is inconvenient (loss of type safety), there is another approach to retrieve these values, if the annotation can be resolved on the classpath. It's then possible to access a simple proxy
    
       MyAnnotation moreConvenient = myAnnotation.as(MyAnnotation.class);
       moreConvenient.anAttribute(); // → returns 7
     
    ----------
    NOTE
    ----------
    ArchUnit holds the annotation in a classpath independent representation, i.e. some types will be mapped, when the access is proxied. Consider
    
      @SomeAnnotation(type = String.class)
      class MyClass {}
     
    Accesses to 'type' will be different for the proxied version:
    
       someAnnotation.get("type"); // → returns JavaClass{String}
       someAnnotation.as(SomeAnnotation.class).type(); // → returns String.class
     
    • Method Detail

      • getAnnotatedElement

        @PublicAPI(usage=ACCESS)
        public CanBeAnnotated getAnnotatedElement()
        Returns either the element annotated with this JavaAnnotation (a class or member) or in case this annotation is an annotation parameter, the element annotated with an annotation that transitively declares this annotation as an annotation parameter.

        Example:
        
         (1)@SomeAnnotation class SomeClass {}
         (2) class SomeClass {
                @SomeAnnotation SomeField someField;
             }
         (3)@ComplexAnnotation(param = @SomeAnnotation) class SomeClass {}
         
        For case (1) the result of someAnnotation.getAnnotatedElement() would be SomeClass, for case (2) the result of someAnnotation.getAnnotatedElement() would be someField and for (3) the result of someAnnotation.getAnnotatedElement() would also be SomeClass, even though @SomeAnnotation is a parameter of @ComplexAnnotation.
        Returns:
        The closest element traversing up the tree, that can be annotated
      • get

        @PublicAPI(usage=ACCESS)
        public Optional<java.lang.Object> get​(java.lang.String property)
        Returns the value of the property with the given name, i.e. the result of the method with the property name of the represented Annotation. E.g. for
              @SomeAnnotation(value = "someString", types = {SomeType.class, AnotherType.class})
         class SomeAnnotatedClass {}
         
        the results will be
               someAnnotation.get("value") → "someString"
         someAnnotation.get("types") → [JavaClass{SomeType}, JavaClass{AnotherType}]
         
        Parameters:
        property - The name of the annotation property, i.e. the declared method name
        Returns:
        the value of the given property, where the result type is more precisely
        • Class<?> → JavaClass{clazz}
        • Class<?>[] → [JavaClass{clazz},...]
        • Enum → JavaEnumConstant
        • Enum[] → [JavaEnumConstant,...]
        • Annotation → JavaAnnotation
        • Annotation[] → [JavaAnnotation,...]
        • anyOtherType → anyOtherType
      • getProperties

        @PublicAPI(usage=ACCESS)
        public java.util.Map<java.lang.String,​java.lang.Object> getProperties()
        Returns:
        a map containing all [property → value], where each value correlates to get(String property)
      • accept

        @PublicAPI(usage=ACCESS)
        public void accept​(JavaAnnotation.ParameterVisitor parameterVisitor)
        Simple implementation of the Visitor pattern (compare e.g. https://en.wikipedia.org/wiki/Visitor_pattern).

        While it is fairly convenient to analyse a JavaAnnotation that is on the classpath (e.g. by using as(Class)), it is quite tedious to do so for a JavaAnnotation not on the classpath (i.e. via getProperties()).

        accept(ParameterVisitor) offers an alternative by taking away traversal and casting logic when analysing potentially unknown JavaAnnotations of different parameter structures.
        Whether using this method or performing casting operations on getProperties() might depend on the use case. For a known annotation where only a single known parameter is relevant, a solution like

        
         String value = (String) knownJavaAnnotation.get("value").get()
         
        might be completely sufficient. However an analysis like "all class parameters of all annotations in package foo.bar should implement a certain interface" (or potentially nested annotations), this might not be an easily readable approach. accept(ParameterVisitor) makes this use case fairly trivial:

        
         unknownJavaAnnotation.accept(new DefaultParameterVisitor() {
            @Override
             public void visitClass(String propertyName, JavaClass javaClass) {
                 // do whatever check on the class parameter javaClass
             }
         });
        Furthermore JavaAnnotation.ParameterVisitor does exactly specify which cases can occur for JavaAnnotation parameters without the need to introspect and cast the values. In case traversal into nested JavaAnnotations is necessary, this also becomes quite simple:

        
         unknownJavaAnnotation.accept(new DefaultParameterVisitor() {
             // parameter handling logic
        
            @Override
             public void visitAnnotation(String propertyName, JavaAnnotation<?> nestedAnnotation) {
                 nestedAnnotation.accept(this);
             }
         });
        Parameters:
        parameterVisitor - A visitor which allows to implement behavior for different types of annotation parameters
      • as

        @PublicAPI(usage=ACCESS)
        public <A extends java.lang.annotation.Annotation> A as​(java.lang.Class<A> annotationType)
        Returns a compile safe proxied version of the respective JavaAnnotation. In other words, the result of as(MyAnnotation.class) will be of type MyAnnotation and allow property access in a compile safe manner. For this to work the respective Annotation including all referred parameter types must be on the classpath or an Exception will be thrown. Furthermore the respective JavaAnnotation must actually be an import of the passed parameter annotationType or a RuntimeException will likely occur.
        Type Parameters:
        A - The type of the imported Annotation backing this JavaAnnotation
        Parameters:
        annotationType - Any type implementing Annotation
        Returns:
        A compile safe proxy of type JavaAnnotation
      • toString

        public java.lang.String toString()
        Overrides:
        toString in class java.lang.Object