Class | Line # | Actions | ||||
---|---|---|---|---|---|---|
PA | 61 | 32 | 24 | 43 |
1 | /* | |
2 | * Copyright 2004-2012 Sebastian Dietrich (Sebastian.Dietrich@e-movimento.com) | |
3 | * | |
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 | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
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. | |
15 | */ | |
16 | package junit.extensions; | |
17 | ||
18 | import java.util.Collection; | |
19 | ||
20 | /** | |
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. | |
23 | * <p> | |
24 | * a.k.a. The "ObjectMolester" | |
25 | * <p> | |
26 | * Here is an example of using this to access a private member: <br> | |
27 | * Given the following class <code>MyClass</code>: <br> | |
28 | * | |
29 | * <pre> | |
30 | * public class MyClass { | |
31 | * private String name; // private attribute | |
32 | * | |
33 | * // private constructor | |
34 | * private MyClass() { | |
35 | * super(); | |
36 | * } | |
37 | * | |
38 | * // private method | |
39 | * private void setName(String newName) { | |
40 | * this.name = newName; | |
41 | * } | |
42 | * } | |
43 | * </pre> | |
44 | * | |
45 | * We now want to access the class: <br> | |
46 | * | |
47 | * <pre> | |
48 | * MyClass myObj = PA.instantiate(MyClass.class); | |
49 | * PA.invokeMethod(myObj, "setName(java.lang.String)", "myNewName"); | |
50 | * String name = PA.getValue(myObj, "name"); | |
51 | * </pre> | |
52 | * | |
53 | * This class extends {@link PrivilegedAccessor} by re-throwing checked {@link Exception}s as {@link RuntimeException}s. | |
54 | * | |
55 | * | |
56 | * @see PrivilegedAccessor | |
57 | * | |
58 | * @author Sebastian Dietrich (sebastian.dietrich@e-movimento.com) | |
59 | * @author Lubos Bistak (lubos@bistak.sk) | |
60 | */ | |
61 | public class PA { | |
62 | private final Object instanceOrClass; | |
63 | ||
64 | /** | |
65 | * Private constructor to make it impossible to instantiate this class from outside of PA. | |
66 | * | |
67 | * @param instanceOrClass | |
68 | */ | |
69 | 18 | private PA(Object instanceOrClass) { |
70 | 18 | this.instanceOrClass = instanceOrClass; |
71 | } | |
72 | ||
73 | /** | |
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. | |
77 | * | |
78 | * @param instanceOrClass the object or class to get a string representation of | |
79 | * @return a string representation of the given object | |
80 | * | |
81 | * @see PrivilegedAccessor#toString(Object) | |
82 | */ | |
83 | 0 | public static String toString(final Object instanceOrClass) { |
84 | 0 | return PrivilegedAccessor.toString(instanceOrClass); |
85 | } | |
86 | ||
87 | /** | |
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. | |
90 | * | |
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 | |
93 | * | |
94 | * @see PrivilegedAccessor#getFieldNames(Object) | |
95 | */ | |
96 | 0 | public static Collection<String> getFieldNames(final Object instanceOrClass) { |
97 | 0 | return PrivilegedAccessor.getFieldNames(instanceOrClass); |
98 | } | |
99 | ||
100 | /** | |
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. | |
103 | * | |
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 | |
106 | * | |
107 | * @see PrivilegedAccessor#getMethodSignatures(Object) | |
108 | */ | |
109 | 0 | public static Collection<String> getMethodSignatures(final Object instanceOrClass) { |
110 | 0 | return PrivilegedAccessor.getMethodSignatures(instanceOrClass); |
111 | } | |
112 | ||
113 | /** | |
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. | |
115 | * | |
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 | |
120 | * | |
121 | * @see PrivilegedAccessor#getValue(Object,String) | |
122 | */ | |
123 | 126 | public static Object getValue(final Object instanceOrClass, final String fieldName) { |
124 | 126 | try { |
125 | 126 | return PrivilegedAccessor.getValue(instanceOrClass, fieldName); |
126 | } catch (Exception e) { | |
127 | 0 | throw new IllegalArgumentException("Can't get value of " + fieldName + " from " + instanceOrClass, e); |
128 | } | |
129 | } | |
130 | ||
131 | /** | |
132 | * Gets the value of the named field and returns it as an object. | |
133 | * | |
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 | |
137 | * | |
138 | * @see PA#getValue(Object,String) | |
139 | */ | |
140 | 0 | public Object getValue(final String fieldName) { |
141 | 0 | return PA.getValue(instanceOrClass, fieldName); |
142 | } | |
143 | ||
144 | /** | |
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. | |
147 | * | |
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. | |
158 | * | |
159 | * @see PrivilegedAccessor#instantiate(Class,Class[],Object[]) | |
160 | */ | |
161 | 0 | public static <T> T instantiate(final Class<? extends T> fromClass, final Class<?>[] argumentTypes, final Object... arguments) { |
162 | 0 | try { |
163 | 0 | return PrivilegedAccessor.instantiate(fromClass, argumentTypes, correctVarargs(arguments)); |
164 | } catch (Exception e) { | |
165 | 0 | throw new IllegalArgumentException("Can't instantiate class " + fromClass + " with arguments " + arguments, e); |
166 | } | |
167 | } | |
168 | ||
169 | /** | |
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. | |
172 | * | |
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. | |
182 | * | |
183 | * @see PrivilegedAccessor#instantiate(Class,Object[]) | |
184 | */ | |
185 | 0 | public static <T> T instantiate(final Class<? extends T> fromClass, final Object... arguments) { |
186 | 0 | try { |
187 | 0 | return PrivilegedAccessor.instantiate(fromClass, correctVarargs(arguments)); |
188 | } catch (Exception e) { | |
189 | 0 | throw new IllegalArgumentException("Can't instantiate class " + fromClass + " with arguments " + arguments, e); |
190 | } | |
191 | } | |
192 | ||
193 | /** | |
194 | * Calls a method on the given object instance with the given arguments. Arguments can be object types or representations for | |
195 | * primitives. | |
196 | * | |
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 | |
205 | * | |
206 | * @see PrivilegedAccessor#invokeMethod(Object,String,Object[]) | |
207 | */ | |
208 | 0 | public static Object invokeMethod(final Object instanceOrClass, final String methodSignature, final Object... arguments) { |
209 | 0 | try { |
210 | 0 | return PrivilegedAccessor.invokeMethod(instanceOrClass, methodSignature, correctVarargs(arguments)); |
211 | } catch (Exception e) { | |
212 | 0 | throw new IllegalArgumentException("Can't invoke method " + methodSignature + " on " + instanceOrClass + " with arguments " |
213 | + arguments, e); | |
214 | } | |
215 | } | |
216 | ||
217 | /** | |
218 | * Calls a method with the given arguments. Arguments can be object types or representations for primitives. | |
219 | * | |
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...) | |
228 | */ | |
229 | 0 | public Object invokeMethod(final String methodSignature, final Object... arguments) { |
230 | 0 | return PA.invokeMethod(instanceOrClass, methodSignature, arguments); |
231 | } | |
232 | ||
233 | /** | |
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. | |
236 | * | |
237 | * @param arguments the possibly converted arguments of a vararg method | |
238 | * @return arguments possibly converted | |
239 | */ | |
240 | 0 | private static Object[] correctVarargs(final Object... arguments) { |
241 | 0 | if ((arguments == null) || changedByVararg(arguments)) return new Object[] {arguments}; |
242 | 0 | return arguments; |
243 | } | |
244 | ||
245 | /** | |
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 | |
248 | * Object[int[]] | |
249 | * | |
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] | |
252 | * | |
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] | |
255 | * | |
256 | * @param parameters the parameters | |
257 | * @return true if parameters were changes by varargs, false otherwise | |
258 | */ | |
259 | 0 | private static boolean changedByVararg(final Object[] parameters) { |
260 | 0 | if ((parameters.length == 0) || (parameters[0] == null)) return false; |
261 | ||
262 | 0 | if (parameters.getClass() == Object[].class) return false; |
263 | ||
264 | 0 | return true; |
265 | } | |
266 | ||
267 | /** | |
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/> | |
271 | * <br/> | |
272 | * Example:<br/> | |
273 | * <br/> | |
274 | * <code> | |
275 | * String myString = "Test"; <br/> | |
276 | * <br/> | |
277 | * //setting the private field value<br/> | |
278 | * PrivilegedAccessor.setValue(myString, "value", new char[] {'T', 'e', 's', 't'});<br/> | |
279 | * <br/> | |
280 | * //setting the static final field serialVersionUID - MIGHT FAIL<br/> | |
281 | * PrivilegedAccessor.setValue(myString.getClass(), "serialVersionUID", 1);<br/> | |
282 | * <br/> | |
283 | * </code> | |
284 | * | |
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 | |
290 | * | |
291 | * @see PrivilegedAccessor.setValue(Object,String,Object) | |
292 | */ | |
293 | 18 | public static PA setValue(final Object instanceOrClass, final String fieldName, final Object value) { |
294 | 18 | try { |
295 | 18 | PrivilegedAccessor.setValue(instanceOrClass, fieldName, value); |
296 | } catch (Exception e) { | |
297 | 0 | throw new IllegalArgumentException("Can't set value " + value + " at " + fieldName + " in " + instanceOrClass, e); |
298 | } | |
299 | 18 | return new PA(instanceOrClass); |
300 | } | |
301 | ||
302 | /** | |
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/> | |
306 | * <br/> | |
307 | * Example:<br/> | |
308 | * <br/> | |
309 | * <code> | |
310 | * String myString = "Test"; <br/> | |
311 | * <br/> | |
312 | * //setting the private field value<br/> | |
313 | * PrivilegedAccessor.setValue(myString, "value", new char[] {'T', 'e', 's', 't'});<br/> | |
314 | * <br/> | |
315 | * //setting the static final field serialVersionUID - MIGHT FAIL<br/> | |
316 | * PrivilegedAccessor.setValue(myString.getClass(), "serialVersionUID", 1);<br/> | |
317 | * <br/> | |
318 | * </code> | |
319 | * | |
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 | |
324 | * | |
325 | * @see PA.setValue(Object,String,Object) | |
326 | */ | |
327 | 0 | public PA setValue(final String fieldName, final Object value) { |
328 | 0 | PA.setValue(instanceOrClass, fieldName, value); |
329 | 0 | return this; |
330 | } | |
331 | } |