001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.lang3; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.Method; 021import java.lang.reflect.Modifier; 022import java.util.Arrays; 023 024import org.apache.commons.lang3.builder.AbstractReflection; 025import org.apache.commons.lang3.builder.ToStringBuilder; 026import org.apache.commons.lang3.builder.ToStringStyle; 027import org.apache.commons.lang3.exception.UncheckedException; 028 029/** 030 * Helper methods for working with {@link Annotation} instances. 031 * 032 * <p> 033 * This class contains various utility methods that make working with 034 * annotations simpler. 035 * </p> 036 * 037 * <p> 038 * {@link Annotation} instances are always proxy objects; unfortunately 039 * dynamic proxies cannot be depended upon to know how to implement certain 040 * methods in the same manner as would be done by "natural" {@link Annotation}s. 041 * The methods presented in this class can be used to avoid that possibility. It 042 * is of course also possible for dynamic proxies to actually delegate their 043 * e.g. {@link Annotation#equals(Object)}/{@link Annotation#hashCode()}/ 044 * {@link Annotation#toString()} implementations to {@link AnnotationUtils}. 045 * </p> 046 * 047 * <p> 048 * #ThreadSafe# 049 * </p> 050 * 051 * @since 3.0 052 */ 053public class AnnotationUtils { 054 055 /** 056 * A style that prints annotations as recommended. 057 */ 058 private static final ToStringStyle TO_STRING_STYLE = new ToStringStyle() { 059 060 /** Serialization version */ 061 private static final long serialVersionUID = 1L; 062 063 { 064 setDefaultFullDetail(true); 065 setArrayContentDetail(true); 066 setUseClassName(true); 067 setUseShortClassName(true); 068 setUseIdentityHashCode(false); 069 setContentStart("("); 070 setContentEnd(")"); 071 setFieldSeparator(", "); 072 setArrayStart("["); 073 setArrayEnd("]"); 074 } 075 076 /** 077 * {@inheritDoc} 078 */ 079 @Override 080 protected void appendDetail(final StringBuffer buffer, final String fieldName, Object value) { 081 if (value instanceof Annotation) { 082 value = AnnotationUtils.toString((Annotation) value); 083 } 084 super.appendDetail(buffer, fieldName, value); 085 } 086 087 /** 088 * {@inheritDoc} 089 */ 090 @Override 091 protected String getShortClassName(final Class<?> cls) { 092 // formatter:off 093 return ClassUtils.getAllInterfaces(cls).stream().filter(Annotation.class::isAssignableFrom).findFirst() 094 .map(iface -> "@" + iface.getName()) 095 .orElse(StringUtils.EMPTY); 096 // formatter:on 097 } 098 099 }; 100 101 /** 102 * Helper method for comparing two arrays of annotations. 103 * 104 * @param a1 The first array 105 * @param a2 The second array 106 * @return A flag whether these arrays are equal 107 */ 108 private static boolean annotationArrayMemberEquals(final Annotation[] a1, final Annotation[] a2) { 109 if (a1.length != a2.length) { 110 return false; 111 } 112 for (int i = 0; i < a1.length; i++) { 113 if (!equals(a1[i], a2[i])) { 114 return false; 115 } 116 } 117 return true; 118 } 119 120 /** 121 * Generates an array hash code using {@link #hashCode(Annotation)} for each non-null element. 122 * 123 * <p>Uses the element order, initial value, multiplier, and null-element handling of {@link Arrays#hashCode(Object[])}.</p> 124 * 125 * @param annotations The array 126 * @return A hash code for the specified array 127 */ 128 private static int annotationArrayMemberHash(final Annotation[] annotations) { 129 int result = 1; 130 for (final Annotation annotation : annotations) { 131 result = 31 * result + (annotation == null ? 0 : hashCode(annotation)); 132 } 133 return result; 134 } 135 136 /** 137 * Helper method for comparing two objects of an array type. 138 * 139 * @param componentType The component type of the array 140 * @param o1 The first object 141 * @param o2 The second object 142 * @return A flag whether these objects are equal 143 */ 144 private static boolean arrayMemberEquals(final Class<?> componentType, final Object o1, final Object o2) { 145 if (componentType.isAnnotation()) { 146 return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2); 147 } 148 if (componentType.equals(Byte.TYPE)) { 149 return Arrays.equals((byte[]) o1, (byte[]) o2); 150 } 151 if (componentType.equals(Short.TYPE)) { 152 return Arrays.equals((short[]) o1, (short[]) o2); 153 } 154 if (componentType.equals(Integer.TYPE)) { 155 return Arrays.equals((int[]) o1, (int[]) o2); 156 } 157 if (componentType.equals(Character.TYPE)) { 158 return Arrays.equals((char[]) o1, (char[]) o2); 159 } 160 if (componentType.equals(Long.TYPE)) { 161 return Arrays.equals((long[]) o1, (long[]) o2); 162 } 163 if (componentType.equals(Float.TYPE)) { 164 return Arrays.equals((float[]) o1, (float[]) o2); 165 } 166 if (componentType.equals(Double.TYPE)) { 167 return Arrays.equals((double[]) o1, (double[]) o2); 168 } 169 if (componentType.equals(Boolean.TYPE)) { 170 return Arrays.equals((boolean[]) o1, (boolean[]) o2); 171 } 172 return Arrays.equals((Object[]) o1, (Object[]) o2); 173 } 174 175 /** 176 * Helper method for generating a hash code for an array. 177 * 178 * @param componentType The component type of the array 179 * @param o The array 180 * @return A hash code for the specified array 181 */ 182 private static int arrayMemberHash(final Class<?> componentType, final Object o) { 183 if (componentType.isAnnotation()) { 184 return annotationArrayMemberHash((Annotation[]) o); 185 } 186 if (componentType.equals(Byte.TYPE)) { 187 return Arrays.hashCode((byte[]) o); 188 } 189 if (componentType.equals(Short.TYPE)) { 190 return Arrays.hashCode((short[]) o); 191 } 192 if (componentType.equals(Integer.TYPE)) { 193 return Arrays.hashCode((int[]) o); 194 } 195 if (componentType.equals(Character.TYPE)) { 196 return Arrays.hashCode((char[]) o); 197 } 198 if (componentType.equals(Long.TYPE)) { 199 return Arrays.hashCode((long[]) o); 200 } 201 if (componentType.equals(Float.TYPE)) { 202 return Arrays.hashCode((float[]) o); 203 } 204 if (componentType.equals(Double.TYPE)) { 205 return Arrays.hashCode((double[]) o); 206 } 207 if (componentType.equals(Boolean.TYPE)) { 208 return Arrays.hashCode((boolean[]) o); 209 } 210 return Arrays.hashCode((Object[]) o); 211 } 212 213 /** 214 * Checks if two annotations are equal using the criteria for equality 215 * presented in the {@link Annotation#equals(Object)} API docs. 216 * 217 * @param a1 The first Annotation to compare, {@code null} returns 218 * {@code false} unless both are {@code null} 219 * @param a2 The second Annotation to compare, {@code null} returns 220 * {@code false} unless both are {@code null} 221 * @return {@code true} if the two annotations are {@code equal} or both 222 * {@code null} 223 */ 224 public static boolean equals(final Annotation a1, final Annotation a2) { 225 if (a1 == a2) { 226 return true; 227 } 228 if (a1 == null || a2 == null) { 229 return false; 230 } 231 final Class<? extends Annotation> type1 = a1.annotationType(); 232 final Class<? extends Annotation> type2 = a2.annotationType(); 233 Validate.notNull(type1, "Annotation %s with null annotationType()", a1); 234 Validate.notNull(type2, "Annotation %s with null annotationType()", a2); 235 if (!type1.equals(type2)) { 236 return false; 237 } 238 try { 239 for (final Method m : type1.getDeclaredMethods()) { 240 if (isAnnotationMember(m)) { 241 final Object v1 = invokeAnnotationMember(m, a1); 242 final Object v2 = invokeAnnotationMember(m, a2); 243 if (!memberEquals(m.getReturnType(), v1, v2)) { 244 return false; 245 } 246 } 247 } 248 } catch (final ReflectiveOperationException ex) { 249 throw new IllegalStateException(ex); 250 } 251 return true; 252 } 253 254 /** 255 * Generate a hash code for the given annotation using the algorithm 256 * presented in the {@link Annotation#hashCode()} API docs. 257 * 258 * @param a The Annotation for a hash code calculation is desired, not 259 * {@code null} 260 * @return The calculated hash code 261 * @throws RuntimeException Thrown if an {@link Exception} is encountered during annotation member access. 262 * @throws IllegalStateException Thrown if an annotation method invocation returns {@code null}. 263 */ 264 public static int hashCode(final Annotation a) { 265 int result = 0; 266 final Class<? extends Annotation> type = a.annotationType(); 267 for (final Method m : type.getDeclaredMethods()) { 268 if (!isAnnotationMember(m)) { 269 continue; 270 } 271 try { 272 final Object value = invokeAnnotationMember(m, a); 273 if (value == null) { 274 throw new IllegalStateException(String.format("Annotation method %s returned null", m)); 275 } 276 result += hashMember(m.getName(), value); 277 } catch (final ReflectiveOperationException ex) { 278 throw new UncheckedException(ex); 279 } 280 } 281 return result; 282 } 283 284 //besides modularity, this has the advantage of autoboxing primitives: 285 /** 286 * Helper method for generating a hash code for a member of an annotation. 287 * 288 * @param name The name of the member 289 * @param value The value of the member 290 * @return A hash code for this member 291 */ 292 private static int hashMember(final String name, final Object value) { 293 final int part1 = name.hashCode() * 127; 294 if (ObjectUtils.isArray(value)) { 295 return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value); 296 } 297 if (value instanceof Annotation) { 298 return part1 ^ hashCode((Annotation) value); 299 } 300 return part1 ^ value.hashCode(); 301 } 302 303 /** 304 * Invokes an annotation member, enabling reflective access only when needed. 305 * 306 * @param method the annotation member method 307 * @param annotation the annotation instance 308 * @return the annotation member value 309 * @throws ReflectiveOperationException if the method cannot be invoked 310 */ 311 private static Object invokeAnnotationMember(final Method method, final Annotation annotation) 312 throws ReflectiveOperationException { 313 try { 314 return method.invoke(annotation); 315 } catch (final IllegalAccessException ex) { 316 if (!AbstractReflection.setAccessible(AbstractReflection.getForceAccessible(), method)) { 317 throw ex; 318 } 319 return method.invoke(annotation); 320 } 321 } 322 323 /** 324 * Tests whether the specified method declares an annotation member. 325 * 326 * @param method The method to check 327 * @return whether the method is public, abstract, non-synthetic, has no parameters, 328 * and has a valid annotation member return type 329 */ 330 private static boolean isAnnotationMember(final Method method) { 331 final int modifiers = method.getModifiers(); 332 return Modifier.isPublic(modifiers) && Modifier.isAbstract(modifiers) && !method.isSynthetic() 333 && method.getParameterCount() == 0 && isValidAnnotationMemberType(method.getReturnType()); 334 } 335 336 /** 337 * Tests whether the specified type is permitted as an annotation member. 338 * 339 * <p> 340 * The Java language specification only permits certain types to be used 341 * in annotations. These include {@link String}, {@link Class}, primitive 342 * types, {@link Annotation}, {@link Enum}, and single-dimensional arrays of 343 * these types. 344 * </p> 345 * 346 * @param type The type to check, {@code null} 347 * @return {@code true} if the type is a valid type to use in an annotation 348 */ 349 public static boolean isValidAnnotationMemberType(Class<?> type) { 350 if (type == null) { 351 return false; 352 } 353 if (type.isArray()) { 354 type = type.getComponentType(); 355 } 356 return type.isPrimitive() || type.isEnum() || type.isAnnotation() 357 || String.class.equals(type) || Class.class.equals(type); 358 } 359 360 /** 361 * Helper method for checking whether two objects of the given type are 362 * equal. This method is used to compare the parameters of two annotation 363 * instances. 364 * 365 * @param type The type of the objects to be compared 366 * @param o1 The first object 367 * @param o2 The second object 368 * @return A flag whether these objects are equal 369 */ 370 private static boolean memberEquals(final Class<?> type, final Object o1, final Object o2) { 371 if (o1 == o2) { 372 return true; 373 } 374 if (o1 == null || o2 == null) { 375 return false; 376 } 377 if (type.isArray()) { 378 return arrayMemberEquals(type.getComponentType(), o1, o2); 379 } 380 if (type.isAnnotation()) { 381 return equals((Annotation) o1, (Annotation) o2); 382 } 383 return o1.equals(o2); 384 } 385 386 /** 387 * Generate a string representation of an Annotation, as suggested by 388 * {@link Annotation#toString()}. 389 * 390 * @param a The annotation of which a string representation is desired 391 * @return The standard string representation of an annotation, not 392 * {@code null} 393 */ 394 public static String toString(final Annotation a) { 395 final ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE); 396 for (final Method m : a.annotationType().getDeclaredMethods()) { 397 if (!isAnnotationMember(m)) { 398 continue; 399 } 400 try { 401 builder.append(m.getName(), invokeAnnotationMember(m, a)); 402 } catch (final ReflectiveOperationException ex) { 403 throw new UncheckedException(ex); 404 } 405 } 406 return builder.build(); 407 } 408 409 /** 410 * {@link AnnotationUtils} instances should NOT be constructed in 411 * standard programming. Instead, the class should be used statically. 412 * 413 * <p> 414 * This constructor is public to permit tools that require a JavaBean 415 * instance to operate. 416 * </p> 417 * 418 * @deprecated TODO Make private in 4.0. 419 */ 420 @Deprecated 421 public AnnotationUtils() { 422 // empty 423 } 424}