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.annotation.AnnotationFormatError; 21 import java.lang.reflect.Constructor; 22 import java.lang.reflect.Field; 23 import java.lang.reflect.Method; 24 import java.lang.reflect.Proxy; 25 import java.util.TreeMap; 26 27 public class TestAnnotations { 28 /** 29 * Print the annotations in sorted order, so as to avoid 30 * any (legitimate) non-determinism with regard to the iteration order. 31 */ printAnnotationArray(String prefix, Annotation[] arr)32 static private void printAnnotationArray(String prefix, Annotation[] arr) { 33 TreeMap<String, Annotation> sorted = 34 new TreeMap<String, Annotation>(); 35 36 for (Annotation a : arr) { 37 sorted.put(a.annotationType().getName(), a); 38 } 39 40 for (Annotation a : sorted.values()) { 41 System.out.println(prefix + " " + a); 42 System.out.println(prefix + " " + a.annotationType()); 43 } 44 } 45 printAnnotations(Class<?> clazz)46 static void printAnnotations(Class<?> clazz) { 47 Annotation[] annos; 48 Annotation[][] parAnnos; 49 50 annos = clazz.getAnnotations(); 51 System.out.println("annotations on TYPE " + clazz + 52 "(" + annos.length + "):"); 53 printAnnotationArray("", annos); 54 System.out.println(); 55 56 for (Constructor<?> c: clazz.getDeclaredConstructors()) { 57 annos = c.getDeclaredAnnotations(); 58 System.out.println(" annotations on CTOR " + c + ":"); 59 printAnnotationArray(" ", annos); 60 61 System.out.println(" constructor parameter annotations:"); 62 for (Annotation[] pannos: c.getParameterAnnotations()) { 63 printAnnotationArray(" ", pannos); 64 } 65 } 66 67 for (Method m: clazz.getDeclaredMethods()) { 68 annos = m.getDeclaredAnnotations(); 69 System.out.println(" annotations on METH " + m + ":"); 70 printAnnotationArray(" ", annos); 71 72 System.out.println(" method parameter annotations:"); 73 for (Annotation[] pannos: m.getParameterAnnotations()) { 74 printAnnotationArray(" ", pannos); 75 } 76 } 77 78 for (Field f: clazz.getDeclaredFields()) { 79 annos = f.getDeclaredAnnotations(); 80 System.out.println(" annotations on FIELD " + f + ":"); 81 printAnnotationArray(" ", annos); 82 83 AnnoFancyField aff; 84 aff = (AnnoFancyField) f.getAnnotation(AnnoFancyField.class); 85 if (aff != null) { 86 System.out.println(" aff: " + aff + " / " + Proxy.isProxyClass(aff.getClass())); 87 System.out.println(" --> nombre is '" + aff.nombre() + "'"); 88 } 89 } 90 System.out.println(); 91 } 92 93 94 @ExportedProperty(mapping = { 95 @IntToString(from = 0, to = "NORMAL_FOCUS"), 96 @IntToString(from = 2, to = "WEAK_FOCUS") 97 }) getFocusType()98 public int getFocusType() { 99 return 2; 100 } 101 102 103 @AnnoArrayField 104 String thing1; 105 106 @AnnoArrayField( 107 zz = {true,false,true}, 108 bb = {-1,0,1}, 109 cc = {'Q'}, 110 ss = {12,13,14,15,16,17}, 111 ii = {1,2,3,4}, 112 ff = {1.1f,1.2f,1.3f}, 113 jj = {-5,0,5}, 114 dd = {0.3,0.6,0.9}, 115 str = {"hickory","dickory","dock"} 116 ) 117 String thing2; 118 testArrays()119 public static void testArrays() { 120 TestAnnotations ta = new TestAnnotations(); 121 Field field; 122 Annotation[] annotations; 123 124 try { 125 field = TestAnnotations.class.getDeclaredField("thing1"); 126 annotations = field.getAnnotations(); 127 System.out.println(field + ": " + annotations[0].toString()); 128 129 field = TestAnnotations.class.getDeclaredField("thing2"); 130 annotations = field.getAnnotations(); 131 System.out.println(field + ": " + annotations[0].toString()); 132 } catch (NoSuchFieldException nsfe) { 133 throw new RuntimeException(nsfe); 134 } 135 } 136 testArrayProblem()137 public static void testArrayProblem() { 138 Method meth; 139 ExportedProperty property; 140 final IntToString[] mapping; 141 142 try { 143 meth = TestAnnotations.class.getMethod("getFocusType"); 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 (Error expected) { 246 System.out.println("Got expected Error for renamed enum"); 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