com.healthmarketscience.common.util.ClassUtil Maven / Gradle / Ivy
The newest version!
/*
Copyright (c) 2008 Health Market Science, Inc.
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 com.healthmarketscience.common.util;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* Static utility methods for working with classes and reflection.
*
*/
public class ClassUtil {
/**
* Get a collection of fields of the given object with the given annotation.
*
* @param the type of the annotation to look for.
* @param instance the object to scan for annotated fields.
* @param annotationClass the type of the annotation to look for.
* @param inherit whether to get fields from super classes.
* @return a collection of pairs of fields and their annotations
*/
public static
Collection> findAnnotatedFields(
Object instance,
final Class annotationClass,
boolean inherit) {
return findAnnotatedElements(annotationClass,
getFields(instance.getClass(), inherit));
}
/**
* Get a collection of methods of the given object with the given
* annotation.
*
* @param the type of the annotation to look for.
* @param instance the object to scan for annotated methods.
* @param annotationClass the type of the annotation to look for.
* @param inherit whether to get methods from super classes.
* @return a collection of pairs of methods and their annotations
*/
public static
Collection> findAnnotatedMethods(
Object instance,
final Class annotationClass,
boolean inherit) {
return findAnnotatedElements(annotationClass,
getMethods(instance.getClass(), inherit));
}
/**
* Search for elements (fields or methods) which have the given
* annotation.
*
* @param annotationClass the annotation to search for
* @param members the members to search over
* @return a collection of the members with the annotation (an empty
* collection if none are found)
*/
public static
Collection> findAnnotatedElements(
Class annotationClass, MType... members ) {
List> res = new ArrayList>();
for (MType member : members) {
AType ann = member.getAnnotation(annotationClass);
if (null != ann) {
res.add(Tuple2.create(ann, member));
}
}
return res;
}
/**
* Test if the class is Boolean or boolean.
*/
public static boolean isBoolean( Class> clazz ) {
return clazz.equals(boolean.class)
|| clazz.equals(Boolean.class);
}
/**
* Test if the method takes one and only one arg which is a Boolean or
* boolean.
*/
public static boolean methodTakesBoolean ( Method method ) {
return (method.getParameterTypes().length == 1)
&& (isBoolean(method.getParameterTypes()[0]));
}
/**
* Test if the field is a Boolean or boolean.
*/
public static boolean fieldIsBoolean (Field field) {
return isBoolean(field.getType());
}
/**
* Gets the declared fields in the given class and, optionally, visible
* fields in any super class.
*
* @param clazz the class for which to retrieve the fields
* @param inherit whether or not to search super classes
*
* @return non-{@code null} array of found fields
*/
private static Field[] getFields(Class> clazz, boolean inherit) {
Set fields = getFields(clazz, clazz, inherit);
return fields.toArray(new Field[fields.size()]);
}
/**
* Gets the declared methods in the given class and, optionally, visible
* methods in any super class.
*
* @param clazz the class for which to retrieve the methods
* @param inherit whether or not to search super classes
*
* @return non-{@code null} array of found methods
*/
private static Method[] getMethods(Class> clazz, boolean inherit) {
Set methods = getMethods(clazz, clazz, inherit);
return methods.toArray(new Method[methods.size()]);
}
/**
* Gets the fields in the given class visible to the given lowestClass and,
* optionally, visible fields in any super class.
*
* @param clazz the class for which to retrieve the fields
* @param lowestClass the class to which all found fields must be visible
* @param inherit whether or not to search super classes
*
* @return non-{@code null} collection of found fields
*/
private static Set getFields(
Class> clazz, Class> lowestClass, boolean inherit) {
// Use linked hash set to preserve insertion order
return collectFields(new LinkedHashSet(),
clazz, lowestClass, inherit);
}
/**
* Gets the methods in the given class visible to the given lowestClass and,
* optionally, visible methods in any super class.
*
* @param clazz the class for which to retrieve the methods
* @param lowestClass the class to which all found methods must be visible
* @param inherit whether or not to search super classes
*
* @return non-{@code null} collection of found methods
*/
private static Set getMethods(
Class> clazz, Class> lowestClass, boolean inherit) {
// Use linked hash set to preserve insertion order
return collectMethods(new LinkedHashSet(),
clazz, lowestClass, inherit);
}
/**
* Adds the fields in the given class visible to the given lowestClass and,
* optionally, visible fields in any super class to the given collection of
* fields.
*
* @param fields collection in which to accumulate the found fields
* @param clazz the class for which to retrieve the fields
* @param lowestClass the class to which all found fields must be visible
* @param inherit whether or not to search super classes
*
* @return the given collection of fields, possibly modified
*/
private static Set collectFields(
Set fields, Class> clazz, Class> lowestClass,
boolean inherit)
{
addAllVisible(fields, lowestClass, clazz.getDeclaredFields());
Class> superClass = clazz.getSuperclass();
if (inherit && (superClass != null)) {
collectFields(fields, superClass, lowestClass, inherit);
}
return fields;
}
/**
* Adds the methods in the given class visible to the given lowestClass and,
* optionally, visible methods in any super class to the given collection of
* methods.
*
* @param methods collection in which to accumulate the found methods
* @param clazz the class for which to retrieve the methods
* @param lowestClass the class to which all found methods must be visible
* @param inherit whether or not to search super classes
*
* @return the given collection of methods, possibly modified
*/
private static Set collectMethods(
Set methods, Class> clazz, Class> lowestClass,
boolean inherit)
{
addAllVisible(methods, lowestClass, clazz.getDeclaredMethods());
Class> superClass = clazz.getSuperclass();
if (inherit && (superClass != null)) {
collectMethods(methods, superClass, lowestClass, inherit);
}
return methods;
}
/**
* Adds the members from the given array of members which are visible to the
* given class to the given collection of visibleMembers.
*
* @param visibleMembers collection to which to add visible members
* @param clazz to use to test visibility
* @param members members to add if visible
*/
private static void addAllVisible(
Collection visibleMembers, Class> clazz, T... members)
{
for (T member : members) {
if (shouldSee(clazz, member)) {
visibleMembers.add(member);
}
}
}
/**
* Returns whether or not the given member should be visible to the given
* class.
*
* @param clazz the class that wants to see the given member
* @param member the member
* @return whether or not the given member should be visible to the given
* class
*/
private static boolean shouldSee(Class> clazz, Member member) {
int modifiers = member.getModifiers();
// It's visible if it's declared in this class or if it's public/protected
if ((member.getDeclaringClass() == clazz) ||
Modifier.isProtected(modifiers) ||
Modifier.isPublic(modifiers)) {
return true;
}
// If it wasn't declared in this class, it's invisible if it's private
if (Modifier.isPrivate(modifiers)) {
return false;
}
// Otherwise it's visible if we're in the same package
return member.getDeclaringClass().getPackage().equals(clazz.getPackage());
}
}