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