001/*
002 * Copyright 2022-2026 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 org.jspecify.annotations.NonNull;
020
021import java.lang.reflect.Parameter;
022
023import static java.util.Objects.requireNonNull;
024
025/**
026 * Contract for concrete instance generation given type information.
027 * <p>
028 * A standard threadsafe implementation can be acquired via the {@link #defaultInstance()} factory method.
029 * <p>
030 * See <a href="https://www.soklet.com/docs/instance-creation">https://www.soklet.com/docs/instance-creation</a> for detailed documentation.
031 *
032 * @author <a href="https://www.revetkn.com">Mark Allen</a>
033 */
034@FunctionalInterface
035public interface InstanceProvider {
036        /**
037         * Vends an instance of the given class.
038         * <p>
039         * The instance does not necessarily have to be new for every invocation (for example, implementors might return cached instances).
040         *
041         * @param instanceClass type token which represents the class to instantiate
042         * @param <T>           the type of class to instantiate
043         * @return an instance of {@code T}
044         */
045        @NonNull
046        <T> T provide(@NonNull Class<T> instanceClass);
047
048        /**
049         * Vends an instance appropriate for the supplied {@link Parameter}.
050         * <p>
051         * This is useful for solving the "robot legs" problem, where you might examine a qualifying annotation on a parameter to disambiguate vended instances.
052         * <p>
053         * For example, given this <em>Resource Method</em>:
054         * <pre>{@code @GET("/robot-generator")
055         * public Robot robotGenerator(
056         *   @Left LegFactory leftLegFactory, // Custom annotation
057         *   @Right LegFactory rightLegFactory, // Custom annotation
058         *   BodyFactory bodyFactory
059         * ) {
060         *   // Build a robot
061         *   LeftLeg leftLeg = leftLegFactory.buildLeg();
062         *   LeftLeg rightLeg = rightLegFactory.buildLeg();
063         *   Body body = bodyFactory.build();
064         *   return new Robot(leftLeg, rightLeg, body);
065         * }}</pre>
066         * <p>
067         * Your implementation might look like this:
068         * <pre>{@code @Override
069         * @SuppressWarnings("unchecked")
070         * public <T> T provide(@NonNull Parameter parameter) {
071         *   Class<T> type = (Class<T>) parameter.getType();
072         *
073         *   // Pick the appropriate LegFactory by inspecting annotations
074         *   if (type == LegFactory.class) {
075         *     if (parameter.isAnnotationPresent(Left.class))
076         *       return (T) LEFT_INSTANCE;
077         *     if (parameter.isAnnotationPresent(Right.class))
078         *       return (T) RIGHT_INSTANCE;
079         *
080         *     throw new IllegalArgumentException("LegFactory requires @Left or @Right");
081         *   }
082         *
083         *   // No qualifier logic needed - use the class-based path
084         *   return provide(type);
085         * }}</pre>
086         * <p>
087         * The default implementation delegates to {@link #provide(Class)} using the parameter's raw type.
088         *
089         * @param parameter the parameter instance to instantiate
090         * @param <T>       the type of class to instantiate (by default, the value of {@link Parameter#getType()})
091         * @return an instance of {@code T}
092         */
093        @NonNull
094        @SuppressWarnings("unchecked")
095        default <T> T provide(@NonNull Parameter parameter) {
096                requireNonNull(parameter);
097                return provide((Class<T>) parameter.getType());
098        }
099
100        /**
101         * Acquires a threadsafe {@link InstanceProvider} with a reflection-based {@code instanceClass.getDeclaredConstructor().newInstance()} instantiation strategy.
102         * <p>
103         * The returned instance is guaranteed to be a JVM-wide singleton.
104         *
105         * @return an {@code InstanceProvider} with a reflection-based instantiation strategy
106         */
107        @NonNull
108        static InstanceProvider defaultInstance() {
109                return DefaultInstanceProvider.defaultInstance();
110        }
111}