All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codemonkey.javareflection.JReflect Maven / Gradle / Ivy

Go to download

Java Reflection provides a small package with nifty reflection features that will help with finding constructors, methods and value conversions

The newest version!
package org.codemonkey.javareflection;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

/**
 * This class utilizes functionality of the Java class java.lang.reflect. Specifically, this reflection tool is designed to
 * perform advanced method or constructor lookups, using a combination of {@link LookupMode} strategies.
 * 

* Aside from that there are some nifty methods for things such as: *

    *
  • finding out if a given string matches a package name ({@link #isPackage(String)}),
  • *
  • returning the widest number type that will fit a any number on a given list of numbers ({@link #widestNumberClass(Number...)}),
  • *
  • a method to autobox a given value to its counterpart ({@link #autobox(Class)}),
  • *
  • A couple of methods to resolve a direct property of a given object (though they may move to {@link FieldUtils}),
  • *
  • Various handy methods for collecting information on a given Object, such as list of methods, properties or types
  • *
  • An advanced Class lookup ({@link #locateClass(String, boolean, ExternalClassLoader)}), that allows a full scan (to try * all packages known) and an optional {@link ExternalClassLoader} instance that is able to actually compile a .java file on the fly and * load its compile .class file
  • *
* About the method / constructor lookup facility: *

* An expanded version of getConstructor is implemented that tries to find a constructor of a given datatype, with a given * argument datatypelist, where types do not have to match formal types (auto-boxing, supertypes, implemented interfaces and type * conversions are allowed as they are included in the lookup cycles). This expanded version tries a simple call first (exact match, which * is provided natively by the Java) and when this fails, it generates a list of datatype arrays (signatures) with all possible versions of * any type in the original list possible, and combinations thereof. *

* Observe the following (trivial) example: * *

 * 	interface Foo {
 * 		void foo(Double value, Fruit fruit, char c);
 * 	}
 * 	abstract class A implements Foo {
 * 	}
 * 	abstract class B extends A {
 * 	}
 * 
 * 	JReflect.findCompatibleJavaMethod(B.class, "foo", EnumSet.allOf(LookupMode.class), double.class, Pear.class, String.class)}
 * 
* * In the above example, the method foo will be found by finding all methods named "Foo" on the interfaces implemented by supertype * A, and then foo's method signature will be matched using autoboxing on the double type, a cast to the * Fruit supertype for the Pear type and finally by attempting a common conversion from String to * char. This will give you a Java {@link Method}, but you won't be able to invoke it if it was found using a less strict * lookup than one with a simple exact match. There are two ways to do this: use * {@link #invokeCompatibleMethod(Object, Class, String, Object...)} instead or perform the conversion yourself using * {@link ValueConverter#convert(Object[], Class[], boolean)} prior to invoking the method. ValueConverter.convert(args, method.getParameterTypes()). *

* // TODO fix comment about switching the cache
* Because this lookup is potentially very expensive, a cache is present to store lookup results. This cache is turned on/off using the * constructor. *

* Types that are candidates for Autoboxing: *

    *
  • boolean java.lang.Boolean
  • *
  • char java.lang.Character
  • *
  • byte java.lang.Byte
  • *
  • short java.lang.Short
  • *
  • int java.lang.Integer
  • *
  • long java.lang.Long
  • *
  • float java.lang.Float
  • *
  • double java.lang.Double
  • *
*

* For types that are candidates for common conversion, please see {@link ValueConverter}. *

* FIXME make JReflect cache optional * * @author Benny Bottema * @see ValueConverter * @see FieldUtils * @see ExternalClassLoader */ public final class JReflect { /** * Defines to allowed lookup modes for matching Java methods and constructors. Each time a lookup failed on signature type, a less * strict lookup is performed, in the following order (signature means: the list of parameters defined for a method or constructor): *

    *
  1. exact matching: the given type should exactly match the found types during lookup. This lookup cycle is always * performed first.
  2. *
  3. autobox matching: the given type should match its boxed/unboxed version.
  4. *
  5. polymorphic interface matching: the given type should match one of the implemented interfaces
  6. *
  7. polymorphic superclass matching: the given type should match one of the super classes (for each superclass, the * cycle is repeated, so exact and interface matching come first again before the next superclass up in the chain)
  8. *
  9. common conversion matching: if all other lookups fail, one last resort is to try to convert the given type, if a * common type, to any other common type and then try to find a matching method or constructor. See {@link ValueConverter} for more on * the possibilities.
  10. *
* * @author Benny Bottema */ public enum LookupMode { /** * Indicates that looking for methods includes trying to find compatible signatures by autoboxing the specified arguments. */ AUTOBOX, /** * Indicates that looking for methods includes trying to find compatible signatures by casting the specified arguments to a super * type. */ CAST_TO_SUPER, /** * Indicates that looking for methods includes trying to find compatible signatures by casting the specified arguments to an * implemented interface. */ CAST_TO_INTERFACE, /** * Indicates that looking for methods includes trying to find compatible signatures by automatically convert the specified * arguments. */ COMMON_CONVERT; } /** * {@link Method} cache categorized by owning Classes (since several owners can have a method with the same name and * signature). Methods are stored based on Method reference along with their unique signature (per owner), so multiple * methods on one owner with the same name can coexist.
*
* This cache is being maintained to reduce lookup times when trying to find signature compatible Java methods. The possible signature * combinations using autoboxing and/or automatic common conversions can become very large (7000+ with only three parameters) and can * become a real problem. The more frequently a method is being called the larger the performance gain, especially for methods with long * parameter lists * * @see JReflect#addMethodToCache(Class, String, AccessibleObject, Class[]) * @see JReflect#getMethodFromCache(Class, String, Class[]) */ private final static Map, Map[]>>> methodCache = new LinkedHashMap, Map[]>>>(); /** * A list with Number types in ascending order to wideness (or size) of each type (ie. double is wider than integer). */ private static final Map, Integer> numSizes; static { numSizes = new LinkedHashMap, Integer>(); int size = 0; numSizes.put(Byte.class, ++size); numSizes.put(Short.class, ++size); numSizes.put(Integer.class, ++size); numSizes.put(Long.class, ++size); numSizes.put(Float.class, ++size); numSizes.put(Double.class, ++size); } /** * Private constructor to prevent creating instances of this class. */ private JReflect() { } /** * Searches the JVM and optionally all of its packages * * @param className The name of the class to locate. * @param fullscan Whether a full scan through all available java packages is required. * @param classLoader Optional user-provided classloader. * @return The Class reference if found or null otherwise. */ public static Class locateClass(final String className, final boolean fullscan, final ExternalClassLoader classLoader) { if (fullscan) { Class _class = null; // cycle through all packages and try allocating dynamically final Package[] ps = Package.getPackages(); for (int i = 0; i < ps.length && _class == null; i++) { _class = locateClass(ps[i].getName() + "." + className, classLoader); } return _class; } else { // try standard package used for most common classes return locateClass("java.lang." + className, classLoader); } } /** * This function dynamically tries to locate a class. First it searches the class-cache list, then it tries to get it from the Virtual * Machine using {@code Class.forName(String)}. * * @param fullClassName The Class that needs to be found. * @param classLoader Optional user-provided classloader. * @return The {@code Class} object found from cache or VM. */ public static Class locateClass(final String fullClassName, final ExternalClassLoader classLoader) { try { Class _class = null; // /CLOVER:OFF if (classLoader != null) { _class = classLoader.loadClass(fullClassName); } // /CLOVER:ON if (_class == null) { _class = Class.forName(fullClassName); } return _class; } catch (final ClassNotFoundException e) { return null; } } /** * Simply calls {@link Class#newInstance()} and hides the exception handling boilerplate code. * * @param _class The datatype for which we need to create a new instance of. * @param Type used to parameterize the return instance. * @return A new parameterized instance of the given type. */ public static T newInstanceSimple(final Class _class) { // /CLOVER:OFF try { // /CLOVER:ON return _class.getConstructor().newInstance(); // /CLOVER:OFF } catch (IllegalArgumentException e) { assert false : "we don't pass in arguments"; throw new RuntimeException("unable to invoke parameterless constructor", e); } catch (SecurityException e) { throw new RuntimeException("unable to invoke parameterless constructor; security problem", e); } catch (InstantiationException e) { throw new RuntimeException("unable to complete instantiation of object", e); } catch (IllegalAccessException e) { throw new RuntimeException("unable to access parameterless constructor", e); } catch (InvocationTargetException e) { throw new RuntimeException("unable to invoke parameterless constructor", e); } catch (NoSuchMethodException e) { throw new RuntimeException("unable to find parameterless constructor (not public?)", e); } // /CLOVER:ON } /** * Locates a method on an Object using serveral searchmodes for optimization. First of all a {@link Method} cache is being maintained to * quickly fetch heavily used methods. If not cached before and if a simple search (autoboxing and supertype casts) fails a more complex * search is done where all interfaces are searched for the method as well. If this fails as well, this method will try to autoconvert * the types of the arguments and find a matching signature that way. * * @param context The object to call the method from (can be null). * @param datatype The class to find the method on. * @param identifier The name of the method to locate. * @param args A list of [non-formal] arguments. * @return The return value of the invoke method, if successful. * @throws NoSuchMethodException Thrown by {@link #findCompatibleMethod(Class, String, EnumSet, Class...)}. * @throws IllegalArgumentException Thrown by {@link Method#invoke(Object, Object...)}. * @throws IllegalAccessException Thrown by {@link Method#invoke(Object, Object...)}. * @throws InvocationTargetException Thrown by {@link Method#invoke(Object, Object...)}. */ public static Object invokeCompatibleMethod(final Object context, final Class datatype, final String identifier, final Object... args) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { // determine the signature we want to find a compatible java method for final Class[] signature = JReflect.collectTypes(args); // setup lookup procedure starting with simple search mode EnumSet lookupMode = EnumSet.of(LookupMode.AUTOBOX, LookupMode.CAST_TO_SUPER); Method method; // try to fina a compatible Java method using various lookup modes try { method = findCompatibleMethod(datatype, identifier, lookupMode, signature); } catch (final NoSuchMethodException e1) { try { // moderate search mode lookupMode.add(LookupMode.CAST_TO_INTERFACE); method = findCompatibleMethod(datatype, identifier, lookupMode, signature); } catch (final NoSuchMethodException e2) { // full searchmode lookupMode.add(LookupMode.COMMON_CONVERT); method = findCompatibleMethod(datatype, identifier, lookupMode, signature); } } // try to invoke the method with unconverted arguments or convert them if needed try { return method.invoke(context, args); } catch (final IllegalArgumentException e) { final Object[] convertedArgs = ValueConverter.convert(args, method.getParameterTypes(), true); return method.invoke(context, convertedArgs); } } /** * Locates and invokes a {@link Constructor}using {@link #invokeConstructor(Class, Class[], Object[])} * * @param Used to parameterize the returned object so that the caller doesn't need to cast. * @param datatype The class to find the constructor for. * @param args A list of [non-formal] arguments. * @return The instantiated object of class datatype. * @throws IllegalAccessException Thrown by {@link #invokeConstructor(Class, Class[], Object[])}. * @throws InvocationTargetException Thrown by {@link #invokeConstructor(Class, Class[], Object[])}. * @throws InstantiationException Thrown by {@link #invokeConstructor(Class, Class[], Object[])}. * @throws NoSuchMethodException Thrown by {@link #invokeConstructor(Class, Class[], Object[])}. * @see java.lang.reflect.Constructor#newInstance(Object[]) */ public static T invokeCompatibleConstructor(final Class datatype, final Object... args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { final Class[] signature = JReflect.collectTypes(args); return invokeConstructor(datatype, signature, args); } /** * Locates and invokes a {@link Constructor}, using a customized typelist. Avoids dynamically trying to find correct parameter type * list. Can also be used to force up/down casting (ie. passing a specific type of List into a generic type) * * @param Used to parameterize the returned object so that the caller doesn't need to cast. * @param datatype The class to find the constructor for. * @param signature The typelist used to find correct constructor. * @param args A list of [non-formal] arguments. * @return The instantiated object of class datatype. * @throws IllegalAccessException Thrown by {@link Constructor#newInstance(Object...)}. * @throws InvocationTargetException Thrown by {@link Constructor#newInstance(Object...)}. * @throws InstantiationException Thrown by {@link Constructor#newInstance(Object...)}. * @throws NoSuchMethodException Thrown by {@link #findCompatibleConstructor(Class, EnumSet, Class...)}. * @see java.lang.reflect.Constructor#newInstance(Object[]) */ public static T invokeConstructor(final Class datatype, final Class[] signature, final Object[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { // setup lookup procedure EnumSet lookupMode = EnumSet.of(LookupMode.AUTOBOX, LookupMode.CAST_TO_SUPER); Constructor constructor; // try to find a compatible Java constructor try { // simple search mode constructor = findCompatibleConstructor(datatype, lookupMode, signature); } catch (final NoSuchMethodException e1) { try { // moderate search mode lookupMode.add(LookupMode.CAST_TO_INTERFACE); constructor = findCompatibleConstructor(datatype, lookupMode, signature); } catch (final NoSuchMethodException e2) { // full searchmode lookupMode.add(LookupMode.COMMON_CONVERT); constructor = findCompatibleConstructor(datatype, lookupMode, signature); } } // try to invoke the constructor with unconverted arguments or convert them if needed try { return constructor.newInstance(args); } catch (final IllegalArgumentException e) { final Object[] convertedArgs = ValueConverter.convert(args, constructor.getParameterTypes(), true); return constructor.newInstance(convertedArgs); } } /** * Creates a new array of class objects harvested from an array of objects. *

* NOTE: this method will never return primitive classes (such as double.class, as you can't put primitive values into an array of * Objects (they will be autoboxes by the JVM). * * @param objects The array of objects to harvest classtypes from. * @return The array with the harvested classtypes. */ public static Class[] collectTypes(final Object[] objects) { // collect classtypes of the arguments final Class[] types = new Class[objects.length]; for (int i = 0; i < objects.length; i++) { final Object o = objects[i]; types[i] = o != null ? o.getClass() : Object.class; } return types; } /** * Tries to find a {@link Constructor} of a given type, with a given typelist, where types do not match due to formal types simple * types. This expanded version tries a simple call first and when it fails, it generates a list of type arrays with all possible * (un)wraps of any type in the original list possible, and combinations thereof. * * @param Used to parameterize the returned constructor. * @param datatype The class to get the constructor from. * @param lookupMode Flag indicating the search steps that need to be done. * @param types The list of types as specified by the user. * @return The constructor if found, otherwise exception is thrown. * @exception NoSuchMethodException Thrown when the {@link Constructor} could not be found on the data type, even after performing * optional conversions. */ @SuppressWarnings("unchecked") public static Constructor findCompatibleConstructor(final Class datatype, final EnumSet lookupMode, final Class... types) throws NoSuchMethodException { // first try to find the constructor in the method cache Constructor constructor = (Constructor) getMethodFromCache(datatype, datatype.getName(), types); if (constructor != null) { return constructor; } else { try { // try standard call constructor = datatype.getConstructor(types); } catch (final NoSuchMethodException e) { // failed, try all possible wraps/unwraps final List[]> typeslist = generateCompatibleSignatures(lookupMode, types); for (final Class[] aTypeslist : typeslist) { try { constructor = datatype.getConstructor(aTypeslist); break; } catch (final NoSuchMethodException x) { // do nothing } } } } // if a constructor was found (and it wasn't in the cache, because method would've returned already) if (constructor != null) { addMethodToCache(datatype, datatype.getName(), constructor, types); return constructor; } else { throw new NoSuchMethodException(); } } /** * Delegates to {@link #findCompatibleMethod(Class, String, EnumSet, Class...)}, using strict lookupmode (no autoboxing, casting etc.) * and optional signature parameters. * * @param datatype The class to get the constructor from. * @param methodName The name of the method to retrieve from the class. * @param signature The list of types as specified by the user. * @return null in case of a NoSuchMethodException exception. * @see #findCompatibleMethod(Class, String, EnumSet, Class...) */ public static Method findSimpleCompatibleMethod(final Class datatype, final String methodName, final Class... signature) { try { return findCompatibleMethod(datatype, methodName, EnumSet.noneOf(LookupMode.class), signature); } catch (final NoSuchMethodException e) { return null; } } /** * Same as getConstructor(), except for getting a {@link Method} of a classtype, using the name to indicate which method * should be located. * * @param datatype The class to get the constructor from. * @param methodName The name of the method to retrieve from the class. * @param lookupMode Flag indicating the search steps that need to be done. * @param signature The list of types as specified by the user. * @return The method if found, otherwise exception is thrown. * @exception NoSuchMethodException Thrown when the {@link Method} could not be found on the data type, even after performing optional * conversions. */ public static Method findCompatibleMethod(final Class datatype, final String methodName, final EnumSet lookupMode, final Class... signature) throws NoSuchMethodException { // first try to find the method in the method cache Method method = (Method) getMethodFromCache(datatype, methodName, signature); if (method != null) { return method; } else { try { // try standard call method = getMethod(datatype, methodName, signature); } catch (final NoSuchMethodException e) { // failed, try all possible wraps/unwraps final List[]> signatures = generateCompatibleSignatures(lookupMode, signature); for (final Class[] compatibleSignature : signatures) { try { method = getMethod(datatype, methodName, compatibleSignature); break; } catch (final NoSuchMethodException x) { // do nothing } } } } // if a method was found (and it wasn't in the cache, because method would've returned already) if (method != null) { addMethodToCache(datatype, methodName, method, signature); return method; } else { throw new NoSuchMethodException(); } } /** * Searches a specific class object for a {@link Method} using java reflect using a specific signature. This method will first search * all implemented interfaces for the method to avoid visibility problems.
*
* An example of such a problem is the Iterator as implemented by the ArrayList. The Iterator is implemented * as a private innerclass and as such not accessible by java reflect (even though the implemented methods are declared public), * unlike the interface's definition. * * @param datatype The class reference to locate the method on. * @param name The name of the method to find. * @param signature The signature the method should match. * @return The Method found on the data type that matched the specified signature. * @exception NoSuchMethodException Thrown when the {@link Method} could not be found on the interfaces implemented by the given data * type. * @see java.lang.Class#getMethod(String, Class[]) */ public static Method getMethod(final Class datatype, final String name, final Class... signature) throws NoSuchMethodException { for (final Class iface : datatype.getInterfaces()) { try { return iface.getMethod(name, signature); } catch (final NoSuchMethodException e) { // do nothing } } return datatype.getMethod(name, signature); } /** * Initializes the list with type-arrays and starts generating beginning from index 0. This method is used for (un)wrapping. * * @param lookupMode Flag indicating the search steps that need to be done. * @param signature The list with original user specified types. * @return The list with converted type-arrays. */ private static List[]> generateCompatibleSignatures(final EnumSet lookupMode, final Class... signature) { final List[]> signatures = new ArrayList[]>(); generateCompatibleSignatures(0, lookupMode, signatures, signature); return signatures; } /** * Recursively generates a complete list of all possible (un)wraps (autoboxing), supertypes, implemented interfaces, type conversions * and any combination thereof with the specified signature's elements (the individual parameter types).
*
* The combination signatures are generated in the following order: *

    *
  1. no conversion; highest priority as it comes closest to user's requirement/specification
  2. *
  3. autoboxing; the autoboxed counterversion comes closest to the original datatype
  4. *
  5. interface; where methods can't be found using original type, interface placeholders are attempted
  6. *
  7. supertype; where methods can't be found using implemented interfaces, supertype placeholders are attempted
  8. *
  9. conversions; if all else fails, try to convert the datatype for common types (ie. int to String)
  10. *
* * @param index The current index to start mutating from. * @param lookupMode Flag indicating the search steps that need to be done. * @param signatures The central storage list for new type-arrays. * @param signature The list with current types, to mutate further upon. */ private static void generateCompatibleSignatures(final int index, final EnumSet lookupMode, final List[]> signatures, final Class... signature) { // if new type array is completed if (index == signature.length) { signatures.add(signature); } else { // generate new array of types final Class original = signature[index]; // 1. don't generate compatible list; just try the normal type first // remember, in combinations types should be allowed to be converted) generateCompatibleSignatures(index + 1, lookupMode, signatures, signature.clone()); // 2. generate type in which the original can be (un)wrapped if (lookupMode.contains(LookupMode.AUTOBOX)) { final Class autoboxed = autobox(original); if (autoboxed != null) { final Class[] newSignature = replaceInArray(signature.clone(), index, autoboxed); generateCompatibleSignatures(index + 1, lookupMode, signatures, newSignature); } } // autocast to supertype or interface? if (lookupMode.contains(LookupMode.CAST_TO_INTERFACE)) { // 3. generate implemented interfaces the original value could be converted (cast) into for (final Class iface : original.getInterfaces()) { final Class[] newSignature = replaceInArray(signature.clone(), index, iface); generateCompatibleSignatures(index + 1, lookupMode, signatures, newSignature); } } if (lookupMode.contains(LookupMode.CAST_TO_SUPER)) { // 4. generate supertypes the original value could be converted (cast) into Class supertype = original; while ((supertype = supertype.getSuperclass()) != null) { final Class[] newSignature = replaceInArray(signature.clone(), index, supertype); generateCompatibleSignatures(index + 1, lookupMode, signatures, newSignature); } } // 5. generate types the original value could be converted into if (lookupMode.contains(LookupMode.COMMON_CONVERT)) { for (final Class convert : ValueConverter.collectCompatibleTypes(original)) { final Class[] newSignature = replaceInArray(signature.clone(), index, convert); generateCompatibleSignatures(index + 1, lookupMode, signatures, newSignature); } } } } /** * Emulates Java's Autoboxing feature; tries to convert a type to its (un)wrapped counter version. * * @param c The datatype to convert (autobox). * @return The converted version of the specified type, or null. */ public static Class autobox(final Class c) { // integer if (c == Integer.class) { return int.class; } else if (c == int.class) { return Integer.class; } else if (c == Boolean.class) { return boolean.class; } else if (c == boolean.class) { return Boolean.class; } else if (c == Character.class) { return char.class; } else if (c == char.class) { return Character.class; } else if (c == Byte.class) { return byte.class; } else if (c == byte.class) { return Byte.class; } else if (c == Short.class) { return short.class; } else if (c == short.class) { return Short.class; } else if (c == Long.class) { return long.class; } else if (c == long.class) { return Long.class; } else if (c == Float.class) { return float.class; } else if (c == float.class) { return Float.class; } else if (c == Double.class) { return double.class; } else if (c == double.class) { return Double.class; } else { return null; } } /** * Returns a field from the given object that goes by the name of fieldName. If o is a Class object, a static * field will be returned. * * @param o The reference to the object to fetch the property value from. * @param fieldName The identifier or name of the member field/property. * @return The value of the Field or in case of a Dynamic the property value. */ public static Field solveField(final Object o, final String fieldName) { try { if (o.getClass().equals(Class.class)) { // Java static field return ((Class) o).getField(fieldName); } else { // Java instance field return o.getClass().getField(fieldName); } } catch (NoSuchFieldException e) { return null; } } /** * Retrieves a {@link Method} from a cache. * * @param datatype The owning {@link Class} of the Method being searched for. * @param method The name of the method that is being searched for. * @param signature The parameter list of the method we need to match if a method was found by name. * @return The Method found on the specified owner with matching name and signature. * @see JReflect#methodCache * @see JReflect#addMethodToCache(Class, String, AccessibleObject, Class...) */ private final static AccessibleObject getMethodFromCache(final Class datatype, final String method, final Class... signature) { final Map[]>> owner = methodCache.get(datatype); // we know only methods with parameter list are stored in the cache if (signature.length > 0) { // get owner, its methods matching specified name and match their signatures if (owner != null) { final Map[]> signatures = owner.get(method); if (signatures != null) { for (final Map.Entry[]> entry : signatures.entrySet()) { if (Arrays.equals(entry.getValue(), signature)) { return entry.getKey(); } } } } } // method not found or known not to be stored due to absent parameter list return null; } /** * Adds a specific Method to the cache. * * @param datatype The Class that owns the Method. * @param method The Method's name by which methods can be found on the specified owner. * @param methodRef The Method reference that's actually being stored in the cache. * @param signature The parameter list of the Method being stored. * @see JReflect#methodCache * @see JReflect#getMethodFromCache(Class, String, Class...) */ private final static void addMethodToCache(final Class datatype, final String method, final AccessibleObject methodRef, final Class... signature) { // only store methods with a parameter list if (signature.length > 0) { // get or create owner entry Map[]>> owner = methodCache.get(datatype); owner = owner != null ? owner : new LinkedHashMap[]>>(); // get or create list of methods with specified method name Map[]> methods = owner.get(method); methods = methods != null ? methods : new LinkedHashMap[]>(); // add or overwrite method entry methods.put(methodRef, signature); // finally shelve all the stuff back methods.put(methodRef, signature); owner.put(method, methods); methodCache.put(datatype, owner); } } /** * Assigns a value to a field id on the given object o. If a simple assignment fails, a common conversion will * be attempted. * * @param o The object to find the field on. * @param property The name of the field we're assigning the value to. * @param value The value to assign to the field, may be converted to the field's type through common conversion. * @return The actual value that was assigned (the original or the converted value). * @throws IllegalAccessException Thrown by {@link Field#set(Object, Object)} * @throws NoSuchFieldException Thrown if the {@link Field} could not be found, even after trying to convert the value to the target * type. * @see ValueConverter#convert(Object, Class) */ public static Object assignToField(final Object o, final String property, final Object value) throws IllegalAccessException, NoSuchFieldException { Object assignedValue = value; final Field field = solveField(o, property); if (field != null) { try { field.set(o, value); } catch (final IllegalArgumentException ie) { assignedValue = ValueConverter.convert(value, field.getType()); field.set(o, assignedValue); } return assignedValue; } else { throw new NoSuchFieldException(); } } /** * Validates whether a string represents a valid package. * * @param name The string representing a list of packages. * @return A boolean indicating whether name represents a valid package. */ public static boolean isPackage(final String name) { return name.equals("java") || Package.getPackage(name) != null; } /** * Returns the smallest class that can hold all of the specified numbers. * * @param numbers The list with numbers that all should fit in the Number container. * @return The Number container that is just large enough for all specified numbers. */ public static Class widestNumberClass(final Number... numbers) { // find widest number Integer widest = 0; Class widestNumberType = Byte.class; for (final Number number : numbers) { final Integer size = numSizes.get(number.getClass()); if (size > widest) { widestNumberType = number.getClass(); widest = size; } } return widestNumberType; } /** * Returns a list of names that represent the fields on an Object. * * @param subject The Object who's properties/fields need to be reflected. * @return A list of names that represent the fields on the given Object. */ public static Collection collectProperties(final Object subject) { final Collection properties = new LinkedHashSet(); // collect properties/fields final Field[] fields = subject.getClass().getFields(); for (final Field f : fields) { properties.add(f.getName()); } return properties; } /** * Returns a list of names that represent the methods on an Object. For Dynamics, the stored methods are returned (not the * Java methods of the class Dynamic). * * @param subject The Object or Dynamic who's methods need to be reflected. * @return Returns a list with methods, either {@link Method}s, or Dynamic methods. */ public static Collection collectMethods(final Object subject) { final Collection methods = new LinkedHashSet(); // collect methods final Method[] methodlist = subject.getClass().getMethods(); for (final Method m : methodlist) { methods.add(m.getName()); } return methods; } /** * Shortcut helper method that replaces an item in an array and returns the array itself. * * @param The type of object that goes into the array. * @param array The array that needs an item replaced. * @param index The index at which the new value should be inserted. * @param value The value to insert at the specified index in the specified array. * @return The specified array with the item replaced at specified index. */ public static T[] replaceInArray(final T[] array, final int index, final T value) { array[index] = value; return array; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy