content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  import java.lang.annotation.Annotation;
  import java.lang.reflect.Method;
  
  public class AnnotationTest extends AnnotationTestHelpers {
    public static void testAnnotationsByType() {
      System.out.println("==============================");
      System.out.println("Class annotations by type:");
      System.out.println("==============================");
  
      // Print associated annotations:
      // * A is directly present or repeatably present on an element E;
      // * No annotation of A is directly/repeatably present on an element
      //   AND E is a class AND A's type is inheritable, AND A is associated with its superclass.
      // (Looks through subtypes recursively only if there's 0 result at each level,
      // and the annotation is @Inheritable).
      printAnnotationsByType(Calendar.class, SingleUser.class);
      printAnnotationsByType(Calendars.class, SingleUser.class);
  
      printAnnotationsByType(Calendar.class, User.class);
      printAnnotationsByType(Calendars.class, User.class);
  
      printAnnotationsByType(Calendar.class, User2.class);  // Enforce ordering 'z,x,y'
      printAnnotationsByType(Calendars.class, User2.class);
  
      // NOTE:
      //    Order of outer-most annotations Calendars[C,C],S vs C,Calendars[C,C] is unspecified.
      //    In particular it's the order of #getDeclaredAnnotations which is completely unmentioned.
      //    The only requirement for #getAnnotationsByType is to have same ordering as
      //    #getDeclaredAnnotations.
      //    (Calendars[] itself has to maintain value() order).
      printAnnotationsByType(Calendar.class, UserComplex.class);  // Cs(C,C),C collapses into C,C,C.
      printAnnotationsByType(Calendars.class, UserComplex.class);
  
      printAnnotationsByType(Calendar.class, UserSub.class);
      printAnnotationsByType(Calendars.class, UserSub.class);
  
      printAnnotationsByType(Calendar.class, UserSub2.class);
      // The directly present "Calendar" annotation masks all the repeatably present
      // "Calendar" annotations coming from User.
      printAnnotationsByType(Calendars.class, UserSub2.class);
      // Edge case: UserSub2 doesn't directly have a Calendars annotation,
      // so it doesn't mask the "User" Calendars annotation.
  
      System.out.println("-----------------------------");
      System.out.println("-----------------------------");
  
    }
  
    public static void testDeclaredAnnotation() {
      System.out.println("==============================");
      System.out.println("Class declared annotation:");
      System.out.println("==============================");
  
      // Print directly present annotations:
      //
      // The element E has an annotation_item for it (accessible through an
      // annotations_directory_item) corresponding to an annotation A,
      // and A's type_idx must match that on the encoded_annotation (from the annotation_item).
      // (Does not look through the subtypes recursively)
      printDeclaredAnnotation(SingleUser.class, Calendar.class);
      printDeclaredAnnotation(SingleUser.class, Calendars.class);
  
      printDeclaredAnnotation(User.class, Calendar.class);
      printDeclaredAnnotation(User.class, Calendars.class);
  
      printDeclaredAnnotation(UserComplex.class, Calendar.class);
      printDeclaredAnnotation(UserComplex.class, Calendars.class);
  
      printDeclaredAnnotation(UserSub.class, Calendar.class);
      printDeclaredAnnotation(UserSub.class, Calendars.class);
  
      printDeclaredAnnotation(UserSub2.class, Calendar.class);
      printDeclaredAnnotation(UserSub2.class, Calendars.class);
  
      System.out.println("-----------------------------");
      System.out.println("-----------------------------");
    }
  
    public static void testDeclaredAnnotationsByType() {
      System.out.println("==============================");
      System.out.println("Declared class annotations by type:");
      System.out.println("==============================");
  
      // A is directly present or repeatably present on an element E;
      // -- (does not do any recursion for classes regardless of @Inherited)
      printDeclaredAnnotationsByType(Calendar.class, SingleUser.class);
      printDeclaredAnnotationsByType(Calendars.class, SingleUser.class);
  
      printDeclaredAnnotationsByType(Calendar.class, User.class);
      printDeclaredAnnotationsByType(Calendars.class, User.class);
  
      printDeclaredAnnotationsByType(Calendar.class, User2.class);  // Enforce ordering 'z,x,y'
      printDeclaredAnnotationsByType(Calendars.class, User2.class);
  
      printDeclaredAnnotationsByType(Calendar.class, UserComplex.class);
      printDeclaredAnnotationsByType(Calendars.class, UserComplex.class);
  
      printDeclaredAnnotationsByType(Calendar.class, UserSub.class);
      printDeclaredAnnotationsByType(Calendars.class, UserSub.class);
  
      printDeclaredAnnotationsByType(Calendar.class, UserSub2.class);
      // The directly present "Calendar" annotation masks all the repeatably present "Calendar"
      // annotations coming from User.
      printDeclaredAnnotationsByType(Calendars.class, UserSub2.class);
      // Edge case: UserSub2 doesn't directly have a Calendars annotation,
      // so it doesn't mask the "User" Calendars annotation.
  
      System.out.println("-----------------------------");
      System.out.println("-----------------------------");
    }
  
    // Print the annotation "annotationClass" that is associated with an element denoted by
    // "annotationUseClass."
    private static <A extends Annotation> void printAnnotationsByType(Class<A> annotationClass,
        Class<?> annotationUseClass) {
      A[] annotationsByType = annotationUseClass.getAnnotationsByType(annotationClass);
  
      String msg = "Annotations by type, defined by class "
          + annotationUseClass.getName() + " with annotation " + annotationClass.getName() + ": "
          + asString(annotationsByType);
  
  
      System.out.println(msg);
    }
  
    private static <A extends Annotation> void printDeclaredAnnotation(Class<?> annotationUseClass,
        Class<A> annotationDefClass) {
      A anno = annotationUseClass.getDeclaredAnnotation(annotationDefClass);
  
      String msg = asString(anno);
  
      System.out.println("Declared annotations by class " + annotationUseClass
          + ", annotation " + annotationDefClass + ": " + msg);
    }
  
    // Print the annotation "annotationClass" that is directly/indirectly present with an element
    // denoted by "annotationUseClass."
    private static <A extends Annotation> void printDeclaredAnnotationsByType(
        Class<A> annotationClass, Class<?> annotationUseClass) {
      A[] annotationsByType = annotationUseClass.getDeclaredAnnotationsByType(annotationClass);
  
      String msg = "Declared annnotations by type, defined by class " + annotationUseClass.getName()
          + " with annotation " + annotationClass.getName() + ": "
          + asString(annotationsByType);
  
      System.out.println(msg);
    }
  
    public static void testMethodAnnotationsByType() {
      System.out.println("==============================");
      System.out.println("Method annotations by type:");
      System.out.println("==============================");
  
      // Print associated annotations:
      // * A is directly present or repeatably present on an element E;
      // * No annotation of A is directly/repeatably present on an element AND E is a class
      //   AND A's type is inheritable, AND A is associated with its superclass.
      // (Looks through subtypes recursively only if there's 0 result at each level,
      // and the annotation is @Inheritable).
      printMethodAnnotationsByType(Calendar.class, "singleUser", AnnotationTestFixture.class);
      printMethodAnnotationsByType(Calendars.class, "singleUser", AnnotationTestFixture.class);
  
      printMethodAnnotationsByType(Calendar.class, "user", AnnotationTestFixture.class);
      printMethodAnnotationsByType(Calendars.class, "user", AnnotationTestFixture.class);
  
      printMethodAnnotationsByType(Calendar.class, "user2", AnnotationTestFixture.class);
      printMethodAnnotationsByType(Calendars.class, "user2", AnnotationTestFixture.class);
  
      printMethodAnnotationsByType(Calendar.class, "userComplex", AnnotationTestFixture.class);
      printMethodAnnotationsByType(Calendars.class, "userComplex", AnnotationTestFixture.class);
  
      System.out.println("-----------------------------");
      System.out.println("-----------------------------");
    }
  
    // Print the annotation "annotationClass" that is associated with an element denoted by
    // "annotationUseClass" method methodName.
    private static <A extends Annotation> void printMethodAnnotationsByType(Class<A> annotationClass,
        String methodName, Class<?> annotationUseClass) {
      Method m = null;
      try {
        m = annotationUseClass.getDeclaredMethod(methodName);
      } catch (Throwable t) {
        throw new AssertionError(t);
      }
      A[] annotationsByType = m.getAnnotationsByType(annotationClass);
  
      String msg = "Annotations by type, defined by method " + m.getName() + " with annotation " +
        annotationClass.getName() + ": " +
        asString(annotationsByType);
  
      System.out.println(msg);
    }
  
    public static void testMethodDeclaredAnnotations() {
      System.out.println("==============================");
      System.out.println("Declared method annotations:");
      System.out.println("==============================");
  
      printMethodDeclaredAnnotation(Calendar.class, "singleUser", AnnotationTestFixture.class);
      printMethodDeclaredAnnotation(Calendars.class, "singleUser", AnnotationTestFixture.class);
  
      printMethodDeclaredAnnotation(Calendar.class, "user", AnnotationTestFixture.class);
      printMethodDeclaredAnnotation(Calendars.class, "user", AnnotationTestFixture.class);
  
      printMethodDeclaredAnnotation(Calendar.class, "user2", AnnotationTestFixture.class);
      printMethodDeclaredAnnotation(Calendars.class, "user2", AnnotationTestFixture.class);
  
      printMethodDeclaredAnnotation(Calendar.class, "userComplex", AnnotationTestFixture.class);
      printMethodDeclaredAnnotation(Calendars.class, "userComplex", AnnotationTestFixture.class);
  
      System.out.println("-----------------------------");
      System.out.println("-----------------------------");
    }
  
    // Print the annotation "annotationClass" that is associated with an element denoted by
    // methodName in annotationUseClass.
    private static <A extends Annotation> void printMethodDeclaredAnnotation(Class<A> annotationClass,
        String methodName, Class<?> annotationUseClass) {
      Method m = null;
      try {
        m = annotationUseClass.getDeclaredMethod(methodName);
      } catch (Throwable t) {
        throw new AssertionError(t);
      }
      Annotation annotationsByType = m.getDeclaredAnnotation(annotationClass);
  
      String msg = "Annotations declared by method " + m.getName() + " with annotation "
          + annotationClass.getName() + ": "
          + asString(annotationsByType);
  
      System.out.println(msg);
    }
  
    public static void testMethodDeclaredAnnotationsByType() {
      System.out.println("==============================");
      System.out.println("Declared method annotations by type:");
      System.out.println("==============================");
  
      printMethodDeclaredAnnotationByType(Calendar.class, "singleUser", AnnotationTestFixture.class);
      printMethodDeclaredAnnotationByType(Calendars.class, "singleUser", AnnotationTestFixture.class);
  
      printMethodDeclaredAnnotationByType(Calendar.class, "user", AnnotationTestFixture.class);
      printMethodDeclaredAnnotationByType(Calendars.class, "user", AnnotationTestFixture.class);
  
      printMethodDeclaredAnnotationByType(Calendar.class, "user2", AnnotationTestFixture.class);
      printMethodDeclaredAnnotationByType(Calendars.class, "user2", AnnotationTestFixture.class);
  
      printMethodDeclaredAnnotationByType(Calendar.class, "userComplex", AnnotationTestFixture.class);
      printMethodDeclaredAnnotationByType(Calendars.class, "userComplex",
          AnnotationTestFixture.class);
  
      System.out.println("-----------------------------");
      System.out.println("-----------------------------");
    }
  
    // Print the annotation "annotationClass" that is associated with an element denoted by
    // methodName in annotationUseClass.
    private static <A extends Annotation> void printMethodDeclaredAnnotationByType(
        Class<A> annotationClass, String methodName, Class<?> annotationUseClass) {
      Method m = null;
      try {
        m = annotationUseClass.getDeclaredMethod(methodName);
      } catch (Throwable t) {
        throw new AssertionError(t);
      }
      A[] annotationsByType = m.getDeclaredAnnotationsByType(annotationClass);
  
      String msg = "Annotations by type, defined by method " + m.getName() + " with annotation "
          + annotationClass.getName() + ": "
          + asString(annotationsByType);
  
      System.out.println(msg);
    }
  }
  