package com.google.gwt.gwtpages.generator.loader; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import com.google.gwt.core.client.GWT; 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.JClassType; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.gwtpages.client.PageRequestSession; import com.google.gwt.gwtpages.client.Pages; import com.google.gwt.gwtpages.client.message.exceptions.PageNotFoundException; import com.google.gwt.gwtpages.client.page.ApplicationPresenter; import com.google.gwt.gwtpages.client.page.LoadedPageContainer; import com.google.gwt.gwtpages.client.page.Page; import com.google.gwt.gwtpages.client.page.PageAttributes; import com.google.gwt.gwtpages.client.page.loader.AbstractPageLoader; import com.google.gwt.gwtpages.client.page.loader.AbstractPageLoader.PageRegistry; import com.google.gwt.gwtpages.client.page.loader.PageLoadCallback; import com.google.gwt.gwtpages.client.page.loader.PageLoader; import com.google.gwt.gwtpages.client.page.parameters.PageParameters; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; import com.google.gwt.user.rebind.SourceWriter; public class PageLoaderGenerator extends Generator { @Override public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException { try { TypeOracle typeOracle = context.getTypeOracle(); // get classType and save instance variables JClassType classType = typeOracle.getType(typeName); String packageName = classType.getPackage().getName(); AbstractPageLoader loader = null; try { loader = (AbstractPageLoader) Class.forName(typeName) .newInstance(); loader.registerPages(); } catch (Exception e) { logger.log( logger.ERROR, "The page loader could not be instantiated for deferred binding generation", e); throw new UnableToCompleteException(); } List<PageRegistry> registeredPages = new ArrayList<PageRegistry>(); Iterator<String> pageTokens = loader.getValidPageTokens(); while (pageTokens.hasNext()) { PageRegistry pageRegister = loader.getPageRegister(pageTokens .next()); registeredPages.add(pageRegister); } String newClassName = classType.getSimpleSourceName() + "_Generated"; // Generate class source code generateClass(logger, context, packageName, typeName, newClassName, registeredPages); return packageName + "." + newClassName; } catch (Exception e) { logger.log(TreeLogger.ERROR, "AsyncPageLoader generation Error", e); throw new RuntimeException("Couldn't generate async page loader", e); } } /** * Generate source code for new class. Class extends <code>HashMap</code>. * * @param logger * Logger object * @param context * Generator context */ protected void generateClass(TreeLogger logger, GeneratorContext context, String packageName, String typeName, String newClassName, List<PageRegistry> registeredPages) throws UnableToCompleteException { // get print writer that receives the source code PrintWriter printWriter = null; printWriter = context.tryCreate(logger, packageName, newClassName); // print writer if null, source code has ALREADY been generated, if (printWriter == null) return; // init composer, set class properties, create source writer ClassSourceFileComposerFactory composer = null; composer = new ClassSourceFileComposerFactory(packageName, newClassName); composer.addImport(Page.class.getName()); composer.addImport(GWT.class.getName()); composer.addImport(PageNotFoundException.class.getName()); composer.setSuperclass(typeName); SourceWriter sourceWriter = null; sourceWriter = composer.createSourceWriter(context, printWriter); // generator constructor source code sourceWriter .println("protected Page newInstance(String pageToken) throws PageNotFoundException {"); sourceWriter.indent(); for (PageRegistry pr : registeredPages) { sourceWriter.println("if (pageToken.equals(\"" + pr.getPageToken() + "\")) {"); sourceWriter.indent(); if (null != pr.getDisplayClass()) { boolean done = writeSetDisplayMethod(pr, sourceWriter, logger); if (!done) done = writeSetViewMethod(pr, sourceWriter, logger); if (!done) done = writeConstructorMethod(pr, sourceWriter); if (!done) { logger.log( logger.ERROR, "Unable to determine method to set the display on the page (presenter) for '" + pr.getPageClass() + "' - must be setView(...), setDisplay(...) or single argument constructor"); throw new UnableToCompleteException(); } } else { sourceWriter.println("return (" + pr.getPageClass().getName() + ") GWT.create(" + pr.getPageClass().getName() + ".class);"); } sourceWriter.outdent(); sourceWriter.println("}"); } sourceWriter.println("throw new PageNotFoundException(pageToken);"); // close method sourceWriter.outdent(); sourceWriter.println("}"); // close generated class sourceWriter.outdent(); sourceWriter.println("}"); // commit generated class context.commit(logger, printWriter); } protected boolean writeConstructorMethod(PageRegistry pr, SourceWriter sw) { for (Constructor c : pr.getPageClass().getDeclaredConstructors()) { Class[] parameterTypes = c.getParameterTypes(); if (null != parameterTypes && parameterTypes.length == 1) { // make sure the parameter type is compatible with the display // class if (parameterTypes[0].isAssignableFrom(pr.getDisplayClass())) { sw.println(pr.getDisplayClass().getName() + " display = (" + pr.getDisplayClass().getName() + ") GWT.create(" + pr.getDisplayClass().getName() + ".class);"); sw.println("Page page = new " + pr.getPageClass().getName() + "(display);"); sw.println("return page;"); return true; } } } return false; } protected void emptyConstructorSanityCheck(PageRegistry pr, TreeLogger logger) throws UnableToCompleteException { for (Constructor c : pr.getPageClass().getDeclaredConstructors()) { if (c.getParameterTypes().length == 0) return; } logger.log(logger.ERROR, "The class '" + pr.getPageClass().getName() + "' must have a no-arg constructor"); throw new UnableToCompleteException(); } protected boolean writeSetDisplayMethod(PageRegistry pr, SourceWriter sw, TreeLogger logger) throws UnableToCompleteException { for (Method m : pr.getPageClass().getMethods()) { if (m.getName().equals("setDisplay")) { Class[] parameterTypes = m.getParameterTypes(); if (null != parameterTypes && parameterTypes.length == 1) { // make sure the parameter type is compatible with the // display class if (parameterTypes[0] .isAssignableFrom(pr.getDisplayClass())) { writeNewPageAndDisplay(pr, sw, parameterTypes[0]); sw.println("page.setDisplay(display);"); sw.println("return page;"); emptyConstructorSanityCheck(pr, logger); return true; } } } } return false; } protected boolean writeSetViewMethod(PageRegistry pr, SourceWriter sw, TreeLogger logger) throws UnableToCompleteException { for (Method m : pr.getPageClass().getMethods()) { if (m.getName().equals("setView")) { Class[] parameterTypes = m.getParameterTypes(); if (null != parameterTypes && parameterTypes.length == 1) { // make sure the parameter type is compatible with the // display class if (parameterTypes[0] .isAssignableFrom(pr.getDisplayClass())) { writeNewPageAndDisplay(pr, sw, parameterTypes[0]); sw.println("page.setView(display);"); sw.println("return page;"); return true; } } } } return false; } protected void writeNewPageAndDisplay(PageRegistry pr, SourceWriter sw, Class defType) { sw.println(pr.getPageClass().getName() + " page = (" + pr.getPageClass().getName() + ") GWT.create(" + pr.getPageClass().getName() + ".class);"); sw.println(defType.getName().replace('$','.') + " display = (" + defType.getName().replace('$','.') + ") GWT.create(" + pr.getDisplayClass().getName() + ".class);"); } private static class DummyPageLoader implements PageLoader { public void init(Pages pages) { } public Iterator<String> getValidPageTokens() { return null; } public boolean isValidPageToken(String pageToken) { return false; } public PageAttributes getPageAttributes(String pageToken) throws PageNotFoundException { return null; } public void getPage(String pageToken, PageLoadCallback pageHandler) { } } private static class DummyApplicationPresenter implements ApplicationPresenter { public void showPage(LoadedPageContainer page, PageParameters parameters, PageRequestSession session) { } public void init(Pages settings) { } public Widget asWidget() { return null; } public void clearCurrentPage() { } } }