X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=test%2Fjunit%2Fextensions%2FPrivilegedAccessor.java;fp=test%2Fjunit%2Fextensions%2FPrivilegedAccessor.java;h=49409e615760b3e0a4bb549c21d527efcbb8f9c4;hb=57738a1f3c19b1c3a00bd3ac5108f8cd0af32f99;hp=c5c0f0469f00a9ad39e630d12e196f5793ff3c58;hpb=e7338a61f3ce96dadf44ac80b2b32cc5ba4b94c8;p=jalview.git diff --git a/test/junit/extensions/PrivilegedAccessor.java b/test/junit/extensions/PrivilegedAccessor.java index c5c0f04..49409e6 100644 --- a/test/junit/extensions/PrivilegedAccessor.java +++ b/test/junit/extensions/PrivilegedAccessor.java @@ -32,618 +32,827 @@ import java.util.Map; import java.util.StringTokenizer; /** - * This class is used to access a method or field of an object no matter what the access modifier of the method or field. The syntax - * for accessing fields and methods is out of the ordinary because this class uses reflection to peel away protection. + * This class is used to access a method or field of an object no matter what + * the access modifier of the method or field. The syntax for accessing fields + * and methods is out of the ordinary because this class uses reflection to peel + * away protection. *

* a.k.a. The "ObjectMolester" *

* Here is an example of using this to access a private member:
- * myObject is an object of type MyClass. setName(String) is a private method of - * MyClass. + * myObject is an object of type MyClass. + * setName(String) is a private method of MyClass. * *

- * PrivilegedAccessor.invokeMethod(myObject, "setName(java.lang.String)", "newName");
+ * PrivilegedAccessor.invokeMethod(myObject, "setName(java.lang.String)",
+ *         "newName");
  * 
* * @author Charlie Hubbard (chubbard@iss.net) * @author Prashant Dhokte (pdhokte@iss.net) * @author Sebastian Dietrich (sebastian.dietrich@e-movimento.com) * - * @deprecated use PA instead. PA improves the functionality of PrivilegedAccessor by introducing support for varargs and removal of - * the necessity to catch exceptions. + * @deprecated use PA instead. PA improves the functionality of + * PrivilegedAccessor by introducing support for varargs and removal + * of the necessity to catch exceptions. */ @Deprecated final class PrivilegedAccessor { - /** - * Private constructor to make it impossible to instantiate this class. - */ - private PrivilegedAccessor() { - assert false : "You mustn't instantiate PrivilegedAccessor, use its methods statically"; - } - - /** - * Returns a string representation of the given object. The string has the following format: " {}" - * whereas is a comma separated list with = includes - * all attributes of the objects class followed by the attributes of its superclass (if any) and so on. - * - * @param instanceOrClass the object or class to get a string representation of - * @return a string representation of the given object - */ - public static String toString(final Object instanceOrClass) { - Collection fields = getFieldNames(instanceOrClass); - - if (fields.isEmpty()) + /** + * Private constructor to make it impossible to instantiate this class. + */ + private PrivilegedAccessor() + { + assert false : "You mustn't instantiate PrivilegedAccessor, use its methods statically"; + } + + /** + * Returns a string representation of the given object. The string has the + * following format: " {}" whereas + * is a comma separated list with + * = includes all + * attributes of the objects class followed by the attributes of its + * superclass (if any) and so on. + * + * @param instanceOrClass + * the object or class to get a string representation of + * @return a string representation of the given object + */ + public static String toString(final Object instanceOrClass) + { + Collection fields = getFieldNames(instanceOrClass); + + if (fields.isEmpty()) + { + return getClass(instanceOrClass).getName(); + } + + StringBuffer stringBuffer = new StringBuffer(); + + stringBuffer.append(getClass(instanceOrClass).getName() + " {"); + + for (String fieldName : fields) + { + try { - return getClass(instanceOrClass).getName(); - } - - StringBuffer stringBuffer = new StringBuffer(); - - stringBuffer.append(getClass(instanceOrClass).getName() + " {"); - - for (String fieldName : fields) { - try { - stringBuffer.append(fieldName + "=" + getValue(instanceOrClass, fieldName) + ", "); - } catch (NoSuchFieldException e) { - assert false : "It should always be possible to get a field that was just here"; - } - } - - stringBuffer.replace(stringBuffer.lastIndexOf(", "), stringBuffer.length(), "}"); - return stringBuffer.toString(); - } - - /** - * Gets the name of all fields (public, private, protected, default) of the given instance or class. This includes as well all - * fields (public, private, protected, default) of all its super classes. - * - * @param instanceOrClass the instance or class to get the fields of - * @return the collection of field names of the given instance or class - */ - public static Collection getFieldNames(final Object instanceOrClass) { - if (instanceOrClass == null) - { - return Collections.EMPTY_LIST; - } - - Class clazz = getClass(instanceOrClass); - Field[] fields = clazz.getDeclaredFields(); - Collection fieldNames = new ArrayList(fields.length); - - for (Field field : fields) { - fieldNames.add(field.getName()); - } - fieldNames.addAll(getFieldNames(clazz.getSuperclass())); - - return fieldNames; - } - - /** - * Gets the signatures of all methods (public, private, protected, default) of the given instance or class. This includes as well - * all methods (public, private, protected, default) of all its super classes. This does not include constructors. - * - * @param instanceOrClass the instance or class to get the method signatures of - * @return the collection of method signatures of the given instance or class - */ - public static Collection getMethodSignatures(final Object instanceOrClass) { - if (instanceOrClass == null) - { - return Collections.EMPTY_LIST; - } - - Class clazz = getClass(instanceOrClass); - Method[] methods = clazz.getDeclaredMethods(); - Collection methodSignatures = new ArrayList(methods.length + Object.class.getDeclaredMethods().length); - - for (Method method : methods) { - methodSignatures.add(method.getName() + "(" + getParameterTypesAsString(method.getParameterTypes()) + ")"); - } - methodSignatures.addAll(getMethodSignatures(clazz.getSuperclass())); - - return methodSignatures; - } - - /** - * Gets the value of the named field and returns it as an object. If instanceOrClass is a class then a static field is returned. - * - * @param instanceOrClass the instance or class to get the field from - * @param fieldName the name of the field - * @return an object representing the value of the field - * @throws NoSuchFieldException if the field does not exist - */ - public static Object getValue(final Object instanceOrClass, final String fieldName) throws NoSuchFieldException { - Field field = getField(instanceOrClass, fieldName); - try { - return field.get(instanceOrClass); - } catch (IllegalAccessException e) { - assert false : "getField() should have setAccessible(true), so an IllegalAccessException should not occur in this place"; - return null; - } - } - - /** - * Instantiates an object of the given class with the given arguments. If you want to instantiate a member class, you must provide - * the object it is a member of as first argument. - * - * @param fromClass the class to instantiate an object from - * @param args the arguments to pass to the constructor - * @return an object of the given type - * @throws IllegalArgumentException if the number of actual and formal parameters differ; if an unwrapping conversion for primitive - * arguments fails; or if, after possible unwrapping, a parameter value cannot be converted to the corresponding formal - * parameter type by a method invocation conversion. - * @throws IllegalAccessException if this Constructor object enforces Java language access control and the underlying constructor is - * inaccessible. - * @throws InvocationTargetException if the underlying constructor throws an exception. - * @throws NoSuchMethodException if the constructor could not be found - * @throws InstantiationException if the class that declares the underlying constructor represents an abstract class. - * - * @see PrivilegedAccessor#instantiate(Class,Class[],Object[]) - */ - public static T instantiate(final Class fromClass, final Object[] args) throws IllegalArgumentException, - InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { - return instantiate(fromClass, getParameterTypes(args), args); - } - - /** - * Instantiates an object of the given class with the given arguments and the given argument types. If you want to instantiate a - * member class, you must provide the object it is a member of as first argument. - * - * - * @param fromClass the class to instantiate an object from - * @param args the arguments to pass to the constructor - * @param argumentTypes the fully qualified types of the arguments of the constructor - * @return an object of the given type - * @throws IllegalArgumentException if the number of actual and formal parameters differ; if an unwrapping conversion for primitive - * arguments fails; or if, after possible unwrapping, a parameter value cannot be converted to the corresponding formal - * parameter type by a method invocation conversion. - * @throws IllegalAccessException if this Constructor object enforces Java language access control and the underlying constructor is - * inaccessible. - * @throws InvocationTargetException if the underlying constructor throws an exception. - * @throws NoSuchMethodException if the constructor could not be found - * @throws InstantiationException if the class that declares the underlying constructor represents an abstract class. - * - * @see PrivilegedAccessor#instantiate(Class,Object[]) - */ - public static T instantiate(final Class fromClass, final Class[] argumentTypes, final Object[] args) - throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, - NoSuchMethodException { - return getConstructor(fromClass, argumentTypes).newInstance(args); - } - - /** - * Calls a method on the given object instance with the given arguments. Arguments can be object types or representations for - * primitives. - * - * @param instanceOrClass the instance or class to invoke the method on - * @param methodSignature the name of the method and the parameters
- * (e.g. "myMethod(java.lang.String, com.company.project.MyObject)") - * @param arguments an array of objects to pass as arguments - * @return the return value of this method or null if void - * @throws IllegalAccessException if the method is inaccessible - * @throws InvocationTargetException if the underlying method throws an exception. - * @throws NoSuchMethodException if no method with the given methodSignature could be found - * @throws IllegalArgumentException if an argument couldn't be converted to match the expected type - */ - public static Object invokeMethod(final Object instanceOrClass, final String methodSignature, final Object[] arguments) - throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { - if ((methodSignature.indexOf('(') == -1) || (methodSignature.indexOf('(') >= methodSignature.indexOf(')'))) - { - throw new NoSuchMethodException(methodSignature); - } - Class[] parameterTypes = getParameterTypes(methodSignature); - return getMethod(instanceOrClass, getMethodName(methodSignature), parameterTypes).invoke(instanceOrClass, - getCorrectedArguments(parameterTypes, arguments)); - } - - /** - * Gets the given arguments corrected to match the given methodSignature. Correction is necessary for array arguments not to be - * mistaken by varargs. - * - * @param parameterTypes the method signatue the given arguments should match - * @param arguments the arguments that should be corrected - * @return the corrected arguments - */ - private static Object[] getCorrectedArguments(Class[] parameterTypes, Object[] arguments) { - if (arguments == null) - { - return arguments; - } - if (parameterTypes.length > arguments.length) - { - return arguments; - } - if (parameterTypes.length < arguments.length) - { - return getCorrectedArguments(parameterTypes, new Object[] {arguments}); - } - - Object[] correctedArguments = new Object[arguments.length]; - int currentArgument = 0; - for (Class parameterType : parameterTypes) { - correctedArguments[currentArgument] = getCorrectedArgument(parameterType, arguments[currentArgument]); - currentArgument++; - } - return correctedArguments; - } - - /** - * Gets the given argument corrected to match the given parameterType. Correction is necessary for array arguments not to be - * mistaken by varargs. - * - * @param parameterType the type to match the given argument upon - * @param argument the argument to match the given parameterType - * @return the corrected argument - */ - private static Object getCorrectedArgument(Class parameterType, Object argument) { - if (!parameterType.isArray() || (argument == null)) { - return argument; // normal argument for normal parameterType - } - - if (!argument.getClass().isArray()) { - return new Object[] {argument}; - } - - if (parameterType.equals(argument.getClass())) - { - return argument; // no need to cast - } - - // (typed) array argument for (object) array parameterType, elements need to be casted - Object correctedArrayArgument = Array.newInstance(parameterType.getComponentType(), Array.getLength(argument)); - for (int index = 0; index < Array.getLength(argument); index++) { - if (parameterType.getComponentType().isPrimitive()) { // rely on autoboxing - Array.set(correctedArrayArgument, index, Array.get(argument, index)); - } else { // cast to expected type - try { - Array.set(correctedArrayArgument, index, parameterType.getComponentType().cast(Array.get(argument, index))); - } catch (ClassCastException e) { - throw new IllegalArgumentException("Argument " + argument + " of type " + argument.getClass() - + " does not match expected argument type " + parameterType + "."); - } - } - } - return correctedArrayArgument; - } - - /** - * Sets the value of the named field. If fieldName denotes a static field, provide a class, otherwise provide an instance. If the - * fieldName denotes a final field, this method could fail with an IllegalAccessException, since setting the value of final fields - * at other times than instantiation can have unpredictable effects.
- *
- * Example:
- *
- * - * String myString = "Test";
- *
- * //setting the private field value
- * PrivilegedAccessor.setValue(myString, "value", new char[] {'T', 'e', 's', 't'});
- *
- * //setting the static final field serialVersionUID - MIGHT FAIL
- * PrivilegedAccessor.setValue(myString.getClass(), "serialVersionUID", 1);
- *
- *
- * - * @param instanceOrClass the instance or class to set the field - * @param fieldName the name of the field - * @param value the new value of the field - * @throws NoSuchFieldException if no field with the given fieldName can be found - * @throws IllegalAccessException possibly if the field was final - */ - public static void setValue(final Object instanceOrClass, final String fieldName, final Object value) throws NoSuchFieldException, - IllegalAccessException { - Field field = getField(instanceOrClass, fieldName); - if (Modifier.isFinal(field.getModifiers())) { - PrivilegedAccessor.setValue(field, "modifiers", field.getModifiers() ^ Modifier.FINAL); - } - field.set(instanceOrClass, value); - } - - /** - * Gets the class with the given className. - * - * @param className the name of the class to get - * @return the class for the given className - * @throws ClassNotFoundException if the class could not be found - */ - private static Class getClassForName(final String className) throws ClassNotFoundException { - if (className.indexOf('[') > -1) { - Class clazz = getClassForName(className.substring(0, className.indexOf('['))); - return Array.newInstance(clazz, 0).getClass(); - } - - if (className.indexOf("...") > -1) { - Class clazz = getClassForName(className.substring(0, className.indexOf("..."))); - return Array.newInstance(clazz, 0).getClass(); - } - - try { - return Class.forName(className, false, Thread.currentThread().getContextClassLoader()); - } catch (ClassNotFoundException e) { - return getSpecialClassForName(className); - } - } - - /** - * Maps string representation of primitives to their corresponding classes. - */ - private static final Map> PRIMITIVE_MAPPER = new HashMap>(8); - - /** - * Fills the map with all java primitives and their corresponding classes. - */ - static { - PRIMITIVE_MAPPER.put("int", Integer.TYPE); - PRIMITIVE_MAPPER.put("float", Float.TYPE); - PRIMITIVE_MAPPER.put("double", Double.TYPE); - PRIMITIVE_MAPPER.put("short", Short.TYPE); - PRIMITIVE_MAPPER.put("long", Long.TYPE); - PRIMITIVE_MAPPER.put("byte", Byte.TYPE); - PRIMITIVE_MAPPER.put("char", Character.TYPE); - PRIMITIVE_MAPPER.put("boolean", Boolean.TYPE); - } - - /** - * Gets special classes for the given className. Special classes are primitives and "standard" Java types (like String) - * - * @param className the name of the class to get - * @return the class for the given className - * @throws ClassNotFoundException if the class could not be found - */ - private static Class getSpecialClassForName(final String className) throws ClassNotFoundException { - if (PRIMITIVE_MAPPER.containsKey(className)) + stringBuffer.append(fieldName + "=" + + getValue(instanceOrClass, fieldName) + ", "); + } catch (NoSuchFieldException e) { - return PRIMITIVE_MAPPER.get(className); - } - - if (missesPackageName(className)) - { - return getStandardClassForName(className); - } - - throw new ClassNotFoundException(className); - } - - /** - * Gets a 'standard' java class for the given className. - * - * @param className the className - * @return the class for the given className (if any) - * @throws ClassNotFoundException of no 'standard' java class was found for the given className - */ - private static Class getStandardClassForName(String className) throws ClassNotFoundException { - try { - return Class.forName("java.lang." + className, false, Thread.currentThread().getContextClassLoader()); - } catch (ClassNotFoundException e) { - try { - return Class.forName("java.util." + className, false, Thread.currentThread().getContextClassLoader()); - } catch (ClassNotFoundException e1) { - throw new ClassNotFoundException(className); - } + assert false : "It should always be possible to get a field that was just here"; + } + } + + stringBuffer.replace(stringBuffer.lastIndexOf(", "), + stringBuffer.length(), "}"); + return stringBuffer.toString(); + } + + /** + * Gets the name of all fields (public, private, protected, default) of the + * given instance or class. This includes as well all fields (public, private, + * protected, default) of all its super classes. + * + * @param instanceOrClass + * the instance or class to get the fields of + * @return the collection of field names of the given instance or class + */ + public static Collection getFieldNames( + final Object instanceOrClass) + { + if (instanceOrClass == null) + { + return Collections.EMPTY_LIST; + } + + Class clazz = getClass(instanceOrClass); + Field[] fields = clazz.getDeclaredFields(); + Collection fieldNames = new ArrayList(fields.length); + + for (Field field : fields) + { + fieldNames.add(field.getName()); + } + fieldNames.addAll(getFieldNames(clazz.getSuperclass())); + + return fieldNames; + } + + /** + * Gets the signatures of all methods (public, private, protected, default) of + * the given instance or class. This includes as well all methods (public, + * private, protected, default) of all its super classes. This does not + * include constructors. + * + * @param instanceOrClass + * the instance or class to get the method signatures of + * @return the collection of method signatures of the given instance or class + */ + public static Collection getMethodSignatures( + final Object instanceOrClass) + { + if (instanceOrClass == null) + { + return Collections.EMPTY_LIST; + } + + Class clazz = getClass(instanceOrClass); + Method[] methods = clazz.getDeclaredMethods(); + Collection methodSignatures = new ArrayList( + methods.length + Object.class.getDeclaredMethods().length); + + for (Method method : methods) + { + methodSignatures.add(method.getName() + "(" + + getParameterTypesAsString(method.getParameterTypes()) + + ")"); + } + methodSignatures.addAll(getMethodSignatures(clazz.getSuperclass())); + + return methodSignatures; + } + + /** + * Gets the value of the named field and returns it as an object. If + * instanceOrClass is a class then a static field is returned. + * + * @param instanceOrClass + * the instance or class to get the field from + * @param fieldName + * the name of the field + * @return an object representing the value of the field + * @throws NoSuchFieldException + * if the field does not exist + */ + public static Object getValue(final Object instanceOrClass, + final String fieldName) throws NoSuchFieldException + { + Field field = getField(instanceOrClass, fieldName); + try + { + return field.get(instanceOrClass); + } catch (IllegalAccessException e) + { + assert false : "getField() should have setAccessible(true), so an IllegalAccessException should not occur in this place"; + return null; + } + } + + /** + * Instantiates an object of the given class with the given arguments. If you + * want to instantiate a member class, you must provide the object it is a + * member of as first argument. + * + * @param fromClass + * the class to instantiate an object from + * @param args + * the arguments to pass to the constructor + * @return an object of the given type + * @throws IllegalArgumentException + * if the number of actual and formal parameters differ; if an + * unwrapping conversion for primitive arguments fails; or if, after + * possible unwrapping, a parameter value cannot be converted to the + * corresponding formal parameter type by a method invocation + * conversion. + * @throws IllegalAccessException + * if this Constructor object enforces Java language access control + * and the underlying constructor is inaccessible. + * @throws InvocationTargetException + * if the underlying constructor throws an exception. + * @throws NoSuchMethodException + * if the constructor could not be found + * @throws InstantiationException + * if the class that declares the underlying constructor represents + * an abstract class. + * + * @see PrivilegedAccessor#instantiate(Class,Class[],Object[]) + */ + public static T instantiate(final Class fromClass, + final Object[] args) throws IllegalArgumentException, + InstantiationException, IllegalAccessException, + InvocationTargetException, NoSuchMethodException + { + return instantiate(fromClass, getParameterTypes(args), args); + } + + /** + * Instantiates an object of the given class with the given arguments and the + * given argument types. If you want to instantiate a member class, you must + * provide the object it is a member of as first argument. + * + * + * @param fromClass + * the class to instantiate an object from + * @param args + * the arguments to pass to the constructor + * @param argumentTypes + * the fully qualified types of the arguments of the constructor + * @return an object of the given type + * @throws IllegalArgumentException + * if the number of actual and formal parameters differ; if an + * unwrapping conversion for primitive arguments fails; or if, after + * possible unwrapping, a parameter value cannot be converted to the + * corresponding formal parameter type by a method invocation + * conversion. + * @throws IllegalAccessException + * if this Constructor object enforces Java language access control + * and the underlying constructor is inaccessible. + * @throws InvocationTargetException + * if the underlying constructor throws an exception. + * @throws NoSuchMethodException + * if the constructor could not be found + * @throws InstantiationException + * if the class that declares the underlying constructor represents + * an abstract class. + * + * @see PrivilegedAccessor#instantiate(Class,Object[]) + */ + public static T instantiate(final Class fromClass, + final Class[] argumentTypes, final Object[] args) + throws IllegalArgumentException, InstantiationException, + IllegalAccessException, InvocationTargetException, + NoSuchMethodException + { + return getConstructor(fromClass, argumentTypes).newInstance(args); + } + + /** + * Calls a method on the given object instance with the given arguments. + * Arguments can be object types or representations for primitives. + * + * @param instanceOrClass + * the instance or class to invoke the method on + * @param methodSignature + * the name of the method and the parameters
+ * (e.g. "myMethod(java.lang.String, com.company.project.MyObject)") + * @param arguments + * an array of objects to pass as arguments + * @return the return value of this method or null if void + * @throws IllegalAccessException + * if the method is inaccessible + * @throws InvocationTargetException + * if the underlying method throws an exception. + * @throws NoSuchMethodException + * if no method with the given methodSignature could be + * found + * @throws IllegalArgumentException + * if an argument couldn't be converted to match the expected type + */ + public static Object invokeMethod(final Object instanceOrClass, + final String methodSignature, final Object[] arguments) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException, NoSuchMethodException + { + if ((methodSignature.indexOf('(') == -1) || (methodSignature + .indexOf('(') >= methodSignature.indexOf(')'))) + { + throw new NoSuchMethodException(methodSignature); + } + Class[] parameterTypes = getParameterTypes(methodSignature); + return getMethod(instanceOrClass, getMethodName(methodSignature), + parameterTypes).invoke(instanceOrClass, + getCorrectedArguments(parameterTypes, arguments)); + } + + /** + * Gets the given arguments corrected to match the given methodSignature. + * Correction is necessary for array arguments not to be mistaken by varargs. + * + * @param parameterTypes + * the method signatue the given arguments should match + * @param arguments + * the arguments that should be corrected + * @return the corrected arguments + */ + private static Object[] getCorrectedArguments(Class[] parameterTypes, + Object[] arguments) + { + if (arguments == null) + { + return arguments; + } + if (parameterTypes.length > arguments.length) + { + return arguments; + } + if (parameterTypes.length < arguments.length) + { + return getCorrectedArguments(parameterTypes, + new Object[] + { arguments }); + } + + Object[] correctedArguments = new Object[arguments.length]; + int currentArgument = 0; + for (Class parameterType : parameterTypes) + { + correctedArguments[currentArgument] = getCorrectedArgument( + parameterType, arguments[currentArgument]); + currentArgument++; + } + return correctedArguments; + } + + /** + * Gets the given argument corrected to match the given parameterType. + * Correction is necessary for array arguments not to be mistaken by varargs. + * + * @param parameterType + * the type to match the given argument upon + * @param argument + * the argument to match the given parameterType + * @return the corrected argument + */ + private static Object getCorrectedArgument(Class parameterType, + Object argument) + { + if (!parameterType.isArray() || (argument == null)) + { + return argument; // normal argument for normal parameterType + } + + if (!argument.getClass().isArray()) + { + return new Object[] { argument }; + } + + if (parameterType.equals(argument.getClass())) + { + return argument; // no need to cast + } + + // (typed) array argument for (object) array parameterType, elements need to + // be casted + Object correctedArrayArgument = Array.newInstance( + parameterType.getComponentType(), Array.getLength(argument)); + for (int index = 0; index < Array.getLength(argument); index++) + { + if (parameterType.getComponentType().isPrimitive()) + { // rely on autoboxing + Array.set(correctedArrayArgument, index, + Array.get(argument, index)); + } + else + { // cast to expected type + try + { + Array.set(correctedArrayArgument, index, parameterType + .getComponentType().cast(Array.get(argument, index))); + } catch (ClassCastException e) + { + throw new IllegalArgumentException( + "Argument " + argument + " of type " + argument.getClass() + + " does not match expected argument type " + + parameterType + "."); + } } - } - - /** - * Tests if the given className possibly misses its package name. - * - * @param className the className - * @return true if the className might miss its package name, otherwise false - */ - private static boolean missesPackageName(String className) { - if (className.contains(".")) + } + return correctedArrayArgument; + } + + /** + * Sets the value of the named field. If fieldName denotes a static field, + * provide a class, otherwise provide an instance. If the fieldName denotes a + * final field, this method could fail with an IllegalAccessException, since + * setting the value of final fields at other times than instantiation can + * have unpredictable effects.
+ *
+ * Example:
+ *
+ * + * String myString = "Test";
+ *
+ * //setting the private field value
+ * PrivilegedAccessor.setValue(myString, "value", new char[] {'T', 'e', 's', 't'});
+ *
+ * //setting the static final field serialVersionUID - MIGHT FAIL
+ * PrivilegedAccessor.setValue(myString.getClass(), "serialVersionUID", 1);
+ *
+ *
+ * + * @param instanceOrClass + * the instance or class to set the field + * @param fieldName + * the name of the field + * @param value + * the new value of the field + * @throws NoSuchFieldException + * if no field with the given fieldName can be found + * @throws IllegalAccessException + * possibly if the field was final + */ + public static void setValue(final Object instanceOrClass, + final String fieldName, final Object value) + throws NoSuchFieldException, IllegalAccessException + { + Field field = getField(instanceOrClass, fieldName); + if (Modifier.isFinal(field.getModifiers())) + { + PrivilegedAccessor.setValue(field, "modifiers", + field.getModifiers() ^ Modifier.FINAL); + } + field.set(instanceOrClass, value); + } + + /** + * Gets the class with the given className. + * + * @param className + * the name of the class to get + * @return the class for the given className + * @throws ClassNotFoundException + * if the class could not be found + */ + private static Class getClassForName(final String className) + throws ClassNotFoundException + { + if (className.indexOf('[') > -1) + { + Class clazz = getClassForName( + className.substring(0, className.indexOf('['))); + return Array.newInstance(clazz, 0).getClass(); + } + + if (className.indexOf("...") > -1) + { + Class clazz = getClassForName( + className.substring(0, className.indexOf("..."))); + return Array.newInstance(clazz, 0).getClass(); + } + + try + { + return Class.forName(className, false, + Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException e) + { + return getSpecialClassForName(className); + } + } + + /** + * Maps string representation of primitives to their corresponding classes. + */ + private static final Map> PRIMITIVE_MAPPER = new HashMap>( + 8); + + /** + * Fills the map with all java primitives and their corresponding classes. + */ + static + { + PRIMITIVE_MAPPER.put("int", Integer.TYPE); + PRIMITIVE_MAPPER.put("float", Float.TYPE); + PRIMITIVE_MAPPER.put("double", Double.TYPE); + PRIMITIVE_MAPPER.put("short", Short.TYPE); + PRIMITIVE_MAPPER.put("long", Long.TYPE); + PRIMITIVE_MAPPER.put("byte", Byte.TYPE); + PRIMITIVE_MAPPER.put("char", Character.TYPE); + PRIMITIVE_MAPPER.put("boolean", Boolean.TYPE); + } + + /** + * Gets special classes for the given className. Special classes are + * primitives and "standard" Java types (like String) + * + * @param className + * the name of the class to get + * @return the class for the given className + * @throws ClassNotFoundException + * if the class could not be found + */ + private static Class getSpecialClassForName(final String className) + throws ClassNotFoundException + { + if (PRIMITIVE_MAPPER.containsKey(className)) + { + return PRIMITIVE_MAPPER.get(className); + } + + if (missesPackageName(className)) + { + return getStandardClassForName(className); + } + + throw new ClassNotFoundException(className); + } + + /** + * Gets a 'standard' java class for the given className. + * + * @param className + * the className + * @return the class for the given className (if any) + * @throws ClassNotFoundException + * of no 'standard' java class was found for the given className + */ + private static Class getStandardClassForName(String className) + throws ClassNotFoundException + { + try + { + return Class.forName("java.lang." + className, false, + Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException e) + { + try { - return false; - } - if (className.startsWith(className.substring(0, 1).toUpperCase(Locale.ROOT))) + return Class.forName("java.util." + className, false, + Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException e1) { - return true; - } + throw new ClassNotFoundException(className); + } + } + } + + /** + * Tests if the given className possibly misses its package name. + * + * @param className + * the className + * @return true if the className might miss its package name, otherwise false + */ + private static boolean missesPackageName(String className) + { + if (className.contains(".")) + { return false; - } - - /** - * Gets the constructor for a given class with the given parameters. - * - * @param type the class to instantiate - * @param parameterTypes the types of the parameters - * @return the constructor - * @throws NoSuchMethodException if the method could not be found - */ - private static Constructor getConstructor(final Class type, final Class[] parameterTypes) throws NoSuchMethodException { - Constructor constructor = type.getDeclaredConstructor(parameterTypes); - constructor.setAccessible(true); - return constructor; - } - - /** - * Return the named field from the given instance or class. Returns a static field if instanceOrClass is a class. - * - * @param instanceOrClass the instance or class to get the field from - * @param fieldName the name of the field to get - * @return the field - * @throws NoSuchFieldException if no such field can be found - * @throws InvalidParameterException if instanceOrClass was null - */ - private static Field getField(final Object instanceOrClass, final String fieldName) throws NoSuchFieldException, - InvalidParameterException { - if (instanceOrClass == null) + } + if (className + .startsWith(className.substring(0, 1).toUpperCase(Locale.ROOT))) + { + return true; + } + return false; + } + + /** + * Gets the constructor for a given class with the given parameters. + * + * @param type + * the class to instantiate + * @param parameterTypes + * the types of the parameters + * @return the constructor + * @throws NoSuchMethodException + * if the method could not be found + */ + private static Constructor getConstructor(final Class type, + final Class[] parameterTypes) throws NoSuchMethodException + { + Constructor constructor = type + .getDeclaredConstructor(parameterTypes); + constructor.setAccessible(true); + return constructor; + } + + /** + * Return the named field from the given instance or class. Returns a static + * field if instanceOrClass is a class. + * + * @param instanceOrClass + * the instance or class to get the field from + * @param fieldName + * the name of the field to get + * @return the field + * @throws NoSuchFieldException + * if no such field can be found + * @throws InvalidParameterException + * if instanceOrClass was null + */ + private static Field getField(final Object instanceOrClass, + final String fieldName) + throws NoSuchFieldException, InvalidParameterException + { + if (instanceOrClass == null) + { + throw new InvalidParameterException( + "Can't get field on null object/class"); + } + + Class type = getClass(instanceOrClass); + + try + { + Field field = type.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) + { + if (type.getSuperclass() == null) { - throw new InvalidParameterException("Can't get field on null object/class"); - } - - Class type = getClass(instanceOrClass); - - try { - Field field = type.getDeclaredField(fieldName); - field.setAccessible(true); - return field; - } catch (NoSuchFieldException e) { - if (type.getSuperclass() == null) - { - throw e; - } - return getField(type.getSuperclass(), fieldName); - } - } - - /** - * Gets the class of the given parameter. If the parameter is a class, it is returned, if it is an object, its class is returned - * - * @param instanceOrClass the instance or class to get the class of - * @return the class of the given parameter - */ - private static Class getClass(final Object instanceOrClass) { - if (instanceOrClass instanceof Class) + throw e; + } + return getField(type.getSuperclass(), fieldName); + } + } + + /** + * Gets the class of the given parameter. If the parameter is a class, it is + * returned, if it is an object, its class is returned + * + * @param instanceOrClass + * the instance or class to get the class of + * @return the class of the given parameter + */ + private static Class getClass(final Object instanceOrClass) + { + if (instanceOrClass instanceof Class) + { + return (Class) instanceOrClass; + } + + return instanceOrClass.getClass(); + } + + /** + * Return the named method with a method signature matching classTypes from + * the given class. + * + * @param type + * the class to get the method from + * @param methodName + * the name of the method to get + * @param parameterTypes + * the parameter-types of the method to get + * @return the method + * @throws NoSuchMethodException + * if the method could not be found + */ + private static Method getMethod(final Class type, + final String methodName, final Class[] parameterTypes) + throws NoSuchMethodException + { + try + { + return type.getDeclaredMethod(methodName, parameterTypes); + } catch (NoSuchMethodException e) + { + if (type.getSuperclass() == null) { - return (Class) instanceOrClass; - } - - return instanceOrClass.getClass(); - } - - /** - * Return the named method with a method signature matching classTypes from the given class. - * - * @param type the class to get the method from - * @param methodName the name of the method to get - * @param parameterTypes the parameter-types of the method to get - * @return the method - * @throws NoSuchMethodException if the method could not be found - */ - private static Method getMethod(final Class type, final String methodName, final Class[] parameterTypes) - throws NoSuchMethodException { - try { - return type.getDeclaredMethod(methodName, parameterTypes); - } catch (NoSuchMethodException e) { - if (type.getSuperclass() == null) - { - throw e; - } - return getMethod(type.getSuperclass(), methodName, parameterTypes); - } - } - - /** - * Gets the method with the given name and parameters from the given instance or class. If instanceOrClass is a class, then we get a - * static method. - * - * @param instanceOrClass the instance or class to get the method of - * @param methodName the name of the method - * @param parameterTypes the parameter-types of the method to get - * @return the method - * @throws NoSuchMethodException if the method could not be found - */ - private static Method getMethod(final Object instanceOrClass, final String methodName, final Class[] parameterTypes) - throws NoSuchMethodException { - Class type; - - type = getClass(instanceOrClass); - - Method accessMethod = getMethod(type, methodName, parameterTypes); - accessMethod.setAccessible(true); - return accessMethod; - } - - /** - * Gets the name of a method. - * - * @param methodSignature the signature of the method - * @return the name of the method - */ - private static String getMethodName(final String methodSignature) { - try { - return methodSignature.substring(0, methodSignature.indexOf('(')).trim(); - } catch (StringIndexOutOfBoundsException e) { - assert false : "Signature must have been checked before this method was called"; - return null; - } - } - - /** - * Gets the types of the parameters. - * - * @param parameters the parameters - * @return the class-types of the arguments - */ - private static Class[] getParameterTypes(final Object[] parameters) { - if (parameters == null) + throw e; + } + return getMethod(type.getSuperclass(), methodName, parameterTypes); + } + } + + /** + * Gets the method with the given name and parameters from the given instance + * or class. If instanceOrClass is a class, then we get a static method. + * + * @param instanceOrClass + * the instance or class to get the method of + * @param methodName + * the name of the method + * @param parameterTypes + * the parameter-types of the method to get + * @return the method + * @throws NoSuchMethodException + * if the method could not be found + */ + private static Method getMethod(final Object instanceOrClass, + final String methodName, final Class[] parameterTypes) + throws NoSuchMethodException + { + Class type; + + type = getClass(instanceOrClass); + + Method accessMethod = getMethod(type, methodName, parameterTypes); + accessMethod.setAccessible(true); + return accessMethod; + } + + /** + * Gets the name of a method. + * + * @param methodSignature + * the signature of the method + * @return the name of the method + */ + private static String getMethodName(final String methodSignature) + { + try + { + return methodSignature.substring(0, methodSignature.indexOf('(')) + .trim(); + } catch (StringIndexOutOfBoundsException e) + { + assert false : "Signature must have been checked before this method was called"; + return null; + } + } + + /** + * Gets the types of the parameters. + * + * @param parameters + * the parameters + * @return the class-types of the arguments + */ + private static Class[] getParameterTypes(final Object[] parameters) + { + if (parameters == null) + { + return new Class[0]; + } + + Class[] typesOfParameters = new Class[parameters.length]; + + for (int i = 0; i < parameters.length; i++) + { + typesOfParameters[i] = parameters[i].getClass(); + } + return typesOfParameters; + } + + /** + * Gets the types of the given parameters. If the parameters don't match the + * given methodSignature an IllegalArgumentException is thrown. + * + * @param methodSignature + * the signature of the method + * @return the parameter types as class[] + * @throws NoSuchMethodException + * if the method could not be found + * @throws IllegalArgumentException + * if one of the given parameters doesn't math the given + * methodSignature + */ + private static Class[] getParameterTypes(final String methodSignature) + throws NoSuchMethodException, IllegalArgumentException + { + String signature = getSignatureWithoutBraces(methodSignature); + + StringTokenizer tokenizer = new StringTokenizer(signature, ", *"); + Class[] typesInSignature = new Class[tokenizer.countTokens()]; + + for (int x = 0; tokenizer.hasMoreTokens(); x++) + { + String className = tokenizer.nextToken(); + try { - return new Class[0]; - } - - Class[] typesOfParameters = new Class[parameters.length]; - - for (int i = 0; i < parameters.length; i++) { - typesOfParameters[i] = parameters[i].getClass(); - } - return typesOfParameters; - } - - /** - * Gets the types of the given parameters. If the parameters don't match the given methodSignature an IllegalArgumentException is - * thrown. - * - * @param methodSignature the signature of the method - * @return the parameter types as class[] - * @throws NoSuchMethodException if the method could not be found - * @throws IllegalArgumentException if one of the given parameters doesn't math the given methodSignature - */ - private static Class[] getParameterTypes(final String methodSignature) throws NoSuchMethodException, IllegalArgumentException { - String signature = getSignatureWithoutBraces(methodSignature); - - StringTokenizer tokenizer = new StringTokenizer(signature, ", *"); - Class[] typesInSignature = new Class[tokenizer.countTokens()]; - - for (int x = 0; tokenizer.hasMoreTokens(); x++) { - String className = tokenizer.nextToken(); - try { - typesInSignature[x] = getClassForName(className); - } catch (ClassNotFoundException e) { - NoSuchMethodException noSuchMethodException = new NoSuchMethodException(methodSignature); - noSuchMethodException.initCause(e); - throw noSuchMethodException; - } - } - return typesInSignature; - } - - /** - * Gets the parameter types as a string. - * - * @param classTypes the types to get as names. - * @return the parameter types as a string - * - * @see java.lang.Class#argumentTypesToString(Class[]) - */ - private static String getParameterTypesAsString(final Class[] classTypes) { - assert classTypes != null : "getParameterTypes() should have been called before this method and should have provided not-null classTypes"; - if (classTypes.length == 0) + typesInSignature[x] = getClassForName(className); + } catch (ClassNotFoundException e) { - return ""; - } - - StringBuilder parameterTypes = new StringBuilder(); - for (Class clazz : classTypes) { - assert clazz != null : "getParameterTypes() should have been called before this method and should have provided not-null classTypes"; - parameterTypes.append(clazz.getName()).append(", "); - } - - return parameterTypes.substring(0, parameterTypes.length() - 2); - } - - /** - * Removes the braces around the methods signature. - * - * @param methodSignature the signature with braces - * @return the signature without braces - */ - private static String getSignatureWithoutBraces(final String methodSignature) { - try { - return methodSignature.substring(methodSignature.indexOf('(') + 1, methodSignature.indexOf(')')); - } catch (IndexOutOfBoundsException e) { - assert false : "signature must have been checked before this method"; - return null; - } - } + NoSuchMethodException noSuchMethodException = new NoSuchMethodException( + methodSignature); + noSuchMethodException.initCause(e); + throw noSuchMethodException; + } + } + return typesInSignature; + } + + /** + * Gets the parameter types as a string. + * + * @param classTypes + * the types to get as names. + * @return the parameter types as a string + * + * @see java.lang.Class#argumentTypesToString(Class[]) + */ + private static String getParameterTypesAsString( + final Class[] classTypes) + { + assert classTypes != null : "getParameterTypes() should have been called before this method and should have provided not-null classTypes"; + if (classTypes.length == 0) + { + return ""; + } + + StringBuilder parameterTypes = new StringBuilder(); + for (Class clazz : classTypes) + { + assert clazz != null : "getParameterTypes() should have been called before this method and should have provided not-null classTypes"; + parameterTypes.append(clazz.getName()).append(", "); + } + + return parameterTypes.substring(0, parameterTypes.length() - 2); + } + + /** + * Removes the braces around the methods signature. + * + * @param methodSignature + * the signature with braces + * @return the signature without braces + */ + private static String getSignatureWithoutBraces( + final String methodSignature) + { + try + { + return methodSignature.substring(methodSignature.indexOf('(') + 1, + methodSignature.indexOf(')')); + } catch (IndexOutOfBoundsException e) + { + assert false : "signature must have been checked before this method"; + return null; + } + } }