package xapi.dev.reflect;
import static xapi.inject.X_Inject.singletonLazy;
import javax.inject.Provider;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.StandardGeneratorContext;
import com.google.gwt.dev.jjs.MagicMethodGenerator;
import com.google.gwt.dev.jjs.UnifyAstView;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.util.Name.BinaryName;
import com.google.gwt.reflect.rebind.ReflectionUtilJava;
import com.google.gwt.reflect.rebind.generators.ReflectionGeneratorContext;
import xapi.annotation.inject.SingletonDefault;
import xapi.collect.impl.AbstractMultiInitMap;
import xapi.source.read.SourceUtil;
@SingletonDefault(implFor = MagicClassInjector.class)
public class MagicClassInjector implements MagicMethodGenerator {
private class InitMap extends AbstractMultiInitMap<String, JExpression, ReflectionGeneratorContext> {
public InitMap() {
super(PASS_THRU);
}
@Override
public JExpression get(final String key, final ReflectionGeneratorContext params) {
// because we cache results, super dev mode recompiles need to skip the
// cache if the magic class does not exist, so we test for that state on
// every get().
final JDeclaredType type =
params.getAst().searchForTypeBySource(params.getClazz().getRefType().getName());
if (null == type) {
removeValue(key);
} else {
final String typeName = BinaryName.toSourceName(type.getName());
final String generatedName = ReflectionUtilJava.generatedMagicClassName(typeName);
if (null == params.getAst().searchForTypeBySource(generatedName)) {
removeValue(key);
}
}
return super.get(key, params);
}
@Override
protected JExpression initialize(final String clsName, final ReflectionGeneratorContext params) {
try {
return doRebind(clsName, params);
} catch (final Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException("Could not initialize magic class for " + clsName, e);
}
}
}
public static JExpression injectMagicClass(final TreeLogger logger, final JMethodCall x, final JMethod currentMethod,
final Context context, final UnifyAstView ast) throws UnableToCompleteException {
// defer to our static final instance here
return generator.get().injectMagic(logger, x, currentMethod, context, ast);
}
// Yes, we let you use our jre injection mechanism during gwt injection
// generation.
//
// Third party tools should probably not override this;
// it's more for end users to experiment with while developing their own
// plugins,
// but you can if you want and are fine with upgrading as the api grows.
//
// If you need more functionality than provided, submit a pull request
// and we can make a usable upstream api
private static final Provider<MagicClassInjector> generator = singletonLazy(MagicClassInjector.class);
private final InitMap mappedClasses = new InitMap();
public JExpression doRebind(final String clsName, final ReflectionGeneratorContext params) throws UnableToCompleteException {
// generate
params.getLogger().log(Type.INFO, "Binding magic class for " + clsName);
// JType type = params.getClazz().getRefType();
final JDeclaredType type = params.getAst().searchForTypeBySource(params.getClazz().getRefType().getName());
final StandardGeneratorContext ctx = params.getGeneratorContext();
final Class<? extends Generator> generator = MagicClassGenerator.class;
final String result = ctx.runGenerator(params.getLogger(), generator,
SourceUtil.toSourceName(type.getName()));
ctx.finish(params.getLogger());
params.getLogger().log(Type.INFO, "Generated Class Enhancer: " + result);
final JDeclaredType success = params.getAst().searchForTypeBySource(result);
// Okay, we've generated the correct magic class subtype;
// Now pull off its static accessor method to grab our generated class.
for (final JMethod method : success.getMethods()) {
if (method.isStatic() && method.getName().equals("enhanceClass")) {
final JMethodCall call = new JMethodCall(method.getSourceInfo(), null, method);
call.addArg(params.getClazz().makeStatement().getExpr());
return call;
}
}
params.getLogger().log(Type.ERROR, "Unable to load " + result + ".enhanceClass()");
throw new UnableToCompleteException();
}
@Override
public JExpression injectMagic(final TreeLogger logger, final JMethodCall methodCall, final JMethod currentMethod,
final Context context, final UnifyAstView ast) throws UnableToCompleteException {
if (methodCall.getArgs().size() != 1) {
logger.log(Type.ERROR, "X_Reflect.magicClass accepts one and only one argument: a class literal.");
throw new UnableToCompleteException();
}
if (!(methodCall.getArgs().get(0) instanceof JClassLiteral)) {
logger.log(Type.ERROR, "X_Reflect.magicClass accepts one and only one argument: a class literal." +
" You sent a " + methodCall.getArgs().get(0).getClass() + " : " +
methodCall.getArgs().get(0).toSource());
throw new UnableToCompleteException();
}
final JClassLiteral clazz = (JClassLiteral) methodCall.getArgs().get(0);
return mappedClasses.get(clazz.getRefType().getName(), new ReflectionGeneratorContext(logger, clazz, methodCall,
currentMethod, context, ast));
}
}