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.util.Arrays;
022
023import org.apache.commons.lang3.builder.AbstractReflection;
024import org.apache.commons.lang3.builder.ToStringBuilder;
025import org.apache.commons.lang3.builder.ToStringStyle;
026import org.apache.commons.lang3.exception.UncheckedException;
027
028/**
029 * Helper methods for working with {@link Annotation} instances.
030 *
031 * <p>
032 * This class contains various utility methods that make working with
033 * annotations simpler.
034 * </p>
035 *
036 * <p>
037 * {@link Annotation} instances are always proxy objects; unfortunately
038 * dynamic proxies cannot be depended upon to know how to implement certain
039 * methods in the same manner as would be done by "natural" {@link Annotation}s.
040 * The methods presented in this class can be used to avoid that possibility. It
041 * is of course also possible for dynamic proxies to actually delegate their
042 * e.g. {@link Annotation#equals(Object)}/{@link Annotation#hashCode()}/
043 * {@link Annotation#toString()} implementations to {@link AnnotationUtils}.
044 * </p>
045 *
046 * <p>
047 * #ThreadSafe#
048 * </p>
049 *
050 * @since 3.0
051 */
052public class AnnotationUtils {
053
054    /**
055     * A style that prints annotations as recommended.
056     */
057    private static final ToStringStyle TO_STRING_STYLE = new ToStringStyle() {
058
059        /** Serialization version */
060        private static final long serialVersionUID = 1L;
061
062        {
063            setDefaultFullDetail(true);
064            setArrayContentDetail(true);
065            setUseClassName(true);
066            setUseShortClassName(true);
067            setUseIdentityHashCode(false);
068            setContentStart("(");
069            setContentEnd(")");
070            setFieldSeparator(", ");
071            setArrayStart("[");
072            setArrayEnd("]");
073        }
074
075        /**
076         * {@inheritDoc}
077         */
078        @Override
079        protected void appendDetail(final StringBuffer buffer, final String fieldName, Object value) {
080            if (value instanceof Annotation) {
081                value = AnnotationUtils.toString((Annotation) value);
082            }
083            super.appendDetail(buffer, fieldName, value);
084        }
085
086        /**
087         * {@inheritDoc}
088         */
089        @Override
090        protected String getShortClassName(final Class<?> cls) {
091            // formatter:off
092            return ClassUtils.getAllInterfaces(cls).stream().filter(Annotation.class::isAssignableFrom).findFirst()
093                .map(iface -> "@" + iface.getName())
094                .orElse(StringUtils.EMPTY);
095            // formatter:on
096        }
097
098    };
099
100    /**
101     * Helper method for comparing two arrays of annotations.
102     *
103     * @param a1 The first array
104     * @param a2 The second array
105     * @return A flag whether these arrays are equal
106     */
107    private static boolean annotationArrayMemberEquals(final Annotation[] a1, final Annotation[] a2) {
108        if (a1.length != a2.length) {
109            return false;
110        }
111        for (int i = 0; i < a1.length; i++) {
112            if (!equals(a1[i], a2[i])) {
113                return false;
114            }
115        }
116        return true;
117    }
118
119    /**
120     * Helper method for comparing two objects of an array type.
121     *
122     * @param componentType The component type of the array
123     * @param o1 The first object
124     * @param o2 The second object
125     * @return A flag whether these objects are equal
126     */
127    private static boolean arrayMemberEquals(final Class<?> componentType, final Object o1, final Object o2) {
128        if (componentType.isAnnotation()) {
129            return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2);
130        }
131        if (componentType.equals(Byte.TYPE)) {
132            return Arrays.equals((byte[]) o1, (byte[]) o2);
133        }
134        if (componentType.equals(Short.TYPE)) {
135            return Arrays.equals((short[]) o1, (short[]) o2);
136        }
137        if (componentType.equals(Integer.TYPE)) {
138            return Arrays.equals((int[]) o1, (int[]) o2);
139        }
140        if (componentType.equals(Character.TYPE)) {
141            return Arrays.equals((char[]) o1, (char[]) o2);
142        }
143        if (componentType.equals(Long.TYPE)) {
144            return Arrays.equals((long[]) o1, (long[]) o2);
145        }
146        if (componentType.equals(Float.TYPE)) {
147            return Arrays.equals((float[]) o1, (float[]) o2);
148        }
149        if (componentType.equals(Double.TYPE)) {
150            return Arrays.equals((double[]) o1, (double[]) o2);
151        }
152        if (componentType.equals(Boolean.TYPE)) {
153            return Arrays.equals((boolean[]) o1, (boolean[]) o2);
154        }
155        return Arrays.equals((Object[]) o1, (Object[]) o2);
156    }
157
158    /**
159     * Helper method for generating a hash code for an array.
160     *
161     * @param componentType The component type of the array
162     * @param o The array
163     * @return A hash code for the specified array
164     */
165    private static int arrayMemberHash(final Class<?> componentType, final Object o) {
166        if (componentType.equals(Byte.TYPE)) {
167            return Arrays.hashCode((byte[]) o);
168        }
169        if (componentType.equals(Short.TYPE)) {
170            return Arrays.hashCode((short[]) o);
171        }
172        if (componentType.equals(Integer.TYPE)) {
173            return Arrays.hashCode((int[]) o);
174        }
175        if (componentType.equals(Character.TYPE)) {
176            return Arrays.hashCode((char[]) o);
177        }
178        if (componentType.equals(Long.TYPE)) {
179            return Arrays.hashCode((long[]) o);
180        }
181        if (componentType.equals(Float.TYPE)) {
182            return Arrays.hashCode((float[]) o);
183        }
184        if (componentType.equals(Double.TYPE)) {
185            return Arrays.hashCode((double[]) o);
186        }
187        if (componentType.equals(Boolean.TYPE)) {
188            return Arrays.hashCode((boolean[]) o);
189        }
190        return Arrays.hashCode((Object[]) o);
191    }
192
193    /**
194     * Checks if two annotations are equal using the criteria for equality
195     * presented in the {@link Annotation#equals(Object)} API docs.
196     *
197     * @param a1 The first Annotation to compare, {@code null} returns
198     * {@code false} unless both are {@code null}
199     * @param a2 The second Annotation to compare, {@code null} returns
200     * {@code false} unless both are {@code null}
201     * @return {@code true} if the two annotations are {@code equal} or both
202     * {@code null}
203     */
204    public static boolean equals(final Annotation a1, final Annotation a2) {
205        if (a1 == a2) {
206            return true;
207        }
208        if (a1 == null || a2 == null) {
209            return false;
210        }
211        final Class<? extends Annotation> type1 = a1.annotationType();
212        final Class<? extends Annotation> type2 = a2.annotationType();
213        Validate.notNull(type1, "Annotation %s with null annotationType()", a1);
214        Validate.notNull(type2, "Annotation %s with null annotationType()", a2);
215        if (!type1.equals(type2)) {
216            return false;
217        }
218        try {
219            for (final Method m : type1.getDeclaredMethods()) {
220                if (m.getParameterTypes().length == 0
221                        && isValidAnnotationMemberType(m.getReturnType())) {
222                    AbstractReflection.setAccessible(AbstractReflection.getForceAccessible(), m);
223                    final Object v1 = m.invoke(a1);
224                    final Object v2 = m.invoke(a2);
225                    if (!memberEquals(m.getReturnType(), v1, v2)) {
226                        return false;
227                    }
228                }
229            }
230        } catch (final ReflectiveOperationException ex) {
231            throw new IllegalStateException(ex);
232        }
233        return true;
234    }
235
236    /**
237     * Generate a hash code for the given annotation using the algorithm
238     * presented in the {@link Annotation#hashCode()} API docs.
239     *
240     * @param a The Annotation for a hash code calculation is desired, not
241     * {@code null}
242     * @return The calculated hash code
243     * @throws RuntimeException Thrown if an {@link Exception} is encountered during annotation member access.
244     * @throws IllegalStateException Thrown if an annotation method invocation returns {@code null}.
245     */
246    public static int hashCode(final Annotation a) {
247        int result = 0;
248        final Class<? extends Annotation> type = a.annotationType();
249        for (final Method m : type.getDeclaredMethods()) {
250            try {
251                AbstractReflection.setAccessible(AbstractReflection.getForceAccessible(), m);
252                final Object value = m.invoke(a);
253                if (value == null) {
254                    throw new IllegalStateException(String.format("Annotation method %s returned null", m));
255                }
256                result += hashMember(m.getName(), value);
257            } catch (final ReflectiveOperationException ex) {
258                throw new UncheckedException(ex);
259            }
260        }
261        return result;
262    }
263
264    //besides modularity, this has the advantage of autoboxing primitives:
265    /**
266     * Helper method for generating a hash code for a member of an annotation.
267     *
268     * @param name The name of the member
269     * @param value The value of the member
270     * @return A hash code for this member
271     */
272    private static int hashMember(final String name, final Object value) {
273        final int part1 = name.hashCode() * 127;
274        if (ObjectUtils.isArray(value)) {
275            return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value);
276        }
277        if (value instanceof Annotation) {
278            return part1 ^ hashCode((Annotation) value);
279        }
280        return part1 ^ value.hashCode();
281    }
282
283    /**
284     * Tests whether the specified type is permitted as an annotation member.
285     *
286     * <p>
287     * The Java language specification only permits certain types to be used
288     * in annotations. These include {@link String}, {@link Class}, primitive
289     * types, {@link Annotation}, {@link Enum}, and single-dimensional arrays of
290     * these types.
291     * </p>
292     *
293     * @param type The type to check, {@code null}
294     * @return {@code true} if the type is a valid type to use in an annotation
295     */
296    public static boolean isValidAnnotationMemberType(Class<?> type) {
297        if (type == null) {
298            return false;
299        }
300        if (type.isArray()) {
301            type = type.getComponentType();
302        }
303        return type.isPrimitive() || type.isEnum() || type.isAnnotation()
304                || String.class.equals(type) || Class.class.equals(type);
305    }
306
307    /**
308     * Helper method for checking whether two objects of the given type are
309     * equal. This method is used to compare the parameters of two annotation
310     * instances.
311     *
312     * @param type The type of the objects to be compared
313     * @param o1 The first object
314     * @param o2 The second object
315     * @return A flag whether these objects are equal
316     */
317    private static boolean memberEquals(final Class<?> type, final Object o1, final Object o2) {
318        if (o1 == o2) {
319            return true;
320        }
321        if (o1 == null || o2 == null) {
322            return false;
323        }
324        if (type.isArray()) {
325            return arrayMemberEquals(type.getComponentType(), o1, o2);
326        }
327        if (type.isAnnotation()) {
328            return equals((Annotation) o1, (Annotation) o2);
329        }
330        return o1.equals(o2);
331    }
332
333    /**
334     * Generate a string representation of an Annotation, as suggested by
335     * {@link Annotation#toString()}.
336     *
337     * @param a The annotation of which a string representation is desired
338     * @return The standard string representation of an annotation, not
339     * {@code null}
340     */
341    public static String toString(final Annotation a) {
342        final ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE);
343        for (final Method m : a.annotationType().getDeclaredMethods()) {
344            if (m.getParameterTypes().length > 0) {
345                continue; // what?
346            }
347            try {
348                AbstractReflection.setAccessible(AbstractReflection.getForceAccessible(), m);
349                builder.append(m.getName(), m.invoke(a));
350            } catch (final ReflectiveOperationException ex) {
351                throw new UncheckedException(ex);
352            }
353        }
354        return builder.build();
355    }
356
357    /**
358     * {@link AnnotationUtils} instances should NOT be constructed in
359     * standard programming. Instead, the class should be used statically.
360     *
361     * <p>
362     * This constructor is public to permit tools that require a JavaBean
363     * instance to operate.
364     * </p>
365     *
366     * @deprecated TODO Make private in 4.0.
367     */
368    @Deprecated
369    public AnnotationUtils() {
370        // empty
371    }
372}