Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
PA | 67 | 32 | 24 |
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 | |
22 | * the access modifier of the method or field. The syntax for accessing fields | |
23 | * and methods is out of the ordinary because this class uses reflection to peel | |
24 | * away protection. | |
25 | * <p> | |
26 | * a.k.a. The "ObjectMolester" | |
27 | * <p> | |
28 | * Here is an example of using this to access a private member: <br> | |
29 | * Given the following class <code>MyClass</code>: <br> | |
30 | * | |
31 | * <pre> | |
32 | * public class MyClass | |
33 | * { | |
34 | * private String name; // private attribute | |
35 | * | |
36 | * // private constructor | |
37 | * private MyClass() | |
38 | * { | |
39 | * super(); | |
40 | * } | |
41 | * | |
42 | * // private method | |
43 | * private void setName(String newName) | |
44 | * { | |
45 | * this.name = newName; | |
46 | * } | |
47 | * } | |
48 | * </pre> | |
49 | * | |
50 | * We now want to access the class: <br> | |
51 | * | |
52 | * <pre> | |
53 | * MyClass myObj = PA.instantiate(MyClass.class); | |
54 | * PA.invokeMethod(myObj, "setName(java.lang.String)", "myNewName"); | |
55 | * String name = PA.getValue(myObj, "name"); | |
56 | * </pre> | |
57 | * | |
58 | * This class extends {@link PrivilegedAccessor} by re-throwing checked | |
59 | * {@link Exception}s as {@link RuntimeException}s. | |
60 | * | |
61 | * | |
62 | * @see PrivilegedAccessor | |
63 | * | |
64 | * @author Sebastian Dietrich (sebastian.dietrich@e-movimento.com) | |
65 | * @author Lubos Bistak (lubos@bistak.sk) | |
66 | */ | |
67 | public class PA | |
68 | { | |
69 | private final Object instanceOrClass; | |
70 | ||
71 | /** | |
72 | * Private constructor to make it impossible to instantiate this class from | |
73 | * outside of PA. | |
74 | * | |
75 | * @param instanceOrClass | |
76 | */ | |
77 | 18 | private PA(Object instanceOrClass) |
78 | { | |
79 | 18 | this.instanceOrClass = instanceOrClass; |
80 | } | |
81 | ||
82 | /** | |
83 | * Returns a string representation of the given object. The string has the | |
84 | * following format: "<classname> {<attributes and values>}" whereas | |
85 | * <attributes and values> is a comma separated list with | |
86 | * <attributeName>=<attributeValue> <atributes and values> includes all | |
87 | * attributes of the objects class followed by the attributes of its | |
88 | * superclass (if any) and so on. | |
89 | * | |
90 | * @param instanceOrClass | |
91 | * the object or class to get a string representation of | |
92 | * @return a string representation of the given object | |
93 | * | |
94 | * @see PrivilegedAccessor#toString(Object) | |
95 | */ | |
96 | 0 | public static String toString(final Object instanceOrClass) |
97 | { | |
98 | 0 | return PrivilegedAccessor.toString(instanceOrClass); |
99 | } | |
100 | ||
101 | /** | |
102 | * Gets the name of all fields (public, private, protected, default) of the | |
103 | * given instance or class. This includes as well all fields (public, private, | |
104 | * protected, default) of all its super classes. | |
105 | * | |
106 | * @param instanceOrClass | |
107 | * the instance or class to get the fields of | |
108 | * @return the collection of field names of the given instance or class | |
109 | * | |
110 | * @see PrivilegedAccessor#getFieldNames(Object) | |
111 | */ | |
112 | 0 | public static Collection<String> getFieldNames( |
113 | final Object instanceOrClass) | |
114 | { | |
115 | 0 | return PrivilegedAccessor.getFieldNames(instanceOrClass); |
116 | } | |
117 | ||
118 | /** | |
119 | * Gets the signatures of all methods (public, private, protected, default) of | |
120 | * the given instance or class. This includes as well all methods (public, | |
121 | * private, protected, default) of all its super classes. This does not | |
122 | * include constructors. | |
123 | * | |
124 | * @param instanceOrClass | |
125 | * the instance or class to get the method signatures of | |
126 | * @return the collection of method signatures of the given instance or class | |
127 | * | |
128 | * @see PrivilegedAccessor#getMethodSignatures(Object) | |
129 | */ | |
130 | 0 | public static Collection<String> getMethodSignatures( |
131 | final Object instanceOrClass) | |
132 | { | |
133 | 0 | return PrivilegedAccessor.getMethodSignatures(instanceOrClass); |
134 | } | |
135 | ||
136 | /** | |
137 | * Gets the value of the named field and returns it as an object. If | |
138 | * instanceOrClass is a class then a static field is returned. | |
139 | * | |
140 | * @param instanceOrClass | |
141 | * the instance or class to get the field from | |
142 | * @param fieldName | |
143 | * the name of the field | |
144 | * @return an object representing the value of the field | |
145 | * @throws IllegalArgumentException | |
146 | * if the field does not exist | |
147 | * | |
148 | * @see PrivilegedAccessor#getValue(Object,String) | |
149 | */ | |
150 | 174 | public static Object getValue(final Object instanceOrClass, |
151 | final String fieldName) | |
152 | { | |
153 | 174 | try |
154 | { | |
155 | 174 | return PrivilegedAccessor.getValue(instanceOrClass, fieldName); |
156 | } catch (Exception e) | |
157 | { | |
158 | 0 | throw new IllegalArgumentException("Can't get value of " + fieldName |
159 | + " from " + instanceOrClass, e); | |
160 | } | |
161 | } | |
162 | ||
163 | /** | |
164 | * Gets the value of the named field and returns it as an object. | |
165 | * | |
166 | * @param fieldName | |
167 | * the name of the field | |
168 | * @return an object representing the value of the field | |
169 | * @throws IllegalArgumentException | |
170 | * if the field does not exist | |
171 | * | |
172 | * @see PA#getValue(Object,String) | |
173 | */ | |
174 | 0 | public Object getValue(final String fieldName) |
175 | { | |
176 | 0 | return PA.getValue(instanceOrClass, fieldName); |
177 | } | |
178 | ||
179 | /** | |
180 | * Instantiates an object of the given class with the given arguments and the | |
181 | * given argument types. If you want to instantiate a member class, you must | |
182 | * provide the object it is a member of as first argument. | |
183 | * | |
184 | * @param fromClass | |
185 | * the class to instantiate an object from | |
186 | * @param arguments | |
187 | * the arguments to pass to the constructor | |
188 | * @param argumentTypes | |
189 | * the fully qualified types of the arguments of the constructor | |
190 | * @return an object of the given type | |
191 | * @throws IllegalArgumentException | |
192 | * if the class can't be instantiated. This could be the case if the | |
193 | * number of actual and formal parameters differ; if an unwrapping | |
194 | * conversion for primitive arguments fails; if, after possible | |
195 | * unwrapping, a parameter value cannot be converted to the | |
196 | * corresponding formal parameter type by a method invocation | |
197 | * conversion; if this Constructor object enforces Java language | |
198 | * access control and the underlying constructor is inaccessible; if | |
199 | * the underlying constructor throws an exception; if the | |
200 | * constructor could not be found; or if the class that declares the | |
201 | * underlying constructor represents an abstract class. | |
202 | * | |
203 | * @see PrivilegedAccessor#instantiate(Class,Class[],Object[]) | |
204 | */ | |
205 | 0 | public static <T> T instantiate(final Class<? extends T> fromClass, |
206 | final Class<?>[] argumentTypes, final Object... arguments) | |
207 | { | |
208 | 0 | try |
209 | { | |
210 | 0 | return PrivilegedAccessor.instantiate(fromClass, argumentTypes, |
211 | correctVarargs(arguments)); | |
212 | } catch (Exception e) | |
213 | { | |
214 | 0 | throw new IllegalArgumentException("Can't instantiate class " |
215 | + fromClass + " with arguments " + arguments, e); | |
216 | } | |
217 | } | |
218 | ||
219 | /** | |
220 | * Instantiates an object of the given class with the given arguments. If you | |
221 | * want to instantiate a member class, you must provide the object it is a | |
222 | * member of as first argument. | |
223 | * | |
224 | * @param fromClass | |
225 | * the class to instantiate an object from | |
226 | * @param arguments | |
227 | * the arguments to pass to the constructor | |
228 | * @return an object of the given type | |
229 | * @throws IllegalArgumentException | |
230 | * if the class can't be instantiated. This could be the case if the | |
231 | * number of actual and formal parameters differ; if an unwrapping | |
232 | * conversion for primitive arguments fails; or if, after possible | |
233 | * unwrapping, a parameter value cannot be converted to the | |
234 | * corresponding formal parameter type by a method invocation | |
235 | * conversion; if this Constructor object enforces Java language | |
236 | * access control and the underlying constructor is inaccessible; if | |
237 | * the underlying constructor throws an exception; if the | |
238 | * constructor could not be found; or if the class that declares the | |
239 | * underlying constructor represents an abstract class. | |
240 | * | |
241 | * @see PrivilegedAccessor#instantiate(Class,Object[]) | |
242 | */ | |
243 | 0 | public static <T> T instantiate(final Class<? extends T> fromClass, |
244 | final Object... arguments) | |
245 | { | |
246 | 0 | try |
247 | { | |
248 | 0 | return PrivilegedAccessor.instantiate(fromClass, |
249 | correctVarargs(arguments)); | |
250 | } catch (Exception e) | |
251 | { | |
252 | 0 | throw new IllegalArgumentException("Can't instantiate class " |
253 | + fromClass + " with arguments " + arguments, e); | |
254 | } | |
255 | } | |
256 | ||
257 | /** | |
258 | * Calls a method on the given object instance with the given arguments. | |
259 | * Arguments can be object types or representations for primitives. | |
260 | * | |
261 | * @param instanceOrClass | |
262 | * the instance or class to invoke the method on | |
263 | * @param methodSignature | |
264 | * the name of the method and the parameters <br> | |
265 | * (e.g. "myMethod(java.lang.String, com.company.project.MyObject)") | |
266 | * @param arguments | |
267 | * an array of objects to pass as arguments | |
268 | * @return the return value of this method or null if void | |
269 | * @throws IllegalArgumentException | |
270 | * if the method could not be invoked. This could be the case if the | |
271 | * method is inaccessible; if the underlying method throws an | |
272 | * exception; if no method with the given | |
273 | * <code>methodSignature</code> could be found; or if an argument | |
274 | * couldn't be converted to match the expected type | |
275 | * | |
276 | * @see PrivilegedAccessor#invokeMethod(Object,String,Object[]) | |
277 | */ | |
278 | 0 | public static Object invokeMethod(final Object instanceOrClass, |
279 | final String methodSignature, final Object... arguments) | |
280 | { | |
281 | 0 | try |
282 | { | |
283 | 0 | return PrivilegedAccessor.invokeMethod(instanceOrClass, |
284 | methodSignature, correctVarargs(arguments)); | |
285 | } catch (Exception e) | |
286 | { | |
287 | 0 | throw new IllegalArgumentException( |
288 | "Can't invoke method " + methodSignature + " on " | |
289 | + instanceOrClass + " with arguments " + arguments, | |
290 | e); | |
291 | } | |
292 | } | |
293 | ||
294 | /** | |
295 | * Calls a method with the given arguments. Arguments can be object types or | |
296 | * representations for primitives. | |
297 | * | |
298 | * @param methodSignature | |
299 | * the name of the method and the parameters <br> | |
300 | * (e.g. "myMethod(java.lang.String, com.company.project.MyObject)") | |
301 | * @param arguments | |
302 | * an array of objects to pass as arguments | |
303 | * @return the return value of this method or null if void | |
304 | * @throws IllegalArgumentException | |
305 | * if the method could not be invoked. This could be the case if the | |
306 | * method is inaccessible; if the underlying method throws an | |
307 | * exception; if no method with the given | |
308 | * <code>methodSignature</code> could be found; or if an argument | |
309 | * couldn't be converted to match the expected type | |
310 | * @see PA#invokeMethod(Object, String, Object...) | |
311 | */ | |
312 | 0 | public Object invokeMethod(final String methodSignature, |
313 | final Object... arguments) | |
314 | { | |
315 | 0 | return PA.invokeMethod(instanceOrClass, methodSignature, arguments); |
316 | } | |
317 | ||
318 | /** | |
319 | * Corrects varargs to their initial form. If you call a method with an | |
320 | * object-array as last argument the Java varargs mechanism converts this | |
321 | * array in single arguments. This method returns an object array if the | |
322 | * arguments are all of the same type. | |
323 | * | |
324 | * @param arguments | |
325 | * the possibly converted arguments of a vararg method | |
326 | * @return arguments possibly converted | |
327 | */ | |
328 | 0 | private static Object[] correctVarargs(final Object... arguments) |
329 | { | |
330 | 0 | if ((arguments == null) || changedByVararg(arguments)) |
331 | 0 | return new Object[] { arguments }; |
332 | 0 | return arguments; |
333 | } | |
334 | ||
335 | /** | |
336 | * Tests if the arguments were changed by vararg. Arguments are changed by | |
337 | * vararg if they are of a non primitive array type. E.g. arguments[] = | |
338 | * Object[String[]] is converted to String[] while e.g. arguments[] = | |
339 | * Object[int[]] is not converted and stays Object[int[]] | |
340 | * | |
341 | * Unfortunately we can't detect the difference for arg = Object[primitive] | |
342 | * since arguments[] = Object[Object[primitive]] which is converted to | |
343 | * Object[primitive] and arguments[] = Object[primitive] which stays | |
344 | * Object[primitive] | |
345 | * | |
346 | * and we can't detect the difference for arg = Object[non primitive] since | |
347 | * arguments[] = Object[Object[non primitive]] is converted to Object[non | |
348 | * primitive] and arguments[] = Object[non primitive] stays Object[non | |
349 | * primitive] | |
350 | * | |
351 | * @param parameters | |
352 | * the parameters | |
353 | * @return true if parameters were changes by varargs, false otherwise | |
354 | */ | |
355 | 0 | private static boolean changedByVararg(final Object[] parameters) |
356 | { | |
357 | 0 | if ((parameters.length == 0) || (parameters[0] == null)) |
358 | 0 | return false; |
359 | ||
360 | 0 | if (parameters.getClass() == Object[].class) |
361 | 0 | return false; |
362 | ||
363 | 0 | return true; |
364 | } | |
365 | ||
366 | /** | |
367 | * Sets the value of the named field. If fieldName denotes a static field, | |
368 | * provide a class, otherwise provide an instance. If the fieldName denotes a | |
369 | * final field, this method could fail with an IllegalAccessException, since | |
370 | * setting the value of final fields at other times than instantiation can | |
371 | * have unpredictable effects.<br/> | |
372 | * <br/> | |
373 | * Example:<br/> | |
374 | * <br/> | |
375 | * <code> | |
376 | * String myString = "Test"; <br/> | |
377 | * <br/> | |
378 | * //setting the private field value<br/> | |
379 | * PrivilegedAccessor.setValue(myString, "value", new char[] {'T', 'e', 's', 't'});<br/> | |
380 | * <br/> | |
381 | * //setting the static final field serialVersionUID - MIGHT FAIL<br/> | |
382 | * PrivilegedAccessor.setValue(myString.getClass(), "serialVersionUID", 1);<br/> | |
383 | * <br/> | |
384 | * </code> | |
385 | * | |
386 | * @param instanceOrClass | |
387 | * the instance or class to set the field | |
388 | * @param fieldName | |
389 | * the name of the field | |
390 | * @param value | |
391 | * the new value of the field | |
392 | * @throws IllegalArgumentException | |
393 | * if the value could not be set. This could be the case if no field | |
394 | * with the given <code>fieldName</code> can be found; or if the | |
395 | * field was final | |
396 | * | |
397 | * @see PrivilegedAccessor.setValue(Object,String,Object) | |
398 | */ | |
399 | 18 | public static PA setValue(final Object instanceOrClass, |
400 | final String fieldName, final Object value) | |
401 | { | |
402 | 18 | try |
403 | { | |
404 | 18 | PrivilegedAccessor.setValue(instanceOrClass, fieldName, value); |
405 | } catch (Exception e) | |
406 | { | |
407 | 0 | throw new IllegalArgumentException("Can't set value " + value + " at " |
408 | + fieldName + " in " + instanceOrClass, e); | |
409 | } | |
410 | 18 | return new PA(instanceOrClass); |
411 | } | |
412 | ||
413 | /** | |
414 | * Sets the value of the named field. If fieldName denotes a static field, | |
415 | * provide a class, otherwise provide an instance. If the fieldName denotes a | |
416 | * final field, this method could fail with an IllegalAccessException, since | |
417 | * setting the value of final fields at other times than instantiation can | |
418 | * have unpredictable effects.<br/> | |
419 | * <br/> | |
420 | * Example:<br/> | |
421 | * <br/> | |
422 | * <code> | |
423 | * String myString = "Test"; <br/> | |
424 | * <br/> | |
425 | * //setting the private field value<br/> | |
426 | * PrivilegedAccessor.setValue(myString, "value", new char[] {'T', 'e', 's', 't'});<br/> | |
427 | * <br/> | |
428 | * //setting the static final field serialVersionUID - MIGHT FAIL<br/> | |
429 | * PrivilegedAccessor.setValue(myString.getClass(), "serialVersionUID", 1);<br/> | |
430 | * <br/> | |
431 | * </code> | |
432 | * | |
433 | * @param fieldName | |
434 | * the name of the field | |
435 | * @param value | |
436 | * the new value of the field | |
437 | * @throws IllegalArgumentException | |
438 | * if the value could not be set. This could be the case if no field | |
439 | * with the given <code>fieldName</code> can be found; or if the | |
440 | * field was final | |
441 | * | |
442 | * @see PA.setValue(Object,String,Object) | |
443 | */ | |
444 | 0 | public PA setValue(final String fieldName, final Object value) |
445 | { | |
446 | 0 | PA.setValue(instanceOrClass, fieldName, value); |
447 | 0 | return this; |
448 | } | |
449 | } |