diff --git a/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/DependencyHandler.java b/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/DependencyHandler.java index db060bff6..34e70a1c1 100644 --- a/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/DependencyHandler.java +++ b/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/DependencyHandler.java @@ -19,8 +19,7 @@ package com.seibel.lod.core.handlers.dependencyInjection; -import org.apache.logging.log4j.Logger; - +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -29,66 +28,80 @@ import java.util.Map; * * @param extends IBindable and defines what interfaces this dependency handler can deal with. * @author James Seibel - * @version 2022-7-15 + * @version 2022-7-16 */ public class DependencyHandler { - protected final Logger logger; - - protected final Map, Object> dependencies = new HashMap, Object>(); + protected final Map, ArrayList> dependencies = new HashMap<>(); /** Internal class reference to BindableType since we can't get it any other way. */ protected final Class bindableInterface; + protected final boolean allowDuplicateBindings; - public DependencyHandler(Class newBindableInterface, Logger newLogger) + public DependencyHandler(Class newBindableInterface) { this.bindableInterface = newBindableInterface; - this.logger = newLogger; + this.allowDuplicateBindings = false; + } + + public DependencyHandler(Class newBindableInterface, boolean newAllowDuplicateBindings) + { + this.bindableInterface = newBindableInterface; + this.allowDuplicateBindings = newAllowDuplicateBindings; } - + + /** * Links the given implementation object to an interface, so it can be referenced later. - * - * @param dependencyInterface The interface the implementation object should implement. + * + * @param dependencyInterface The interface (or parent class) the implementation object should implement. * @param dependencyImplementation An object that implements the dependencyInterface interface. - * @throws IllegalStateException if the implementation object doesn't implement + * @throws IllegalStateException if the implementation object doesn't implement * the interface or the interface has already been bound. */ - public void bind(Class dependencyInterface, Object dependencyImplementation) throws IllegalStateException + public void bind(Class dependencyInterface, BindableType dependencyImplementation) throws IllegalStateException { - // make sure we haven't already bound this dependency - if (dependencies.containsKey(dependencyInterface)) + // duplicate check if requested + if (dependencies.containsKey(dependencyInterface) && !this.allowDuplicateBindings) { throw new IllegalStateException("The dependency [" + dependencyInterface.getSimpleName() + "] has already been bound."); } // make sure the given dependency implements the necessary interfaces - boolean implementsInterface = checkIfClassImplements(dependencyImplementation.getClass(), dependencyInterface); + boolean implementsInterface = checkIfClassImplements(dependencyImplementation.getClass(), dependencyInterface) + || checkIfClassExtends(dependencyImplementation.getClass(), dependencyInterface); boolean implementsBindable = checkIfClassImplements(dependencyImplementation.getClass(), this.bindableInterface); // display any errors if (!implementsInterface) { - throw new IllegalStateException("The dependency [" + dependencyImplementation.getClass().getSimpleName() + "] doesn't implement the interface [" + dependencyInterface.getSimpleName() + "]."); + throw new IllegalStateException("The dependency [" + dependencyImplementation.getClass().getSimpleName() + "] doesn't implement or extend: [" + dependencyInterface.getSimpleName() + "]."); } if (!implementsBindable) { - throw new IllegalStateException("The dependency [" + dependencyImplementation.getClass().getSimpleName() + "] doesn't implement the interface [" + IBindable.class.getSimpleName() + "]."); + throw new IllegalStateException("The dependency [" + dependencyImplementation.getClass().getSimpleName() + "] doesn't implement the interface: [" + IBindable.class.getSimpleName() + "]."); } - dependencies.put(dependencyInterface, dependencyImplementation); + // make sure the hashSet has an array to hold the dependency + if (!dependencies.containsKey(dependencyInterface)) + { + dependencies.put(dependencyInterface, new ArrayList()); + } + + // add the dependency + dependencies.get(dependencyInterface).add(dependencyImplementation); } /** * Checks if classToTest (or one of its ancestors) * implements the given interface. */ - private boolean checkIfClassImplements(Class classToTest, Class interfaceToLookFor) + protected boolean checkIfClassImplements(Class classToTest, Class interfaceToLookFor) { // check the parent class (if applicable) if (classToTest.getSuperclass() != Object.class && classToTest.getSuperclass() != null) @@ -120,40 +133,100 @@ public class DependencyHandler return false; } + /** Checks if classToTest extends the given class. */ + protected boolean checkIfClassExtends(Class classToTest, Class extensionToLookFor) + { + return extensionToLookFor.isAssignableFrom(classToTest); + } + - /** - * Returns a dependency of type T if one has been bound. - * Returns null otherwise. - * - * @param class of the dependency - * (inferred from the objectClass parameter) - * @param interfaceClass Interface of the dependency - * @return the dependency of type T - * @throws ClassCastException If the dependency isn't able to be cast to type T. - * (this shouldn't normally happen, unless the bound object changed somehow) + * This does not return incomplete dependencies.
+ * See {@link #get(Class, boolean) get(Class, boolean)} for full documentation. + * + * @see #get(Class, boolean) */ @SuppressWarnings("unchecked") public T get(Class interfaceClass) throws ClassCastException { - T dependency = (T) dependencies.get(interfaceClass); - if (dependency != null && !dependency.getDelayedSetupComplete()) + return (T) getInternalLogic(interfaceClass, false).get(0); + } + + /** + * Returns all dependencies of type T that have been bound.
+ * Returns an empty list if no dependencies have been bound. + * + * @param class of the dependency + * (inferred from the objectClass parameter) + * @param interfaceClass Interface of the dependency + * @return the dependency of type T + * @throws ClassCastException If the dependency isn't able to be cast to type T. + * (this shouldn't normally happen, unless the bound object changed somehow) + */ + public ArrayList getAll(Class interfaceClass) throws ClassCastException + { + return getInternalLogic(interfaceClass, false); + } + + /** + * Returns a dependency of type T if one has been bound.
+ * Returns null if a dependency hasn't been bound.

+ * + * If the handler's {@link #allowDuplicateBindings} is true this returns the first bound dependency. + * + * @param class of the dependency + * (inferred from the objectClass parameter) + * @param interfaceClass Interface of the dependency + * @param allowIncompleteDependencies If true this method will also return dependencies that haven't completed their delayed setup. + * @return the dependency of type T + * @throws ClassCastException If the dependency isn't able to be cast to type T. + * (this shouldn't normally happen, unless the bound object changed somehow) + */ + @SuppressWarnings("unchecked") + public T get(Class interfaceClass, boolean allowIncompleteDependencies) throws ClassCastException + { + return (T) getInternalLogic(interfaceClass, allowIncompleteDependencies).get(0); + } + + /** + * Always returns a list of size 1 or greater, + * if no dependencies have been bound the list will contain null. + */ + @SuppressWarnings("unchecked") + private ArrayList getInternalLogic(Class interfaceClass, boolean allowIncompleteDependencies) throws ClassCastException + { + ArrayList dependencyList = dependencies.get(interfaceClass); + if (dependencyList != null && dependencyList.size() != 0) { - // a warning can be used here instead if desired - //this.logger.warn("Got dependency of type [" + interfaceClass.getSimpleName() + "], but the dependency's delayed setup hasn't been run!"); - throw new IllegalStateException("Got dependency of type [" + interfaceClass.getSimpleName() + "], but the dependency's delayed setup hasn't been run!"); + // check if each dependencies' delayed setup has been completed + for (IBindable dependency : dependencyList) + { + if (!dependency.getDelayedSetupComplete() && !allowIncompleteDependencies) + { + // a warning can be used here instead if desired + //this.logger.warn("Got dependency of type [" + interfaceClass.getSimpleName() + "], but the dependency's delayed setup hasn't been run!"); + throw new IllegalStateException("Got dependency of type [" + interfaceClass.getSimpleName() + "], but the dependency's delayed setup hasn't been run!"); + } + } + + return (ArrayList) dependencyList; } - return dependency; + + // return an empty list to prevent null pointers + ArrayList emptyList = new ArrayList(); + emptyList.add(null); + return emptyList; } + /** Runs delayed setup for any dependencies that require it. */ public void runDelayedSetup() { for (Class interfaceKey : dependencies.keySet()) { - IBindable concreteObject = get(interfaceKey); + IBindable concreteObject = get(interfaceKey, true); if (!concreteObject.getDelayedSetupComplete()) { concreteObject.finishDelayedSetup(); diff --git a/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/IBindable.java b/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/IBindable.java index f40ac0366..9ce077eff 100644 --- a/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/IBindable.java +++ b/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/IBindable.java @@ -23,7 +23,7 @@ package com.seibel.lod.core.handlers.dependencyInjection; * Necessary for all singletons that can be dependency injected. * * @author James Seibel - * @version 3-4-2022 + * @version 2022-7-16 */ public interface IBindable { diff --git a/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/ModAccessorHandler.java b/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/ModAccessorHandler.java index d4ce88eb1..ccac68eff 100644 --- a/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/ModAccessorHandler.java +++ b/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/ModAccessorHandler.java @@ -34,30 +34,30 @@ import java.lang.invoke.MethodHandles; * * @author James Seibel * @author Leetom - * @version 2022-7-15 + * @version 2022-7-16 */ public class ModAccessorHandler extends DependencyHandler { private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); - public static final ModAccessorHandler INSTANCE = new ModAccessorHandler(IModAccessor.class, LOGGER); + public static final ModAccessorHandler INSTANCE = new ModAccessorHandler(IModAccessor.class); - public ModAccessorHandler(Class newBindableInterface, Logger newLogger) + public ModAccessorHandler(Class newBindableInterface) { - super(newBindableInterface, LOGGER); + super(newBindableInterface, false); } /** - * Go to {@link DependencyHandler#bind(Class, Object) DependencyHandler.bind()} + * Go to {@link DependencyHandler#bind(Class, IBindable)} DependencyHandler.bind()} * for this method's javadocs. */ public void bind(Class interfaceClass, IModAccessor modAccessor) throws IllegalStateException { super.bind(interfaceClass, modAccessor); - LOGGER.info("Registered mod compatibility accessor for " + modAccessor.getModName()); + LOGGER.info("Registered mod compatibility accessor for: [" + modAccessor.getModName() + "]."); } } diff --git a/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/SingletonHandler.java b/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/SingletonHandler.java index 3dd9e6620..2652e35ad 100644 --- a/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/SingletonHandler.java +++ b/src/main/java/com/seibel/lod/core/handlers/dependencyInjection/SingletonHandler.java @@ -19,28 +19,18 @@ package com.seibel.lod.core.handlers.dependencyInjection; -import com.seibel.lod.core.logging.DhLoggerBuilder; -import com.seibel.lod.core.wrapperInterfaces.modAccessor.IModAccessor; -import org.apache.logging.log4j.Logger; - -import java.lang.invoke.MethodHandles; - /** * This class takes care of dependency injection * for singletons. * * @author James Seibel - * @version 2022-7-15 + * @version 2022-7-16 */ public class SingletonHandler extends DependencyHandler { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); - - public static final SingletonHandler INSTANCE = new SingletonHandler(IBindable.class, LOGGER); + public static final SingletonHandler INSTANCE = new SingletonHandler(IBindable.class); - public SingletonHandler(Class newBindableInterface, Logger newLogger) - { - super(newBindableInterface, newLogger); - } + public SingletonHandler(Class newBindableInterface) { super(newBindableInterface, false); } + }