001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.functors;
018
019import java.io.Serializable;
020import java.lang.reflect.Constructor;
021import java.lang.reflect.InvocationTargetException;
022
023import org.apache.commons.collections4.Factory;
024import org.apache.commons.collections4.FunctorException;
025
026/**
027 * Factory implementation that creates a new object instance by reflection.
028 *
029 * @since 3.0
030 * @version $Id: InstantiateFactory.html 972421 2015-11-14 20:00:04Z tn $
031 */
032public class InstantiateFactory<T> implements Factory<T>, Serializable {
033
034    /** The serial version */
035    private static final long serialVersionUID = -7732226881069447957L;
036
037    /** The class to create */
038    private final Class<T> iClassToInstantiate;
039    /** The constructor parameter types */
040    private final Class<?>[] iParamTypes;
041    /** The constructor arguments */
042    private final Object[] iArgs;
043    /** The constructor */
044    private transient Constructor<T> iConstructor = null;
045
046    /**
047     * Factory method that performs validation.
048     *
049     * @param <T>  the type the factory creates
050     * @param classToInstantiate  the class to instantiate, not null
051     * @param paramTypes  the constructor parameter types, cloned
052     * @param args  the constructor arguments, cloned
053     * @return a new instantiate factory
054     */
055    public static <T> Factory<T> instantiateFactory(final Class<T> classToInstantiate,
056                                                    final Class<?>[] paramTypes,
057                                                    final Object[] args) {
058        if (classToInstantiate == null) {
059            throw new IllegalArgumentException("Class to instantiate must not be null");
060        }
061        if (paramTypes == null && args != null
062            || paramTypes != null && args == null
063            || paramTypes != null && args != null && paramTypes.length != args.length) {
064            throw new IllegalArgumentException("Parameter types must match the arguments");
065        }
066
067        if (paramTypes == null || paramTypes.length == 0) {
068            return new InstantiateFactory<T>(classToInstantiate);
069        }
070        return new InstantiateFactory<T>(classToInstantiate, paramTypes, args);
071    }
072
073    /**
074     * Constructor that performs no validation.
075     * Use <code>instantiateFactory</code> if you want that.
076     *
077     * @param classToInstantiate  the class to instantiate
078     */
079    public InstantiateFactory(final Class<T> classToInstantiate) {
080        super();
081        iClassToInstantiate = classToInstantiate;
082        iParamTypes = null;
083        iArgs = null;
084        findConstructor();
085    }
086
087    /**
088     * Constructor that performs no validation.
089     * Use <code>instantiateFactory</code> if you want that.
090     *
091     * @param classToInstantiate  the class to instantiate
092     * @param paramTypes  the constructor parameter types, cloned
093     * @param args  the constructor arguments, cloned
094     */
095    public InstantiateFactory(final Class<T> classToInstantiate, final Class<?>[] paramTypes, final Object[] args) {
096        super();
097        iClassToInstantiate = classToInstantiate;
098        iParamTypes = paramTypes.clone();
099        iArgs = args.clone();
100        findConstructor();
101    }
102
103    /**
104     * Find the Constructor for the class specified.
105     */
106    private void findConstructor() {
107        try {
108            iConstructor = iClassToInstantiate.getConstructor(iParamTypes);
109        } catch (final NoSuchMethodException ex) {
110            throw new IllegalArgumentException("InstantiateFactory: The constructor must exist and be public ");
111        }
112    }
113
114    /**
115     * Creates an object using the stored constructor.
116     *
117     * @return the new object
118     */
119    public T create() {
120        // needed for post-serialization
121        if (iConstructor == null) {
122            findConstructor();
123        }
124
125        try {
126            return iConstructor.newInstance(iArgs);
127        } catch (final InstantiationException ex) {
128            throw new FunctorException("InstantiateFactory: InstantiationException", ex);
129        } catch (final IllegalAccessException ex) {
130            throw new FunctorException("InstantiateFactory: Constructor must be public", ex);
131        } catch (final InvocationTargetException ex) {
132            throw new FunctorException("InstantiateFactory: Constructor threw an exception", ex);
133        }
134    }
135
136}