/* * Copyright 2004-2012 Sebastian Dietrich (Sebastian.Dietrich@e-movimento.com) * * 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 junit.extensions; import java.util.Collection; /** * 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:
* Given the following class MyClass
:
*
*
* public class MyClass { * private String name; // private attribute * * // private constructor * private MyClass() { * super(); * } * * // private method * private void setName(String newName) { * this.name = newName; * } * } ** * We now want to access the class:
* MyClass myObj = PA.instantiate(MyClass.class); * PA.invokeMethod(myObj, "setName(java.lang.String)", "myNewName"); * String name = PA.getValue(myObj, "name"); ** * This class extends {@link PrivilegedAccessor} by re-throwing checked {@link Exception}s as {@link RuntimeException}s. * * * @see PrivilegedAccessor * * @author Sebastian Dietrich (sebastian.dietrich@e-movimento.com) * @author Lubos Bistak (lubos@bistak.sk) */ public class PA { private final Object instanceOrClass; /** * Private constructor to make it impossible to instantiate this class from outside of PA. * * @param instanceOrClass */ private PA(Object instanceOrClass) { this.instanceOrClass = instanceOrClass; } /** * Returns a string representation of the given object. The string has the following format: "
methodSignature
could be found; or if
* an argument couldn't be converted to match the expected type
*
* @see PrivilegedAccessor#invokeMethod(Object,String,Object[])
*/
public static Object invokeMethod(final Object instanceOrClass, final String methodSignature, final Object... arguments) {
try {
return PrivilegedAccessor.invokeMethod(instanceOrClass, methodSignature, correctVarargs(arguments));
} catch (Exception e) {
throw new IllegalArgumentException("Can't invoke method " + methodSignature + " on " + instanceOrClass + " with arguments "
+ arguments, e);
}
}
/**
* Calls a method with the given arguments. Arguments can be object types or representations for primitives.
*
* @param methodSignature the name of the method and the parameters methodSignature
could be found; or if
* an argument couldn't be converted to match the expected type
* @see PA#invokeMethod(Object, String, Object...)
*/
public Object invokeMethod(final String methodSignature, final Object... arguments) {
return PA.invokeMethod(instanceOrClass, methodSignature, arguments);
}
/**
* Corrects varargs to their initial form. If you call a method with an object-array as last argument the Java varargs mechanism
* converts this array in single arguments. This method returns an object array if the arguments are all of the same type.
*
* @param arguments the possibly converted arguments of a vararg method
* @return arguments possibly converted
*/
private static Object[] correctVarargs(final Object... arguments) {
if ((arguments == null) || changedByVararg(arguments)) return new Object[] {arguments};
return arguments;
}
/**
* Tests if the arguments were changed by vararg. Arguments are changed by vararg if they are of a non primitive array type. E.g.
* arguments[] = Object[String[]] is converted to String[] while e.g. arguments[] = Object[int[]] is not converted and stays
* Object[int[]]
*
* Unfortunately we can't detect the difference for arg = Object[primitive] since arguments[] = Object[Object[primitive]] which is
* converted to Object[primitive] and arguments[] = Object[primitive] which stays Object[primitive]
*
* and we can't detect the difference for arg = Object[non primitive] since arguments[] = Object[Object[non primitive]] is converted
* to Object[non primitive] and arguments[] = Object[non primitive] stays Object[non primitive]
*
* @param parameters the parameters
* @return true if parameters were changes by varargs, false otherwise
*/
private static boolean changedByVararg(final Object[] parameters) {
if ((parameters.length == 0) || (parameters[0] == null)) return false;
if (parameters.getClass() == Object[].class) return false;
return true;
}
/**
* 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.
* 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 IllegalArgumentException if the value could not be set. This could be the case if no field with the given
* fieldName
can be found; or if the field was final
*
* @see PrivilegedAccessor.setValue(Object,String,Object)
*/
public static PA setValue(final Object instanceOrClass, final String fieldName, final Object value) {
try {
PrivilegedAccessor.setValue(instanceOrClass, fieldName, value);
} catch (Exception e) {
throw new IllegalArgumentException("Can't set value " + value + " at " + fieldName + " in " + instanceOrClass, e);
}
return new PA(instanceOrClass);
}
/**
* 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.
* 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 fieldName the name of the field
* @param value the new value of the field
* @throws IllegalArgumentException if the value could not be set. This could be the case if no field with the given
* fieldName
can be found; or if the field was final
*
* @see PA.setValue(Object,String,Object)
*/
public PA setValue(final String fieldName, final Object value) {
PA.setValue(instanceOrClass, fieldName, value);
return this;
}
}