• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 
17 package android.car.test.util;
18 
19 import static com.google.common.truth.Truth.assertWithMessage;
20 
21 import android.car.annotation.AddedInOrBefore;
22 
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 
30 // TODO(b/237565347): Refactor this class so that 'field' and 'method' code is not repeated.
31 public class AnnotationHelper {
32 
checkForAnnotation(String[] classes, Class... annotationClasses)33     public static void checkForAnnotation(String[] classes, Class... annotationClasses)
34             throws Exception {
35         List<String> errorsNoAnnotation = new ArrayList<>();
36         List<String> errorsExtraAnnotation = new ArrayList<>();
37 
38         for (int i = 0; i < classes.length; i++) {
39             String className = classes[i];
40             Field[] fields = Class.forName(className).getDeclaredFields();
41             for (int j = 0; j < fields.length; j++) {
42                 Field field = fields[j];
43                 boolean isAnnotated = containsAddedInAnnotation(field, annotationClasses);
44                 boolean isPrivate = Modifier.isPrivate(field.getModifiers());
45 
46                 if (isPrivate && isAnnotated) {
47                     errorsExtraAnnotation.add(className + " FIELD: " + field.getName());
48                 }
49 
50                 if (!isPrivate && !isAnnotated) {
51                     errorsNoAnnotation.add(className + " FIELD: " + field.getName());
52                 }
53             }
54 
55             Method[] methods = Class.forName(className).getDeclaredMethods();
56             for (int j = 0; j < methods.length; j++) {
57                 Method method = methods[j];
58 
59                 // These are some internal methods
60                 if (method.getName().contains("$")) continue;
61 
62                 boolean isAnnotated = containsAddedInAnnotation(method, annotationClasses);
63                 boolean isPrivate = Modifier.isPrivate(method.getModifiers());
64 
65                 if (isPrivate && isAnnotated) {
66                     errorsExtraAnnotation.add(className + " METHOD: " + method.getName());
67                 }
68 
69                 if (!isPrivate && !isAnnotated) {
70                     errorsNoAnnotation.add(className + " METHOD: " + method.getName());
71                 }
72             }
73         }
74 
75         StringBuilder errorFlatten = new StringBuilder();
76         if (!errorsNoAnnotation.isEmpty()) {
77             // TODO(b/240343308): remove @AddedIn once all usages have been replaced
78             errorFlatten.append("Errors:\nMissing ApiRequirements (or AddedIn) annotation for-\n");
79             errorFlatten.append(String.join("\n", errorsNoAnnotation));
80         }
81 
82         if (!errorsExtraAnnotation.isEmpty()) {
83             // TODO(b/240343308): remove @AddedIn once all usages have been replaced
84             errorFlatten.append("\nErrors:\nApiRequirements (or AddedIn) annotation used for "
85                     + "private members/methods-\n");
86             errorFlatten.append(String.join("\n", errorsExtraAnnotation));
87         }
88 
89         assertWithMessage(errorFlatten.toString())
90                 .that(errorsExtraAnnotation.size() + errorsNoAnnotation.size()).isEqualTo(0);
91     }
92 
93     @SuppressWarnings("unchecked")
containsAddedInAnnotation(Field field, Class... annotationClasses)94     private static boolean containsAddedInAnnotation(Field field, Class... annotationClasses) {
95         for (int i = 0; i < annotationClasses.length; i++) {
96             if (field.getAnnotation(annotationClasses[i]) != null) {
97                 validatedAddInOrBeforeAnnotation(field);
98                 return true;
99             }
100         }
101         return false;
102     }
103 
104     @SuppressWarnings("unchecked")
containsAddedInAnnotation(Method method, Class... annotationClasses)105     private static boolean containsAddedInAnnotation(Method method, Class... annotationClasses) {
106         for (int i = 0; i < annotationClasses.length; i++) {
107             if (method.getAnnotation(annotationClasses[i]) != null) {
108                 validatedAddInOrBeforeAnnotation(method);
109                 return true;
110             }
111         }
112         return false;
113     }
114 
validatedAddInOrBeforeAnnotation(Field field)115     private static void validatedAddInOrBeforeAnnotation(Field field) {
116         AddedInOrBefore annotation = field.getAnnotation(AddedInOrBefore.class);
117         if (annotation != null) {
118             assertWithMessage(field.getDeclaringClass() + ", field:" + field.getName()
119                     + " should not use AddedInOrBefore annotation. The annotation was reserved only"
120                     + " for APIs added in or before majorVersion:33, minorVersion:0")
121                             .that(annotation.majorVersion()).isEqualTo(33);
122             assertWithMessage(field.getDeclaringClass() + ", field:" + field.getName()
123                     + " should not use AddedInOrBefore annotation. The annotation was reserved only"
124                     + " for APIs added in or before majorVersion:33, minorVersion:0")
125                     .that(annotation.minorVersion()).isEqualTo(0);
126         }
127     }
128 
validatedAddInOrBeforeAnnotation(Method method)129     private static void validatedAddInOrBeforeAnnotation(Method method) {
130         AddedInOrBefore annotation = method.getAnnotation(AddedInOrBefore.class);
131         if (annotation != null) {
132             assertWithMessage(method.getDeclaringClass() + ", method:" + method.getName()
133                     + " should not use AddedInOrBefore annotation. The annotation was reserved only"
134                     + " for APIs added in or before majorVersion:33, minorVersion:0")
135                             .that(annotation.majorVersion()).isEqualTo(33);
136             assertWithMessage(method.getDeclaringClass() + ", method:" + method.getName()
137                     + " should not use AddedInOrBefore annotation. The annotation was reserved only"
138                     + " for APIs added in or before majorVersion:33, minorVersion:0")
139                             .that(annotation.minorVersion()).isEqualTo(0);
140         }
141     }
142 }
143