• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.stream.Collectors;
23 import java.util.HashMap;
24 import java.util.Map;
25 import java.util.Set;
26 
27 /**
28  * Checks that the runtime representation of a class matches the API representation of a class.
29  */
30 public class AnnotationChecker extends ApiPresenceChecker {
31 
32     private final String annotationSpec;
33 
34     private final ResultFilter filter;
35 
36     private final Map<String, Class<?>> annotatedClassesMap = new HashMap<>();
37     private final Map<String, Set<Constructor<?>>> annotatedConstructorsMap = new HashMap<>();
38     private final Map<String, Set<Method>> annotatedMethodsMap = new HashMap<>();
39     private final Map<String, Set<Field>> annotatedFieldsMap = new HashMap<>();
40 
41     /**
42      * @param annotationSpec name of the annotation class for the API type (e.g.
43      *      android.annotation.SystemApi)
44      */
AnnotationChecker( ResultObserver resultObserver, ClassProvider classProvider, String annotationSpec, ResultFilter filter)45     public AnnotationChecker(
46             ResultObserver resultObserver, ClassProvider classProvider, String annotationSpec,
47             ResultFilter filter) {
48         super(classProvider, resultObserver);
49 
50         this.annotationSpec = annotationSpec;
51         this.filter = filter;
52         classProvider.getAllClasses().forEach(clazz -> {
53             if (ReflectionHelper.hasMatchingAnnotation(clazz, annotationSpec)) {
54                 annotatedClassesMap.put(clazz.getName(), clazz);
55             }
56             Set<Constructor<?>> constructors = ReflectionHelper.getAnnotatedConstructors(clazz,
57                     annotationSpec);
58             if (!constructors.isEmpty()) {
59                 annotatedConstructorsMap.put(clazz.getName(), constructors.stream().
60                         filter(c -> !c.isSynthetic()).collect(Collectors.toSet()));
61             }
62             Set<Method> methods = ReflectionHelper.getAnnotatedMethods(clazz, annotationSpec);
63             if (!methods.isEmpty()) {
64                 annotatedMethodsMap.put(clazz.getName(), methods.stream().
65                         filter(m -> !m.isSynthetic()).collect(Collectors.toSet()));
66             }
67             Set<Field> fields = ReflectionHelper.getAnnotatedFields(clazz, annotationSpec);
68             if (!fields.isEmpty()) {
69                 annotatedFieldsMap.put(clazz.getName(), fields.stream().
70                         filter(f -> !f.isSynthetic()).collect(Collectors.toSet()));
71             }
72         });
73     }
74 
75     /**
76      * ResultFilter allows users to skip the check for certain types of APIs.
77      */
78     public interface ResultFilter {
skip(Class<?> clazz)79         public boolean skip(Class<?> clazz);
skip(Constructor<?> ctor)80         public boolean skip(Constructor<?> ctor);
skip(Method m)81         public boolean skip(Method m);
skip(Field f)82         public boolean skip(Field f);
83     }
84 
checkDeferred()85     public void checkDeferred() {
86         for (Class<?> clazz : annotatedClassesMap.values()) {
87             if (filter != null && filter.skip(clazz)) continue;
88             resultObserver.notifyFailure(FailureType.extra(clazz), clazz.getName(),
89                     "Class annotated with " + annotationSpec
90                             + " does not exist in the documented API");
91         }
92         for (Set<Constructor<?>> set : annotatedConstructorsMap.values()) {
93             for (Constructor<?> c : set) {
94                 if (filter != null && filter.skip(c)) continue;
95                 resultObserver.notifyFailure(FailureType.EXTRA_CONSTRUCTOR, c.toString(),
96                         "Constructor annotated with " + annotationSpec
97                                 + " does not exist in the API");
98             }
99         }
100         for (Set<Method> set : annotatedMethodsMap.values()) {
101             for (Method m : set) {
102                 if (filter != null && filter.skip(m)) continue;
103                 resultObserver.notifyFailure(FailureType.EXTRA_METHOD, m.toString(),
104                         "Method annotated with " + annotationSpec
105                                 + " does not exist in the API");
106             }
107         }
108         for (Set<Field> set : annotatedFieldsMap.values()) {
109             for (Field f : set) {
110                 if (filter != null && filter.skip(f)) continue;
111                 resultObserver.notifyFailure(FailureType.EXTRA_FIELD, f.toString(),
112                         "Field annotated with " + annotationSpec
113                                 + " does not exist in the API");
114             }
115         }
116     }
117 
118     @Override
checkClass(JDiffClassDescription classDescription, Class<?> runtimeClass)119     protected boolean checkClass(JDiffClassDescription classDescription, Class<?> runtimeClass) {
120         // remove the class from the set if found
121         annotatedClassesMap.remove(runtimeClass.getName());
122         return true;
123     }
124 
125     @Override
checkField(JDiffClassDescription classDescription, Class<?> runtimeClass, JDiffClassDescription.JDiffField fieldDescription, Field field)126     protected void checkField(JDiffClassDescription classDescription, Class<?> runtimeClass,
127             JDiffClassDescription.JDiffField fieldDescription, Field field) {
128         // make sure that the field (or its declaring class) is annotated
129         if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(field, annotationSpec)) {
130             resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
131                     field.toString(),
132                     "Annotation " + annotationSpec + " is missing");
133         }
134 
135         // remove it from the set if found in the API doc
136         Set<Field> annotatedFields = annotatedFieldsMap.get(runtimeClass.getName());
137         if (annotatedFields != null) {
138             annotatedFields.remove(field);
139         }
140     }
141 
142     @Override
checkConstructor(JDiffClassDescription classDescription, Class<?> runtimeClass, JDiffClassDescription.JDiffConstructor ctorDescription, Constructor<?> ctor)143     protected void checkConstructor(JDiffClassDescription classDescription, Class<?> runtimeClass,
144             JDiffClassDescription.JDiffConstructor ctorDescription, Constructor<?> ctor) {
145         Set<Constructor<?>> annotatedConstructors = annotatedConstructorsMap
146                 .get(runtimeClass.getName());
147 
148         // make sure that the constructor (or its declaring class) is annotated
149         if (annotationSpec != null) {
150             if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(ctor, annotationSpec)) {
151                 resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
152                         ctor.toString(),
153                         "Annotation " + annotationSpec + " is missing");
154             }
155         }
156 
157         // remove it from the set if found in the API doc
158         if (annotatedConstructors != null) {
159             annotatedConstructors.remove(ctor);
160         }
161     }
162 
163     @Override
checkMethod(JDiffClassDescription classDescription, Class<?> runtimeClass, JDiffClassDescription.JDiffMethod methodDescription, Method method)164     protected void checkMethod(JDiffClassDescription classDescription, Class<?> runtimeClass,
165             JDiffClassDescription.JDiffMethod methodDescription, Method method) {
166         // make sure that the method (or its declaring class) is annotated or overriding
167         // annotated method.
168         if (!ReflectionHelper.isAnnotatedOrInAnnotatedClass(method, annotationSpec)
169                 && !ReflectionHelper.isOverridingAnnotatedMethod(method,
170                         annotationSpec)) {
171             resultObserver.notifyFailure(FailureType.MISSING_ANNOTATION,
172                     method.toString(),
173                     "Annotation " + annotationSpec + " is missing");
174         }
175 
176         // remove it from the set if found in the API doc
177         Set<Method> annotatedMethods = annotatedMethodsMap.get(runtimeClass.getName());
178         if (annotatedMethods != null) {
179             annotatedMethods.remove(method);
180         }
181     }
182 }
183