2013-04-20 23:31:22 -04:00
package cpw.mods.fml.common ;
import java.lang.reflect.Field ;
2013-05-08 05:36:28 +02:00
import java.lang.reflect.InvocationTargetException ;
import java.lang.reflect.Method ;
2013-05-08 19:04:35 +02:00
import java.util.logging.Level ;
import cpw.mods.fml.relauncher.Side ;
2013-04-20 23:31:22 -04:00
public interface ILanguageAdapter {
2013-06-26 14:52:56 -04:00
public Object getNewInstance ( FMLModContainer container , Class <?> objectClass , ClassLoader classLoader , Method factoryMarkedAnnotation ) throws Exception ;
2013-04-20 23:31:22 -04:00
public boolean supportsStatics ();
public void setProxy ( Field target , Class <?> proxyTarget , Object proxy ) throws IllegalArgumentException , IllegalAccessException , NoSuchFieldException , SecurityException ;
2013-05-08 19:04:35 +02:00
public void setInternalProxies ( ModContainer mod , Side side , ClassLoader loader );
2013-04-20 23:31:22 -04:00
public static class ScalaAdapter implements ILanguageAdapter {
@Override
2013-06-26 14:52:56 -04:00
public Object getNewInstance ( FMLModContainer container , Class <?> scalaObjectClass , ClassLoader classLoader , Method factoryMarkedAnnotation ) throws Exception
2013-04-20 23:31:22 -04:00
{
Class <?> sObjectClass = Class . forName ( scalaObjectClass . getName () + "$" , true , classLoader );
return sObjectClass . getField ( "MODULE$" ). get ( null );
}
@Override
public boolean supportsStatics ()
{
return false ;
}
@Override
public void setProxy ( Field target , Class <?> proxyTarget , Object proxy ) throws IllegalArgumentException , IllegalAccessException , NoSuchFieldException , SecurityException
{
2013-05-08 05:36:28 +02:00
try
{
2013-05-08 19:04:35 +02:00
// Get the actual singleton class. The two variants are from
// whether the @SidedProxy is declared in a the class block
// of the object directly, or in the object block, i.e.
// whether it's:
// class ModName {
// @SidedProxy ...
// }
// object ModName extends ModName {}
// which leads to us getting the outer class, or
// object ModName {
// @SidedProxy ...
// }
// which leads to us getting the inner class.
if ( ! proxyTarget . getName (). endsWith ( "$" ))
{
// Get internal class generated by Scala.
proxyTarget = Class . forName ( proxyTarget . getName () + "$" , true , proxyTarget . getClassLoader ());
}
}
catch ( ClassNotFoundException e )
{
// Not a singleton, look for @Instance field as a fallback.
2013-06-04 12:51:46 -04:00
FMLLog . log ( Level . INFO , e , "An error occured trying to load a proxy into %s.%s. Did you declare your mod as 'class' instead of 'object'?" , proxyTarget . getSimpleName (), target . getName ());
return ;
2013-05-08 19:04:35 +02:00
}
2013-05-08 05:36:28 +02:00
2013-05-08 20:16:15 +02:00
// Get the instance via the MODULE$ field which is
// automatically generated by the Scala compiler for
// singletons.
Object targetInstance = proxyTarget . getField ( "MODULE$" ). get ( null );
2013-05-08 19:04:35 +02:00
try
{
2013-05-08 05:36:28 +02:00
// Find setter function. We do it this way because we can't
// necessarily use proxyTarget.getMethod(proxy.getClass()), as
// it might be a subclass and not the exact parameter type.
2013-05-08 19:04:35 +02:00
// All fields are private in Scala, wrapped by a getter and
2013-05-08 05:36:28 +02:00
// setter named <fieldname> and <fieldname>_$eq. To those
// familiar with scala.reflect.BeanProperty: these will always
// be there, set<Fieldname> and get<Fieldname> will always
// only be generated *additionally*.
final String setterName = target . getName () + "_$eq" ;
for ( Method setter : proxyTarget . getMethods ())
{
Class <?>[] setterParameters = setter . getParameterTypes ();
if ( setterName . equals ( setter . getName ()) &&
// Some more validation.
setterParameters . length == 1 &&
setterParameters [ 0 ] . isAssignableFrom ( proxy . getClass ()))
{
2013-05-08 19:04:35 +02:00
// Here goes nothing...
2013-05-08 05:36:28 +02:00
setter . invoke ( targetInstance , proxy );
return ;
}
}
}
catch ( InvocationTargetException e )
{
2013-05-08 19:04:35 +02:00
FMLLog . log ( Level . SEVERE , e , "An error occured trying to load a proxy into %s.%s" , proxyTarget . getSimpleName (), target . getName ());
throw new LoaderException ( e );
2013-05-08 05:36:28 +02:00
}
2013-05-08 19:04:35 +02:00
// If we come here we could not find a setter for this proxy.
FMLLog . severe ( "Failed loading proxy into %s.%s, could not find setter function. Did you declare the field with 'val' instead of 'var'?" , proxyTarget . getSimpleName (), target . getName ());
throw new LoaderException ();
}
@Override
public void setInternalProxies ( ModContainer mod , Side side , ClassLoader loader )
{
// For Scala mods, we want to enable authors to write them like so:
// object ModName {
// @SidedProxy(...)
// var proxy: ModProxy = null
// }
// For this to work, we have to search inside the inner class Scala
// generates for singletons, which is in called ModName$. These are
// not automatically handled, because the mod discovery code ignores
// internal classes.
// Note that it is alternatively possible to write this like so:
// class ModName {
// @SidedProxy(...)
// var proxy: ModProxy = null
// }
// object ModName extends ModName { ... }
// which will fall back to the normal injection code which calls
// setProxy in turn.
// Get the actual mod implementation, which will be the inner class
// if we have a singleton.
Class <?> proxyTarget = mod . getMod (). getClass ();
if ( proxyTarget . getName (). endsWith ( "$" ))
{
// So we have a singleton class, check if there are targets.
for ( Field target : proxyTarget . getDeclaredFields ())
{
// This will not turn up anything if the alternative
// approach was taken (manually declaring the class).
// So we don't initialize the field twice.
if ( target . getAnnotation ( SidedProxy . class ) != null )
{
String targetType = side . isClient () ? target . getAnnotation ( SidedProxy . class ). clientSide () : target . getAnnotation ( SidedProxy . class ). serverSide ();
try
{
Object proxy = Class . forName ( targetType , true , loader ). newInstance ();
if ( ! target . getType (). isAssignableFrom ( proxy . getClass ()))
{
FMLLog . severe ( "Attempted to load a proxy type %s into %s.%s, but the types don't match" , targetType , proxyTarget . getSimpleName (), target . getName ());
throw new LoaderException ();
}
setProxy ( target , proxyTarget , proxy );
}
catch ( Exception e ) {
FMLLog . log ( Level . SEVERE , e , "An error occured trying to load a proxy into %s.%s" , proxyTarget . getSimpleName (), target . getName ());
throw new LoaderException ( e );
}
}
}
}
else
{
FMLLog . finer ( "Mod does not appear to be a singleton." );
}
2013-04-20 23:31:22 -04:00
}
}
2013-05-08 19:04:35 +02:00
2013-04-20 23:31:22 -04:00
public static class JavaAdapter implements ILanguageAdapter {
@Override
2013-06-26 14:52:56 -04:00
public Object getNewInstance ( FMLModContainer container , Class <?> objectClass , ClassLoader classLoader , Method factoryMarkedMethod ) throws Exception
2013-04-20 23:31:22 -04:00
{
2013-06-26 14:52:56 -04:00
if ( factoryMarkedMethod != null )
{
return factoryMarkedMethod . invoke ( null );
}
else
{
return objectClass . newInstance ();
}
2013-04-20 23:31:22 -04:00
}
@Override
public boolean supportsStatics ()
{
return true ;
}
@Override
public void setProxy ( Field target , Class <?> proxyTarget , Object proxy ) throws IllegalArgumentException , IllegalAccessException , NoSuchFieldException ,
SecurityException
{
target . set ( null , proxy );
}
2013-05-08 19:04:35 +02:00
@Override
public void setInternalProxies ( ModContainer mod , Side side , ClassLoader loader )
{
// Nothing to do here.
}
2013-04-20 23:31:22 -04:00
}
}