147 lines
4.2 KiB
Java
147 lines
4.2 KiB
Java
|
package appeng.core;
|
||
|
|
||
|
|
||
|
import java.lang.reflect.Constructor;
|
||
|
import java.util.Collection;
|
||
|
import java.util.HashSet;
|
||
|
import java.util.Map;
|
||
|
import java.util.Set;
|
||
|
import java.util.stream.Collectors;
|
||
|
|
||
|
import com.google.common.collect.ImmutableMap;
|
||
|
|
||
|
import net.minecraftforge.fml.common.discovery.ASMDataTable;
|
||
|
|
||
|
import appeng.api.AEInjectable;
|
||
|
import appeng.api.AEPlugin;
|
||
|
import appeng.api.exceptions.AppEngException;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Loads AE plugins on startup and provides them with access to various components of the AE API.
|
||
|
*/
|
||
|
class PluginLoader
|
||
|
{
|
||
|
|
||
|
public void loadPlugins( Collection<Object> injectables, ASMDataTable asmDataTable )
|
||
|
{
|
||
|
Map<Class<?>, Object> injectableMap = mapInjectables( injectables );
|
||
|
findAndInstantiatePlugins( asmDataTable, injectableMap );
|
||
|
}
|
||
|
|
||
|
private static void findAndInstantiatePlugins( ASMDataTable dataTable, Map<Class<?>, Object> injectableMap )
|
||
|
{
|
||
|
Set<ASMDataTable.ASMData> allAnnotated = dataTable.getAll( AEPlugin.class.getCanonicalName() );
|
||
|
|
||
|
for( ASMDataTable.ASMData candidate : allAnnotated )
|
||
|
{
|
||
|
|
||
|
Class<?> aClass;
|
||
|
try
|
||
|
{
|
||
|
aClass = Class.forName( candidate.getClassName() );
|
||
|
}
|
||
|
catch( ClassNotFoundException e )
|
||
|
{
|
||
|
AELog.error( e, "Couldn't find annotated AE plugin class " + candidate.getClassName() );
|
||
|
throw new RuntimeException( "Couldn't find annotated AE plugin class " + candidate.getClassName(), e );
|
||
|
}
|
||
|
|
||
|
// Try instantiating the plugin
|
||
|
try
|
||
|
{
|
||
|
Object plugin = instantiatePlugin( aClass, injectableMap );
|
||
|
AELog.info( "Loaded AE2 Plugin {}", plugin.getClass() );
|
||
|
}
|
||
|
catch( Exception e )
|
||
|
{
|
||
|
AELog.error( e, "Unable to instantiate AE plugin " + candidate.getClassName() );
|
||
|
throw new RuntimeException( "Unable to instantiate AE plugin " + candidate.getClassName(), e );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static Object instantiatePlugin( Class<?> aClass, Map<Class<?>, Object> injectableMap ) throws Exception
|
||
|
{
|
||
|
|
||
|
Constructor<?>[] constructors = aClass.getDeclaredConstructors();
|
||
|
|
||
|
if( constructors.length == 0 )
|
||
|
{
|
||
|
// This is the default no-arg constructor, although it seems pointless to instantiate anything but not take any AE dependencies as parameters
|
||
|
return aClass.newInstance();
|
||
|
}
|
||
|
else if( constructors.length != 1 )
|
||
|
{
|
||
|
throw new IllegalArgumentException( "Expected a single constructor, but found: " + constructors.length );
|
||
|
}
|
||
|
|
||
|
Constructor<?> constructor = constructors[0];
|
||
|
constructor.setAccessible( true );
|
||
|
|
||
|
Object[] args = findInjectables( constructor, injectableMap );
|
||
|
|
||
|
return constructor.newInstance( args );
|
||
|
}
|
||
|
|
||
|
private static Object[] findInjectables( Constructor<?> constructor, Map<Class<?>, Object> injectableMap )
|
||
|
{
|
||
|
|
||
|
Class<?>[] types = constructor.getParameterTypes();
|
||
|
Object[] args = new Object[types.length];
|
||
|
|
||
|
for( int i = 0; i < types.length; i++ )
|
||
|
{
|
||
|
args[i] = injectableMap.get( types[i] );
|
||
|
if( args[i] == null )
|
||
|
{
|
||
|
throw new IllegalArgumentException( "Constructor has parameter of type " + types[i] + " which is not an injectable type."
|
||
|
+ " Please see the documentation for @AEPlugin." );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return args;
|
||
|
}
|
||
|
|
||
|
private static Map<Class<?>, Object> mapInjectables( Collection<Object> injectables )
|
||
|
{
|
||
|
ImmutableMap.Builder<Class<?>, Object> builder = ImmutableMap.builder();
|
||
|
|
||
|
for( Object injectable : injectables )
|
||
|
{
|
||
|
// Get all super-interfaces that were annotated with @AEInjectable
|
||
|
Set<Class<?>> injectableIfs = getInjectableInterfaces( injectable.getClass() );
|
||
|
for( Class<?> injectableIf : injectableIfs )
|
||
|
{
|
||
|
builder.put( injectableIf, injectable );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return builder.build();
|
||
|
}
|
||
|
|
||
|
private static Set<Class<?>> getInjectableInterfaces( Class<?> aClass )
|
||
|
{
|
||
|
Set<Class<?>> hierarchy = new HashSet<>();
|
||
|
getFullHierarchy( aClass, hierarchy );
|
||
|
|
||
|
return hierarchy.stream()
|
||
|
.filter( c -> c.getAnnotation( AEInjectable.class ) != null )
|
||
|
.collect( Collectors.toSet() );
|
||
|
}
|
||
|
|
||
|
// Recursively gather all superclasses and superinterfaces of the given class and put them into the given collection
|
||
|
private static void getFullHierarchy( Class<?> aClass, Set<Class<?>> classes )
|
||
|
{
|
||
|
classes.add( aClass );
|
||
|
for( Class<?> anIf : aClass.getInterfaces() )
|
||
|
{
|
||
|
getFullHierarchy( anIf, classes );
|
||
|
}
|
||
|
if( aClass.getSuperclass() != null )
|
||
|
{
|
||
|
getFullHierarchy( aClass.getSuperclass(), classes );
|
||
|
}
|
||
|
}
|
||
|
}
|