Added/moved class utilities.
This commit is contained in:
parent
b70539c18e
commit
23d661d245
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package dorkbox.util.generics;
|
package dorkbox.util.classes;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
|
190
src/dorkbox/util/classes/ClassHierarchy.java
Normal file
190
src/dorkbox/util/classes/ClassHierarchy.java
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* 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.reflect.Array;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||||
|
|
||||||
|
import dorkbox.util.collections.IdentityMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dorkbox
|
||||||
|
* Date: 4/1/15
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
class ClassHierarchy {
|
||||||
|
|
||||||
|
private volatile IdentityMap<Class<?>, Class<?>> arrayCache;
|
||||||
|
private volatile IdentityMap<Class<?>, Class<?>[]> superClassesCache;
|
||||||
|
|
||||||
|
// Recommended for best performance while adhering to the "single writer principle". Must be static-final
|
||||||
|
private static final AtomicReferenceFieldUpdater<ClassHierarchy, IdentityMap> arrayREF =
|
||||||
|
AtomicReferenceFieldUpdater.newUpdater(ClassHierarchy.class,
|
||||||
|
IdentityMap.class,
|
||||||
|
"arrayCache");
|
||||||
|
|
||||||
|
private static final AtomicReferenceFieldUpdater<ClassHierarchy, IdentityMap> superClassesREF =
|
||||||
|
AtomicReferenceFieldUpdater.newUpdater(ClassHierarchy.class,
|
||||||
|
IdentityMap.class,
|
||||||
|
"superClassesCache");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These data structures are never reset because the class hierarchy doesn't change at runtime. This class uses the "single writer
|
||||||
|
* principle" for storing data, EVEN THOUGH it's not accessed by a single writer. This DOES NOT MATTER because duplicates DO NOT matter
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
ClassHierarchy(int loadFactor) {
|
||||||
|
this.arrayCache = new IdentityMap<Class<?>, Class<?>>(32, loadFactor);
|
||||||
|
this.superClassesCache = new IdentityMap<Class<?>, Class<?>[]>(32, loadFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* will return the class + parent classes as an array.
|
||||||
|
* if parameter clazz is of type array, then the super classes are of array type as well
|
||||||
|
* <p>
|
||||||
|
* race conditions will result in DUPLICATE answers, which we don't care if happens
|
||||||
|
* never returns null
|
||||||
|
* never reset (class hierarchy never changes during runtime)
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
Class<?>[] getClassAndSuperClasses(final Class<?> clazz) {
|
||||||
|
// access a snapshot of the subscriptions (single-writer-principle)
|
||||||
|
final IdentityMap<Class<?>, Class<?>[]> cache = cast(superClassesREF.get(this));
|
||||||
|
|
||||||
|
Class<?>[] classes = cache.get(clazz);
|
||||||
|
|
||||||
|
// duplicates DO NOT MATTER
|
||||||
|
if (classes == null) {
|
||||||
|
// publish all super types of class
|
||||||
|
final Iterator<Class<?>> superTypesIterator = getSuperTypes(clazz);
|
||||||
|
final ArrayList<Class<?>> newList = new ArrayList<Class<?>>(16);
|
||||||
|
|
||||||
|
Class<?> c;
|
||||||
|
final boolean isArray = clazz.isArray();
|
||||||
|
|
||||||
|
if (isArray) {
|
||||||
|
// have to add the original class to the front of the list
|
||||||
|
c = getArrayClass(clazz);
|
||||||
|
newList.add(c);
|
||||||
|
|
||||||
|
while (superTypesIterator.hasNext()) {
|
||||||
|
c = superTypesIterator.next();
|
||||||
|
c = getArrayClass(c);
|
||||||
|
|
||||||
|
if (c != clazz) {
|
||||||
|
newList.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// have to add the original class to the front of the list
|
||||||
|
newList.add(clazz);
|
||||||
|
|
||||||
|
while (superTypesIterator.hasNext()) {
|
||||||
|
c = superTypesIterator.next();
|
||||||
|
|
||||||
|
if (c != clazz) {
|
||||||
|
newList.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classes = new Class<?>[newList.size()];
|
||||||
|
newList.toArray(classes);
|
||||||
|
cache.put(clazz, classes);
|
||||||
|
|
||||||
|
// save this snapshot back to the original (single writer principle)
|
||||||
|
superClassesREF.lazySet(this, cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* race conditions will result in DUPLICATE answers, which we don't care if happens
|
||||||
|
* never returns null
|
||||||
|
* never resets (class hierarchy never changes during runtime)
|
||||||
|
*
|
||||||
|
* https://bugs.openjdk.java.net/browse/JDK-6525802 (fixed this in 2007, so Array.newInstance is just as fast (via intrinsics) new [])
|
||||||
|
* Cache is in place to keep GC down.
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
Class<?> getArrayClass(final Class<?> c) {
|
||||||
|
// access a snapshot of the subscriptions (single-writer-principle)
|
||||||
|
final IdentityMap<Class<?>, Class<?>> cache = cast(arrayREF.get(this));
|
||||||
|
|
||||||
|
Class<?> clazz = cache.get(c);
|
||||||
|
|
||||||
|
if (clazz == null) {
|
||||||
|
// messy, but the ONLY way to do it. Array super types are also arrays
|
||||||
|
final Object[] newInstance = (Object[]) Array.newInstance(c, 0);
|
||||||
|
clazz = newInstance.getClass();
|
||||||
|
cache.put(c, clazz);
|
||||||
|
|
||||||
|
// save this snapshot back to the original (single writer principle)
|
||||||
|
arrayREF.lazySet(this, cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect all directly and indirectly related super types (classes and interfaces) of a given class.
|
||||||
|
*
|
||||||
|
* @param from The root class to start with
|
||||||
|
* @return An array of classes, each representing a super type of the root class
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
Iterator<Class<?>> getSuperTypes(Class<?> from) {
|
||||||
|
// This must be a 'set' because there can be duplicates, depending on the object hierarchy
|
||||||
|
final IdentityMap<Class<?>, Boolean> superclasses = new IdentityMap<Class<?>, Boolean>();
|
||||||
|
collectInterfaces(from, superclasses);
|
||||||
|
|
||||||
|
while (!from.equals(Object.class) && !from.isInterface()) {
|
||||||
|
superclasses.put(from.getSuperclass(), Boolean.TRUE);
|
||||||
|
from = from.getSuperclass();
|
||||||
|
collectInterfaces(from, superclasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
return superclasses.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
void collectInterfaces(Class<?> from, IdentityMap<Class<?>, Boolean> accumulator) {
|
||||||
|
for (Class<?> intface : from.getInterfaces()) {
|
||||||
|
accumulator.put(intface, Boolean.TRUE);
|
||||||
|
collectInterfaces(intface, accumulator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the caches, should only be called on shutdown
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
void shutdown() {
|
||||||
|
this.arrayCache.clear();
|
||||||
|
this.superClassesCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static
|
||||||
|
<T> T cast(Object obj) {
|
||||||
|
return (T) obj;
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package dorkbox.util;
|
package dorkbox.util.classes;
|
||||||
|
|
||||||
public abstract class ClassResolver {
|
public abstract class ClassResolver {
|
||||||
/**
|
/**
|
118
src/dorkbox/util/classes/FastThreadLocal.java
Normal file
118
src/dorkbox/util/classes/FastThreadLocal.java
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2012-2014 Lightweight Java Game Library Project
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
* permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright notice, this list
|
||||||
|
* of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.
|
||||||
|
* - Neither the name of 'Light Weight Java Game Library' nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||||
|
* OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package dorkbox.util.classes;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fast {@code ThreadLocal} implementation, adapted from the
|
||||||
|
* <a href="https://github.com/riven8192/LibStruct/blob/master/src/net/indiespot/struct/runtime/FastThreadLocal.java">LibStruct</a> library.
|
||||||
|
*
|
||||||
|
* <p>This implementation replaces the {@code ThreadLocalMap} lookup in {@link ThreadLocal} with a simple array access. The big advantage of this method is
|
||||||
|
* that thread-local accesses are identified as invariant by the JVM, which enables significant code-motion optimizations.</p>
|
||||||
|
*
|
||||||
|
* <p>The underlying array contains a slot for each thread that uses the {@link FastThreadLocal} instance. The slot is indexed by {@link Thread#getId()}. The
|
||||||
|
* array grows if necessary when the {@link #set} method is called.</p>
|
||||||
|
*
|
||||||
|
* <p>It is assumed that usages of this class will be read heavy, so any contention/false-sharing issues caused by the {@link #set} method are ignored.</p>
|
||||||
|
*
|
||||||
|
* @param <T> the thread-local value type
|
||||||
|
*
|
||||||
|
* @author Riven
|
||||||
|
* @see ThreadLocal
|
||||||
|
*/
|
||||||
|
public class FastThreadLocal<T> {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private T[] threadIDMap = (T[])new Object[1];
|
||||||
|
|
||||||
|
/** Creates a thread local variable. */
|
||||||
|
public
|
||||||
|
FastThreadLocal() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current thread's "initial value" for this thread-local variable.
|
||||||
|
*
|
||||||
|
* @see ThreadLocal#initialValue()
|
||||||
|
*/
|
||||||
|
public T initialValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current thread's copy of this thread-local variable to the specified value.
|
||||||
|
*
|
||||||
|
* @param value the value to be stored in the current thread's copy of this thread-local.
|
||||||
|
*
|
||||||
|
* @see ThreadLocal#set(T)
|
||||||
|
*/
|
||||||
|
public void set(T value) {
|
||||||
|
int id = (int)Thread.currentThread().getId();
|
||||||
|
|
||||||
|
synchronized ( this ) {
|
||||||
|
int len = threadIDMap.length;
|
||||||
|
if (len <= id) {
|
||||||
|
threadIDMap = Arrays.copyOf(threadIDMap, id + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
threadIDMap[id] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value in the current thread's copy of this thread-local variable.
|
||||||
|
*
|
||||||
|
* @see ThreadLocal#get()
|
||||||
|
*/
|
||||||
|
public final T get() {
|
||||||
|
int id = (int)Thread.currentThread().getId();
|
||||||
|
|
||||||
|
T[] threadIDMap = this.threadIDMap; // It's OK if the array is resized after this access, will just use the old array.
|
||||||
|
|
||||||
|
T value = threadIDMap.length <= id ? null : threadIDMap[id];
|
||||||
|
|
||||||
|
if ( value == null ) {
|
||||||
|
value = initialValue();
|
||||||
|
set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the current thread's value for this thread-local variable.
|
||||||
|
*
|
||||||
|
* @see ThreadLocal#remove()
|
||||||
|
*/
|
||||||
|
public void remove() {
|
||||||
|
set(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
187
src/dorkbox/util/classes/ReflectionUtils.java
Normal file
187
src/dorkbox/util/classes/ReflectionUtils.java
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.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
|
||||||
|
* @param annotationClass
|
||||||
|
* @param <A>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user