001/*
002 * Copyright 2022-2025 Revetware LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.soklet;
018
019import javax.annotation.Nonnull;
020import java.lang.reflect.Parameter;
021
022import static java.util.Objects.requireNonNull;
023
024/**
025 * Contract for concrete instance generation given type information.
026 * <p>
027 * A standard threadsafe implementation can be acquired via the {@link #defaultInstance()} factory method.
028 * <p>
029 * See <a href="https://www.soklet.com/docs/instance-creation">https://www.soklet.com/docs/instance-creation</a> for detailed documentation.
030 *
031 * @author <a href="https://www.revetkn.com">Mark Allen</a>
032 */
033@FunctionalInterface
034public interface InstanceProvider {
035        /**
036         * Vends an instance of the given class.
037         * <p>
038         * The instance does not necessarily have to be new for every invocation (for example, implementors might return cached instances).
039         *
040         * @param instanceClass type token which represents the class to instantiate
041         * @param <T>           the type of class to instantiate
042         * @return an instance of {@code T}
043         */
044        @Nonnull
045        <T> T provide(@Nonnull Class<T> instanceClass);
046
047        /**
048         * Vends an instance appropriate for the supplied {@link Parameter}.
049         * <p>
050         * This is useful for solving the "robot legs" problem, where you might examine a qualifying annotation on a parameter to disambiguate vended instances.
051         * <p>
052         * For example, given this <em>Resource Method</em>:
053         * <pre>{@code @GET("/robot-generator")
054         * public Robot robotGenerator(
055         *   @Left LegFactory leftLegFactory, // Custom annotation
056         *   @Right LegFactory rightLegFactory, // Custom annotation
057         *   BodyFactory bodyFactory
058         * ) {
059         *   // Build a robot
060         *   LeftLeg leftLeg = leftLegFactory.buildLeg();
061         *   LeftLeg rightLeg = rightLegFactory.buildLeg();
062         *   Body body = bodyFactory.build();
063         *   return new Robot(leftLeg, rightLeg, body);
064         * }}</pre>
065         * <p>
066         * Your implementation might look like this:
067         * <pre>{@code @Override
068         * @SuppressWarnings("unchecked")
069         * public <T> T provide(@Nonnull Parameter parameter) {
070         *   Class<T> type = (Class<T>) parameter.getType();
071         *
072         *   // Pick the appropriate LegFactory by inspecting annotations
073         *   if (type == LegFactory.class) {
074         *     if (parameter.isAnnotationPresent(Left.class))
075         *       return (T) LEFT_INSTANCE;
076         *     if (parameter.isAnnotationPresent(Right.class))
077         *       return (T) RIGHT_INSTANCE;
078         *
079         *     throw new IllegalArgumentException("LegFactory requires @Left or @Right");
080         *   }
081         *
082         *   // No qualifier logic needed - use the class-based path
083         *   return provide(type);
084         * }}</pre>
085         * <p>
086         * The default implementation delegates to {@link #provide(Class)} using the parameter's raw type.
087         *
088         * @param parameter the parameter instance to instantiate
089         * @param <T>       the type of class to instantiate (by default, the value of {@link Parameter#getType()})
090         * @return an instance of {@code T}
091         */
092        @Nonnull
093        @SuppressWarnings("unchecked")
094        default <T> T provide(@Nonnull Parameter parameter) {
095                requireNonNull(parameter);
096                return provide((Class<T>) parameter.getType());
097        }
098
099        /**
100         * Acquires a threadsafe {@link InstanceProvider} with a reflection-based {@code instanceClass.getDeclaredConstructor().newInstance()} instantiation strategy.
101         * <p>
102         * The returned instance is guaranteed to be a JVM-wide singleton.
103         *
104         * @return an {@code InstanceProvider} with a reflection-based instantiation strategy
105         */
106        @Nonnull
107        static InstanceProvider defaultInstance() {
108                return DefaultInstanceProvider.defaultInstance();
109        }
110}