/* * AsyncShimGenerator.java * * Copyright (C) 2009-12 by RStudio, Inc. * * Unless you have received this program directly from RStudio pursuant * to the terms of a commercial license agreement with RStudio, then * this program is licensed to you under the terms of version 3 of the * GNU Affero General Public License. This program is distributed WITHOUT * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. * */ package org.rstudio.core.rebind; import com.google.gwt.core.ext.Generator; import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.*; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; import com.google.gwt.user.rebind.SourceWriter; import com.google.gwt.user.rebind.StringSourceWriter; import java.io.PrintWriter; public class AsyncShimGenerator extends Generator { private class Helper { public Helper(TreeLogger logger, GeneratorContext context, String typeName) throws Exception { logger_ = logger; context_ = context; baseType_ = context_.getTypeOracle().getType(typeName); packageName_ = baseType_.getPackage().getName(); } private void doAssert(boolean assertion, String message) { if (!assertion) throw new IllegalArgumentException(message); } public String generate() throws Exception { String simpleName = baseType_.getName().replace('.', '_') + "__Impl"; PrintWriter printWriter = context_.tryCreate( logger_, packageName_, simpleName); if (printWriter != null) { ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(packageName_, simpleName); factory.setSuperclass(baseType_.getName()); factory.addImport("com.google.gwt.core.client.GWT"); factory.addImport("com.google.gwt.core.client.RunAsyncCallback"); factory.addImport("com.google.inject.Provider"); factory.addImport("com.google.gwt.user.client.Command"); SourceWriter writer = factory.createSourceWriter(context_, printWriter); emitBody(writer); // Close the class and commit it writer.outdent(); writer.println("}"); context_.commit(logger_, printWriter); } return packageName_ + "." + simpleName; } private void emitBody(SourceWriter w) throws NotFoundException { JClassType baseClass = context_.getTypeOracle().getType( "org.rstudio.core.client.AsyncShim"); JClassType c = baseType_.asParameterizationOf(baseClass.isGenericType()); JType delayedType = c.isParameterized().getTypeArgs()[0]; w.println(); w.println("private " + delayedType.getQualifiedSourceName() + " o;"); w.println("private Provider<" + delayedType.getQualifiedSourceName() + "> po;"); w.println(); w.println("@Override"); w.println("public void initialize(Provider<" + delayedType.getQualifiedSourceName() + "> provider) {"); w.indentln("po = provider;"); w.println("}"); w.println(); w.println("@Override"); w.println("public void forceLoad(boolean downloadCodeOnly, Command continuation) {"); w.indentln("load(downloadCodeOnly ? -1 : 0, new Object[] {continuation});"); w.println("}"); w.println(); StringSourceWriter w_switch = new StringSourceWriter(); int methodNum = 0; for (JMethod method : baseType_.getOverridableMethods()) { if (!method.isAbstract()) continue; doAssert(method.getReturnType().equals(JPrimitiveType.VOID), "Async method had a non-void return type"); w.print("public final void "); w.print(method.getName() + "("); String delim = ""; for (JParameter param : method.getParameters()) { w.print(delim); delim = ", "; w.print(param.getType().getQualifiedSourceName()); w.print(" " + param.getName()); } w.print(") "); if (method.getThrows().length > 0) { w.print("throws "); String delim2 = ""; for (JType eType : method.getThrows()) { w.print(delim2); delim2 = ", "; w.print(eType.getQualifiedSourceName()); } } w.println("{"); w.indent(); methodNum++; if (method.getParameters().length == 0) { w.println("load(" + methodNum + ", null);"); } else { w.println("load(" + methodNum + ", new Object[] {"); w.indent(); String delim3 = ""; for (JParameter p : method.getParameters()) { w.print(delim3); delim3 = ", "; w.print(p.getName()); } w.outdent(); w.println("});"); } w.outdent(); w.println("}"); w.println(); w_switch.println("case " + methodNum + ":"); w_switch.indent(); w_switch.print("o." + method.getName() + "("); String delim4 = ""; for (int i = 0; i < method.getParameters().length; i++) { w_switch.print(delim4); delim4 = ", "; w_switch.print("("); w_switch.print(method.getParameters()[i].getType().getQualifiedSourceName()); w_switch.print(")"); w_switch.print("args[" + i + "]"); } w_switch.println(");"); w_switch.println("break;"); w_switch.outdent(); } w.println("private void load(final int method, final Object[] args) {"); w.indent(); w.println("GWT.runAsync(new RunAsyncCallback() {"); w.indent(); w.println("public void onFailure(Throwable reason) {"); w.indent(); w.println("try {"); w.indentln("onDelayLoadFailure(reason);"); w.println("} finally {"); w.indentln("if (method <= 0 && args[0] != null) ((Command)args[0]).execute();"); w.println("}"); w.outdent(); w.println("}"); w.println("public void onSuccess() {"); w.indent(); w.println("preInstantiationHook(new Command() {"); w.indent(); w.println("public void execute() {"); w.indent(); w.println("onSuccess2();"); w.outdent(); w.println("}"); w.outdent(); w.println("});"); w.outdent(); w.println("}"); w.println("private void onSuccess2() {"); w.indent(); w.println("try {"); w.indent(); w.println("if (method < 0) return; // download code only"); w.println("if (o == null) {"); w.indent(); w.println("o = po.get();"); w.println("onDelayLoadSuccess(o);"); w.outdent(); w.println("}"); w.println("switch (method) {"); w.println("case 0: break;"); w.println(w_switch.toString()); w.println("}"); w.outdent(); w.println("} finally {"); w.indentln("if (method <= 0 && args[0] != null) ((Command)args[0]).execute();"); w.println("}"); w.outdent(); w.println("}"); w.outdent(); w.println("});"); w.outdent(); w.println("}"); } private final TreeLogger logger_; private final GeneratorContext context_; private final JClassType baseType_; private final String packageName_; } @Override public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException { try { return new Helper(logger, context, typeName).generate(); } catch (UnableToCompleteException e) { throw e; } catch (Exception e) { logger.log(TreeLogger.Type.ERROR, "Barf", e); throw new UnableToCompleteException(); } } }