1 /* 2 * Copyright (C) 2015 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.test.anno; 18 19 import java.lang.annotation.Annotation; 20 import java.lang.reflect.Constructor; 21 import java.lang.reflect.Field; 22 import java.lang.reflect.Method; 23 import java.lang.reflect.Proxy; 24 import java.util.TreeMap; 25 26 public class TestAnnotations { 27 /** 28 * Print the annotations in sorted order, so as to avoid 29 * any (legitimate) non-determinism with regard to the iteration order. 30 */ printAnnotationArray(String prefix, Annotation[] arr)31 static private void printAnnotationArray(String prefix, Annotation[] arr) { 32 TreeMap<String, Annotation> sorted = 33 new TreeMap<String, Annotation>(); 34 35 for (Annotation a : arr) { 36 sorted.put(a.annotationType().getName(), a); 37 } 38 39 for (Annotation a : sorted.values()) { 40 System.out.println(prefix + " " + a); 41 System.out.println(prefix + " " + a.annotationType()); 42 } 43 } 44 printAnnotations(Class<?> clazz)45 static void printAnnotations(Class<?> clazz) { 46 Annotation[] annos; 47 Annotation[][] parAnnos; 48 49 annos = clazz.getAnnotations(); 50 System.out.println("annotations on TYPE " + clazz + 51 "(" + annos.length + "):"); 52 printAnnotationArray("", annos); 53 System.out.println(); 54 55 for (Constructor<?> c: clazz.getDeclaredConstructors()) { 56 annos = c.getDeclaredAnnotations(); 57 System.out.println(" annotations on CTOR " + c + ":"); 58 printAnnotationArray(" ", annos); 59 60 System.out.println(" constructor parameter annotations:"); 61 for (Annotation[] pannos: c.getParameterAnnotations()) { 62 printAnnotationArray(" ", pannos); 63 } 64 } 65 66 for (Method m: clazz.getDeclaredMethods()) { 67 annos = m.getDeclaredAnnotations(); 68 System.out.println(" annotations on METH " + m + ":"); 69 printAnnotationArray(" ", annos); 70 71 System.out.println(" method parameter annotations:"); 72 for (Annotation[] pannos: m.getParameterAnnotations()) { 73 printAnnotationArray(" ", pannos); 74 } 75 } 76 77 for (Field f: clazz.getDeclaredFields()) { 78 annos = f.getDeclaredAnnotations(); 79 System.out.println(" annotations on FIELD " + f + ":"); 80 printAnnotationArray(" ", annos); 81 82 AnnoFancyField aff; 83 aff = (AnnoFancyField) f.getAnnotation(AnnoFancyField.class); 84 if (aff != null) { 85 System.out.println(" aff: " + aff + " / " + Proxy.isProxyClass(aff.getClass())); 86 System.out.println(" --> nombre is '" + aff.nombre() + "'"); 87 } 88 } 89 System.out.println(); 90 } 91 92 93 @ExportedProperty(mapping = { 94 @IntToString(from = 0, to = "NORMAL_FOCUS"), 95 @IntToString(from = 2, to = "WEAK_FOCUS") 96 }) getFocusType()97 public int getFocusType() { 98 return 2; 99 } 100 101 102 @AnnoArrayField 103 String thing1; 104 105 @AnnoArrayField( 106 zz = {true,false,true}, 107 bb = {-1,0,1}, 108 cc = {'Q'}, 109 ss = {12,13,14,15,16,17}, 110 ii = {1,2,3,4}, 111 ff = {1.1f,1.2f,1.3f}, 112 jj = {-5,0,5}, 113 dd = {0.3,0.6,0.9}, 114 str = {"hickory","dickory","dock"} 115 ) 116 String thing2; 117 testArrays()118 public static void testArrays() { 119 TestAnnotations ta = new TestAnnotations(); 120 Field field; 121 Annotation[] annotations; 122 123 try { 124 field = TestAnnotations.class.getDeclaredField("thing1"); 125 annotations = field.getAnnotations(); 126 System.out.println(field + ": " + annotations[0].toString()); 127 128 field = TestAnnotations.class.getDeclaredField("thing2"); 129 annotations = field.getAnnotations(); 130 System.out.println(field + ": " + annotations[0].toString()); 131 } catch (NoSuchFieldException nsfe) { 132 throw new RuntimeException(nsfe); 133 } 134 } 135 testArrayProblem()136 public static void testArrayProblem() { 137 Method meth; 138 ExportedProperty property; 139 final IntToString[] mapping; 140 141 try { 142 meth = TestAnnotations.class.getMethod("getFocusType"); 143 } catch (NoSuchMethodException nsme) { 144 throw new RuntimeException(nsme); 145 } 146 property = meth.getAnnotation(ExportedProperty.class); 147 mapping = property.mapping(); 148 149 System.out.println("mapping is " + mapping.getClass() + 150 "\n 0='" + mapping[0] + "'\n 1='" + mapping[1] + "'"); 151 152 /* while we're here, check isAnnotationPresent on Method */ 153 System.out.println("present(getFocusType, ExportedProperty): " + 154 meth.isAnnotationPresent(ExportedProperty.class)); 155 System.out.println("present(getFocusType, AnnoSimpleType): " + 156 meth.isAnnotationPresent(AnnoSimpleType.class)); 157 158 System.out.println(""); 159 } 160 testVisibilityCompatibility()161 public static void testVisibilityCompatibility() throws Exception { 162 if (!VMRuntime.isAndroid()) { 163 return; 164 } 165 Object runtime = VMRuntime.getRuntime(); 166 int currentSdkVersion = VMRuntime.getTargetSdkVersion(runtime); 167 // SDK version 23 is M. 168 int oldSdkVersion = 23; 169 VMRuntime.setTargetSdkVersion(runtime, oldSdkVersion); 170 // This annotation has CLASS retention, but is visible to the runtime in M and earlier. 171 Annotation anno = SimplyNoted.class.getAnnotation(AnnoSimpleTypeInvis.class); 172 if (anno == null) { 173 System.out.println("testVisibilityCompatibility failed: " + 174 "SimplyNoted.get(AnnoSimpleTypeInvis) should not be null"); 175 } 176 VMRuntime.setTargetSdkVersion(runtime, currentSdkVersion); 177 } 178 main(String[] args)179 public static void main(String[] args) { 180 System.out.println("TestAnnotations..."); 181 182 testArrays(); 183 testArrayProblem(); 184 185 System.out.println( 186 "AnnoSimpleField " + AnnoSimpleField.class.isAnnotation() + 187 ", SimplyNoted " + SimplyNoted.class.isAnnotation()); 188 189 printAnnotations(SimplyNoted.class); 190 printAnnotations(INoted.class); 191 printAnnotations(SubNoted.class); 192 printAnnotations(FullyNoted.class); 193 194 try { 195 ClassWithInnerAnnotationClass.class.getDeclaredClasses(); 196 throw new AssertionError(); 197 } catch (NoClassDefFoundError expected) { 198 } 199 200 // this is expected to be non-null 201 Annotation anno = SimplyNoted.class.getAnnotation(AnnoSimpleType.class); 202 System.out.println("SimplyNoted.get(AnnoSimpleType) = " + anno); 203 // this is expected to be null 204 anno = SimplyNoted.class.getAnnotation(AnnoSimpleTypeInvis.class); 205 System.out.println("SimplyNoted.get(AnnoSimpleTypeInvis) = " + anno); 206 // this is non-null if the @Inherited tag is present 207 anno = SubNoted.class.getAnnotation(AnnoSimpleType.class); 208 System.out.println("SubNoted.get(AnnoSimpleType) = " + anno); 209 210 System.out.println(); 211 212 // Package annotations aren't inherited, so getAnnotations and getDeclaredAnnotations are 213 // the same. 214 System.out.println("Package annotations:"); 215 printAnnotationArray(" ", TestAnnotations.class.getPackage().getAnnotations()); 216 System.out.println("Package declared annotations:"); 217 printAnnotationArray(" ", TestAnnotations.class.getPackage().getDeclaredAnnotations()); 218 219 System.out.println(); 220 221 // Test inner classes. 222 System.out.println("Inner Classes:"); 223 new ClassWithInnerClasses().print(); 224 225 System.out.println(); 226 227 // Test TypeNotPresentException. 228 try { 229 AnnoMissingClass missingAnno = 230 ClassWithMissingAnnotation.class.getAnnotation(AnnoMissingClass.class); 231 System.out.println("Get annotation with missing class should not throw"); 232 System.out.println(missingAnno.value()); 233 System.out.println("Getting value of missing annotaton should have thrown"); 234 } catch (TypeNotPresentException expected) { 235 System.out.println("Got expected TypeNotPresentException"); 236 } 237 238 // Test renamed enums. 239 try { 240 for (Method m: RenamedNoted.class.getDeclaredMethods()) { 241 Annotation[] annos = m.getDeclaredAnnotations(); 242 System.out.println(" annotations on METH " + m + ":"); 243 } 244 } catch (NoSuchFieldError expected) { 245 System.out.println("Got expected NoSuchFieldError"); 246 } 247 248 // Test if annotations marked VISIBILITY_BUILD are visible to runtime in M and earlier. 249 try { 250 testVisibilityCompatibility(); 251 } catch (Exception e) { 252 System.out.println("testVisibilityCompatibility failed: " + e); 253 } 254 } 255 256 private static class VMRuntime { 257 private static Class<?> vmRuntimeClass; 258 private static Method getRuntimeMethod; 259 private static Method getTargetSdkVersionMethod; 260 private static Method setTargetSdkVersionMethod; 261 static { init()262 init(); 263 } 264 init()265 private static void init() { 266 try { 267 vmRuntimeClass = Class.forName("dalvik.system.VMRuntime"); 268 } catch (Exception e) { 269 return; 270 } 271 try { 272 getRuntimeMethod = vmRuntimeClass.getDeclaredMethod("getRuntime"); 273 getTargetSdkVersionMethod = 274 vmRuntimeClass.getDeclaredMethod("getTargetSdkVersion"); 275 setTargetSdkVersionMethod = 276 vmRuntimeClass.getDeclaredMethod("setTargetSdkVersion", Integer.TYPE); 277 } catch (Exception e) { 278 throw new RuntimeException(e); 279 } 280 } 281 isAndroid()282 public static boolean isAndroid() { 283 return vmRuntimeClass != null; 284 } 285 getRuntime()286 public static Object getRuntime() throws Exception { 287 return getRuntimeMethod.invoke(null); 288 } 289 getTargetSdkVersion(Object runtime)290 public static int getTargetSdkVersion(Object runtime) throws Exception { 291 return (int) getTargetSdkVersionMethod.invoke(runtime); 292 } 293 setTargetSdkVersion(Object runtime, int version)294 public static void setTargetSdkVersion(Object runtime, int version) throws Exception { 295 setTargetSdkVersionMethod.invoke(runtime, version); 296 } 297 } 298 } 299