Coverage Report - org.crosswire.common.util.ReflectionUtil
 
Classes in this File Line Coverage Branch Coverage Complexity
ReflectionUtil
0%
0/46
0%
0/18
2
 
 1  
 /**
 2  
  * Distribution License:
 3  
  * JSword is free software; you can redistribute it and/or modify it under
 4  
  * the terms of the GNU Lesser General Public License, version 2.1 or later
 5  
  * as published by the Free Software Foundation. This program is distributed
 6  
  * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 7  
  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 8  
  * See the GNU Lesser General Public License for more details.
 9  
  *
 10  
  * The License is available on the internet at:
 11  
  *      http://www.gnu.org/copyleft/lgpl.html
 12  
  * or by writing to:
 13  
  *      Free Software Foundation, Inc.
 14  
  *      59 Temple Place - Suite 330
 15  
  *      Boston, MA 02111-1307, USA
 16  
  *
 17  
  * © CrossWire Bible Society, 2005 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.common.util;
 21  
 
 22  
 import java.lang.reflect.Constructor;
 23  
 import java.lang.reflect.InvocationTargetException;
 24  
 import java.lang.reflect.Method;
 25  
 
 26  
 /**
 27  
  * Various utilities for calling constructors and methods via introspection.
 28  
  * 
 29  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 30  
  * @author Joe Walker
 31  
  * @author DM Smith
 32  
  */
 33  
 public final class ReflectionUtil {
 34  
     /**
 35  
      * Prevent instantiation
 36  
      */
 37  0
     private ReflectionUtil() {
 38  0
     }
 39  
 
 40  
     /**
 41  
      * Build an object using its default constructor. Note: a constructor that
 42  
      * takes a boolean needs a type of boolean.class, but a parameter of type
 43  
      * Boolean. Likewise for other primitives. If this is needed, do not call
 44  
      * this method.
 45  
      * 
 46  
      * @param <T> the type of the object to construct
 47  
      * @param className
 48  
      *            the full class name of the object
 49  
      * @return the constructed object
 50  
      * @throws ClassNotFoundException if the class is not found
 51  
      * @throws InstantiationException
 52  
      *               if this {@code data} represents an abstract class,
 53  
      *               an interface, an array class, a primitive type, or void;
 54  
      *               or if the class has no nullary constructor;
 55  
      *               or if the instantiation fails for some other reason.
 56  
      * @throws IllegalAccessException  if the class or its nullary
 57  
      *               constructor is not accessible.
 58  
      */
 59  
     public static <T> T construct(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
 60  0
         Class<T> clazz = (Class<T>) ClassUtil.forName(className);
 61  0
         return clazz.newInstance();
 62  
     }
 63  
 
 64  
     /**
 65  
      * Build an object using the supplied parameters. Note: a constructor that
 66  
      * takes a boolean needs a type of boolean.class, but a parameter of type
 67  
      * Boolean. Likewise for other primitives.
 68  
      * 
 69  
      * @param <T> the type of the object to construct
 70  
      * @param className
 71  
      *            the full class name of the object
 72  
      * @param params
 73  
      *            the constructor's arguments
 74  
      * @return the built object
 75  
      * @throws ClassNotFoundException if the class is not found
 76  
      * @throws NoSuchMethodException
 77  
      *              the method does not exist
 78  
      * @throws  InstantiationException
 79  
      *               if this {@code data} represents an abstract class,
 80  
      *               an interface, an array class, a primitive type, or void;
 81  
      *               or if the class has no nullary constructor;
 82  
      *               or if the instantiation fails for some other reason.
 83  
      * @throws IllegalAccessException  if the class or its nullary
 84  
      *               constructor is not accessible.
 85  
      * @throws InvocationTargetException if the underlying constructor
 86  
      *              throws an exception.
 87  
      * @throws InstantiationException
 88  
      *              if the class that declares the
 89  
      *              underlying constructor represents an abstract class.
 90  
      */
 91  
     public static <T> T construct(String className, Object... params) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
 92  
             InvocationTargetException, InstantiationException
 93  
     {
 94  0
         Class<T> clazz = (Class<T>) ClassUtil.forName(className);
 95  0
         return construct(clazz, params);
 96  
     }
 97  
 
 98  
     /**
 99  
      * Build an object using the supplied parameters. Note: a constructor that
 100  
      * takes a boolean needs a type of boolean.class, but a parameter of type
 101  
      * Boolean. Likewise for other primitives.
 102  
      *
 103  
      * @param <T> the type of the object to construct
 104  
      * @param clazz
 105  
      *            the class of the object
 106  
      * @param params
 107  
      *            the constructor's arguments
 108  
      * @return the built object
 109  
      * @throws NoSuchMethodException
 110  
      *              the method does not exist
 111  
      * @throws InstantiationException
 112  
      *              if the class that declares the
 113  
      *              underlying constructor represents an abstract class.
 114  
      * @throws IllegalAccessException
 115  
      *              if this {@code Constructor} object
 116  
      *              is enforcing Java language access control and the underlying
 117  
      *              constructor is inaccessible.
 118  
      * @throws InvocationTargetException if the underlying constructor
 119  
      *              throws an exception.
 120  
      */
 121  
     public static <T> T construct(final Class<T> clazz, final Object... params) throws NoSuchMethodException, InstantiationException, IllegalAccessException,
 122  
             InvocationTargetException
 123  
     {
 124  0
         Class<?>[] paramTypes = describeParameters(params);
 125  0
         final Constructor<T> c = clazz.getConstructor(paramTypes);
 126  0
         return c.newInstance(params);
 127  
     }
 128  
 
 129  
     /**
 130  
      * Build an object using the supplied parameters.
 131  
      * 
 132  
      * @param <T> the type of the object to construct
 133  
      * @param className
 134  
      *            the full class name of the object
 135  
      * @param params
 136  
      *            the constructor's arguments
 137  
      * @param paramTypes
 138  
      *            the types of the parameters
 139  
      * @return the built object
 140  
      * @throws ClassNotFoundException if the class is not found
 141  
      * @throws NoSuchMethodException
 142  
      *              the method does not exist
 143  
      * @throws InstantiationException
 144  
      *              if the class that declares the
 145  
      *              underlying constructor represents an abstract class.
 146  
      * @throws IllegalAccessException
 147  
      *              if this {@code Constructor} object
 148  
      *              is enforcing Java language access control and the underlying
 149  
      *              constructor is inaccessible.
 150  
      * @throws InvocationTargetException if the underlying constructor
 151  
      *              throws an exception.
 152  
      */
 153  
     public static <T> T construct(String className, Object[] params, Class<?>[] paramTypes) throws ClassNotFoundException, NoSuchMethodException,
 154  
             IllegalAccessException, InvocationTargetException, InstantiationException
 155  
     {
 156  0
         Class<?>[] calledTypes = paramTypes;
 157  0
         if (calledTypes == null) {
 158  0
             calledTypes = describeParameters(params);
 159  
         }
 160  0
         Class<T> clazz = (Class<T>) ClassUtil.forName(className);
 161  0
         final Constructor<T> c = clazz.getConstructor(calledTypes);
 162  0
         return c.newInstance(params);
 163  
     }
 164  
 
 165  
     /**
 166  
      * Call a method on a class given a sting
 167  
      * 
 168  
      * @param base
 169  
      *            The object to invoke a method on
 170  
      * @param methodName
 171  
      *            The text of the invocation, for example "getName"
 172  
      * @param params
 173  
      *            For example new Object[] { ...}
 174  
      * @return whatever the method returs
 175  
      * @throws NoSuchMethodException
 176  
      *              the method does not exist
 177  
      * @throws IllegalAccessException
 178  
      *              if this {@code Constructor} object
 179  
      *              is enforcing Java language access control and the underlying
 180  
      *              constructor is inaccessible.
 181  
      * @throws InvocationTargetException if the underlying constructor
 182  
      *              throws an exception.
 183  
      */
 184  
     public static Object invoke(Object base, String methodName, Object... params) throws NoSuchMethodException, IllegalAccessException,
 185  
             InvocationTargetException
 186  
     {
 187  0
         Class<?> clazz = base.getClass();
 188  0
         return invoke(clazz, base, methodName, params);
 189  
     }
 190  
 
 191  
     /**
 192  
      * Call a static method on a class given a string
 193  
      * 
 194  
      * @param call
 195  
      *            The text of the invocation, for example
 196  
      *            "java.lang.String.getName"
 197  
      * @param params
 198  
      *            For example new Object[] { ...}
 199  
      * @return whatever the method returs
 200  
      * @throws ClassNotFoundException if the class is not found
 201  
      * @throws NoSuchMethodException
 202  
      *              the method does not exist
 203  
      * @throws IllegalAccessException
 204  
      *              if this {@code Constructor} object
 205  
      *              is enforcing Java language access control and the underlying
 206  
      *              constructor is inaccessible.
 207  
      * @throws InvocationTargetException if the underlying constructor
 208  
      *              throws an exception.
 209  
      */
 210  
     public static Object invoke(String call, Object... params) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
 211  
             InvocationTargetException
 212  
     {
 213  
         // Split the call into class name and method name
 214  0
         int lastDot = call.lastIndexOf('.');
 215  0
         String className = call.substring(0, lastDot);
 216  0
         String methodName = call.substring(lastDot + 1);
 217  0
         Class<?> clazz = ClassUtil.forName(className);
 218  0
         return invoke(clazz, clazz, methodName, params);
 219  
     }
 220  
 
 221  
     /**
 222  
      * Call a method on an object, or statically, with the supplied parameters.
 223  
      * 
 224  
      * Note: a method that takes a boolean needs a type of boolean.class, but a
 225  
      * parameter of type Boolean. Likewise for other primitives. If this is
 226  
      * needed, do not call this method.
 227  
      * 
 228  
      * @param <T> the type of the object to construct
 229  
      * @param clazz
 230  
      *            the class of the object
 231  
      * @param obj
 232  
      *            the object having the method, or null to call a static method
 233  
      * @param methodName
 234  
      *            the method to be called
 235  
      * @param params
 236  
      *            the parameters
 237  
      * @return whatever the method returns
 238  
      * @throws NoSuchMethodException
 239  
      *              the method does not exist
 240  
      * @throws IllegalAccessException
 241  
      *              if this {@code Constructor} object
 242  
      *              is enforcing Java language access control and the underlying
 243  
      *              constructor is inaccessible.
 244  
      * @throws InvocationTargetException if the underlying constructor
 245  
      *              throws an exception.
 246  
      */
 247  
     public static <T> Object invoke(Class<T> clazz, Object obj, String methodName, Object... params) throws NoSuchMethodException, IllegalAccessException,
 248  
             InvocationTargetException
 249  
     {
 250  0
         return invoke(clazz, obj, methodName, params, null);
 251  
     }
 252  
 
 253  
     /**
 254  
      * Call a method on an object, or statically, with the supplied parameters.
 255  
      * 
 256  
      * Note: a method that takes a boolean needs a type of boolean.class, but a
 257  
      * parameter of type Boolean. Likewise for other primitives.
 258  
      * 
 259  
      * @param <T> the type of the object to construct
 260  
      * @param clazz
 261  
      *            the class of the object
 262  
      * @param obj
 263  
      *            the object having the method, or null to call a static method
 264  
      * @param methodName
 265  
      *            the method to be called
 266  
      * @param params
 267  
      *            the parameters
 268  
      * @param paramTypes
 269  
      *            the types of each of the parameters
 270  
      * @return whatever the method returns
 271  
      * @throws NoSuchMethodException
 272  
      *              the method does not exist
 273  
      * @throws IllegalAccessException
 274  
      *              if this {@code Constructor} object
 275  
      *              is enforcing Java language access control and the underlying
 276  
      *              constructor is inaccessible.
 277  
      * @throws InvocationTargetException if the underlying constructor
 278  
      *              throws an exception.
 279  
      */
 280  
     public static <T> Object invoke(Class<T> clazz, Object obj, String methodName, Object[] params, Class<?>[] paramTypes) throws NoSuchMethodException,
 281  
             IllegalAccessException, InvocationTargetException
 282  
     {
 283  0
         Class<?>[] calledTypes = paramTypes;
 284  0
         if (calledTypes == null) {
 285  0
             calledTypes = describeParameters(params);
 286  
         }
 287  0
         return getMethod(clazz, methodName, calledTypes).invoke(obj, params);
 288  
     }
 289  
 
 290  
     /**
 291  
      * Construct a parallel array of class objects for each element in params.
 292  
      * 
 293  
      * @param params
 294  
      *            the types to describe
 295  
      * @return the parallel array of class objects
 296  
      */
 297  
     private static Class<?>[] describeParameters(Object... params) {
 298  0
         Class<?>[] calledTypes = new Class[params.length];
 299  0
         for (int i = 0; i < params.length; i++) {
 300  0
             Class<?> clazz = params[i].getClass();
 301  0
             if (clazz.equals(Boolean.class)) {
 302  0
                 clazz = boolean.class;
 303  
             }
 304  0
             calledTypes[i] = clazz;
 305  
         }
 306  0
         return calledTypes;
 307  
     }
 308  
 
 309  
     private static <T> Method getMethod(Class<T> clazz, String methodName, Class<?>[] calledTypes) throws NoSuchMethodException {
 310  
         // The bad news is that we can't use something like:
 311  
         // clazz.getMethod(methodNames, called_types);
 312  
         // because it does not cope with inheritance (at least in the MVM)
 313  
         // so we have to search ourselves...
 314  0
         Method[] testMethods = clazz.getMethods();
 315  0
         outer: for (int i = 0; i < testMethods.length; i++) {
 316  
             // This this the right method name?
 317  0
             if (!testMethods[i].getName().equals(methodName)) {
 318  0
                 continue outer;
 319  
             }
 320  
 
 321  
             // The right number of params
 322  0
             Class<?>[] testTypes = testMethods[i].getParameterTypes();
 323  0
             if (testTypes.length != calledTypes.length) {
 324  0
                 continue;
 325  
             }
 326  
 
 327  
             // Of the right types?
 328  0
             for (int j = 0; j < testTypes.length; j++) {
 329  0
                 if (!testTypes[j].isAssignableFrom(calledTypes[j])) {
 330  0
                     continue outer;
 331  
                 }
 332  
             }
 333  
 
 334  
             // So this is a match
 335  0
             return testMethods[i];
 336  
         }
 337  
 
 338  0
         throw new NoSuchMethodException(methodName);
 339  
     }
 340  
 }