package xapi.dev.ui.autoui;
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.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.jjs.MagicMethodGenerator;
import com.google.gwt.dev.jjs.SourceInfo;
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.JClassType;
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.jjs.ast.JNullLiteral;
import com.google.gwt.reflect.rebind.ReflectionUtilAst;
import java.util.List;
import xapi.ui.autoui.api.UserInterface;
/**
* A magic method injector for the method X_AutoUi.makeUi:
* <pre>
* public static <T, U extends UserInterface<T>> U makeUi(T model, Class<? extends T> uiOptions, Class<U> uiType)
* </pre>
* This magic method will create a generated class that is much more efficient at runtime than
* what the default code does (via reflection). At present, sending non-class literals to this
* method is not supported. It can be supported by using reflection in Gwt, but it requires a lot
* of reflection which comes with a lot of overhead, and will not be supported unless there is a
* very compelling use case offered.
* <br/>
* If you do want to use an abstraction where you do not know the literal types when you invoke
* the ui generator, simply create a factory abstraction which does know the literal types,
* and pass those around to do your decoupled ui generation.
*
* @author "James X. Nelson (james@wetheinter.net)"
*
*/
public class AutoUiInjector implements MagicMethodGenerator {
@Override
public JExpression injectMagic(final TreeLogger logger, final JMethodCall methodCall,
final JMethod enclosingMethod, final Context context, final UnifyAstView ast)
throws UnableToCompleteException {
final List<JExpression> args = methodCall.getArgs();
final JClassLiteral uiOptionsLiteral = ReflectionUtilAst.extractClassLiteral(logger, args.get(1), ast, true);
final JClassLiteral uiTypeLiteral = ReflectionUtilAst.extractClassLiteral(logger, args.get(2), ast, true);
final TypeOracle oracle = ast.getTypeOracle();
ast.translate(uiOptionsLiteral.getRefType());
com.google.gwt.core.ext.typeinfo.JClassType uiOptions;
try {
uiOptions = oracle.getType(uiOptionsLiteral.getRefType().getName().replace('$', '.'));
} catch (final NotFoundException e) {
logger.log(Type.ERROR, "Unable to load "+uiOptionsLiteral.getRefType()+" from the type oracle");
throw new UnableToCompleteException();
}
String provider;
try {
final com.google.gwt.core.ext.typeinfo.JClassType uiType = oracle.getType(uiTypeLiteral.getRefType().getName().replace('$', '.'));
provider = AutoUiGenerator.generateUiProvider(logger, ast, uiOptions, uiType);
} catch (final NotFoundException e) {
logger.log(Type.ERROR, "Unable to load "+uiOptionsLiteral.getRefType()+" from the type oracle");
throw new UnableToCompleteException();
}
// Force load the type
ast.getTypeOracle().findType(provider);
// Grab the type from the jjs ast
final JClassType uiType = (JClassType) ast.searchForTypeBySource(provider);
final SourceInfo info = methodCall.getSourceInfo().makeChild();
JExpression inst = null;
for (final JMethod method : uiType.getMethods()) {
if ("newUi".equals(method.getName())) {
inst = new JMethodCall(info, null, method);
break;
}
}
if (inst == null) {
logger.log(Type.ERROR, "Unable to find method newUi in generated UI provider "+provider);
throw new UnableToCompleteException();
}
// Possible render the ui immediately, if a non-null model was supplied
if (args.get(0) != JNullLiteral.INSTANCE) {
// We need to call renderUi on the new instance.
final JDeclaredType uiCls = ast.searchForTypeBySource(UserInterface.class.getName());
for (final JMethod method : uiCls.getMethods()) {
if (method.getName().equals("renderUi")) {
final JMethodCall call = new JMethodCall(info.makeChild(), inst, method);
call.addArg(args.get(0));
return call;
}
}
}
return inst;
}
}