package xapi.dev.ui.html; 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.reflect.rebind.ReflectionUtilAst; import java.util.List; import xapi.ui.api.StyleService; import xapi.ui.html.X_Html; /** * A magic method injector for {@link X_Html#injectCss(Class, xapi.ui.api.StyleService)} * <p> * This magic method will replace the call to X_Html.injectStyle with one or more calls * to {@link StyleService#addCss(String, int)}, matching all style defined on the Class, * it's respective methods, and all annotated supertypes. * * It is recommended to do this statically in the classes themselves, so that you get * "just in time injection" so that referencing the class injects the css as needed, * and not referencing the class prevents the inclusion of css that you don't need. * * @author "James X. Nelson (james@wetheinter.net)" * */ public class CssInjector 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 typeLiteral = ReflectionUtilAst.extractClassLiteral(logger, args.get(0), ast, true); logger.log(Type.TRACE, "Injecting css for "+typeLiteral.getRefType().getName()); // Ensure the type is loaded by the type oracle. final TypeOracle oracle = ast.getTypeOracle(); ast.translate(typeLiteral.getRefType()); // Load the type from the type oracle com.google.gwt.core.ext.typeinfo.JClassType templateType; try { templateType = oracle.getType(typeLiteral.getRefType().getName().replace('$', '.')); } catch (final NotFoundException e) { logger.log(Type.ERROR, "Unable to load "+typeLiteral.getRefType()+" from the type oracle"); throw new UnableToCompleteException(); } HtmlGeneratorResult provider; provider = CssInjectorGenerator.generateSnippetProvider(logger, ast, templateType); // Force load the type, to ensure it is parsed into AST ast.getTypeOracle().findType(provider.getFinalName()); // Grab the type from the jjs ast final JClassType injectorType = (JClassType) ast.searchForTypeBySource(provider.getFinalName()); final SourceInfo info = methodCall.getSourceInfo().makeChild(); for (final JMethod method : injectorType.getMethods()) { if (method.getName().equals("inject")) { JExpression arg; if (args.size() > 1) { // The X_Html method receives a reference to the StyleService to use arg = args.get(1); } else { // The X_Element method does not take a StyleService, and instead uses it's own default final JDeclaredType elemental = ast.searchForTypeBySource("xapi.elemental.X_Elemental"); final String serviceType = "Lxapi/elemental/api/ElementalService;"; final JMethod getService = elemental.findMethod("getElementalService()"+serviceType, false); arg = new JMethodCall(method.getSourceInfo(), null, getService); } return new JMethodCall(info, null, method, arg); } } logger.log(Type.ERROR, "Unable to find method inject in generated UI provider "+provider); throw new UnableToCompleteException(); } }