Improve DependencyHandler for DhApiEventHandler support
Specifically dependencyHandler can now optionally store multiple implementations for each dependency interface.
This commit is contained in:
+110
-37
@@ -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 <BindableType> 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<BindableType extends IBindable>
|
||||
{
|
||||
protected final Logger logger;
|
||||
|
||||
protected final Map<Class<? extends BindableType>, Object> dependencies = new HashMap<Class<? extends BindableType>, Object>();
|
||||
protected final Map<Class<? extends BindableType>, ArrayList<BindableType>> dependencies = new HashMap<>();
|
||||
|
||||
/** Internal class reference to BindableType since we can't get it any other way. */
|
||||
protected final Class<? extends BindableType> bindableInterface;
|
||||
|
||||
protected final boolean allowDuplicateBindings;
|
||||
|
||||
|
||||
public DependencyHandler(Class<BindableType> newBindableInterface, Logger newLogger)
|
||||
public DependencyHandler(Class<BindableType> newBindableInterface)
|
||||
{
|
||||
this.bindableInterface = newBindableInterface;
|
||||
this.logger = newLogger;
|
||||
this.allowDuplicateBindings = false;
|
||||
}
|
||||
|
||||
public DependencyHandler(Class<BindableType> 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<? extends BindableType> dependencyInterface, Object dependencyImplementation) throws IllegalStateException
|
||||
public void bind(Class<? extends BindableType> 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<BindableType>());
|
||||
}
|
||||
|
||||
// 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<BindableType extends IBindable>
|
||||
|
||||
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 <T> 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. <Br>
|
||||
* See {@link #get(Class, boolean) get(Class, boolean)} for full documentation.
|
||||
*
|
||||
* @see #get(Class, boolean)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends BindableType> 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. <br>
|
||||
* Returns an empty list if no dependencies have been bound.
|
||||
*
|
||||
* @param <T> 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 <T extends BindableType> ArrayList<T> getAll(Class<?> interfaceClass) throws ClassCastException
|
||||
{
|
||||
return getInternalLogic(interfaceClass, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a dependency of type T if one has been bound. <br>
|
||||
* Returns null if a dependency hasn't been bound. <br> <br>
|
||||
*
|
||||
* If the handler's {@link #allowDuplicateBindings} is true this returns the first bound dependency.
|
||||
*
|
||||
* @param <T> 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 extends BindableType> 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 <T extends BindableType> ArrayList<T> getInternalLogic(Class<?> interfaceClass, boolean allowIncompleteDependencies) throws ClassCastException
|
||||
{
|
||||
ArrayList<BindableType> 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<T>) dependencyList;
|
||||
}
|
||||
|
||||
return dependency;
|
||||
|
||||
// return an empty list to prevent null pointers
|
||||
ArrayList<T> emptyList = new ArrayList<T>();
|
||||
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();
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
+6
-6
@@ -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<IModAccessor>
|
||||
{
|
||||
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<IModAccessor> newBindableInterface, Logger newLogger)
|
||||
public ModAccessorHandler(Class<IModAccessor> 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<? extends IModAccessor> 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() + "].");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-14
@@ -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<IBindable>
|
||||
{
|
||||
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<IBindable> newBindableInterface, Logger newLogger)
|
||||
{
|
||||
super(newBindableInterface, newLogger);
|
||||
}
|
||||
public SingletonHandler(Class<IBindable> newBindableInterface) { super(newBindableInterface, false); }
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user