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    
015    package org.apache.hivemind.util;
016    
017    import java.lang.reflect.Constructor;
018    import java.lang.reflect.InvocationTargetException;
019    import java.lang.reflect.Modifier;
020    import java.util.ArrayList;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Map;
024    
025    import org.apache.hivemind.ApplicationRuntimeException;
026    
027    /**
028     * Static methods for invoking constructors.
029     * 
030     * @author Howard Lewis Ship
031     */
032    public 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            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    }