Utilities/src/dorkbox/util/classes/ReflectionUtils.java

189 lines
6.9 KiB
Java

/*
* Copyright 2012 Benjamin Diedrichsen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*
* Copyright 2015 dorkbox, llc
*
* Licensed 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 dorkbox.util.classes;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import dorkbox.collections.IdentityMap;
/**
* @author bennidi
* Date: 2/16/12
* Time: 12:14 PM
* @author dorkbox
* Date: 2/2/15
*/
public final
class ReflectionUtils {
private static final Method[] EMPTY_METHODS = new Method[0];
private
ReflectionUtils() {
}
/**
* Get methods annotated with the specified annotation.
*
* @param target the class that you are looking for the methods on
* @param annotationClass the annotations that define the method you are looking for
* @param <A> the annotation type
*
* @return the array of methods that match the target + annotation
*/
public static
<A extends Annotation> Method[] getMethods(Class<?> target, Class<A> annotationClass) {
ArrayList<Method> methods = new ArrayList<Method>();
getMethods(target, annotationClass, methods);
return methods.toArray(EMPTY_METHODS);
}
private static
<A extends Annotation> void getMethods(Class<?> target, Class<A> annotationClass, ArrayList<Method> methods) {
try {
for (Method method : target.getDeclaredMethods()) {
if (getAnnotation(method, annotationClass) != null) {
methods.add(method);
}
}
} catch (Exception ignored) {
}
// recursively go until root
if (!target.equals(Object.class)) {
getMethods(target.getSuperclass(), annotationClass, methods);
}
}
/**
* Traverses the class hierarchy upwards, starting at the given subclass, looking
* for an override of the given methods -> finds the bottom most override of the given
* method if any exists
*/
public static
Method getOverridingMethod(final Method overridingMethod, final Class<?> subclass) {
Class<?> current = subclass;
while (!current.equals(overridingMethod.getDeclaringClass())) {
try {
return current.getDeclaredMethod(overridingMethod.getName(), overridingMethod.getParameterTypes());
} catch (NoSuchMethodException e) {
current = current.getSuperclass();
}
}
return null;
}
public static
boolean containsOverridingMethod(final Method[] allMethods, final Method methodToCheck) {
final int length = allMethods.length;
Method method;
for (int i = 0; i < length; i++) {
method = allMethods[i];
if (isOverriddenBy(methodToCheck, method)) {
return true;
}
}
return false;
}
/**
* Searches for an Annotation of the given type on the class. Supports meta annotations.
*
* @param from AnnotatedElement (class, method...)
* @param annotationType Annotation class to look for.
* @param <A> Class of annotation type
* @return Annotation instance or null
*/
private static
<A extends Annotation> A getAnnotation(AnnotatedElement from, Class<A> annotationType, IdentityMap<AnnotatedElement, Boolean> visited) {
if (visited.containsKey(from)) {
return null;
}
visited.put(from, Boolean.TRUE);
A ann = from.getAnnotation(annotationType);
if (ann != null) {
return ann;
}
for (Annotation metaAnn : from.getAnnotations()) {
ann = getAnnotation(metaAnn.annotationType(), annotationType, visited);
if (ann != null) {
return ann;
}
}
return null;
}
public static
<A extends Annotation> A getAnnotation(AnnotatedElement from, Class<A> annotationType) {
return getAnnotation(from, annotationType, new IdentityMap<AnnotatedElement, Boolean>());
}
private static
boolean isOverriddenBy(final Method superclassMethod, final Method subclassMethod) {
// if the declaring classes are the same or the subclass method is not defined in the subclass
// hierarchy of the given superclass method or the method names are not the same then
// subclassMethod does not override superclassMethod
if (superclassMethod.getDeclaringClass().equals(subclassMethod.getDeclaringClass()) ||
!superclassMethod.getDeclaringClass().isAssignableFrom(subclassMethod.getDeclaringClass()) ||
!superclassMethod.getName().equals(subclassMethod.getName())) {
return false;
}
final Class<?>[] superClassMethodParameters = superclassMethod.getParameterTypes();
final Class<?>[] subClassMethodParameters = subclassMethod.getParameterTypes();
// method must specify the same number of parameters
//the parameters must occur in the exact same order
for (int i = 0; i < subClassMethodParameters.length; i++) {
if (!superClassMethodParameters[i].equals(subClassMethodParameters[i])) {
return false;
}
}
return true;
}
}