1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8058202 27 * @summary Test java.lang.Object methods on AnnotatedType objects. 28 */ 29 30 import java.lang.annotation.*; 31 import java.lang.reflect.*; 32 import java.util.*; 33 34 /** 35 * Test toString, equals, and hashCode on various AnnotatedType objects. 36 */ 37 public class TestObjectMethods { 38 private static int errors = 0; 39 40 /* 41 * There are various subtypes of AnnotatedType implementations: 42 * 43 * AnnotatedType 44 * AnnotatedArrayType 45 * AnnotatedParameterizedType 46 * AnnotatedTypeVariable 47 * AnnotatedWildcardType 48 * 49 * The implementations of each these implementions are 50 * examined. Wildcards don't appear as top-level types and need to 51 * be extracted from bounds. 52 * 53 * AnnotatedTypes with and without annotations areexamined as 54 * well. 55 */ 56 public static void main(String... args) { 57 Class<?>[] testClasses = {TypeHost.class, AnnotatedTypeHost.class}; 58 59 for (Class<?> clazz : testClasses) { 60 testEqualsReflexivity(clazz); 61 testEquals(clazz); 62 } 63 64 testToString(TypeHost.class, false); 65 testToString(AnnotatedTypeHost.class, true); 66 67 testAnnotationsMatterForEquals(TypeHost.class, AnnotatedTypeHost.class); 68 69 testGetAnnotations(TypeHost.class, false); 70 testGetAnnotations(AnnotatedTypeHost.class, true); 71 72 testWildcards(); 73 74 if (errors > 0) { 75 throw new RuntimeException(errors + " errors"); 76 } 77 } 78 79 /* 80 * For non-array types, verify toString version of the annotated 81 * type ends with the same string as the generic type. 82 */ 83 static void testToString(Class<?> clazz, boolean leadingAnnotations) { 84 System.err.println("Testing toString on methods of class " + clazz.getName()); 85 Method[] methods = clazz.getDeclaredMethods(); 86 for (Method m : methods) { 87 AnnotatedType annotType = m.getAnnotatedReturnType(); 88 String annotTypeString = annotType.toString(); 89 90 Type type = m.getGenericReturnType(); 91 String typeString = type.toString(); 92 93 boolean isArray = annotType instanceof AnnotatedArrayType; 94 boolean isVoid = "void".equals(typeString); 95 96 boolean valid; 97 if (!isArray) { 98 if (leadingAnnotations && !isVoid) { 99 valid = 100 annotTypeString.endsWith(typeString) && 101 !annotTypeString.startsWith(typeString); 102 } else { 103 valid = annotTypeString.equals(typeString); 104 } 105 } else { 106 // Find final non-array component type and gets its name. 107 typeString = null; 108 109 AnnotatedType componentType = annotType; 110 while (componentType instanceof AnnotatedArrayType) { 111 AnnotatedArrayType annotatedArrayType = (AnnotatedArrayType) componentType; 112 componentType = annotatedArrayType.getAnnotatedGenericComponentType(); 113 } 114 115 String componentName = componentType.getType().getTypeName(); 116 valid = annotTypeString.contains(componentName); 117 } 118 119 if (!valid) { 120 errors++; 121 System.err.println(typeString + "\n" + annotTypeString + 122 "\n " + valid + 123 "\n\n"); 124 } 125 } 126 } 127 128 static void testGetAnnotations(Class<?> clazz, boolean annotationsExpectedOnMethods) { 129 System.err.println("Testing getAnnotations on methods of class " + clazz.getName()); 130 Method[] methods = clazz.getDeclaredMethods(); 131 for (Method m : methods) { 132 Type type = m.getGenericReturnType(); 133 AnnotatedType annotType = m.getAnnotatedReturnType(); 134 Annotation[] annotations = annotType.getAnnotations(); 135 136 boolean isVoid = "void".equals(type.toString()); 137 138 if (annotationsExpectedOnMethods && !isVoid) { 139 if (annotations.length == 0 ) { 140 errors++; 141 System.err.println("Expected annotations missing on " + annotType); 142 143 } 144 } else { 145 if (annotations.length > 0 ) { 146 errors++; 147 System.err.println("Unexpected annotations present on " + annotType); 148 } 149 } 150 } 151 } 152 153 static void testEqualsReflexivity(Class<?> clazz) { 154 System.err.println("Testing reflexivity of equals on methods of class " + clazz.getName()); 155 Method[] methods = clazz.getDeclaredMethods(); 156 for (Method m : methods) { 157 AnnotatedType annotType1 = m.getAnnotatedReturnType(); 158 AnnotatedType annotType2 = m.getAnnotatedReturnType(); 159 160 boolean valid = annotType1.equals(annotType2); 161 162 if (!valid) { 163 errors++; 164 System.err.println(annotType1); 165 System.err.println(" is not equal to "); 166 System.err.println(annotType2); 167 System.err.println(); 168 } 169 } 170 } 171 172 /* 173 * For each of the type host classes, the return type of a method 174 * should only equal the return type of that method. 175 */ 176 static void testEquals(Class<?> clazz) { 177 Method[] methods = clazz.getDeclaredMethods(); 178 179 for (int i = 0; i < methods.length; i++) { 180 for (int j = 0; j < methods.length; j++) { 181 if (i == j) 182 continue; 183 else { 184 AnnotatedType annotType1 = methods[i].getAnnotatedReturnType(); 185 AnnotatedType annotType2 = methods[j].getAnnotatedReturnType(); 186 187 boolean valid = !annotType1.equals(annotType2); 188 189 if (!valid) { 190 errors++; 191 System.err.println(annotType1); 192 System.err.println(" is equal to "); 193 System.err.println(annotType2); 194 System.err.println(); 195 } 196 197 } 198 } 199 } 200 } 201 202 /** 203 * Roughly, compare the return types of corresponding methods on 204 * TypeHost and AnnotatedtypeHost and verify the AnnotatedType 205 * objects are *not* equal even if their underlying generic types 206 * are. 207 */ 208 static void testAnnotationsMatterForEquals(Class<?> clazz1, Class<?> clazz2) { 209 Method[] methods1 = clazz1.getDeclaredMethods(); 210 Method[] methods2 = clazz2.getDeclaredMethods(); 211 212 // Skip 0th element since void cannoted be annotated 213 for (int i = 1; i < methods1.length; i++) { 214 AnnotatedType annotType1 = methods1[i].getAnnotatedReturnType(); 215 AnnotatedType annotType2 = methods2[i].getAnnotatedReturnType(); 216 217 boolean valid = !annotType1.equals(annotType2); 218 219 if (!valid) { 220 errors++; 221 System.err.println(annotType1); 222 System.err.println(" is equal to "); 223 System.err.println(annotType2); 224 System.err.println(); 225 } 226 } 227 } 228 229 230 static void testWildcards() { 231 // public @AnnotType(10) Set<? extends Number> fooNumberSet() {return null;} 232 // public @AnnotType(11) Set<@AnnotType(13) ? extends Number> fooNumberSet2() {return null;} 233 AnnotatedWildcardType awt1 = extractWildcard("fooNumberSet"); 234 AnnotatedWildcardType awt2 = extractWildcard("fooNumberSet2"); 235 236 if (!awt1.equals(extractWildcard("fooNumberSet")) || 237 !awt2.equals(extractWildcard("fooNumberSet2"))) { 238 errors++; 239 System.err.println("Bad equality comparison on wildcards."); 240 } 241 242 if (awt1.equals(awt2)) { 243 errors++; 244 System.err.println(awt1); 245 System.err.println(" is equal to "); 246 System.err.println(awt2); 247 System.err.println(); 248 } 249 250 if (awt2.getAnnotations().length == 0) { 251 errors++; 252 System.err.println("Expected annotations not found."); 253 } 254 255 } 256 257 private static AnnotatedWildcardType extractWildcard(String methodName) { 258 try { 259 return (AnnotatedWildcardType) 260 (((AnnotatedParameterizedType)(AnnotatedTypeHost.class.getMethod(methodName). 261 getAnnotatedReturnType())). 262 getAnnotatedActualTypeArguments()[0] ); 263 } catch (Exception e) { 264 throw new RuntimeException(e); 265 } 266 } 267 268 static class TypeHost<E, F extends Number> { 269 public void fooVoid() {return;} 270 271 public int foo() {return 0;} 272 public String fooString() {return null;} 273 274 public int[] fooIntArray() {return null;} 275 public String[] fooStringArray() {return null;} 276 public String [][] fooStringArrayArray() {return null;} 277 278 public Set<String> fooSetString() {return null;} 279 public E fooE() {return null;} 280 public F fooF() {return null;} 281 public <G> G fooG() {return null;} 282 283 public Set<? extends Number> fooNumberSet() {return null;} 284 public Set<? extends Integer> fooNumberSet2() {return null;} 285 } 286 287 @Retention(RetentionPolicy.RUNTIME) 288 @Target(ElementType.TYPE_USE) 289 static @interface AnnotType { 290 int value() default 0; 291 } 292 293 static class AnnotatedTypeHost<E, F extends Number> { 294 public /*@AnnotType(0)*/ void fooVoid() {return;} // Illegal to annotate void 295 296 public @AnnotType(1) int foo() {return 0;} 297 public @AnnotType(2) String fooString() {return null;} 298 299 public int @AnnotType(3) [] fooIntArray() {return null;} 300 public String @AnnotType(4) [] fooStringArray() {return null;} 301 public @AnnotType(5) String @AnnotType(0) [] @AnnotType(1) [] fooStringArrayArray() {return null;} 302 303 public @AnnotType(6) Set<String> fooSetString() {return null;} 304 public @AnnotType(7) E fooE() {return null;} 305 public @AnnotType(8) F fooF() {return null;} 306 public @AnnotType(9) <G> G fooG() {return null;} 307 308 public @AnnotType(10) Set<? extends Number> fooNumberSet() {return null;} 309 public @AnnotType(11) Set<@AnnotType(13) ? extends Number> fooNumberSet2() {return null;} 310 } 311 }