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 } else { 144 if (annotations.length > 0 ) { 145 errors++; 146 System.err.println("Unexpected annotations present on " + annotType); 147 } 148 } 149 } 150 } 151 152 static void testEqualsReflexivity(Class<?> clazz) { 153 System.err.println("Testing reflexivity of equals on methods of class " + clazz.getName()); 154 Method[] methods = clazz.getDeclaredMethods(); 155 for (Method m : methods) { 156 checkTypesForEquality(m.getAnnotatedReturnType(), 157 m.getAnnotatedReturnType(), 158 true); 159 } 160 } 161 162 private static void checkTypesForEquality(AnnotatedType annotType1, 163 AnnotatedType annotType2, 164 boolean expected) { 165 boolean comparison = annotType1.equals(annotType2); 166 167 if (comparison) { 168 int hash1 = annotType1.hashCode(); 169 int hash2 = annotType1.hashCode(); 170 if (hash1 != hash2) { 171 errors++; 172 System.err.format("Equal AnnotatedTypes with unequal hash codes: %n%s%n%s%n", 173 annotType1.toString(), annotType2.toString()); 174 } 175 } 176 177 if (comparison != expected) { 178 errors++; 179 System.err.println(annotType1); 180 System.err.println(expected ? " is not equal to " : " is equal to "); 181 System.err.println(annotType2); 182 System.err.println(); 183 } 184 } 185 186 /* 187 * For each of the type host classes, the return type of a method 188 * should only equal the return type of that method. 189 */ 190 static void testEquals(Class<?> clazz) { 191 Method[] methods = clazz.getDeclaredMethods(); 192 193 for (int i = 0; i < methods.length; i++) { 194 for (int j = 0; j < methods.length; j++) { 195 if (i == j) 196 continue; 197 else { 198 checkTypesForEquality(methods[i].getAnnotatedReturnType(), 199 methods[j].getAnnotatedReturnType(), 200 false); 201 } 202 } 203 } 204 } 205 206 /** 207 * Roughly, compare the return types of corresponding methods on 208 * TypeHost and AnnotatedtypeHost and verify the AnnotatedType 209 * objects are *not* equal even if their underlying generic types 210 * are. 211 */ 212 static void testAnnotationsMatterForEquals(Class<?> clazz1, Class<?> clazz2) { 213 System.err.println("Testing that presence/absence of annotations matters for equals comparison."); 214 215 String methodName = null; 216 for (Method method : clazz1.getDeclaredMethods()) { 217 if ("void".equals(method.getReturnType().toString())) { 218 continue; 219 } 220 221 methodName = method.getName(); 222 try { 223 checkTypesForEquality(method.getAnnotatedReturnType(), 224 clazz2.getDeclaredMethod(methodName).getAnnotatedReturnType(), 225 false); 226 } catch (Exception e) { 227 errors++; 228 System.err.println("Method " + methodName + " not found."); 229 } 230 } 231 } 232 233 234 static void testWildcards() { 235 System.err.println("Testing wildcards"); 236 // public @AnnotType(10) Set<? extends Number> fooNumberSet() {return null;} 237 // public @AnnotType(11) Set<@AnnotType(13) ? extends Number> fooNumberSet2() {return null;} 238 AnnotatedWildcardType awt1 = extractWildcard("fooNumberSet"); 239 AnnotatedWildcardType awt2 = extractWildcard("fooNumberSet2"); 240 241 if (!awt1.equals(extractWildcard("fooNumberSet")) || 242 !awt2.equals(extractWildcard("fooNumberSet2"))) { 243 errors++; 244 System.err.println("Bad equality comparison on wildcards."); 245 } 246 247 checkTypesForEquality(awt1, awt2, false); 248 249 if (awt2.getAnnotations().length == 0) { 250 errors++; 251 System.err.println("Expected annotations not found."); 252 } 253 } 254 255 private static AnnotatedWildcardType extractWildcard(String methodName) { 256 try { 257 return (AnnotatedWildcardType) 258 (((AnnotatedParameterizedType)(AnnotatedTypeHost.class.getMethod(methodName). 259 getAnnotatedReturnType())). 260 getAnnotatedActualTypeArguments()[0] ); 261 } catch (Exception e) { 262 throw new RuntimeException(e); 263 } 264 } 265 266 static class TypeHost<E, F extends Number> { 267 public void fooVoid() {return;} 268 269 public int foo() {return 0;} 270 public String fooString() {return null;} 271 272 public int[] fooIntArray() {return null;} 273 public String[] fooStringArray() {return null;} 274 public String [][] fooStringArrayArray() {return null;} 275 276 public Set<String> fooSetString() {return null;} 277 public E fooE() {return null;} 278 public F fooF() {return null;} 279 public <G> G fooG() {return null;} 280 281 public Set<? extends Number> fooNumberSet() {return null;} 282 public Set<? extends Integer> fooNumberSet2() {return null;} 283 } 284 285 @Retention(RetentionPolicy.RUNTIME) 286 @Target(ElementType.TYPE_USE) 287 static @interface AnnotType { 288 int value() default 0; 289 } 290 291 static class AnnotatedTypeHost<E, F extends Number> { 292 public /*@AnnotType(0)*/ void fooVoid() {return;} // Illegal to annotate void 293 294 public @AnnotType(1) int foo() {return 0;} 295 public @AnnotType(2) String fooString() {return null;} 296 297 public int @AnnotType(3) [] fooIntArray() {return null;} 298 public String @AnnotType(4) [] fooStringArray() {return null;} 299 public @AnnotType(5) String @AnnotType(0) [] @AnnotType(1) [] fooStringArrayArray() {return null;} 300 301 public @AnnotType(6) Set<String> fooSetString() {return null;} 302 public @AnnotType(7) E fooE() {return null;} 303 public @AnnotType(8) F fooF() {return null;} 304 public @AnnotType(9) <G> G fooG() {return null;} 305 306 public @AnnotType(10) Set<? extends Number> fooNumberSet() {return null;} 307 public @AnnotType(11) Set<@AnnotType(13) ? extends Number> fooNumberSet2() {return null;} 308 } 309 }