001package org.granite.util;
002
003import java.lang.annotation.Annotation;
004import java.lang.reflect.Field;
005import java.lang.reflect.InvocationTargetException;
006import java.lang.reflect.Method;
007import java.lang.reflect.ParameterizedType;
008import java.lang.reflect.Type;
009import java.util.ArrayList;
010import java.util.List;
011
012/**
013 * Reflections class copied from JBoss Seam.
014 * www.seamframework.org 
015 * jboss-seam-2.0.0.GA
016 * Author unattributed
017 *
018 */
019public class Reflections
020{
021   
022   public static Object invoke(Method method, Object target, Object... args) throws Exception
023   {
024      try
025      {
026         return method.invoke( target, args );
027      }
028      catch (IllegalArgumentException iae)
029      {
030         String message = "Could not invoke method by reflection: " + toString(method);
031         if (args!=null && args.length>0) 
032         {
033            message += " with parameters: (" + Strings.toClassNameString(", ", args) + ')';
034         }
035         message += " on: " + target.getClass().getName();
036         throw new IllegalArgumentException(message, iae);
037      }
038      catch (InvocationTargetException ite)
039      {
040         if ( ite.getCause() instanceof Exception )
041         {
042            throw (Exception) ite.getCause();
043         }
044         
045         throw ite;
046      }
047   }
048   
049   public static Object get(Field field, Object target) throws Exception
050   {
051      try
052      {
053         return field.get(target);
054      }
055      catch (IllegalArgumentException iae)
056      {
057         String message = "Could not get field value by reflection: " + toString(field) + 
058            " on: " + target.getClass().getName();
059         throw new IllegalArgumentException(message, iae);
060      }
061   }
062   
063   public static void set(Field field, Object target, Object value) throws Exception
064   {
065      try
066      {
067         field.set(target, value);
068      }
069      catch (IllegalArgumentException iae)
070      {
071         // target may be null if field is static so use field.getDeclaringClass() instead
072         String message = "Could not set field value by reflection: " + toString(field) +
073            " on: " + field.getDeclaringClass().getName();
074         if (value==null)
075         {
076            message += " with null value";
077         }
078         else
079         {
080            message += " with value: " + value.getClass();
081         }
082         throw new IllegalArgumentException(message, iae);
083      }
084   }
085   
086   public static Object getAndWrap(Field field, Object target)
087   {
088      try
089      {
090         return get(field, target);
091      }
092      catch (Exception e)
093      {
094         if (e instanceof RuntimeException)
095         {
096            throw (RuntimeException) e;
097         }
098         
099         throw new IllegalArgumentException("exception setting: " + field.getName(), e);
100      }
101   }
102   
103   public static void setAndWrap(Field field, Object target, Object value)
104   {
105      try
106      {
107         set(field, target, value);
108      }
109      catch (Exception e)
110      {
111         if (e instanceof RuntimeException)
112         {
113            throw (RuntimeException) e;
114         }
115         
116         throw new IllegalArgumentException("exception setting: " + field.getName(), e);
117      }
118   }
119   
120   public static Object invokeAndWrap(Method method, Object target, Object... args)
121   {
122      try
123      {
124         return invoke(method, target, args);
125      }
126      catch (Exception e)
127      {
128         if (e instanceof RuntimeException)
129         {
130            throw (RuntimeException) e;
131         }
132         
133         throw new RuntimeException("exception invoking: " + method.getName(), e);
134      }
135   }
136   
137   private static String toString(Method method)
138   {
139      return Strings.unqualify( method.getDeclaringClass().getName() ) + 
140            '.' + 
141            method.getName() + 
142            '(' + 
143            Strings.toString( ", ", method.getParameterTypes() ) + 
144            ')';
145   }
146   
147   private static String toString(Field field)
148   {
149      return Strings.unqualify( field.getDeclaringClass().getName() ) + 
150            '.' + 
151            field.getName();
152   }
153   
154   public static Class<?> classForName(String name) throws ClassNotFoundException
155   {
156      try 
157      {
158         return Thread.currentThread().getContextClassLoader().loadClass(name);
159      }
160      catch (Exception e)
161      {
162         return Class.forName(name);
163      }
164   }
165   
166   /**
167    * Return's true if the class can be loaded using Reflections.classForName()
168    */
169   public static boolean isClassAvailable(String name)
170   {
171      try 
172      {
173         classForName(name);
174      }
175      catch (ClassNotFoundException e) {
176         return false;
177      }
178      return true;
179   }
180
181   public static Class<?> getCollectionElementType(Type collectionType)
182   {
183      if ( !(collectionType instanceof ParameterizedType) )
184      {
185         throw new IllegalArgumentException("collection type not parameterized");
186      }
187      Type[] typeArguments = ( (ParameterizedType) collectionType ).getActualTypeArguments();
188      if (typeArguments.length==0)
189      {
190         throw new IllegalArgumentException("no type arguments for collection type");
191      }
192      Type typeArgument = typeArguments.length==1 ? typeArguments[0] : typeArguments[1]; //handle Maps
193      if ( !(typeArgument instanceof Class<?>) )
194      {
195         throw new IllegalArgumentException("type argument not a class");
196      }
197      return (Class<?>) typeArgument;
198   }
199   
200   public static Class<?> getMapKeyType(Type collectionType)
201   {
202      if ( !(collectionType instanceof ParameterizedType) )
203      {
204         throw new IllegalArgumentException("collection type not parameterized");
205      }
206      Type[] typeArguments = ( (ParameterizedType) collectionType ).getActualTypeArguments();
207      if (typeArguments.length==0)
208      {
209         throw new IllegalArgumentException("no type arguments for collection type");
210      }
211      Type typeArgument = typeArguments[0];
212      if ( !(typeArgument instanceof Class<?>) )
213      {
214         throw new IllegalArgumentException("type argument not a class");
215      }
216      return (Class<?>) typeArgument;
217   }
218   
219   public static Method getSetterMethod(Class<?> clazz, String name)
220   {
221      Method[] methods = clazz.getMethods();
222      for (Method method: methods)
223      {
224         String methodName = method.getName();
225         if ( methodName.startsWith("set") && method.getParameterTypes().length==1 )
226         {
227            if ( Introspector.decapitalize( methodName.substring(3) ).equals(name) )
228            {
229               return method;
230            }
231         }
232      }
233      throw new IllegalArgumentException("no such setter method: " + clazz.getName() + '.' + name);
234   }
235   
236   public static Method getGetterMethod(Class<?> clazz, String name) {
237       Method[] methods = clazz.getMethods();
238       for (Method method : methods) {
239           String methodName = method.getName();
240           if (methodName.matches("^(get|is).*") && method.getParameterTypes().length == 0) {
241               int idx = methodName.startsWith("get") ? 3 : 2;
242               if (Introspector.decapitalize(methodName.substring(idx)).equals(name))
243                   return method;
244           }
245       }
246       throw new IllegalArgumentException("no such getter method: " + clazz.getName() + '.' + name);
247    }
248   
249   /**
250    * Get all the getter methods annotated with the given annotation. Returns an empty list if
251    * none are found
252    */
253   public static List<Method> getGetterMethods(Class<?> clazz, Class<? extends Annotation> annotation) 
254   {
255      List<Method> methods = new ArrayList<Method>();
256      for (Method method : clazz.getMethods())
257      {
258         if (method.isAnnotationPresent(annotation))
259         {
260            methods.add(method);
261         }
262      }
263      return methods;
264   }
265   
266   public static Field getField(Class<?> clazz, String name)
267   {
268      for ( Class<?> superClass = clazz; superClass!=Object.class; superClass=superClass.getSuperclass() )
269      {
270         try
271         {
272            return superClass.getDeclaredField(name);
273         }
274         catch (NoSuchFieldException nsfe) {}
275      }
276      throw new IllegalArgumentException("no such field: " + clazz.getName() + '.' + name);
277   }
278   
279   /**
280    * Get all the fields which are annotated with the given annotation. Returns an empty list
281    * if none are found
282    */
283   public static List<Field> getFields(Class<?> clazz, Class<? extends Annotation> annotation)
284   {
285      List<Field> fields = new ArrayList<Field>();
286      for (Class<?> superClass = clazz; superClass!=Object.class; superClass=superClass.getSuperclass())
287      {
288         for (Field field : superClass.getDeclaredFields())
289         {
290            if (field.isAnnotationPresent(annotation))
291            {
292               fields.add(field);
293            }
294         }
295      }
296      return fields;
297   }
298
299   public static Method getMethod(Annotation annotation, String name)
300   {
301      try
302      {
303         return annotation.annotationType().getMethod(name);
304      }
305      catch (NoSuchMethodException nsme)
306      {
307         return null;
308      }
309   }
310   
311   public static boolean isInstanceOf(Class<?> clazz, String name)
312   {
313      if (name == null)
314      {
315         throw new IllegalArgumentException("name cannot be null");
316      }
317      for (Class<?> c = clazz; c != Object.class; c = c.getSuperclass())
318      {
319         if (name.equals(c.getName()))
320         {
321            return true;
322         }
323      }
324      for (Class<?> c : clazz.getInterfaces())
325      {
326         if (name.equals(c.getName()))
327         {
328            return true;
329         }
330      }
331      return false;
332   }
333
334        
335   public static Object get(Object object, String fieldName) {
336           Field field = null;
337           for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
338                   try {
339                           field = superClass.getDeclaredField(fieldName);
340                                break;
341                   }
342                   catch (NoSuchFieldException nsfe) {
343                   }
344           }
345           if (field == null)
346                   throw new RuntimeException("Could not find field " + fieldName + " of " + object);
347           field.setAccessible(true);
348           try {
349                   return field.get(object);
350           }
351           catch (Exception e) {
352                   throw new RuntimeException("Could not get field " + fieldName + " of " + object, e);
353           }
354   }
355   
356   @SuppressWarnings("unchecked")
357   public static <T> T get(Object object, String fieldName, Class<T> valueClass) {
358           Field field = null;
359           for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
360                   try {
361                           field = superClass.getDeclaredField(fieldName);
362                           break;
363                   }
364                   catch (NoSuchFieldException nsfe) {
365                   }
366           }
367           if (field == null)
368                   throw new RuntimeException("Could not find field " + fieldName + " of " + object);
369           field.setAccessible(true);
370           try {
371                   return (T)field.get(object);
372           }
373           catch (Exception e) {
374                   throw new RuntimeException("Could not get field " + fieldName + " of " + object, e);
375           }
376   }
377}