001: /*
002: * Copyright 2005-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005: * in compliance with the License. You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software distributed under the License
010: * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011: * or implied. See the License for the specific language governing permissions and limitations under
012: * the License.
013: */
014:
015: package org.strecks.util;
016:
017: import java.beans.PropertyDescriptor;
018: import java.lang.reflect.GenericArrayType;
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.lang.reflect.ParameterizedType;
022: import java.lang.reflect.Type;
023: import java.lang.reflect.TypeVariable;
024: import java.lang.reflect.WildcardType;
025:
026: import org.apache.commons.beanutils.PropertyUtils;
027: import org.strecks.exceptions.ApplicationRuntimeException;
028:
029: /**
030: * Class with reflection-related helper methods
031: * @author Phil Zoio
032: */
033: public class ReflectHelper {
034:
035: /**
036: * Returns the property name associated with a setter method with the given name
037: */
038: public static String getPropertyName(String setterName) {
039: Character firstChar = setterName.charAt(3);
040: String propertyName = setterName.substring(4);
041: propertyName = Character.toLowerCase(firstChar) + propertyName;
042: return propertyName;
043: }
044:
045: /**
046: * Gets getter corresponding with setter method
047: */
048: public static Method getGetter(Method setterMethod) {
049: PropertyDescriptor property = BeanUtils
050: .findPropertyForMethod(setterMethod);
051: if (property == null) {
052: return null;
053: } else {
054: return property.getReadMethod();
055: }
056: }
057:
058: /**
059: * Gets setter corresponding with getter method
060: */
061: public static Method getSetter(Method getterMethod) {
062: PropertyDescriptor property = BeanUtils
063: .findPropertyForMethod(getterMethod);
064: if (property == null) {
065: return null;
066: } else {
067: return property.getWriteMethod();
068: }
069: }
070:
071: /**
072: * Checks whether a particular method begins with "set"
073: */
074: public static String checkSetterMethodName(Method m) {
075: String methodName = m.getName();
076: if (!methodName.startsWith("set")) {
077: throw new ApplicationRuntimeException("Method " + m
078: + " declared in " + m.getDeclaringClass()
079: + " is not a setter method");
080: }
081: return methodName;
082: }
083:
084: /**
085: * Checks that a method has exactly the specified number of parameters
086: */
087: public static void checkParameterTypeLength(Method m,
088: int parameterCount) {
089: Class<?>[] parameterTypes = m.getParameterTypes();
090: if (parameterTypes.length != parameterCount) {
091: throw new ApplicationRuntimeException("Method "
092: + m.getName() + " in class "
093: + m.getDeclaringClass() + " must have "
094: + parameterCount + " parameter(s)");
095: }
096: }
097:
098: public static boolean isGetter(Method method) {
099:
100: PropertyDescriptor property = BeanUtils
101: .findPropertyForMethod(method);
102:
103: if (property == null)
104: return false;
105:
106: return property.getReadMethod().equals(method);
107:
108: }
109:
110: public static boolean hasReturnValue(Method method) {
111: Class<?> returnType = method.getReturnType();
112: boolean equals = returnType.equals(Void.TYPE);
113: return (!equals);
114: }
115:
116: public static boolean isSetter(Method method) {
117: boolean isSetter = method.getName().startsWith("set")
118: && method.getParameterTypes() != null
119: && method.getParameterTypes().length == 1;
120: return isSetter;
121: }
122:
123: /**
124: * Invokes the specified method on the specified object
125: * @return
126: */
127: public static <T extends Object> T invokeMethod(Object o,
128: String methodName, Class<T> returnType) {
129: Method method = getMethod(o, methodName);
130: return invokeMethod(o, method, returnType);
131: }
132:
133: /**
134: * Invokes the specified method on the specified object
135: * @return
136: */
137: public static Object invokeMethod(Object o, String methodName) {
138: Method method = getMethod(o, methodName);
139: return invokeMethod(o, method);
140:
141: }
142:
143: public static Method getMethod(Object o, String methodName) {
144: Assert.notNull(o);
145: Assert.notNull(methodName);
146: Class<? extends Object> clazz = o.getClass();
147:
148: return getMethod(methodName, clazz);
149: }
150:
151: public static Method getMethod(String methodName,
152: Class<? extends Object> clazz) {
153: Assert.notNull(clazz);
154: Assert.notNull(methodName);
155: Method method = null;
156:
157: try {
158: method = clazz.getMethod(methodName);
159: } catch (IllegalArgumentException e) {
160: throw new ApplicationRuntimeException(
161: "Unable to retrieve method " + methodName
162: + "() of class " + clazz.getName(), e);
163: } catch (SecurityException e) {
164: throw new ApplicationRuntimeException("Security violation "
165: + methodName + "() of class " + clazz.getName(), e);
166: } catch (NoSuchMethodException e) {
167: throw new ApplicationRuntimeException("No method "
168: + methodName + "() of class " + clazz.getName(), e);
169: }
170: return method;
171: }
172:
173: public static Object invokeMethod(Object o, Method method) {
174: return invokeMethod(o, method, null);
175: }
176:
177: @SuppressWarnings("unchecked")
178: public static <T extends Object> T invokeMethod(Object o,
179: Method method, Class<T> returnType) {
180: Assert.notNull(o);
181: Assert.notNull(method);
182: try {
183: Object invoke = method.invoke(o);
184:
185: if (returnType != null && invoke != null
186: && !invoke.getClass().isAssignableFrom(returnType)) {
187: throw new ApplicationRuntimeException(
188: "Unable to cast result " + invoke
189: + " of method " + method.getName()
190: + "() of class "
191: + o.getClass().getName()
192: + " to return type " + returnType);
193: }
194:
195: return (T) invoke;
196: } catch (IllegalArgumentException e) {
197: throw new ApplicationRuntimeException(
198: "Unable to invoke method " + method.getName()
199: + " of class " + o.getClass(), e);
200: } catch (SecurityException e) {
201: throw new ApplicationRuntimeException("Security violation "
202: + method.getName() + " of class " + o.getClass(), e);
203: } catch (IllegalAccessException e) {
204: throw new ApplicationRuntimeException("Security violation "
205: + method.getName() + " of class " + o.getClass(), e);
206: } catch (InvocationTargetException e) {
207:
208: Throwable targetException = e.getTargetException();
209: Throwable nested = null;
210:
211: if (targetException instanceof Exception)
212: nested = targetException;
213: else
214: nested = e;
215:
216: throw new ApplicationRuntimeException(
217: "Invocation target exception for "
218: + method.getName() + " of class "
219: + o.getClass(), nested);
220:
221: }
222: }
223:
224: public static <T extends Object> T createInstance(String className,
225: Class<T> type) {
226: Assert.notNull(className);
227:
228: Class c = null;
229: try {
230: c = Class.forName(className, true, ReflectHelper.class
231: .getClassLoader());
232: } catch (ClassNotFoundException e) {
233: throw new ApplicationRuntimeException(
234: "Unable to create new instance of " + className
235: + " as class cannot be located", e);
236: }
237:
238: return createInstance(c, type);
239: }
240:
241: @SuppressWarnings("unchecked")
242: public static <T> T createInstance(Class clazz, Class<T> type) {
243: try {
244:
245: Object newInstance = clazz.newInstance();
246:
247: if (!(type.isAssignableFrom(newInstance.getClass()))) {
248: throw new ApplicationRuntimeException(
249: "Class "
250: + clazz.getName()
251: + " was instantiated but was not an instance of the type "
252: + type);
253: }
254:
255: return (T) newInstance;
256: } catch (InstantiationException e) {
257: throw new ApplicationRuntimeException(
258: "Unable to create new instance of " + clazz
259: + " as class cannot be instantiated", e);
260: } catch (IllegalAccessException e) {
261: throw new ApplicationRuntimeException(
262: "Illegal access in attempting to create instance of "
263: + clazz, e);
264: }
265: }
266:
267: /**
268: * Checks that the input type is parameterized by the class expectedType
269: * @param inputClass
270: * the class being tested
271: * @param genericInterface
272: * is the interface that the inputClass implements
273: * @param specificType
274: * the expected parameterization type
275: * @return false if the expectedType is not assignable from the actual parameterized type
276: */
277:
278: @SuppressWarnings("unchecked")
279: public static boolean checkGenericType(Class inputClass,
280: Class genericInterface, Class specificType) {
281:
282: Class c = inputClass;
283: Type[] genericInterfaces = c.getGenericInterfaces();
284:
285: for (Type type : genericInterfaces) {
286: if (type instanceof ParameterizedType) {
287: ParameterizedType t = (ParameterizedType) type;
288: Type rawType = t.getRawType();
289:
290: if (rawType instanceof Class
291: && rawType.equals(genericInterface)) {
292: Type[] actualTypeArguments = t
293: .getActualTypeArguments();
294:
295: if (actualTypeArguments.length > 0) {
296: Type arg = actualTypeArguments[0];
297:
298: if (arg instanceof Class) {
299:
300: Class<Object> expectedType = (Class<Object>) arg;
301:
302: if (!expectedType
303: .isAssignableFrom(specificType)) {
304: return false;
305:
306: }
307: }
308: }
309: }
310: }
311: }
312:
313: return true;
314:
315: }
316:
317: /**
318: * Returns the (single) generic parameterization type fo the input class, for a given interface,
319: * if this can be determined
320: * @return the parameterization class, if this can be determined, otherwise null
321: */
322:
323: @SuppressWarnings("unchecked")
324: public static Class getGenericType(Class c, Class genericInterface) {
325:
326: Type[] actualTypeArguments = getGenericTypes(c,
327: genericInterface);
328:
329: if (actualTypeArguments != null
330: && actualTypeArguments.length > 0) {
331: Type arg = actualTypeArguments[0];
332:
333: if (arg instanceof Class) {
334: Class<Object> specificType = (Class<Object>) arg;
335: return specificType;
336: }
337: }
338: return null;
339:
340: }
341:
342: /**
343: * Returns the (single) generic parameterization type fo the input class, for a given interface,
344: * if this can be determined
345: * @return the parameterization class, if this can be determined, otherwise null
346: */
347:
348: @SuppressWarnings("unchecked")
349: public static Type[] getGenericTypes(Class c, Class genericInterface) {
350:
351: Class inputClass = c;
352:
353: ParameterizedType genericType = null;
354:
355: do {
356: genericType = getParameterizedType(genericInterface,
357: inputClass);
358: } while (genericType == null
359: && ((inputClass = inputClass.getSuperclass()) != null));
360:
361: if (genericType != null) {
362:
363: Type[] actualTypeArguments = genericType
364: .getActualTypeArguments();
365: return actualTypeArguments;
366: }
367:
368: return null;
369:
370: }
371:
372: static ParameterizedType getParameterizedType(
373: Class genericInterface, Class inputClass) {
374: Type[] genericInterfaces = inputClass.getGenericInterfaces();
375:
376: ParameterizedType genericType = null;
377:
378: for (Type type : genericInterfaces) {
379: if (type instanceof ParameterizedType) {
380: ParameterizedType t = (ParameterizedType) type;
381: Type rawType = t.getRawType();
382:
383: if (rawType instanceof Class
384: && rawType.equals(genericInterface)) {
385: genericType = t;
386: break;
387: }
388: }
389: }
390: return genericType;
391: }
392:
393: /**
394: * Provides basic information on a particular type
395: */
396: public static String getTypeDescription(Type type) {
397:
398: Assert.notNull(type);
399:
400: if (type instanceof Class) {
401: return type.toString();
402: } else if (type instanceof ParameterizedType) {
403: ParameterizedType pType = (ParameterizedType) type;
404: String typeDescription = "parameterized "
405: + getTypeDescription(pType.getRawType());
406:
407: Type[] args = pType.getActualTypeArguments();
408: String typesDescription = getTypeArrayDescription(args);
409: typeDescription = typeDescription + " " + typesDescription;
410:
411: return typeDescription;
412: } else if (type instanceof GenericArrayType) {
413: GenericArrayType gType = (GenericArrayType) type;
414: String typeDescription = "array of "
415: + getTypeDescription(gType
416: .getGenericComponentType());
417: return typeDescription;
418: } else if (type instanceof WildcardType) {
419: WildcardType wType = (WildcardType) type;
420: String typeDescription = "wildcard generic type";
421:
422: String upperDesc = getTypeArrayDescription(wType
423: .getUpperBounds());
424: String lowerDesc = getTypeArrayDescription(wType
425: .getLowerBounds());
426:
427: if (StringUtils.notBlankOrNull(upperDesc))
428: typeDescription = typeDescription
429: + " with upper bound " + upperDesc;
430: if (StringUtils.notBlankOrNull(lowerDesc))
431: typeDescription = typeDescription + " and lower bound "
432: + lowerDesc;
433:
434: return typeDescription;
435: } else if (type instanceof TypeVariable) {
436: TypeVariable tType = (TypeVariable) type;
437: String typeDescription = "type variable named "
438: + tType.getName();
439:
440: typeDescription = typeDescription + " bounded by "
441: + getTypeArrayDescription(tType.getBounds());
442: return typeDescription;
443: }
444: return "Unknown";
445:
446: }
447:
448: public static String getTypeArrayDescription(Type[] types) {
449:
450: Assert.notNull(types);
451:
452: if (types.length == 0)
453: return "";
454:
455: StringBuffer buffer = new StringBuffer("(");
456: for (Type arg : types) {
457: buffer.append(getTypeDescription(arg)).append(", ");
458: }
459: StringUtils.shorten(buffer, 2);
460: buffer.append(")");
461:
462: String typesDescription = buffer.toString();
463: return typesDescription;
464: }
465:
466: /**
467: * Fnd the type of a the property of <code>containingBean</code> identified by
468: * <code>beanPropertyName</code>
469: * @return the type of the bean property as a class <code>Class</code> instance
470: */
471: public static Class<?> getBeanPropertyType(Object containingBean,
472: String beanPropertyName) {
473: Class<?> propertyType = null;
474: try {
475:
476: PropertyDescriptor propertyDescriptor = PropertyUtils
477: .getPropertyDescriptor(containingBean,
478: beanPropertyName);
479: propertyType = propertyDescriptor.getPropertyType();
480:
481: } catch (Exception e) {
482: throw new ApplicationRuntimeException(
483: "Unable to read property descriptor for bean "
484: + containingBean.getClass().getName()
485: + ", property " + beanPropertyName, e);
486: }
487: return propertyType;
488: }
489:
490: }
|