Class JavaAnnotation<OWNER extends HasDescription>

java.lang.Object
com.tngtech.archunit.core.domain.JavaAnnotation<OWNER>
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

@PublicAPI(usage=ACCESS) public final class JavaAnnotation<OWNER extends HasDescription> extends 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 Details

    • getType

      public JavaType getType()
      Specified by:
      getType in interface HasType
    • getRawType

      @PublicAPI(usage=ACCESS) public JavaClass getRawType()
      Specified by:
      getRawType in interface HasType
    • getOwner

      public OWNER getOwner()
      Compare documentation of OWNER on JavaAnnotation
      Specified by:
      getOwner in interface HasOwner<OWNER extends HasDescription>
      Returns:
      The "owner" of this object, compare HasOwner
    • 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<Object> get(String propertyName)
      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}]
       
      This method will return an explicitly declared property, or the default value for the given property as a fallback.
      Parameters:
      propertyName - 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
    • hasExplicitlyDeclaredProperty

      @PublicAPI(usage=ACCESS) public boolean hasExplicitlyDeclaredProperty(String propertyName)
      Returns true, if and only if the value of this property has been explicitly declared within the annotation declaration. E.g.

      
        @SomeAnnotation(prop = "explicitly declared")
         class SomeClass {}
       
      The opposite is a non-explicitly declared property, e.g.

      
        @interface SomeAnnotationWithDefault {
             String prop() default "default";
         }
      
        @SomeAnnotationWithDefault
         class SomeClass {}
       
      Where the property prop has the implicit default value "default", but no explicitly declared value.
      Parameters:
      propertyName - The name of the annotation property, i.e. the declared method name
      Returns:
      true, if and only if the value of this property has been explicitly declared
    • getExplicitlyDeclaredProperty

      @PublicAPI(usage=ACCESS) public Object getExplicitlyDeclaredProperty(String propertyName)
      Parameters:
      propertyName - The name of the annotation property, i.e. the declared method name
      Returns:
      the same value as get(String), if the property is explicitly declared (compare hasExplicitlyDeclaredProperty(String));
      throws an exception if the property has not been explicitly declared (i.e. falls back to a default value)
    • tryGetExplicitlyDeclaredProperty

      @PublicAPI(usage=ACCESS) public Optional<Object> tryGetExplicitlyDeclaredProperty(String propertyName)
      Parameters:
      propertyName - The name of the annotation property, i.e. the declared method name
      Returns:
      the same value as get(String), if the property is explicitly declared (compare hasExplicitlyDeclaredProperty(String)), otherwise Optional.empty()
    • getProperties

      @PublicAPI(usage=ACCESS) public Map<String,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 Annotation> A as(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
    • getDescription

      public String getDescription()
      Specified by:
      getDescription in interface HasDescription
    • toString

      public String toString()
      Overrides:
      toString in class Object