SingleInstance.java

package it.fulminazzo.yagl;

import org.jetbrains.annotations.NotNull;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * A special abstract type that allows to define objects only one time.
 * Upon initiating it for the first time, no more instances will be allowed (check {@link #initialize()} for more)
 * unless the {@link #terminate()} method is invoked.
 * <br>
 * It is possible to retrieve any instance using {@link #getInstance(Class)}.
 */
public abstract class SingleInstance {
    private static final Map<Class<? extends SingleInstance>, SingleInstance> INSTANCES_MAP = new LinkedHashMap<>();

    /**
     * Checks if the current instance is already present in {@link #INSTANCES_MAP}.
     * If it is, a {@link InstanceAlreadyInitializedException} is thrown with the instance itself as parameter.
     */
    public void initialize() {
        if (INSTANCES_MAP.containsKey(getClass()))
            throw new InstanceAlreadyInitializedException(this);
        else INSTANCES_MAP.put(getClass(), this);
    }

    /**
     * Removes the current instance from {@link #INSTANCES_MAP}.
     * If it is already removed, a {@link InstanceNotInitializedException} is thrown.
     */
    public void terminate() {
        if (INSTANCES_MAP.containsKey(getClass())) INSTANCES_MAP.remove(getClass());
        else throw new InstanceNotInitializedException(getClass());
    }

    /**
     * Gets the instance corresponding to the type from {@link #INSTANCES_MAP}.
     * If not present, a {@link InstanceNotInitializedException} is thrown.
     *
     * @param <T>   the type of the instance
     * @param clazz the class of the instance
     * @return the instance
     */
    @SuppressWarnings("unchecked")
    public static <T extends SingleInstance> @NotNull T getInstance(Class<T> clazz) {
        T instance = (T) INSTANCES_MAP.get(clazz);
        if (instance == null) throw new InstanceNotInitializedException(clazz);
        return instance;
    }

}