Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.signature.cts;
     17 
     18 import java.lang.annotation.Annotation;
     19 import java.lang.reflect.Constructor;
     20 import java.lang.reflect.Field;
     21 import java.lang.reflect.Method;
     22 import java.util.HashMap;
     23 import java.util.Map;
     24 import java.util.Set;
     25 
     26 /**
     27  * Checks that the runtime representation of a class matches the API representation of a class.
     28  */
     29 public class AnnotationChecker extends AbstractApiChecker {
     30 
     31     private final Class<? extends Annotation> annotationClass;
     32 
     33     private final Map<String, Class<?>> annotatedClassesMap = new HashMap<>();
     34     private final Map<String, Set<Constructor<?>>> annotatedConstructorsMap = new HashMap<>();
     35     private final Map<String, Set<Method>> annotatedMethodsMap = new HashMap<>();
     36     private final Map<String, Set<Field>> annotatedFieldsMap = new HashMap<>();
     37 
     38     /**
     39      * @param annotationName name of the annotation class for the API type (e.g.
     40      *      android.annotation.SystemApi)
     41      */
     42     public AnnotationChecker(
     43             ResultObserver resultObserver, ClassProvider classProvider, String annotationName) {
     44         super(classProvider, resultObserver);
     45 
     46         annotationClass = ReflectionHelper.getAnnotationClass(annotationName);
     47         classProvider.getAllClasses().forEach(clazz -> {
     48             if (clazz.isAnnotationPresent(annotationClass)) {
     49                 annotatedClassesMap.put(clazz.getName(), clazz);
     50             }
     51             Set<Constructor<?>> constructors = ReflectionHelper.getAnnotatedConstructors(clazz,
     52                     annotationClass);
     53             if (!constructors.isEmpty()) {
     54                 annotatedConstructorsMap.put(clazz.getName(), constructors);
     55             }
     56             Set<Method> methods = ReflectionHelper.getAnnotatedMethods(clazz, annotationClass);
     57             if (!methods.isEmpty()) {
     58                 annotatedMethodsMap.put(clazz.getName(), methods);
     59             }
     60             Set<Field> fields = ReflectionHelper.getAnnotatedFields(clazz, annotationClass);
     61             if (!fields.isEmpty()) {
     62                 annotatedFieldsMap.put(clazz.getName(), fields);
     63             }
     64         });
     65     }
     66 
     67     @Override
     68     public void checkDeferred() {
     69         for (Class<?> clazz : annotatedClassesMap.values()) {
     70             resultObserver.notifyFailure(FailureType.EXTRA_CLASS, clazz.getName(),
     71                     "Class annotated with " + annotationClass.getName()
     72                             + " does not exist in the documented API");
     73         }
     74         for (Set<Constructor<?>> set : annotatedConstructorsMap.values()) {
     75             for (Constructor<?> c : set) {
     76                 resultObserver.notifyFailure(FailureType.EXTRA_METHOD, c.toString(),
     77                         "Constructor annotated with " + annotationClass.getName()
     78                                 + " does not exist in the API");
     79             }
     80         }
     81         for (Set<Method> set : annotatedMethodsMap.values()) {
     82             for (Method m : set) {
     83                 resultObserver.notifyFailure(FailureType.EXTRA_METHOD, m.toString(),
     84                         "Method annotated with " + annotationClass.getName()
     85                                 + " does not exist in the API");
     86             }
     87         }
     88         for (Set<Field> set : annotatedFieldsMap.values()) {
     89             for (Field f : set) {
     90                 resultObserver.notifyFailure(FailureType.EXTRA_FIELD, f.toString(),
     91                         "Field annotated with " + annotationClass.getName()
     92                                 + " does not exist in the API");
     93             }
     94         }
     95     }
     96 
     97     @Override
     98     protected boolean checkClass(JDiffClassDescription classDescription, Class<?> runtimeClass) {
     99         // remove the class from the set if found
    100         annotatedClassesMap.remove(runtimeClass.getName());
    101         return true;
    102     }
    103 
    104     @Override
    105     protected void checkField(JDiffClassDescription classDescription, Class<?> runtimeClass,
    106             JDiffClassDescription.JDiffField fieldDescription, Field field) {
    107         // make sure that the field (or its declaring class) is annotated
    108         if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(field, annotationClass)) {
    109             resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
    110                     field.toString(),
    111                     "Annotation " + annotationClass.getName() + " is missing");
    112         }
    113 
    114         // remove it from the set if found in the API doc
    115         Set<Field> annotatedFields = annotatedFieldsMap.get(runtimeClass.getName());
    116         if (annotatedFields != null) {
    117             annotatedFields.remove(field);
    118         }
    119     }
    120 
    121     @Override
    122     protected void checkConstructor(JDiffClassDescription classDescription, Class<?> runtimeClass,
    123             JDiffClassDescription.JDiffConstructor ctorDescription, Constructor<?> ctor) {
    124         Set<Constructor<?>> annotatedConstructors = annotatedConstructorsMap
    125                 .get(runtimeClass.getName());
    126 
    127         // make sure that the constructor (or its declaring class) is annotated
    128         if (annotationClass != null) {
    129             if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(ctor, annotationClass)) {
    130                 resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
    131                         ctor.toString(),
    132                         "Annotation " + annotationClass.getName() + " is missing");
    133             }
    134         }
    135 
    136         // remove it from the set if found in the API doc
    137         if (annotatedConstructors != null) {
    138             annotatedConstructors.remove(ctor);
    139         }
    140     }
    141 
    142     @Override
    143     protected void checkMethod(JDiffClassDescription classDescription, Class<?> runtimeClass,
    144             JDiffClassDescription.JDiffMethod methodDescription, Method method) {
    145         // make sure that the method (or its declaring class) is annotated or overriding
    146         // annotated method.
    147         if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(method, annotationClass)
    148                 && !ReflectionHelper.isOverridingAnnotatedMethod(method,
    149                         annotationClass)) {
    150             resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
    151                     method.toString(),
    152                     "Annotation " + annotationClass.getName() + " is missing");
    153         }
    154 
    155         // remove it from the set if found in the API doc
    156         Set<Method> annotatedMethods = annotatedMethodsMap.get(runtimeClass.getName());
    157         if (annotatedMethods != null) {
    158             annotatedMethods.remove(method);
    159         }
    160     }
    161 }
    162