2 * Copyright 2004-2012 Sebastian Dietrich (Sebastian.Dietrich@e-movimento.com)
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package junit.extensions;
18 import java.util.Collection;
21 * 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
22 * for accessing fields and methods is out of the ordinary because this class uses reflection to peel away protection.
24 * a.k.a. The "ObjectMolester"
26 * Here is an example of using this to access a private member: <br>
27 * Given the following class <code>MyClass</code>: <br>
30 * public class MyClass {
31 * private String name; // private attribute
33 * // private constructor
39 * private void setName(String newName) {
40 * this.name = newName;
45 * We now want to access the class: <br>
48 * MyClass myObj = PA.instantiate(MyClass.class);
49 * PA.invokeMethod(myObj, "setName(java.lang.String)", "myNewName");
50 * String name = PA.getValue(myObj, "name");
53 * This class extends {@link PrivilegedAccessor} by re-throwing checked {@link Exception}s as {@link RuntimeException}s.
56 * @see PrivilegedAccessor
58 * @author Sebastian Dietrich (sebastian.dietrich@e-movimento.com)
59 * @author Lubos Bistak (lubos@bistak.sk)
62 private final Object instanceOrClass;
65 * Private constructor to make it impossible to instantiate this class from outside of PA.
67 * @param instanceOrClass
69 private PA(Object instanceOrClass) {
70 this.instanceOrClass = instanceOrClass;
74 * Returns a string representation of the given object. The string has the following format: "<classname> {<attributes and values>}"
75 * whereas <attributes and values> is a comma separated list with <attributeName>=<attributeValue> <atributes and values> includes
76 * all attributes of the objects class followed by the attributes of its superclass (if any) and so on.
78 * @param instanceOrClass the object or class to get a string representation of
79 * @return a string representation of the given object
81 * @see PrivilegedAccessor#toString(Object)
83 public static String toString(final Object instanceOrClass) {
84 return PrivilegedAccessor.toString(instanceOrClass);
88 * Gets the name of all fields (public, private, protected, default) of the given instance or class. This includes as well all
89 * fields (public, private, protected, default) of all its super classes.
91 * @param instanceOrClass the instance or class to get the fields of
92 * @return the collection of field names of the given instance or class
94 * @see PrivilegedAccessor#getFieldNames(Object)
96 public static Collection<String> getFieldNames(final Object instanceOrClass) {
97 return PrivilegedAccessor.getFieldNames(instanceOrClass);
101 * Gets the signatures of all methods (public, private, protected, default) of the given instance or class. This includes as well
102 * all methods (public, private, protected, default) of all its super classes. This does not include constructors.
104 * @param instanceOrClass the instance or class to get the method signatures of
105 * @return the collection of method signatures of the given instance or class
107 * @see PrivilegedAccessor#getMethodSignatures(Object)
109 public static Collection<String> getMethodSignatures(final Object instanceOrClass) {
110 return PrivilegedAccessor.getMethodSignatures(instanceOrClass);
114 * Gets the value of the named field and returns it as an object. If instanceOrClass is a class then a static field is returned.
116 * @param instanceOrClass the instance or class to get the field from
117 * @param fieldName the name of the field
118 * @return an object representing the value of the field
119 * @throws IllegalArgumentException if the field does not exist
121 * @see PrivilegedAccessor#getValue(Object,String)
123 public static Object getValue(final Object instanceOrClass, final String fieldName) {
125 return PrivilegedAccessor.getValue(instanceOrClass, fieldName);
126 } catch (Exception e) {
127 throw new IllegalArgumentException("Can't get value of " + fieldName + " from " + instanceOrClass, e);
132 * Gets the value of the named field and returns it as an object.
134 * @param fieldName the name of the field
135 * @return an object representing the value of the field
136 * @throws IllegalArgumentException if the field does not exist
138 * @see PA#getValue(Object,String)
140 public Object getValue(final String fieldName) {
141 return PA.getValue(instanceOrClass, fieldName);
145 * Instantiates an object of the given class with the given arguments and the given argument types. If you want to instantiate a
146 * member class, you must provide the object it is a member of as first argument.
148 * @param fromClass the class to instantiate an object from
149 * @param arguments the arguments to pass to the constructor
150 * @param argumentTypes the fully qualified types of the arguments of the constructor
151 * @return an object of the given type
152 * @throws IllegalArgumentException if the class can't be instantiated. This could be the case if the number of actual and formal
153 * parameters differ; if an unwrapping conversion for primitive arguments fails; if, after possible unwrapping, a
154 * parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion; if
155 * this Constructor object enforces Java language access control and the underlying constructor is inaccessible; if the
156 * underlying constructor throws an exception; if the constructor could not be found; or if the class that declares the
157 * underlying constructor represents an abstract class.
159 * @see PrivilegedAccessor#instantiate(Class,Class[],Object[])
161 public static <T> T instantiate(final Class<? extends T> fromClass, final Class<?>[] argumentTypes, final Object... arguments) {
163 return PrivilegedAccessor.instantiate(fromClass, argumentTypes, correctVarargs(arguments));
164 } catch (Exception e) {
165 throw new IllegalArgumentException("Can't instantiate class " + fromClass + " with arguments " + arguments, e);
170 * Instantiates an object of the given class with the given arguments. If you want to instantiate a member class, you must provide
171 * the object it is a member of as first argument.
173 * @param fromClass the class to instantiate an object from
174 * @param arguments the arguments to pass to the constructor
175 * @return an object of the given type
176 * @throws IllegalArgumentException if the class can't be instantiated. This could be the case if the number of actual and formal
177 * parameters differ; if an unwrapping conversion for primitive arguments fails; or if, after possible unwrapping, a
178 * parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion; if
179 * this Constructor object enforces Java language access control and the underlying constructor is inaccessible; if the
180 * underlying constructor throws an exception; if the constructor could not be found; or if the class that declares the
181 * underlying constructor represents an abstract class.
183 * @see PrivilegedAccessor#instantiate(Class,Object[])
185 public static <T> T instantiate(final Class<? extends T> fromClass, final Object... arguments) {
187 return PrivilegedAccessor.instantiate(fromClass, correctVarargs(arguments));
188 } catch (Exception e) {
189 throw new IllegalArgumentException("Can't instantiate class " + fromClass + " with arguments " + arguments, e);
194 * Calls a method on the given object instance with the given arguments. Arguments can be object types or representations for
197 * @param instanceOrClass the instance or class to invoke the method on
198 * @param methodSignature the name of the method and the parameters <br>
199 * (e.g. "myMethod(java.lang.String, com.company.project.MyObject)")
200 * @param arguments an array of objects to pass as arguments
201 * @return the return value of this method or null if void
202 * @throws IllegalArgumentException if the method could not be invoked. This could be the case if the method is inaccessible; if the
203 * underlying method throws an exception; if no method with the given <code>methodSignature</code> could be found; or if
204 * an argument couldn't be converted to match the expected type
206 * @see PrivilegedAccessor#invokeMethod(Object,String,Object[])
208 public static Object invokeMethod(final Object instanceOrClass, final String methodSignature, final Object... arguments) {
210 return PrivilegedAccessor.invokeMethod(instanceOrClass, methodSignature, correctVarargs(arguments));
211 } catch (Exception e) {
212 throw new IllegalArgumentException("Can't invoke method " + methodSignature + " on " + instanceOrClass + " with arguments "
218 * Calls a method with the given arguments. Arguments can be object types or representations for primitives.
220 * @param methodSignature the name of the method and the parameters <br>
221 * (e.g. "myMethod(java.lang.String, com.company.project.MyObject)")
222 * @param arguments an array of objects to pass as arguments
223 * @return the return value of this method or null if void
224 * @throws IllegalArgumentException if the method could not be invoked. This could be the case if the method is inaccessible; if the
225 * underlying method throws an exception; if no method with the given <code>methodSignature</code> could be found; or if
226 * an argument couldn't be converted to match the expected type
227 * @see PA#invokeMethod(Object, String, Object...)
229 public Object invokeMethod(final String methodSignature, final Object... arguments) {
230 return PA.invokeMethod(instanceOrClass, methodSignature, arguments);
234 * Corrects varargs to their initial form. If you call a method with an object-array as last argument the Java varargs mechanism
235 * converts this array in single arguments. This method returns an object array if the arguments are all of the same type.
237 * @param arguments the possibly converted arguments of a vararg method
238 * @return arguments possibly converted
240 private static Object[] correctVarargs(final Object... arguments) {
241 if ((arguments == null) || changedByVararg(arguments)) return new Object[] {arguments};
246 * Tests if the arguments were changed by vararg. Arguments are changed by vararg if they are of a non primitive array type. E.g.
247 * arguments[] = Object[String[]] is converted to String[] while e.g. arguments[] = Object[int[]] is not converted and stays
250 * Unfortunately we can't detect the difference for arg = Object[primitive] since arguments[] = Object[Object[primitive]] which is
251 * converted to Object[primitive] and arguments[] = Object[primitive] which stays Object[primitive]
253 * and we can't detect the difference for arg = Object[non primitive] since arguments[] = Object[Object[non primitive]] is converted
254 * to Object[non primitive] and arguments[] = Object[non primitive] stays Object[non primitive]
256 * @param parameters the parameters
257 * @return true if parameters were changes by varargs, false otherwise
259 private static boolean changedByVararg(final Object[] parameters) {
260 if ((parameters.length == 0) || (parameters[0] == null)) return false;
262 if (parameters.getClass() == Object[].class) return false;
268 * Sets the value of the named field. If fieldName denotes a static field, provide a class, otherwise provide an instance. If the
269 * fieldName denotes a final field, this method could fail with an IllegalAccessException, since setting the value of final fields
270 * at other times than instantiation can have unpredictable effects.<br/>
275 * String myString = "Test"; <br/>
277 * //setting the private field value<br/>
278 * PrivilegedAccessor.setValue(myString, "value", new char[] {'T', 'e', 's', 't'});<br/>
280 * //setting the static final field serialVersionUID - MIGHT FAIL<br/>
281 * PrivilegedAccessor.setValue(myString.getClass(), "serialVersionUID", 1);<br/>
285 * @param instanceOrClass the instance or class to set the field
286 * @param fieldName the name of the field
287 * @param value the new value of the field
288 * @throws IllegalArgumentException if the value could not be set. This could be the case if no field with the given
289 * <code>fieldName</code> can be found; or if the field was final
291 * @see PrivilegedAccessor.setValue(Object,String,Object)
293 public static PA setValue(final Object instanceOrClass, final String fieldName, final Object value) {
295 PrivilegedAccessor.setValue(instanceOrClass, fieldName, value);
296 } catch (Exception e) {
297 throw new IllegalArgumentException("Can't set value " + value + " at " + fieldName + " in " + instanceOrClass, e);
299 return new PA(instanceOrClass);
303 * Sets the value of the named field. If fieldName denotes a static field, provide a class, otherwise provide an instance. If the
304 * fieldName denotes a final field, this method could fail with an IllegalAccessException, since setting the value of final fields
305 * at other times than instantiation can have unpredictable effects.<br/>
310 * String myString = "Test"; <br/>
312 * //setting the private field value<br/>
313 * PrivilegedAccessor.setValue(myString, "value", new char[] {'T', 'e', 's', 't'});<br/>
315 * //setting the static final field serialVersionUID - MIGHT FAIL<br/>
316 * PrivilegedAccessor.setValue(myString.getClass(), "serialVersionUID", 1);<br/>
320 * @param fieldName the name of the field
321 * @param value the new value of the field
322 * @throws IllegalArgumentException if the value could not be set. This could be the case if no field with the given
323 * <code>fieldName</code> can be found; or if the field was final
325 * @see PA.setValue(Object,String,Object)
327 public PA setValue(final String fieldName, final Object value) {
328 PA.setValue(instanceOrClass, fieldName, value);