Skip to content
Permalink
10a76c9360
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
371 lines (353 sloc) 13.8 KB
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* <p>Helper methods for working with {@link Annotation} instances.</p>
*
* <p>This class contains various utility methods that make working with
* annotations simpler.</p>
*
* <p>{@link Annotation} instances are always proxy objects; unfortunately
* dynamic proxies cannot be depended upon to know how to implement certain
* methods in the same manner as would be done by "natural" {@link Annotation}s.
* The methods presented in this class can be used to avoid that possibility. It
* is of course also possible for dynamic proxies to actually delegate their
* e.g. {@link Annotation#equals(Object)}/{@link Annotation#hashCode()}/
* {@link Annotation#toString()} implementations to {@link AnnotationUtils}.</p>
*
* <p>#ThreadSafe#</p>
*
* @since 3.0
* @version $Id: AnnotationUtils.java 1083850 2011-03-21 15:59:10Z mbenson $
*/
public class AnnotationUtils {//tms change for seek in github
/**
* A style that prints annotations as recommended.
*/
private static final ToStringStyle TO_STRING_STYLE = new ToStringStyle() {
/** Serialization version */
private static final long serialVersionUID = 1L;
{
setDefaultFullDetail(true);
setArrayContentDetail(true);
setUseClassName(true);
setUseShortClassName(true);
setUseIdentityHashCode(false);
setContentStart("(");
setContentEnd(")");
setFieldSeparator(", ");
setArrayStart("[");
setArrayEnd("]");
}
/**
* {@inheritDoc}
*/
@Override
protected String getShortClassName(java.lang.Class<?> cls) {
Class<? extends Annotation> annotationType = null;
for (Class<?> iface : ClassUtils.getAllInterfaces(cls)) {
if (Annotation.class.isAssignableFrom(iface)) {
@SuppressWarnings("unchecked")
//because we just checked the assignability
Class<? extends Annotation> found = (Class<? extends Annotation>) iface;
annotationType = found;
break;
}
}
return new StringBuilder(annotationType == null ? "" : annotationType.getName())
.insert(0, '@').toString();
}
/**
* {@inheritDoc}
*/
@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
if (value instanceof Annotation) {
value = AnnotationUtils.toString((Annotation) value);
}
super.appendDetail(buffer, fieldName, value);
}
};
/**
* <p>{@code AnnotationUtils} instances should NOT be constructed in
* standard programming. Instead, the class should be used statically.</p>
*
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
*/
public AnnotationUtils() {
}
//-----------------------------------------------------------------------
/**
* <p>Checks if two annotations are equal using the criteria for equality
* presented in the {@link Annotation#equals(Object)} API docs.</p>
*
* @param a1 the first Annotation to compare, {@code null} returns
* {@code false} unless both are {@code null}
* @param a2 the second Annotation to compare, {@code null} returns
* {@code false} unless both are {@code null}
* @return {@code true} if the two annotations are {@code equal} or both
* {@code null}
*/
public static boolean equals(Annotation a1, Annotation a2) {
if (a1 == a2) {
return true;
}
if (a1 == null || a2 == null) {
return false;
}
Class<? extends Annotation> type = a1.annotationType();
Class<? extends Annotation> type2 = a2.annotationType();
Validate.notNull(type, "Annotation %s with null annotationType()", a1);
Validate.notNull(type2, "Annotation %s with null annotationType()", a2);
if (!type.equals(type2)) {
return false;
}
try {
for (Method m : type.getDeclaredMethods()) {
if (m.getParameterTypes().length == 0
&& isValidAnnotationMemberType(m.getReturnType())) {
Object v1 = m.invoke(a1);
Object v2 = m.invoke(a2);
if (!memberEquals(m.getReturnType(), v1, v2)) {
return false;
}
}
}
} catch (IllegalAccessException ex) {
return false;
} catch (InvocationTargetException ex) {
return false;
}
return true;
}
/**
* <p>Generate a hash code for the given annotation using the algorithm
* presented in the {@link Annotation#hashCode()} API docs.</p>
*
* @param a the Annotation for a hash code calculation is desired, not
* {@code null}
* @return the calculated hash code
* @throws RuntimeException if an {@code Exception} is encountered during
* annotation member access
* @throws IllegalStateException if an annotation method invocation returns
* {@code null}
*/
public static int hashCode(Annotation a) {
int result = 0;
Class<? extends Annotation> type = a.annotationType();
for (Method m : type.getDeclaredMethods()) {
try {
Object value = m.invoke(a);
if (value == null) {
throw new IllegalStateException(
String.format("Annotation method %s returned null", m));
}
result += hashMember(m.getName(), value);
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
return result;
}
/**
* <p>Generate a string representation of an Annotation, as suggested by
* {@link Annotation#toString()}.</p>
*
* @param a the annotation of which a string representation is desired
* @return the standard string representation of an annotation, not
* {@code null}
*/
public static String toString(final Annotation a) {
ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE);
for (Method m : a.annotationType().getDeclaredMethods()) {
if (m.getParameterTypes().length > 0) {
continue; //wtf?
}
try {
builder.append(m.getName(), m.invoke(a));
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
return builder.build();
}
/**
* <p>Checks if the specified type is permitted as an annotation member.</p>
*
* <p>The Java language specification only permits certain types to be used
* in annotations. These include {@link String}, {@link Class}, primitive
* types, {@link Annotation}, {@link Enum}, and single-dimensional arrays of
* these types.</p>
*
* @param type the type to check, {@code null}
* @return {@code true} if the type is a valid type to use in an annotation
*/
public static boolean isValidAnnotationMemberType(Class<?> type) {
if (type == null) {
return false;
}
if (type.isArray()) {
type = type.getComponentType();
}
return type.isPrimitive() || type.isEnum() || type.isAnnotation()
|| String.class.equals(type) || Class.class.equals(type);
}
//besides modularity, this has the advantage of autoboxing primitives:
/**
* Helper method for generating a hash code for a member of an annotation.
*
* @param name the name of the member
* @param value the value of the member
* @return a hash code for this member
*/
private static int hashMember(String name, Object value) {
int part1 = name.hashCode() * 127;
if (value.getClass().isArray()) {
return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value);
}
if (value instanceof Annotation) {
return part1 ^ hashCode((Annotation) value);
}
return part1 ^ value.hashCode();
}
/**
* Helper method for checking whether two objects of the given type are
* equal. This method is used to compare the parameters of two annotation
* instances.
*
* @param type the type of the objects to be compared
* @param o1 the first object
* @param o2 the second object
* @return a flag whether these objects are equal
*/
private static boolean memberEquals(Class<?> type, Object o1, Object o2) {
if (o1 == o2) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
if (type.isArray()) {
return arrayMemberEquals(type.getComponentType(), o1, o2);
}
if (type.isAnnotation()) {
return equals((Annotation) o1, (Annotation) o2);
}
return o1.equals(o2);
}
/**
* Helper method for comparing two objects of an array type.
*
* @param componentType the component type of the array
* @param o1 the first object
* @param o2 the second object
* @return a flag whether these objects are equal
*/
private static boolean arrayMemberEquals(Class<?> componentType, Object o1, Object o2) {
if (componentType.isAnnotation()) {
return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2);
}
if (componentType.equals(Byte.TYPE)) {
return Arrays.equals((byte[]) o1, (byte[]) o2);
}
if (componentType.equals(Short.TYPE)) {
return Arrays.equals((short[]) o1, (short[]) o2);
}
if (componentType.equals(Integer.TYPE)) {
return Arrays.equals((int[]) o1, (int[]) o2);
}
if (componentType.equals(Character.TYPE)) {
return Arrays.equals((char[]) o1, (char[]) o2);
}
if (componentType.equals(Long.TYPE)) {
return Arrays.equals((long[]) o1, (long[]) o2);
}
if (componentType.equals(Float.TYPE)) {
return Arrays.equals((float[]) o1, (float[]) o2);
}
if (componentType.equals(Double.TYPE)) {
return Arrays.equals((double[]) o1, (double[]) o2);
}
if (componentType.equals(Boolean.TYPE)) {
return Arrays.equals((boolean[]) o1, (boolean[]) o2);
}
return Arrays.equals((Object[]) o1, (Object[]) o2);
}
/**
* Helper method for comparing two arrays of annotations.
*
* @param a1 the first array
* @param a2 the second array
* @return a flag whether these arrays are equal
*/
private static boolean annotationArrayMemberEquals(Annotation[] a1, Annotation[] a2) {
if (a1.length != a2.length) {
return false;
}
for (int i = 0; i < a1.length; i++) {
if (!equals(a1[i], a2[i])) {
return false;
}
}
return true;
}
/**
* Helper method for generating a hash code for an array.
*
* @param componentType the component type of the array
* @param o the array
* @return a hash code for the specified array
*/
private static int arrayMemberHash(Class<?> componentType, Object o) {
if (componentType.equals(Byte.TYPE)) {
return Arrays.hashCode((byte[]) o);
}
if (componentType.equals(Short.TYPE)) {
return Arrays.hashCode((short[]) o);
}
if (componentType.equals(Integer.TYPE)) {
return Arrays.hashCode((int[]) o);
}
if (componentType.equals(Character.TYPE)) {
return Arrays.hashCode((char[]) o);
}
if (componentType.equals(Long.TYPE)) {
return Arrays.hashCode((long[]) o);
}
if (componentType.equals(Float.TYPE)) {
return Arrays.hashCode((float[]) o);
}
if (componentType.equals(Double.TYPE)) {
return Arrays.hashCode((double[]) o);
}
if (componentType.equals(Boolean.TYPE)) {
return Arrays.hashCode((boolean[]) o);
}
return Arrays.hashCode((Object[]) o);
}
}