001: package org.vraptor.reflection;
002:
003: import java.lang.annotation.Annotation;
004: import java.lang.reflect.Array;
005: import java.lang.reflect.Field;
006: import java.lang.reflect.InvocationTargetException;
007: import java.lang.reflect.Method;
008: import java.lang.reflect.Modifier;
009: import java.lang.reflect.Type;
010: import java.util.ArrayList;
011: import java.util.Arrays;
012: import java.util.Calendar;
013: import java.util.Collection;
014: import java.util.GregorianCalendar;
015: import java.util.HashMap;
016: import java.util.LinkedHashSet;
017: import java.util.List;
018: import java.util.Map;
019: import java.util.Set;
020: import java.util.TreeMap;
021: import java.util.Map.Entry;
022:
023: import org.apache.log4j.Logger;
024: import org.vraptor.annotations.Out;
025: import org.vraptor.component.ComponentInstantiationException;
026: import org.vraptor.component.FieldAnnotation;
027: import org.vraptor.component.FieldOutjecter;
028: import org.vraptor.component.GetterOutjecter;
029: import org.vraptor.component.Outjecter;
030:
031: /**
032: * Wrapper class to deal with some reflection. TODO: this is too much procedural
033: * work, should change to an instance. Take care of synchronization issues.
034: *
035: * @author Guilherme Silveira
036: * @author Paulo Silveira
037: */
038: public class ReflectionUtil {
039:
040: private static final String IS_INITIALS = "is";
041:
042: private static final String GET_INITIALS = "get";
043:
044: private static final Logger LOG = Logger
045: .getLogger(ReflectionUtil.class);
046:
047: /**
048: * Instantiates a class.
049: *
050: * @return the new object
051: * @throws ComponentInstantiationException
052: * if something wrong occurs
053: */
054: public static <T> T instantiate(Class<T> clazz)
055: throws ComponentInstantiationException {
056: try {
057: return clazz.getConstructor().newInstance();
058: } catch (InvocationTargetException e) {
059: throw new ComponentInstantiationException(e.getMessage(), e
060: .getCause());
061: } catch (Exception e) {
062: throw new ComponentInstantiationException(e.getMessage(), e);
063: }
064: }
065:
066: /**
067: * Wrapper for method invocation
068: *
069: * @param object
070: * object
071: * @param method
072: * method
073: * @param parameters
074: * parameters
075: * @return the method's return
076: * @throws MethodInvocationException
077: */
078: public static Object invoke(Object object, Method method,
079: Object... parameters) throws MethodInvocationException {
080: if (object == null) {
081: throw new NullPointerException(
082: "Cannot invoke a method on a null instance");
083: }
084: if (method == null) {
085: throw new NullPointerException(
086: "Cannot invoke a null method");
087: }
088: if (parameters == null) {
089: throw new NullPointerException(
090: "Cannot invoke a method with null parameters");
091: }
092: try {
093: return method.invoke(object, parameters);
094: } catch (InvocationTargetException e) {
095: throw new MethodInvocationException(e.getMessage(), e
096: .getCause());
097: } catch (Exception e) {
098: throw new MethodInvocationException("Unable to execute "
099: + method.getName() + ": " + e.getMessage(), e);
100: }
101: }
102:
103: /**
104: * Sets a field (must be accessible)
105: *
106: * @param component
107: * component
108: * @param field
109: * field
110: * @param value
111: * new value
112: * @throws SettingException
113: * unable to call setter
114: */
115: public static void setField(Object component, Field field,
116: Object value) throws SettingException {
117: try {
118: field.set(component, value);
119: } catch (IllegalArgumentException e) {
120: String v = value == null ? "null" : value.getClass()
121: .getName();
122: throw new SettingException("Unable to set field "
123: + field.getName() + ": " + e.getMessage()
124: + " with " + v, e);
125: } catch (IllegalAccessException e) {
126: String v = value == null ? "null" : value.getClass()
127: .getName();
128: throw new SettingException("Unable to set field "
129: + field.getName() + ": " + e.getMessage()
130: + " with " + v, e);
131: }
132: }
133:
134: public static Object get(Object component, Field field)
135: throws GettingException {
136: try {
137: return field.get(component);
138: } catch (IllegalAccessException e) {
139: throw new GettingException("Unable to get field "
140: + field.getName() + ": " + e.getMessage(), e);
141: }
142: }
143:
144: public static Method findGetter(Class type, String property)
145: throws MethodInvocationException {
146: String methodName = GET_INITIALS
147: + Character.toUpperCase(property.charAt(0))
148: + property.substring(1);
149: try {
150: return type.getMethod(methodName);
151: } catch (SecurityException e) {
152: throw new MethodInvocationException(
153: "Unable to get getter method " + property, e);
154: } catch (NoSuchMethodException e) {
155: return null;
156: }
157: }
158:
159: /**
160: * Retrieves the setter method in an object for the specified property
161: *
162: * @param current
163: * the current object
164: * @param property
165: * the setter to find
166: * @return the setter or null if not found
167: */
168: public static Method findSetter(Object current, String property) {
169: String methodName = "set" + upperCaseFirstLetter(property);
170: for (Method m : current.getClass().getMethods()) {
171: if (m.getName().equals(methodName)) {
172: return m;
173: }
174: }
175: return null;
176: }
177:
178: private static String upperCaseFirstLetter(String property) {
179: return Character.toUpperCase(property.charAt(0))
180: + property.substring(1);
181: }
182:
183: /**
184: * Read all field annotations from one type
185: */
186: public static <T extends Annotation> List<FieldAnnotation<T>> readAnnotations(
187: Class type, Class<T> annot) {
188: List<FieldAnnotation<T>> list = new ArrayList<FieldAnnotation<T>>();
189: for (Field f : type.getDeclaredFields()) {
190: if (!f.isAnnotationPresent(annot)) {
191: continue;
192: }
193: T annotation = f.getAnnotation(annot);
194: if (LOG.isDebugEnabled()) {
195: LOG.debug("Adding field annotation on field "
196: + f.getName() + "::" + annotation);
197: }
198: list.add(new FieldAnnotation<T>(annotation, f));
199: f.setAccessible(true);
200: }
201: return list;
202: }
203:
204: /**
205: * Tries to instantiate an array of size 0 or type
206: *
207: * @param type
208: * type
209: * @return instantiates
210: * @throws ComponentInstantiationException
211: * problem instantiating it
212: */
213: public static Object genericInstantiate(Class<?> type)
214: throws ComponentInstantiationException {
215: if (type.isArray()) {
216: return Array.newInstance(type.getComponentType(), 0);
217: }
218: if (Calendar.class.isAssignableFrom(type)) {
219: return new GregorianCalendar();
220: }
221: return instantiate(type);
222: }
223:
224: public static Object instantiateCollection(Type type)
225: throws ComponentInstantiationException {
226: Class clazz = (Class) type;
227: if (List.class.isAssignableFrom(clazz)) {
228: return new ArrayList();
229: }
230: if (Set.class.isAssignableFrom(clazz)) {
231: return new LinkedHashSet();
232: }
233: if (Map.class.isAssignableFrom(clazz)) {
234: return new HashMap();
235: }
236: if (Collection.class.isAssignableFrom(clazz)) {
237: return new ArrayList();
238: }
239: throw new ComponentInstantiationException(
240: "Unable to instantiate the desired collection");
241: }
242:
243: /**
244: * Tries to find an specific annotation inside an array
245: *
246: * @param <T>
247: * the annotation type
248: * @param annotations
249: * the array
250: * @param clazz
251: * the annotation class to be found
252: * @return the annotation found or null if not found
253: */
254: @SuppressWarnings("unchecked")
255: public static <T extends Annotation> T findAnnotation(
256: Annotation[] annotations, Class<T> clazz) {
257: for (Annotation a : annotations) {
258: if (a.annotationType().equals(clazz)) {
259: return (T) a;
260: }
261: }
262: return null;
263: }
264:
265: /**
266: * Tries to instantiate this class
267: *
268: * @param class
269: * the class to instantiate
270: * @return the instance
271: * @throws ComponentInstantiationException
272: */
273: public static Object instantiate(String clazz)
274: throws ComponentInstantiationException {
275: try {
276: return instantiate(Class.forName(clazz));
277: } catch (ClassNotFoundException e) {
278: throw new ComponentInstantiationException(
279: "Unable to instantiate " + clazz, e);
280: }
281: }
282:
283: /**
284: * Returns a prefixed method.
285: *
286: * @param clazz
287: * the clazz where we are looking for a method
288: * @param prefix
289: * the method prefix
290: * @param name
291: * the suffix
292: * @return the found prefixed method
293: * @throws MethodInvocationException
294: * unsufficient rights to look for such method
295: */
296: public static Method getPrefixedMethod(Class<?> clazz,
297: String prefix, String name, Class... parameterTypes) {
298: String methodName = prefix
299: + Character.toUpperCase(name.charAt(0))
300: + name.substring(1);
301: for (Method m : clazz.getMethods()) {
302: if (m.getName().equals(methodName)) {
303: if (Arrays
304: .equals(m.getParameterTypes(), parameterTypes)) {
305: return m;
306: }
307: }
308: }
309: return null;
310: }
311:
312: public static Field getField(Class containingType, String field)
313: throws GettingException {
314: try {
315: Field f = containingType.getDeclaredField(field);
316: f.setAccessible(true);
317: return f;
318: } catch (SecurityException e) {
319: throw new GettingException("Unable to validate field "
320: + field, e);
321: } catch (NoSuchFieldException e) {
322: throw new GettingException("Unable to validate field "
323: + field, e);
324: }
325: }
326:
327: /**
328: * Returns all getter (and iser) methods from the given class, excluding
329: * inherited ones, mapped by its property name. Ignores any method simply
330: * called "get".
331: *
332: * <pre>
333: * name -> getName
334: * closed -> isClosed
335: * </pre>
336: *
337: * @param clazz
338: * @return
339: */
340: public static Map<String, Method> getGetters(Class clazz) {
341: if (!Modifier.isPublic(clazz.getModifiers())) {
342: throw new IllegalArgumentException("class not public "
343: + clazz);
344: }
345: Map<String, Method> methods = new TreeMap<String, Method>();
346: for (Method m : clazz.getMethods()) {
347: if (!isGetter(m)) {
348: continue;
349: }
350: if (m.getDeclaringClass().equals(Object.class)) {
351: // hack: removing getClass()
352: continue;
353: }
354: String propertyName = "";
355: if (m.getName().startsWith(GET_INITIALS)) {
356: propertyName = m.getName().substring(
357: GET_INITIALS.length());
358:
359: } else if (m.getName().startsWith(IS_INITIALS)) {
360: propertyName = m.getName().substring(
361: IS_INITIALS.length());
362: }
363: // ok, this is a hack, cause we can have a problem
364: // with classes with a get() method
365: // (the propertyname would be an empty string)
366: if (propertyName.length() != 0) {
367: if (propertyName.length() == 1
368: || Character
369: .isLowerCase(propertyName.charAt(1))) {
370: propertyName = StringUtil
371: .classNameToInstanceName(propertyName);
372: }
373: methods.put(propertyName, m);
374: }
375: }
376: return methods;
377: }
378:
379: public static boolean isGetter(Method m) {
380: if (m.getParameterTypes().length != 0
381: || !Modifier.isPublic(m.getModifiers())
382: || m.getReturnType().equals(Void.TYPE)) {
383: return false;
384: }
385: if (Modifier.isStatic(m.getModifiers())
386: || !Modifier.isPublic(m.getModifiers())
387: || Modifier.isAbstract(m.getModifiers())) {
388: return false;
389: }
390: if (m.getName().startsWith(GET_INITIALS)
391: && m.getName().length() > GET_INITIALS.length()) {
392: return true;
393: }
394: if (m.getName().startsWith(IS_INITIALS)
395: && m.getName().length() > IS_INITIALS.length()
396: && (m.getReturnType().equals(boolean.class) || m
397: .getReturnType().equals(Boolean.class))) {
398: return true;
399: }
400: return false;
401: }
402:
403: public static <T> List<Outjecter> loadOutjecters(Class<T> type) {
404: List<Outjecter> outjections = new ArrayList<Outjecter>();
405: for (Entry<String, Method> entry : ReflectionUtil.getGetters(
406: type).entrySet()) {
407: outjections.add(new GetterOutjecter(entry.getKey(), entry
408: .getValue()));
409: }
410: for (FieldAnnotation<Out> out : ReflectionUtil.readAnnotations(
411: type, Out.class)) {
412: outjections.add(new FieldOutjecter(out));
413: }
414: return outjections;
415: }
416:
417: }
|