001// Copyright 2004, 2005 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.hivemind.util;
016
017import java.lang.reflect.Constructor;
018import java.lang.reflect.InvocationTargetException;
019import java.lang.reflect.Modifier;
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024
025import org.apache.hivemind.ApplicationRuntimeException;
026
027/**
028 * Static methods for invoking constructors.
029 * 
030 * @author Howard Lewis Ship
031 */
032public class ConstructorUtils
033{
034
035    /**
036     * Map from primitive type to wrapper type.
037     */
038    private static final Map _primitiveMap = new HashMap();
039
040    static
041    {
042        _primitiveMap.put(boolean.class, Boolean.class);
043        _primitiveMap.put(byte.class, Byte.class);
044        _primitiveMap.put(char.class, Character.class);
045        _primitiveMap.put(short.class, Short.class);
046        _primitiveMap.put(int.class, Integer.class);
047        _primitiveMap.put(long.class, Long.class);
048        _primitiveMap.put(float.class, Float.class);
049        _primitiveMap.put(double.class, Double.class);
050    }
051
052    // Prevent instantiation
053
054    private ConstructorUtils()
055    {
056    }
057
058    /**
059     * Searches for a constructor matching against the provided arguments.
060     * 
061     * @param targetClass
062     *            the class to be instantiated
063     * @param parameters
064     *            the parameters to pass to the constructor (may be null or empty)
065     * @return the new instance
066     * @throws ApplicationRuntimeException
067     *             on any failure
068     */
069    public static Object invokeConstructor(Class targetClass, Object[] parameters)
070    {
071        if (parameters == null)
072            parameters = new Object[0];
073
074        Class[] parameterTypes = new Class[parameters.length];
075
076        for (int i = 0; i < parameters.length; i++)
077            parameterTypes[i] = parameters[i] == null ? null : parameters[i].getClass();
078
079        return invokeMatchingConstructor(targetClass, parameterTypes, parameters);
080    }
081
082    private static Object invokeMatchingConstructor(Class targetClass, Class[] parameterTypes,
083            Object[] parameters)
084    {
085        Constructor[] constructors = targetClass.getConstructors();
086
087        for (int i = 0; i < constructors.length; i++)
088        {
089            Constructor c = constructors[i];
090
091            if (isMatch(c, parameterTypes))
092                return invoke(c, parameters);
093        }
094
095        throw new ApplicationRuntimeException(UtilMessages.noMatchingConstructor(targetClass), null);
096    }
097
098    private static boolean isMatch(Constructor c, Class[] types)
099    {
100        Class[] actualTypes = c.getParameterTypes();
101
102        if (actualTypes.length != types.length)
103            return false;
104
105        for (int i = 0; i < types.length; i++)
106        {
107            if (types[i] == null && !actualTypes[i].isPrimitive())
108                continue;
109
110            if (!isCompatible(actualTypes[i], types[i]))
111                return false;
112        }
113
114        return true;
115    }
116
117    public static boolean isCompatible(Class actualType, Class parameterType)
118    {
119        if (actualType.isAssignableFrom(parameterType))
120            return true;
121
122        // Reflection fudges the assignment of a wrapper class to a primitive
123        // type ... we check for that the hard way.
124
125        if (actualType.isPrimitive())
126        {
127            Class wrapperClass = (Class) _primitiveMap.get(actualType);
128
129            return wrapperClass.isAssignableFrom(parameterType);
130        }
131
132        return false;
133    }
134
135    public static Object invoke(Constructor c, Object[] parameters)
136    {
137        try
138        {
139            return c.newInstance(parameters);
140        }
141        catch (InvocationTargetException ex)
142        {
143            Throwable cause = ex.getTargetException();
144
145            throw new ApplicationRuntimeException(UtilMessages.invokeFailed(c, cause), null, cause);
146        }
147        catch (Exception ex)
148        {
149            throw new ApplicationRuntimeException(UtilMessages.invokeFailed(c, ex), null, ex);
150        }
151    }
152
153    public static List getConstructorsOfLength(final Class clazz, final int length)
154    {
155        List fixedLengthConstructors = new ArrayList(1);
156    
157        Constructor[] constructors = clazz.getDeclaredConstructors();
158    
159        outer: for (int i = 0; i < constructors.length; i++)
160        {
161            if (!Modifier.isPublic(constructors[i].getModifiers()))
162                continue;
163    
164            Class[] parameterTypes = constructors[i].getParameterTypes();
165    
166            if (parameterTypes.length == length)
167                fixedLengthConstructors.add(constructors[i]);
168        }
169    
170        return fixedLengthConstructors;
171    }
172}