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 (Class[])null); 144 } catch (NoSuchMethodException nsme) { 145 throw new RuntimeException(nsme); 146 } 147 property = meth.getAnnotation(ExportedProperty.class); 148 mapping = property.mapping(); 149 150 System.out.println("mapping is " + mapping.getClass() + 151 "\n 0='" + mapping[0] + "'\n 1='" + mapping[1] + "'"); 152 153 /* while we're here, check isAnnotationPresent on Method */ 154 System.out.println("present(getFocusType, ExportedProperty): " + 155 meth.isAnnotationPresent(ExportedProperty.class)); 156 System.out.println("present(getFocusType, AnnoSimpleType): " + 157 meth.isAnnotationPresent(AnnoSimpleType.class)); 158 159 System.out.println(""); 160 } 161 testVisibilityCompatibility()162 public static void testVisibilityCompatibility() throws Exception { 163 if (!VMRuntime.isAndroid()) { 164 return; 165 } 166 Object runtime = VMRuntime.getRuntime(); 167 int currentSdkVersion = VMRuntime.getTargetSdkVersion(runtime); 168 // SDK version 23 is M. 169 int oldSdkVersion = 23; 170 VMRuntime.setTargetSdkVersion(runtime, oldSdkVersion); 171 // This annotation has CLASS retention, but is visible to the runtime in M and earlier. 172 Annotation anno = SimplyNoted.class.getAnnotation(AnnoSimpleTypeInvis.class); 173 if (anno == null) { 174 System.out.println("testVisibilityCompatibility failed: " + 175 "SimplyNoted.get(AnnoSimpleTypeInvis) should not be null"); 176 } 177 VMRuntime.setTargetSdkVersion(runtime, currentSdkVersion); 178 } 179 main(String[] args)180 public static void main(String[] args) { 181 System.out.println("TestAnnotations..."); 182 183 testArrays(); 184 testArrayProblem(); 185 186 System.out.println( 187 "AnnoSimpleField " + AnnoSimpleField.class.isAnnotation() + 188 ", SimplyNoted " + SimplyNoted.class.isAnnotation()); 189 190 printAnnotations(SimplyNoted.class); 191 printAnnotations(INoted.class); 192 printAnnotations(SubNoted.class); 193 printAnnotations(FullyNoted.class); 194 195 try { 196 ClassWithInnerAnnotationClass.class.getDeclaredClasses(); 197 throw new AssertionError(); 198 } catch (NoClassDefFoundError expected) { 199 } 200 201 // this is expected to be non-null 202 Annotation anno = SimplyNoted.class.getAnnotation(AnnoSimpleType.class); 203 System.out.println("SimplyNoted.get(AnnoSimpleType) = " + anno); 204 // this is expected to be null 205 anno = SimplyNoted.class.getAnnotation(AnnoSimpleTypeInvis.class); 206 System.out.println("SimplyNoted.get(AnnoSimpleTypeInvis) = " + anno); 207 // this is non-null if the @Inherited tag is present 208 anno = SubNoted.class.getAnnotation(AnnoSimpleType.class); 209 System.out.println("SubNoted.get(AnnoSimpleType) = " + anno); 210 211 System.out.println(); 212 213 // Package annotations aren't inherited, so getAnnotations and getDeclaredAnnotations are 214 // the same. 215 System.out.println("Package annotations:"); 216 printAnnotationArray(" ", TestAnnotations.class.getPackage().getAnnotations()); 217 System.out.println("Package declared annotations:"); 218 printAnnotationArray(" ", TestAnnotations.class.getPackage().getDeclaredAnnotations()); 219 220 System.out.println(); 221 222 // Test inner classes. 223 System.out.println("Inner Classes:"); 224 new ClassWithInnerClasses().print(); 225 226 System.out.println(); 227 228 // Test TypeNotPresentException. 229 try { 230 AnnoMissingClass missingAnno = 231 ClassWithMissingAnnotation.class.getAnnotation(AnnoMissingClass.class); 232 System.out.println("Get annotation with missing class should not throw"); 233 System.out.println(missingAnno.value()); 234 System.out.println("Getting value of missing annotaton should have thrown"); 235 } catch (TypeNotPresentException expected) { 236 System.out.println("Got expected TypeNotPresentException"); 237 } 238 239 // Test renamed enums. 240 try { 241 for (Method m: RenamedNoted.class.getDeclaredMethods()) { 242 Annotation[] annos = m.getDeclaredAnnotations(); 243 System.out.println(" annotations on METH " + m + ":"); 244 } 245 } catch (NoSuchFieldError expected) { 246 System.out.println("Got expected NoSuchFieldError"); 247 } 248 249 // Test if annotations marked VISIBILITY_BUILD are visible to runtime in M and earlier. 250 try { 251 testVisibilityCompatibility(); 252 } catch (Exception e) { 253 System.out.println("testVisibilityCompatibility failed: " + e); 254 } 255 } 256 257 private static class VMRuntime { 258 private static Class vmRuntimeClass; 259 private static Method getRuntimeMethod; 260 private static Method getTargetSdkVersionMethod; 261 private static Method setTargetSdkVersionMethod; 262 static { init()263 init(); 264 } 265 init()266 private static void init() { 267 try { 268 vmRuntimeClass = Class.forName("dalvik.system.VMRuntime"); 269 } catch (Exception e) { 270 return; 271 } 272 try { 273 getRuntimeMethod = vmRuntimeClass.getDeclaredMethod("getRuntime"); 274 getTargetSdkVersionMethod = 275 vmRuntimeClass.getDeclaredMethod("getTargetSdkVersion"); 276 setTargetSdkVersionMethod = 277 vmRuntimeClass.getDeclaredMethod("setTargetSdkVersion", Integer.TYPE); 278 } catch (Exception e) { 279 throw new RuntimeException(e); 280 } 281 } 282 isAndroid()283 public static boolean isAndroid() { 284 return vmRuntimeClass != null; 285 } 286 getRuntime()287 public static Object getRuntime() throws Exception { 288 return getRuntimeMethod.invoke(null); 289 } 290 getTargetSdkVersion(Object runtime)291 public static int getTargetSdkVersion(Object runtime) throws Exception { 292 return (int) getTargetSdkVersionMethod.invoke(runtime); 293 } 294 setTargetSdkVersion(Object runtime, int version)295 public static void setTargetSdkVersion(Object runtime, int version) throws Exception { 296 setTargetSdkVersionMethod.invoke(runtime, version); 297 } 298 } 299 } 300