
com.samskivert.util.ClassUtil Maven / Gradle / Ivy
Show all versions of samskivert Show documentation
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2011 Michael Bayne, et al.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.util;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Class object related utility routines.
*
* This code was adapted from code provided by Paul Hosler in an article for Java
* Report Online.
*/
public class ClassUtil
{
/**
* @param clazz a class.
*
* @return true if the class is accessible, false otherwise. Presently returns true if the
* class is declared public.
*/
public static boolean classIsAccessible (Class> clazz)
{
return Modifier.isPublic(clazz.getModifiers());
}
/**
* Get the fields contained in the class and its superclasses.
*/
public static Field[] getFields (Class> clazz)
{
ArrayList list = new ArrayList();
getFields(clazz, list);
return list.toArray(new Field[list.size()]);
}
/**
* Add all the fields of the specifed class (and its ancestors) to the list. Note, if we are
* running in a sandbox, this will only enumerate public members.
*/
public static void getFields (Class> clazz, List addTo)
{
// first get the fields of the superclass
Class> pclazz = clazz.getSuperclass();
if (pclazz != null && !pclazz.equals(Object.class)) {
getFields(pclazz, addTo);
}
// then reflect on this class's declared fields
Field[] fields;
try {
fields = clazz.getDeclaredFields();
} catch (SecurityException se) {
System.err.println("Unable to get declared fields of " + clazz.getName() + ": " + se);
fields = new Field[0];
}
// override the default accessibility check for the fields
try {
AccessibleObject.setAccessible(fields, true);
} catch (SecurityException se) {
// ah well, only publics for us
}
for (Field field : fields) {
int mods = field.getModifiers();
if (Modifier.isStatic(mods) || Modifier.isTransient(mods)) {
continue; // skip static and transient fields
}
addTo.add(field);
}
}
/**
* @param args an object array.
*
* @return an array of Class objects representing the classes of the objects in the given
* Object array. If args is null, a zero-length Class array is returned. If an element in
* args is null, then Void.TYPE is the corresponding Class in the return array.
*/
public static Class>[] getParameterTypesFrom (Object[] args)
{
Class>[] argTypes = null;
if (args != null) {
argTypes = new Class>[args.length];
for (int i = 0; i < args.length; ++i) {
argTypes[i] = (args[i] == null) ? Void.TYPE : args[i].getClass();
}
} else {
argTypes = new Class>[0];
}
return argTypes;
}
/**
* Tells whether instances of the classes in the 'rhs' array could be used as parameters to a
* reflective method invocation whose parameter list has types denoted by the 'lhs' array.
*
* @param lhs Class array representing the types of the formal parameters of a method.
* @param rhs Class array representing the types of the actual parameters of a method. A null
* value or Void.TYPE is considered to match a corresponding Object or array class in lhs, but
* not a primitive.
*
* @return true if compatible, false otherwise.
*/
public static boolean compatibleClasses (Class>[] lhs, Class>[] rhs)
{
if (lhs.length != rhs.length) {
return false;
}
for (int i = 0; i < lhs.length; ++i) {
if (rhs[i] == null || rhs[i].equals(Void.TYPE)) {
if (lhs[i].isPrimitive()) {
return false;
} else {
continue;
}
}
if (!lhs[i].isAssignableFrom(rhs[i])) {
Class> lhsPrimEquiv = primitiveEquivalentOf(lhs[i]);
Class> rhsPrimEquiv = primitiveEquivalentOf(rhs[i]);
if (!primitiveIsAssignableFrom(lhsPrimEquiv, rhsPrimEquiv)) {
return false;
}
}
}
return true;
}
/**
* Searches for the method with the given name and formal parameter types that is in the
* nearest accessible class in the class hierarchy, starting with clazz's superclass. The
* superclass and implemented interfaces of clazz are searched, then their superclasses,
* etc. until a method is found. Returns null if there is no such method.
*
* @param clazz a class.
* @param methodName name of a method.
* @param parameterTypes Class array representing the types of a method's formal parameters.
*
* @return the nearest method located, or null if there is no such method.
*/
public static Method getAccessibleMethodFrom (
Class> clazz, String methodName, Class>[] parameterTypes)
{
// Look for overridden method in the superclass.
Class> superclass = clazz.getSuperclass();
Method overriddenMethod = null;
if (superclass != null && classIsAccessible(superclass)) {
try {
overriddenMethod = superclass.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException nsme) {
// no problem
}
if (overriddenMethod != null) {
return overriddenMethod;
}
}
// If here, then clazz represents Object, or an interface, or the superclass did not have
// an override. Check implemented interfaces.
Class>[] interfaces = clazz.getInterfaces();
for (int i = 0; i < interfaces.length; ++i) {
if (classIsAccessible(interfaces[i])) {
try {
overriddenMethod = interfaces[i].getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException nsme) {
// no problem
}
if (overriddenMethod != null) {
return overriddenMethod;
}
}
}
// Try superclass's superclass and implemented interfaces.
if (superclass != null) {
overriddenMethod = getAccessibleMethodFrom(superclass, methodName, parameterTypes);
if (overriddenMethod != null) {
return overriddenMethod;
}
}
// Try implemented interfaces' extended interfaces...
for (int i = 0; i < interfaces.length; ++i) {
overriddenMethod = getAccessibleMethodFrom(interfaces[i], methodName, parameterTypes);
if (overriddenMethod != null) {
return overriddenMethod;
}
}
// Give up.
return null;
}
/**
* @param clazz a Class.
*
* @return the class's primitive equivalent, if clazz is a primitive wrapper. If clazz is
* primitive, returns clazz. Otherwise, returns null.
*/
public static Class> primitiveEquivalentOf (Class> clazz)
{
return clazz.isPrimitive() ? clazz : _objectToPrimitiveMap.get(clazz);
}
/**
* @return the class's object equivalent if the class is a primitive type.
*/
public static Class> objectEquivalentOf (Class> clazz)
{
return clazz.isPrimitive() ? _primitiveToObjectMap.get(clazz) : clazz;
}
/**
* Tells whether an instance of the primitive class represented by 'rhs' can be assigned to an
* instance of the primitive class represented by 'lhs'.
*
* @param lhs assignee class.
* @param rhs assigned class.
*
* @return true if compatible, false otherwise. If either argument is null
, or one
* of the parameters does not represent a primitive (e.g. Byte.TYPE), returns false.
*/
public static boolean primitiveIsAssignableFrom (Class> lhs, Class> rhs)
{
if (lhs == null || rhs == null) {
return false;
}
if (!(lhs.isPrimitive() && rhs.isPrimitive())) {
return false;
}
if (lhs.equals(rhs)) {
return true;
}
Set> wideningSet = _primitiveWideningsMap.get(rhs);
if (wideningSet == null) {
return false;
}
return wideningSet.contains(lhs);
}
/** Mapping from primitive wrapper Classes to their corresponding
* primitive Classes. */
protected static final Map,Class>> _objectToPrimitiveMap =
new HashMap,Class>>(13);
protected static final Map,Class>> _primitiveToObjectMap =
new HashMap,Class>>(13);
static {
_objectToPrimitiveMap.put(Boolean.class, Boolean.TYPE);
_primitiveToObjectMap.put(Boolean.TYPE, Boolean.class);
_objectToPrimitiveMap.put(Byte.class, Byte.TYPE);
_primitiveToObjectMap.put(Byte.TYPE, Byte.class);
_objectToPrimitiveMap.put(Character.class, Character.TYPE);
_primitiveToObjectMap.put(Character.TYPE, Character.class);
_objectToPrimitiveMap.put(Double.class, Double.TYPE);
_primitiveToObjectMap.put(Double.TYPE, Double.class);
_objectToPrimitiveMap.put(Float.class, Float.TYPE);
_primitiveToObjectMap.put(Float.TYPE, Float.class);
_objectToPrimitiveMap.put(Integer.class, Integer.TYPE);
_primitiveToObjectMap.put(Integer.TYPE, Integer.class);
_objectToPrimitiveMap.put(Long.class, Long.TYPE);
_primitiveToObjectMap.put(Long.TYPE, Long.class);
_objectToPrimitiveMap.put(Short.class, Short.TYPE);
_primitiveToObjectMap.put(Short.TYPE, Short.class);
}
/** Mapping from primitive wrapper Classes to the sets of primitive classes whose instances can
* be assigned an instance of the first. */
protected static final Map,Set>> _primitiveWideningsMap =
new HashMap,Set>>(11);
static {
Set> set = new HashSet>();
set.add(Short.TYPE);
set.add(Integer.TYPE);
set.add(Long.TYPE);
set.add(Float.TYPE);
set.add(Double.TYPE);
_primitiveWideningsMap.put(Byte.TYPE, set);
set = new HashSet>();
set.add(Integer.TYPE);
set.add(Long.TYPE);
set.add(Float.TYPE);
set.add(Double.TYPE);
_primitiveWideningsMap.put(Short.TYPE, set);
_primitiveWideningsMap.put(Character.TYPE, set);
set = new HashSet>();
set.add(Long.TYPE);
set.add(Float.TYPE);
set.add(Double.TYPE);
_primitiveWideningsMap.put(Integer.TYPE, set);
set = new HashSet>();
set.add(Float.TYPE);
set.add(Double.TYPE);
_primitiveWideningsMap.put(Long.TYPE, set);
set = new HashSet>();
set.add(Double.TYPE);
_primitiveWideningsMap.put(Float.TYPE, set);
}
}