Coverage Report - org.apache.commons.beanutils.ConstructorUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
ConstructorUtils
82%
62/75
71%
27/38
4.1
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.beanutils;
 19  
 
 20  
 import java.lang.reflect.Constructor;
 21  
 import java.lang.reflect.InvocationTargetException;
 22  
 import java.lang.reflect.Modifier;
 23  
 
 24  
 /**
 25  
  * <p> Utility reflection methods focussed on constructors, modelled after {@link MethodUtils}. </p>
 26  
  *
 27  
  * <h3>Known Limitations</h3>
 28  
  * <h4>Accessing Public Constructors In A Default Access Superclass</h4>
 29  
  * <p>There is an issue when invoking public constructors contained in a default access superclass.
 30  
  * Reflection locates these constructors fine and correctly assigns them as public.
 31  
  * However, an <code>IllegalAccessException</code> is thrown if the constructors is invoked.</p>
 32  
  *
 33  
  * <p><code>ConstructorUtils</code> contains a workaround for this situation.
 34  
  * It will attempt to call <code>setAccessible</code> on this constructor.
 35  
  * If this call succeeds, then the method can be invoked as normal.
 36  
  * This call will only succeed when the application has sufficient security privilages.
 37  
  * If this call fails then a warning will be logged and the method may fail.</p>
 38  
  *
 39  
  * @author Craig R. McClanahan
 40  
  * @author Ralph Schaer
 41  
  * @author Chris Audley
 42  
  * @author Rey Francois
 43  
  * @author Gregor Rayman
 44  
  * @author Jan Sorensen
 45  
  * @author Robert Burrell Donkin
 46  
  * @author Rodney Waldhoff
 47  
  * @version $Revision: 555824 $ $Date: 2007-07-12 20:27:15 -0400 (Thu, 12 Jul 2007) $
 48  
  */
 49  0
 public class ConstructorUtils {
 50  
 
 51  
     // --------------------------------------------------------- Private Members
 52  
     /** An empty class array */
 53  1
     private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
 54  
     /** An empty object array */
 55  1
     private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
 56  
 
 57  
     // --------------------------------------------------------- Public Methods
 58  
 
 59  
     /**
 60  
      * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
 61  
      * The formal parameter type is inferred from the actual values of <code>arg</code>.
 62  
      * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
 63  
      *
 64  
      * <p>The signatures should be assignment compatible.</p>
 65  
      *
 66  
      * @param klass the class to be constructed.
 67  
      * @param arg the actual argument
 68  
      * @return new instance of <code>klazz</code>
 69  
      *
 70  
      * @throws NoSuchMethodException If the constructor cannot be found
 71  
      * @throws IllegalAccessException If an error occurs accessing the constructor
 72  
      * @throws InvocationTargetException If an error occurs invoking the constructor
 73  
      * @throws InstantiationException If an error occurs instantiating the class
 74  
      *
 75  
      * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
 76  
      */
 77  
     public static Object invokeConstructor(Class klass, Object arg)
 78  
         throws
 79  
             NoSuchMethodException,
 80  
             IllegalAccessException,
 81  
             InvocationTargetException,
 82  
             InstantiationException {
 83  
 
 84  2
         Object[] args = { arg };
 85  2
         return invokeConstructor(klass, args);
 86  
 
 87  
     }
 88  
 
 89  
     /**
 90  
      * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
 91  
      * The formal parameter types are inferred from the actual values of <code>args</code>.
 92  
      * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
 93  
      *
 94  
      * <p>The signatures should be assignment compatible.</p>
 95  
      *
 96  
      * @param klass the class to be constructed.
 97  
      * @param args actual argument array
 98  
      * @return new instance of <code>klazz</code>
 99  
      *
 100  
      * @throws NoSuchMethodException If the constructor cannot be found
 101  
      * @throws IllegalAccessException If an error occurs accessing the constructor
 102  
      * @throws InvocationTargetException If an error occurs invoking the constructor
 103  
      * @throws InstantiationException If an error occurs instantiating the class
 104  
      *
 105  
      * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
 106  
      */
 107  
     public static Object invokeConstructor(Class klass, Object[] args)
 108  
         throws
 109  
             NoSuchMethodException,
 110  
             IllegalAccessException,
 111  
             InvocationTargetException,
 112  
             InstantiationException {
 113  
 
 114  3
         if (null == args) {
 115  0
             args = EMPTY_OBJECT_ARRAY;
 116  
         }
 117  3
         int arguments = args.length;
 118  3
         Class parameterTypes[] = new Class[arguments];
 119  7
         for (int i = 0; i < arguments; i++) {
 120  4
             parameterTypes[i] = args[i].getClass();
 121  
         }
 122  3
         return invokeConstructor(klass, args, parameterTypes);
 123  
 
 124  
     }
 125  
 
 126  
     /**
 127  
      * <p>Returns new instance of <code>klazz</code> created using constructor
 128  
      * with signature <code>parameterTypes</code> and actual arguments <code>args</code>.</p>
 129  
      *
 130  
      * <p>The signatures should be assignment compatible.</p>
 131  
      *
 132  
      * @param klass the class to be constructed.
 133  
      * @param args actual argument array
 134  
      * @param parameterTypes parameter types array
 135  
      * @return new instance of <code>klazz</code>
 136  
      *
 137  
      * @throws NoSuchMethodException if matching constructor cannot be found
 138  
      * @throws IllegalAccessException thrown on the constructor's invocation
 139  
      * @throws InvocationTargetException thrown on the constructor's invocation
 140  
      * @throws InstantiationException thrown on the constructor's invocation
 141  
      * @see Constructor#newInstance
 142  
      */
 143  
     public static Object invokeConstructor(
 144  
         Class klass,
 145  
         Object[] args,
 146  
         Class[] parameterTypes)
 147  
         throws
 148  
             NoSuchMethodException,
 149  
             IllegalAccessException,
 150  
             InvocationTargetException,
 151  
             InstantiationException {
 152  
 
 153  5
         if (parameterTypes == null) {
 154  0
             parameterTypes = EMPTY_CLASS_PARAMETERS;
 155  
         }
 156  5
         if (args == null) {
 157  0
             args = EMPTY_OBJECT_ARRAY;
 158  
         }
 159  
 
 160  5
         Constructor ctor =
 161  
             getMatchingAccessibleConstructor(klass, parameterTypes);
 162  5
         if (null == ctor) {
 163  0
             throw new NoSuchMethodException(
 164  
                 "No such accessible constructor on object: " + klass.getName());
 165  
         }
 166  5
         return ctor.newInstance(args);
 167  
     }
 168  
 
 169  
 
 170  
     /**
 171  
      * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
 172  
      * The formal parameter type is inferred from the actual values of <code>arg</code>.
 173  
      * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
 174  
      *
 175  
      * <p>The signatures should match exactly.</p>
 176  
      *
 177  
      * @param klass the class to be constructed.
 178  
      * @param arg the actual argument
 179  
      * @return new instance of <code>klazz</code>
 180  
      *
 181  
      * @throws NoSuchMethodException If the constructor cannot be found
 182  
      * @throws IllegalAccessException If an error occurs accessing the constructor
 183  
      * @throws InvocationTargetException If an error occurs invoking the constructor
 184  
      * @throws InstantiationException If an error occurs instantiating the class
 185  
      *
 186  
      * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
 187  
      */
 188  
     public static Object invokeExactConstructor(Class klass, Object arg)
 189  
         throws
 190  
             NoSuchMethodException,
 191  
             IllegalAccessException,
 192  
             InvocationTargetException,
 193  
             InstantiationException {
 194  
 
 195  3
         Object[] args = { arg };
 196  3
         return invokeExactConstructor(klass, args);
 197  
 
 198  
     }
 199  
 
 200  
     /**
 201  
      * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
 202  
      * The formal parameter types are inferred from the actual values of <code>args</code>.
 203  
      * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
 204  
      *
 205  
      * <p>The signatures should match exactly.</p>
 206  
      *
 207  
      * @param klass the class to be constructed.
 208  
      * @param args actual argument array
 209  
      * @return new instance of <code>klazz</code>
 210  
      *
 211  
      * @throws NoSuchMethodException If the constructor cannot be found
 212  
      * @throws IllegalAccessException If an error occurs accessing the constructor
 213  
      * @throws InvocationTargetException If an error occurs invoking the constructor
 214  
      * @throws InstantiationException If an error occurs instantiating the class
 215  
      *
 216  
      * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
 217  
      */
 218  
     public static Object invokeExactConstructor(Class klass, Object[] args)
 219  
         throws
 220  
             NoSuchMethodException,
 221  
             IllegalAccessException,
 222  
             InvocationTargetException,
 223  
             InstantiationException {
 224  5
         if (null == args) {
 225  0
             args = EMPTY_OBJECT_ARRAY;
 226  
         }
 227  5
         int arguments = args.length;
 228  5
         Class parameterTypes[] = new Class[arguments];
 229  12
         for (int i = 0; i < arguments; i++) {
 230  7
             parameterTypes[i] = args[i].getClass();
 231  
         }
 232  5
         return invokeExactConstructor(klass, args, parameterTypes);
 233  
 
 234  
     }
 235  
 
 236  
     /**
 237  
      * <p>Returns new instance of <code>klazz</code> created using constructor
 238  
      * with signature <code>parameterTypes</code> and actual arguments
 239  
      * <code>args</code>.</p>
 240  
      *
 241  
      * <p>The signatures should match exactly.</p>
 242  
      *
 243  
      * @param klass the class to be constructed.
 244  
      * @param args actual argument array
 245  
      * @param parameterTypes parameter types array
 246  
      * @return new instance of <code>klazz</code>
 247  
      *
 248  
      * @throws NoSuchMethodException if matching constructor cannot be found
 249  
      * @throws IllegalAccessException thrown on the constructor's invocation
 250  
      * @throws InvocationTargetException thrown on the constructor's invocation
 251  
      * @throws InstantiationException thrown on the constructor's invocation
 252  
      * @see Constructor#newInstance
 253  
      */
 254  
     public static Object invokeExactConstructor(
 255  
         Class klass,
 256  
         Object[] args,
 257  
         Class[] parameterTypes)
 258  
         throws
 259  
             NoSuchMethodException,
 260  
             IllegalAccessException,
 261  
             InvocationTargetException,
 262  
             InstantiationException {
 263  
 
 264  9
         if (args == null) {
 265  0
             args = EMPTY_OBJECT_ARRAY;
 266  
         }
 267  
 
 268  9
         if (parameterTypes == null) {
 269  0
             parameterTypes = EMPTY_CLASS_PARAMETERS;
 270  
         }
 271  
 
 272  9
         Constructor ctor = getAccessibleConstructor(klass, parameterTypes);
 273  9
         if (null == ctor) {
 274  3
             throw new NoSuchMethodException(
 275  
                 "No such accessible constructor on object: " + klass.getName());
 276  
         }
 277  6
         return ctor.newInstance(args);
 278  
 
 279  
     }
 280  
 
 281  
     /**
 282  
      * Returns a constructor with single argument.
 283  
      * @param klass the class to be constructed
 284  
      * @param parameterType The constructor parameter type
 285  
      * @return null if matching accessible constructor can not be found.
 286  
      * @see Class#getConstructor
 287  
      * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
 288  
      */
 289  
     public static Constructor getAccessibleConstructor(
 290  
         Class klass,
 291  
         Class parameterType) {
 292  
 
 293  3
         Class[] parameterTypes = { parameterType };
 294  3
         return getAccessibleConstructor(klass, parameterTypes);
 295  
 
 296  
     }
 297  
 
 298  
     /**
 299  
      * Returns a constructor given a class and signature.
 300  
      * @param klass the class to be constructed
 301  
      * @param parameterTypes the parameter array
 302  
      * @return null if matching accessible constructor can not be found
 303  
      * @see Class#getConstructor
 304  
      * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
 305  
      */
 306  
     public static Constructor getAccessibleConstructor(
 307  
         Class klass,
 308  
         Class[] parameterTypes) {
 309  
 
 310  
         try {
 311  14
             return getAccessibleConstructor(
 312  
                 klass.getConstructor(parameterTypes));
 313  5
         } catch (NoSuchMethodException e) {
 314  5
             return (null);
 315  
         }
 316  
 
 317  
     }
 318  
 
 319  
     /**
 320  
      * Returns accessible version of the given constructor.
 321  
      * @param ctor prototype constructor object.
 322  
      * @return <code>null</code> if accessible constructor can not be found.
 323  
      * @see java.lang.SecurityManager
 324  
      */
 325  
     public static Constructor getAccessibleConstructor(Constructor ctor) {
 326  
 
 327  
         // Make sure we have a method to check
 328  14
         if (ctor == null) {
 329  0
             return (null);
 330  
         }
 331  
 
 332  
         // If the requested method is not public we cannot call it
 333  14
         if (!Modifier.isPublic(ctor.getModifiers())) {
 334  1
             return (null);
 335  
         }
 336  
 
 337  
         // If the declaring class is public, we are done
 338  13
         Class clazz = ctor.getDeclaringClass();
 339  13
         if (Modifier.isPublic(clazz.getModifiers())) {
 340  13
             return (ctor);
 341  
         }
 342  
 
 343  
         // what else can we do?
 344  0
         return null;
 345  
 
 346  
     }
 347  
 
 348  
     // -------------------------------------------------------- Private Methods
 349  
     /**
 350  
      * <p>Find an accessible constructor with compatible parameters.
 351  
      * Compatible parameters mean that every method parameter is assignable from
 352  
      * the given parameters. In other words, it finds constructor that will take
 353  
      * the parameters given.</p>
 354  
      *
 355  
      * <p>First it checks if there is constructor matching the exact signature.
 356  
      * If no such, all the constructors of the class are tested if their signatures
 357  
      * are assignment compatible with the parameter types.
 358  
      * The first matching constructor is returned.</p>
 359  
      *
 360  
      * @param clazz find constructor for this class
 361  
      * @param parameterTypes find method with compatible parameters
 362  
      * @return a valid Constructor object. If there's no matching constructor, returns <code>null</code>.
 363  
      */
 364  
     private static Constructor getMatchingAccessibleConstructor(
 365  
         Class clazz,
 366  
         Class[] parameterTypes) {
 367  
         // see if we can find the method directly
 368  
         // most of the time this works and it's much faster
 369  
         try {
 370  5
             Constructor ctor = clazz.getConstructor(parameterTypes);
 371  
             try {
 372  
                 //
 373  
                 // XXX Default access superclass workaround
 374  
                 //
 375  
                 // When a public class has a default access superclass
 376  
                 // with public methods, these methods are accessible.
 377  
                 // Calling them from compiled code works fine.
 378  
                 //
 379  
                 // Unfortunately, using reflection to invoke these methods
 380  
                 // seems to (wrongly) to prevent access even when the method
 381  
                 // modifer is public.
 382  
                 //
 383  
                 // The following workaround solves the problem but will only
 384  
                 // work from sufficiently privilages code. 
 385  
                 //
 386  
                 // Better workarounds would be greatfully accepted.
 387  
                 //
 388  3
                 ctor.setAccessible(true);
 389  0
             } catch (SecurityException se) {
 390  
                 /* SWALLOW, if workaround fails don't fret. */
 391  3
             }
 392  3
             return ctor;
 393  
 
 394  2
         } catch (NoSuchMethodException e) { /* SWALLOW */
 395  
         }
 396  
 
 397  
         // search through all methods 
 398  2
         int paramSize = parameterTypes.length;
 399  2
         Constructor[] ctors = clazz.getConstructors();
 400  20
         for (int i = 0, size = ctors.length; i < size; i++) {
 401  
             // compare parameters
 402  20
             Class[] ctorParams = ctors[i].getParameterTypes();
 403  20
             int ctorParamSize = ctorParams.length;
 404  20
             if (ctorParamSize == paramSize) {
 405  10
                 boolean match = true;
 406  13
                 for (int n = 0; n < ctorParamSize; n++) {
 407  11
                     if (!MethodUtils
 408  
                         .isAssignmentCompatible(
 409  
                             ctorParams[n],
 410  
                             parameterTypes[n])) {
 411  8
                         match = false;
 412  8
                         break;
 413  
                     }
 414  
                 }
 415  
 
 416  10
                 if (match) {
 417  
                     // get accessible version of method
 418  2
                     Constructor ctor = getAccessibleConstructor(ctors[i]);
 419  2
                     if (ctor != null) {
 420  
                         try {
 421  2
                             ctor.setAccessible(true);
 422  0
                         } catch (SecurityException se) {
 423  
                             /* Swallow SecurityException
 424  
                              * TODO: Why?
 425  
                              */
 426  2
                         }
 427  2
                         return ctor;
 428  
                     }
 429  
                 }
 430  
             }
 431  
         }
 432  
 
 433  0
         return null;
 434  
     }
 435  
 
 436  
 }