net.hasor.utils.reflect.ConstructorUtils Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.hasor.utils.reflect;
import net.hasor.utils.ArrayUtils;
import net.hasor.utils.ClassUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
/**
* Utility reflection methods focussed on constructors, modelled after
* {@link MethodUtils}.
*
* Known Limitations
Accessing Public Constructors In A Default
* Access Superclass
There is an issue when invoking public constructors
* contained in a default access superclass. Reflection locates these
* constructors fine and correctly assigns them as public. However, an
* IllegalAccessException
is thrown if the constructors is
* invoked.
*
* ConstructorUtils
contains a workaround for this situation. It
* will attempt to call setAccessible
on this constructor. If this
* call succeeds, then the method can be invoked as normal. This call will only
* succeed when the application has sufficient security privilages. If this call
* fails then a warning will be logged and the method may fail.
*
* @author Apache Software Foundation
* @author Craig R. McClanahan
* @author Ralph Schaer
* @author Chris Audley
* @author Rey Francois
* @author Gregor Rayman
* @author Jan Sorensen
* @author Robert Burrell Donkin
* @author Rodney Waldhoff
* @since 2.5
* @version $Id: ConstructorUtils.java 1056863 2011-01-09 02:00:25Z niallp $
*/
public class ConstructorUtils {
/**
* ConstructorUtils instances should NOT be constructed in standard
* programming. Instead, the class should be used as
* ConstructorUtils.invokeConstructor(cls, args)
.
*
* This constructor is public to permit tools that require a JavaBean
* instance to operate.
*/
public ConstructorUtils() {
super();
}
/**
* Returns new instance of klazz
created using the actual
* arguments args
. The formal parameter types are inferred from
* the actual values of args
. See
* {@link #invokeExactConstructor(Class, Object[], Class[])} for more
* details.
*
* The signatures should be assignment compatible.
*
* @param cls the class to be constructed.
* @param arg the actual argument
* @return new instance of klazz
*
* @throws NoSuchMethodException If the constructor cannot be found
* @throws IllegalAccessException If an error occurs accessing the constructor
* @throws InvocationTargetException If an error occurs invoking the constructor
* @throws InstantiationException If an error occurs instantiating the class
*
* @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
*/
public static Object invokeConstructor(final Class> cls, final Object arg) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
return ConstructorUtils.invokeConstructor(cls, new Object[] { arg });
}
/**
* Returns new instance of klazz
created using the actual
* arguments args
. The formal parameter types are inferred from
* the actual values of args
. See
* {@link #invokeExactConstructor(Class, Object[], Class[])} for more
* details.
* The signatures should be assignment compatible.
*
* @param cls the class to be constructed.
* @param args actual argument array
* @return new instance of klazz
*
* @throws NoSuchMethodException If the constructor cannot be found
* @throws IllegalAccessException If an error occurs accessing the
* constructor
* @throws InvocationTargetException If an error occurs invoking the
* constructor
* @throws InstantiationException If an error occurs instantiating the class
*
* @see #invokeConstructor(java.lang.Class, java.lang.Object[], * java.lang.Class[])
*/
public static Object invokeConstructor(final Class> cls, Object[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (null == args) {
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
}
Class> parameterTypes[] = new Class[args.length];
for (int i = 0; i < args.length; i++) {
parameterTypes[i] = args[i].getClass();
}
return ConstructorUtils.invokeConstructor(cls, args, parameterTypes);
}
/**
* Returns new instance of klazz
created using constructor
* with signature parameterTypes
and actual arguments
* args
.
*
* The signatures should be assignment compatible.
*
* @param cls the class to be constructed.
* @param args actual argument array
* @param parameterTypes parameter types array
* @return new instance of klazz
*
* @throws NoSuchMethodException if matching constructor cannot be found
* @throws IllegalAccessException thrown on the constructor's invocation
* @throws InvocationTargetException thrown on the constructor's invocation
* @throws InstantiationException thrown on the constructor's invocation
* @see Constructor#newInstance
*/
public static Object invokeConstructor(final Class> cls, Object[] args, Class>[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (parameterTypes == null) {
parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
}
if (args == null) {
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
}
Constructor> ctor = ConstructorUtils.getMatchingAccessibleConstructor(cls, parameterTypes);
if (null == ctor) {
throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName());
}
return ctor.newInstance(args);
}
/**
* Returns new instance of klazz
created using the actual
* arguments args
. The formal parameter types are inferred from
* the actual values of args
. See
* {@link #invokeExactConstructor(Class, Object[], Class[])} for more
* details.
*
* The signatures should match exactly.
*
* @param cls the class to be constructed.
* @param arg the actual argument
* @return new instance of klazz
*
* @throws NoSuchMethodException If the constructor cannot be found
* @throws IllegalAccessException If an error occurs accessing the constructor
* @throws InvocationTargetException If an error occurs invoking the constructor
* @throws InstantiationException If an error occurs instantiating the class
*
* @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
*/
public static Object invokeExactConstructor(final Class> cls, final Object arg) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
return ConstructorUtils.invokeExactConstructor(cls, new Object[] { arg });
}
/**
* Returns new instance of klazz
created using the actual
* arguments args
. The formal parameter types are inferred from
* the actual values of args
. See
* {@link #invokeExactConstructor(Class, Object[], Class[])} for more
* details.
*
* The signatures should match exactly.
*
* @param cls the class to be constructed.
* @param args actual argument array
* @return new instance of klazz
*
* @throws NoSuchMethodException If the constructor cannot be found
* @throws IllegalAccessException If an error occurs accessing the
* constructor
* @throws InvocationTargetException If an error occurs invoking the
* constructor
* @throws InstantiationException If an error occurs instantiating the class
*
* @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], * java.lang.Class[])
*/
public static Object invokeExactConstructor(final Class> cls, Object[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (null == args) {
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
}
int arguments = args.length;
Class> parameterTypes[] = new Class[arguments];
for (int i = 0; i < arguments; i++) {
parameterTypes[i] = args[i].getClass();
}
return ConstructorUtils.invokeExactConstructor(cls, args, parameterTypes);
}
/**
* Returns new instance of klazz
created using constructor
* with signature parameterTypes
and actual arguments
* args
.
*
* The signatures should match exactly.
*
* @param cls the class to be constructed.
* @param args actual argument array
* @param parameterTypes parameter types array
* @return new instance of klazz
*
* @throws NoSuchMethodException if matching constructor cannot be found
* @throws IllegalAccessException thrown on the constructor's invocation
* @throws InvocationTargetException thrown on the constructor's invocation
* @throws InstantiationException thrown on the constructor's invocation
* @see Constructor#newInstance
*/
public static Object invokeExactConstructor(final Class> cls, Object[] args, Class>[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (args == null) {
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
}
if (parameterTypes == null) {
parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY;
}
Constructor> ctor = ConstructorUtils.getAccessibleConstructor(cls, parameterTypes);
if (null == ctor) {
throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName());
}
return ctor.newInstance(args);
}
/**
* Returns a constructor with single argument.
* @param cls the class to be constructed
* @param parameterType The constructor parameter type
* @return null if matching accessible constructor can not be found.
* @see Class#getConstructor
* @see #getAccessibleConstructor(java.lang.reflect.Constructor)
*/
public static Constructor> getAccessibleConstructor(final Class> cls, final Class> parameterType) {
return ConstructorUtils.getAccessibleConstructor(cls, new Class[] { parameterType });
}
/**
* Returns a constructor given a class and signature.
* @param cls the class to be constructed
* @param parameterTypes the parameter array
* @return null if matching accessible constructor can not be found
* @see Class#getConstructor
* @see #getAccessibleConstructor(java.lang.reflect.Constructor)
*/
public static Constructor> getAccessibleConstructor(final Class> cls, final Class>[] parameterTypes) {
try {
return ConstructorUtils.getAccessibleConstructor(cls.getConstructor(parameterTypes));
} catch (NoSuchMethodException e) {
return null;
}
}
/**
* Returns accessible version of the given constructor.
* @param ctor prototype constructor object.
* @return null
if accessible constructor can not be found.
* @see java.lang.SecurityManager
*/
public static Constructor> getAccessibleConstructor(final Constructor> ctor) {
return MemberUtils.isAccessible(ctor) && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor : null;
}
/**
* Find an accessible constructor with compatible parameters. Compatible
* parameters mean that every method parameter is assignable from the given
* parameters. In other words, it finds constructor that will take the
* parameters given.
*
* First it checks if there is constructor matching the exact signature.
* If no such, all the constructors of the class are tested if their
* signatures are assignment compatible with the parameter types. The first
* matching constructor is returned.
*
* @param cls find constructor for this class
* @param parameterTypes find method with compatible parameters
* @return a valid Constructor object. If there's no matching constructor,
* returns null
.
*/
public static Constructor> getMatchingAccessibleConstructor(final Class> cls, final Class>[] parameterTypes) {
// see if we can find the constructor directly
// most of the time this works and it's much faster
try {
Constructor> ctor = cls.getConstructor(parameterTypes);
MemberUtils.setAccessibleWorkaround(ctor);
return ctor;
} catch (NoSuchMethodException e) { /* SWALLOW */
}
Constructor> result = null;
// search through all constructors
Constructor>[] ctors = cls.getConstructors();
// return best match:
for (Constructor> ctor2 : ctors) {
// compare parameters
if (ClassUtils.isAssignable(parameterTypes, ctor2.getParameterTypes(), true)) {
// get accessible version of constructor
Constructor> ctor = ConstructorUtils.getAccessibleConstructor(ctor2);
if (ctor != null) {
MemberUtils.setAccessibleWorkaround(ctor);
if (result == null || MemberUtils.compareParameterTypes(ctor.getParameterTypes(), result.getParameterTypes(), parameterTypes) < 0) {
result = ctor;
}
}
}
}
return result;
}
}