001: /*
002: * Copyright 2006-2007, Unitils.org
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.unitils.util;
017:
018: import org.apache.commons.lang.StringUtils;
019: import org.unitils.core.UnitilsException;
020:
021: import java.lang.reflect.*;
022: import java.util.ArrayList;
023: import java.util.List;
024:
025: /**
026: * Utility methods that use reflection for instance creation or class inspection.
027: *
028: * @author Filip Neven
029: * @author Tim Ducheyne
030: */
031: public class ReflectionUtils {
032:
033: /**
034: * Creates an instance of the class with the given name.
035: * The class's no argument constructor is used to create an instance.
036: *
037: * @param className The name of the class, not null
038: * @param bypassAccessibility If true, no exception is thrown if the parameterless constructor is not public
039: * @return An instance of this class
040: * @throws UnitilsException if the class could not be found or no instance could be created
041: */
042: @SuppressWarnings({"unchecked"})
043: public static <T> T createInstanceOfType(String className,
044: boolean bypassAccessibility) {
045: try {
046: Class<?> type = Class.forName(className);
047: return (T) createInstanceOfType(type, bypassAccessibility);
048:
049: } catch (ClassCastException e) {
050: throw new UnitilsException("Class " + className
051: + " is not of expected type.", e);
052:
053: } catch (NoClassDefFoundError e) {
054: throw new UnitilsException("Unable to load class "
055: + className, e);
056:
057: } catch (ClassNotFoundException e) {
058: throw new UnitilsException("Class " + className
059: + " not found", e);
060:
061: } catch (Exception e) {
062: throw new UnitilsException(
063: "Error while instantiating class " + className, e);
064: }
065: }
066:
067: /**
068: * Creates an instance of the given type
069: *
070: * @param <T> The type of the instance
071: * @param type The type of the instance
072: * @param bypassAccessibility If true, no exception is thrown if the parameterless constructor is not public
073: * @return An instance of this type
074: * @throws UnitilsException If an instance could not be created
075: */
076: public static <T> T createInstanceOfType(Class<T> type,
077: boolean bypassAccessibility) {
078: try {
079: Constructor<T> constructor = type.getDeclaredConstructor();
080: if (bypassAccessibility) {
081: constructor.setAccessible(true);
082: }
083: return constructor.newInstance();
084:
085: } catch (Exception e) {
086: throw new UnitilsException(
087: "Error while trying to create object of class "
088: + type.getName(), e);
089: }
090: }
091:
092: /**
093: * Returns the value of the given field (may be private) in the given object
094: *
095: * @param object The object containing the field, null for static fields
096: * @param field The field, not null
097: * @return The value of the given field in the given object
098: * @throws UnitilsException if the field could not be accessed
099: */
100: public static Object getFieldValue(Object object, Field field) {
101: try {
102: field.setAccessible(true);
103: return field.get(object);
104:
105: } catch (IllegalArgumentException e) {
106: throw new UnitilsException(
107: "Error while trying to access field " + field, e);
108:
109: } catch (IllegalAccessException e) {
110: throw new UnitilsException(
111: "Error while trying to access field " + field, e);
112: }
113: }
114:
115: /**
116: * Sets the given value to the given field on the given object
117: *
118: * @param object The object containing the field, not null
119: * @param field The field, not null
120: * @param value The value for the given field in the given object
121: * @throws UnitilsException if the field could not be accessed
122: */
123: public static void setFieldValue(Object object, Field field,
124: Object value) {
125: try {
126: field.setAccessible(true);
127: field.set(object, value);
128:
129: } catch (IllegalArgumentException e) {
130: throw new UnitilsException(
131: "Unable to assign the value to field: "
132: + field.getName()
133: + ". Ensure that this field is of the correct type.",
134: e);
135:
136: } catch (IllegalAccessException e) {
137: // Cannot occur, since field.accessible has been set to true
138: throw new UnitilsException(
139: "Error while trying to access field " + field, e);
140: }
141: }
142:
143: /**
144: * Sets the given value to the given field and setters on the given object.
145: *
146: * @param object The object containing the field and setters, not null
147: * @param fields The fields, not null
148: * @param setterMethods The setter methods, not null
149: * @param value The value for the given field and setters in the given object
150: */
151: public static void setFieldAndSetterValue(Object object,
152: List<Field> fields, List<Method> setterMethods, Object value) {
153: for (Field field : fields) {
154: try {
155: setFieldValue(object, field, value);
156:
157: } catch (UnitilsException e) {
158: throw new UnitilsException(
159: "Unable to assign the value to field: "
160: + field.getName()
161: + ". Ensure that this field is of the correct type.",
162: e);
163: }
164: }
165: for (Method method : setterMethods) {
166: if (!isSetter(method)) {
167: throw new UnitilsException(
168: "Method "
169: + method.getName()
170: + " is expected to be a setter method, but is not.");
171: }
172: try {
173: invokeMethod(object, method, value);
174:
175: } catch (UnitilsException e) {
176: throw new UnitilsException(
177: "Unable to invoke method: "
178: + object.getClass().getSimpleName()
179: + "."
180: + method.getName()
181: + ". Ensure that "
182: + "this method has following signature: void myMethod(ValueType value).",
183: e);
184: } catch (InvocationTargetException e) {
185: throw new UnitilsException("Unable to invoke method: "
186: + object.getClass().getSimpleName() + "."
187: + method.getName() + ". Method "
188: + "has thrown an exception.", e.getCause());
189: }
190: }
191: }
192:
193: /**
194: * Invokes the given method with the given parameters on the given target object
195: *
196: * @param target The object containing the method, not null
197: * @param method The method, not null
198: * @param arguments The method arguments
199: * @return The result of the invocation, null if void
200: * @throws UnitilsException if the method could not be invoked
201: * @throws InvocationTargetException If the called method throwed an exception
202: */
203: @SuppressWarnings({"unchecked"})
204: public static <T> T invokeMethod(Object target, Method method,
205: Object... arguments) throws InvocationTargetException {
206: try {
207: method.setAccessible(true);
208: return (T) method.invoke(target, arguments);
209:
210: } catch (ClassCastException e) {
211: throw new UnitilsException(
212: "Unable to invoke method. Unexpected return type "
213: + method, e);
214:
215: } catch (IllegalArgumentException e) {
216: throw new UnitilsException("Error while invoking method "
217: + method, e);
218:
219: } catch (IllegalAccessException e) {
220: throw new UnitilsException("Error while invoking method "
221: + method, e);
222: }
223: }
224:
225: /**
226: * Returns all declared fields of the given class that are assignable from the given type.
227: *
228: * @param clazz The class to get fields from, not null
229: * @param type The type, not null
230: * @param isStatic True if static fields are to be returned, false for non-static
231: * @return A list of Fields, empty list if none found
232: */
233: public static List<Field> getFieldsAssignableFrom(Class<?> clazz,
234: Class<?> type, boolean isStatic) {
235: List<Field> fieldsOfType = new ArrayList<Field>();
236: Field[] declaredFields = clazz.getDeclaredFields();
237: for (Field field : declaredFields) {
238: if (field.getType().isAssignableFrom(type)
239: && Modifier.isStatic(field.getModifiers()) == isStatic) {
240: fieldsOfType.add(field);
241: }
242: }
243: return fieldsOfType;
244: }
245:
246: /**
247: * Returns the fields in the given class that have the exact given type. The class's superclasses are also
248: * investigated.
249: *
250: * @param clazz The class to get the field from, not null
251: * @param type The type, not null
252: * @param isStatic True if static fields are to be returned, false for non-static
253: * @return The fields with the given type
254: */
255: public static List<Field> getFieldsOfType(Class<?> clazz,
256: Class<?> type, boolean isStatic) {
257: List<Field> fields = getFieldsOfTypeIgnoreSuper(clazz, type,
258: isStatic);
259: Class<?> super Class = clazz.getSuperclass();
260: if (!super Class.equals(Object.class)) {
261: fields.addAll(getFieldsOfType(super Class, type, isStatic));
262: }
263: return fields;
264: }
265:
266: /**
267: * Returns the fields in the given class that have the exact given type. The class's superclasses are not
268: * investigated.
269: *
270: * @param clazz The class to get the field from, not null
271: * @param type The type, not null
272: * @param isStatic True if static fields are to be returned, false for non-static
273: * @return The fields with the given type
274: */
275: private static List<Field> getFieldsOfTypeIgnoreSuper(
276: Class<?> clazz, Class<?> type, boolean isStatic) {
277: List<Field> fields = new ArrayList<Field>();
278: Field[] declaredFields = clazz.getDeclaredFields();
279: for (Field field : declaredFields) {
280: if (field.getType().equals(type)
281: && isStatic == Modifier.isStatic(field
282: .getModifiers())) {
283: fields.add(field);
284: }
285: }
286: return fields;
287: }
288:
289: /**
290: * Returns all declared setter methods of fields of the given class that are assignable from the given type.
291: *
292: * @param clazz The class to get setters from, not null
293: * @param type The type, not null
294: * @param isStatic True if static setters are to be returned, false for non-static
295: * @return A list of Methods, empty list if none found
296: */
297: public static List<Method> getSettersAssignableFrom(Class<?> clazz,
298: Class<?> type, boolean isStatic) {
299: List<Method> settersAssignableFrom = new ArrayList<Method>();
300: Method[] declaredMethods = clazz.getDeclaredMethods();
301: for (Method method : declaredMethods) {
302: if (isSetter(method)
303: && method.getParameterTypes()[0]
304: .isAssignableFrom(type)
305: && isStatic == Modifier.isStatic(method
306: .getModifiers())) {
307: settersAssignableFrom.add(method);
308: }
309: }
310: return settersAssignableFrom;
311: }
312:
313: /**
314: * Returns the setter methods in the given class that have an argument with the exact given type. The class's
315: * superclasses are also investigated.
316: *
317: * @param clazz The class to get the setter from, not null
318: * @param type The type, not null
319: * @param isStatic True if static setters are to be returned, false for non-static
320: * @return All setters for an object of the given type
321: */
322: public static List<Method> getSettersOfType(Class<?> clazz,
323: Class<?> type, boolean isStatic) {
324: List<Method> setters = getSettersOfTypeIgnoreSuper(clazz, type,
325: isStatic);
326: Class<?> super Class = clazz.getSuperclass();
327: if (!super Class.equals(Object.class)) {
328: setters
329: .addAll(getSettersOfType(super Class, type, isStatic));
330: }
331: return setters;
332: }
333:
334: /**
335: * Returns the setter methods in the given class that have an argument with the exact given type. The class's
336: * superclasses are not investigated.
337: *
338: * @param clazz The class to get the setter from, not null
339: * @param type The type, not null
340: * @param isStatic True if static setters are to be returned, false for non-static
341: * @return All setters for an object of the given type
342: */
343: private static List<Method> getSettersOfTypeIgnoreSuper(
344: Class<?> clazz, Class<?> type, boolean isStatic) {
345: List<Method> settersOfType = new ArrayList<Method>();
346: Method[] declaredMethods = clazz.getDeclaredMethods();
347: for (Method method : declaredMethods) {
348: if (isSetter(method)
349: && method.getParameterTypes()[0].equals(type)
350: && isStatic == Modifier.isStatic(method
351: .getModifiers())) {
352: settersOfType.add(method);
353: }
354: }
355: return settersOfType;
356: }
357:
358: /**
359: * From the given class, returns the setter for the property with the given name and 1 argument. If isStatic == true,
360: * a static setter is searched. If no such setter exists in the given class, null is returned
361: *
362: * @param clazz The class to get the setter from, not null
363: * @param propertyName The name of the property, not null
364: * @param isStatic True if a static setter is to be returned, false for non-static
365: * @return The setter method that matches the given parameters, null if not found
366: */
367: public static Method getSetter(Class<?> clazz, String propertyName,
368: boolean isStatic) {
369: String setterName = "set"
370: + StringUtils.capitalize(propertyName);
371: Method[] declaredMethods = clazz.getDeclaredMethods();
372: for (Method method : declaredMethods) {
373: if (isSetter(method)
374: && setterName.equals(method.getName())
375: && isStatic == Modifier.isStatic(method
376: .getModifiers())) {
377: return method;
378: }
379: }
380: return null;
381: }
382:
383: /**
384: * From the given class, returns the getter for the given propertyname. If isStatic == true,
385: * a static getter is searched. If no such getter exists in the given class, null is returned.
386: *
387: * @param clazz The class to get the setter from, not null
388: * @param propertyName The name of the property, not null
389: * @param isStatic True if a static getter is to be returned, false for non-static
390: * @return The getter method that matches the given parameters, or null if no such method exists
391: */
392: public static Method getGetter(Class<?> clazz, String propertyName,
393: boolean isStatic) {
394: String getterName = "get"
395: + StringUtils.capitalize(propertyName);
396: try {
397: Method getter = clazz.getDeclaredMethod(getterName);
398: if (isStatic == Modifier.isStatic(getter.getModifiers())) {
399: return getter;
400: } else {
401: return null;
402: }
403: } catch (NoSuchMethodException e) {
404: return null;
405: }
406: }
407:
408: /**
409: * From the given class, returns the getter for the given setter method. If no such getter exists in the
410: * given class, null is returned.
411: *
412: * @param setter The setter method, not null
413: * @return The getter method that matches the given setter, or null if no such method exists
414: */
415: public static Method getGetter(Method setter) {
416: if (!isSetter(setter)) {
417: return null;
418: }
419: String getterName = "get" + setter.getName().substring(3);
420: try {
421: Method getter = setter.getDeclaringClass()
422: .getDeclaredMethod(getterName);
423: if (Modifier.isStatic(setter.getModifiers()) == Modifier
424: .isStatic(getter.getModifiers())) {
425: return getter;
426: } else {
427: return null;
428: }
429: } catch (NoSuchMethodException e) {
430: return null;
431: }
432: }
433:
434: /**
435: * From the given class, returns the field with the given name. isStatic indicates if it should be a static
436: * field or not.
437: *
438: * @param clazz The class to get the field from, not null
439: * @param fieldName The name, not null
440: * @param isStatic True if a static field is to be returned, false for non-static
441: * @return The field that matches the given parameters, or null if no such field exists
442: */
443: public static Field getFieldWithName(Class<?> clazz,
444: String fieldName, boolean isStatic) {
445: try {
446: Field field = clazz.getDeclaredField(fieldName);
447: if (Modifier.isStatic(field.getModifiers()) == isStatic) {
448: return field;
449: } else {
450: return null;
451: }
452: } catch (NoSuchFieldException e) {
453: return null;
454: }
455: }
456:
457: /**
458: * Gets the enum value that has the given name.
459: *
460: * @param enumClass The enum class, not null
461: * @param enumValueName The name of the enum value, not null
462: * @return The actual enum value, not null
463: * @throws UnitilsException if no value could be found with the given name
464: */
465: public static <T extends Enum<?>> T getEnumValue(
466: Class<T> enumClass, String enumValueName) {
467: T[] enumValues = enumClass.getEnumConstants();
468: for (T enumValue : enumValues) {
469: if (enumValueName.equalsIgnoreCase(enumValue.name())) {
470:
471: return enumValue;
472: }
473: }
474: throw new UnitilsException(
475: "Unable to find a enum value in enum: " + enumClass
476: + ", with value name: " + enumValueName);
477: }
478:
479: /**
480: * For each method, check if it can be a setter for an object of the given type. A setter is a method with
481: * the following properties:
482: * <ul>
483: * <li>Method name is > 3 characters long and starts with set</li>
484: * <li>The fourth character is in uppercase</li>
485: * <li>The method has one parameter, with the type of the property to set</li>
486: * </ul>
487: *
488: * @param method The method to check, not null
489: * @return True if the given method is a setter, false otherwise
490: */
491: public static boolean isSetter(Method method) {
492: String methodName = method.getName();
493: if (methodName.length() > 3
494: && method.getName().startsWith("set")
495: && method.getParameterTypes().length == 1) {
496: String fourthLetter = methodName.substring(3, 4);
497: if (fourthLetter.toUpperCase().equals(fourthLetter)) {
498: return true;
499: }
500: }
501: return false;
502: }
503:
504: /**
505: * Gets the name of the field for the given setter method. An exception is raised when
506: * the field name could not be extracted.
507: *
508: * @param setterMethod The method, not null
509: * @return The field name, not null
510: */
511: public static String getFieldName(Method setterMethod) {
512: String methodName = setterMethod.getName();
513: if (methodName.length() < 4 || !methodName.startsWith("set")) {
514: throw new UnitilsException(
515: "Unable to get field name for setter method "
516: + setterMethod);
517: }
518: return methodName.substring(3, 4).toLowerCase()
519: + methodName.substring(4);
520: }
521:
522: /**
523: * Gets the class for the given name.
524: * An UnitilsException is thrown when the class could not be loaded.
525: *
526: * @param className The name of the class, not null
527: * @return The class, not null
528: */
529: public static Class<?> getClassWithName(String className) {
530: try {
531: return Class.forName(className);
532:
533: } catch (Throwable t) {
534: throw new UnitilsException(
535: "Could not load class with name " + className, t);
536: }
537: }
538:
539: public static Method getMethodWithName(Class<?> clazz,
540: String methodName) {
541: try {
542: return clazz.getMethod(methodName);
543: } catch (SecurityException e) {
544: throw new UnitilsException(e);
545: } catch (NoSuchMethodException e) {
546: throw new UnitilsException(e);
547: }
548: }
549:
550: }
|