001package co.codewizards.cloudstore.core.objectfactory;
002
003import java.util.ServiceLoader;
004
005/**
006 * Simple API to use the {@link ObjectFactory}.
007 * <p>
008 * Devs should add the following import:
009 * <pre>
010 * import static co.codewizards.cloudstore.core.objectfactory.ObjectFactoryUtil.*;
011 * </pre>
012 * They can then invoke one of the {@code createObject(...)} methods simply without any prefix.
013 * <p>
014 * In order to register a sub-class as replacement for a certain base-class, implementors have to provide a
015 * {@link ClassExtension} and register it using the {@link ServiceLoader}-mechanism.
016 * <p>
017 * <b>Important:</b> If a certain base-class should be replaceable using this mechanism, <b>all</b> occurrences
018 * of {@code new MyBaseClass(...)} in the entire code-base must be replaced by {@code createObject(MyBaseClass.class, ...)}.
019 * <p>
020 * <b>Important 2:</b> It is urgently recommended <i>not</i> to use this approach, whenever it is possible to use a better solution,
021 * preferably a well-defined service (=&gt; {@link ServiceLoader}). There are situations, though, e.g. data-model-classes
022 * (a.k.a. entities), where services are not possible and {@code ObjectFactoryUtil} + {@code ClassExtension} is the perfect solution.
023 * <p>
024 *
025 * @author Marco หงุ่ยตระกูล-Schulze - marco at codewizards dot co
026 */
027public class ObjectFactoryUtil {
028        protected ObjectFactoryUtil() { }
029
030        /**
031         * Create an instance of the given class or a sub-class registered as replacement.
032         * <p>
033         * If at least one {@link ClassExtension} is registered for the given class, the {@code ClassExtension}
034         * with the highest {@link ClassExtension#getPriority() priority} is chosen and its
035         * {@link ClassExtension#getExtendingClass() extendingClass} instantiated instead of the base-class passed as {@code clazz}.
036         * <p>
037         * This method always invokes the default constructor (without any parameters).
038         * @param clazz the base-class to be instantiated. Must not be <code>null</code>.
039         * @return the object instantiated either from the given base-class or a sub-class registered via {@link ClassExtension}.
040         * Never <code>null</code>.
041         * @see #createObject(Class, Class[], Object...)
042         */
043        public static <T> T createObject(final Class<T> clazz) {
044                return ObjectFactory.getInstance().createObject(clazz);
045        }
046
047        /**
048         * Create an instance of the given class or a sub-class registered as replacement.
049         * <p>
050         * If at least one {@link ClassExtension} is registered for the given class, the {@code ClassExtension}
051         * with the highest {@link ClassExtension#getPriority() priority} is chosen and its
052         * {@link ClassExtension#getExtendingClass() extendingClass} instantiated instead of the base-class passed as {@code clazz}.
053         * <p>
054         * This method tries to automatically find the best constructor matching the given parameters.
055         * If multiple constructors might match and they are not semantically the same (only doing some implicit conversions),
056         * you should instead use {@link #createObject(Class, Class[], Object...)} and pass the specific parameter types.
057         * @param clazz the base-class to be instantiated. Must not be <code>null</code>.
058         * @param parameters the parameters to be passed to the constructor.
059         * @return the object instantiated either from the given base-class or a sub-class registered via {@link ClassExtension}.
060         * Never <code>null</code>.
061         * @see #createObject(Class, Class[], Object...)
062         */
063        public static <T> T createObject(final Class<T> clazz, final Object ... parameters) {
064                return ObjectFactory.getInstance().createObject(clazz, (Class<?>[]) null, parameters);
065        }
066
067        /**
068         * Create an instance of the given class or a sub-class registered as replacement.
069         * <p>
070         * If at least one {@link ClassExtension} is registered for the given class, the {@code ClassExtension}
071         * with the highest {@link ClassExtension#getPriority() priority} is chosen and its
072         * {@link ClassExtension#getExtendingClass() extendingClass} instantiated instead of the base-class passed as {@code clazz}.
073         * <p>
074         * This method invokes the constructor matching exactly the given parameter-types. If {@code parameterTypes} is
075         * <code>null</code>, a matching constructor is searched automatically based on the concrete {@code parameters}.
076         *
077         * @param clazz the base-class to be instantiated. Must not be <code>null</code>.
078         * @param parameterTypes either <code>null</code> or the exact argument-types of the constructor to be invoked.
079         * @param parameters the parameters to be passed to the constructor.
080         * @return the object instantiated either from the given base-class or a sub-class registered via {@link ClassExtension}.
081         * Never <code>null</code>.
082         */
083        public static <T> T createObject(final Class<T> clazz, final Class<?>[] parameterTypes, final Object ... parameters) {
084                return ObjectFactory.getInstance().createObject(clazz, parameterTypes, parameters);
085        }
086
087        public static <T> Class<? extends T> getExtendingClass(final Class<T> clazz) {
088                return ObjectFactory.getInstance().getExtendingClass(clazz);
089        }
090}