/**
   * Generates a new instance of an anonymous inner class that implements the PageNode interface.
   *
   * @param pageClass The class providing the widget content for the page.
   * @param pageName The name of the page (to be used in the URL history fragment).
   */
  private ObjectBuilder generateNewInstanceOfPageImpl(MetaClass pageClass, String pageName) {
    AnonymousClassStructureBuilder pageImplBuilder =
        ObjectBuilder.newInstanceOf(
                MetaClassFactory.parameterizedAs(
                    PageNode.class, MetaClassFactory.typeParametersOf(pageClass)))
            .extend();

    pageImplBuilder
        .publicMethod(String.class, "name")
        .append(Stmt.loadLiteral(pageName).returnValue())
        .finish()
        .publicMethod(Class.class, "contentType")
        .append(Stmt.loadLiteral(pageClass).returnValue())
        .finish()
        .publicMethod(
            void.class, "produceContent", Parameter.of(CreationalCallback.class, "callback"))
        .append(
            Stmt.nestedCall(Refs.get("bm"))
                .invoke("lookupBean", Stmt.loadLiteral(pageClass))
                .invoke("getInstance", Stmt.loadVariable("callback")))
        .finish();

    appendPageHidingMethod(pageImplBuilder, pageClass);
    appendPageHiddenMethod(pageImplBuilder, pageClass);

    appendPageShowingMethod(pageImplBuilder, pageClass);
    appendPageShownMethod(pageImplBuilder, pageClass);

    return pageImplBuilder.finish();
  }
Example #2
0
  @Override
  public Statement getBeanInstance(final InjectableInstance injectableInstance) {
    final IOCProcessingContext pCtx = injectableInstance.getInjectionContext().getProcessingContext();

    pCtx.append(Stmt.declareFinalVariable(varName, proxyClass, newObject(proxyClass)));

    final MetaClass proxyResolverRef = parameterizedAs(ProxyResolver.class, typeParametersOf(proxiedType));

    final BlockBuilder<AnonymousClassStructureBuilder> proxyResolverBody = newObject(proxyResolverRef)
            .extend().publicOverridesMethod("resolve", Parameter.of(proxiedType, "obj"));

    final Statement proxyResolver = proxyResolverBody._(
        ProxyMaker.closeProxy(Refs.get(varName), Refs.get("obj"))

    ).finish().finish();

    proxyResolverBody._(Stmt.loadVariable("context").invoke("addProxyReference", Refs.get(varName), Refs.get("obj")));

    pCtx.append(loadVariable("context").invoke("addUnresolvedProxy", proxyResolver,
            proxiedType, qualifyingMetadata.getQualifiers()));

    for (final Statement statement : closeStatements) {
      proxyResolverBody.append(statement);
    }

    setRendered(true);
    return loadVariable(varName);
  }
Example #3
0
  /**
   * Tries to find a reference for an injected {@link AutoBound} data binder.
   *
   * @return the data binder reference or null if not found.
   */
  private static DataBinderRef lookupAutoBoundBinder(final InjectableInstance<?> inst) {
    Statement dataBinderRef = null;
    MetaClass dataModelType = null;

    InjectUtil.BeanMetric beanMetric =
        InjectUtil.getFilteredBeanMetric(
            inst.getInjectionContext(), inst.getInjector().getInjectedType(), AutoBound.class);

    final Collection<Object> allInjectors = beanMetric.getAllInjectors();
    if (allInjectors.size() > 1) {
      throw new GenerationException(
          "Multiple @AutoBound data binders injected in " + inst.getEnclosingType());
    } else if (allInjectors.size() == 1) {
      final Object injectorElement = allInjectors.iterator().next();

      if (injectorElement instanceof MetaConstructor || injectorElement instanceof MetaMethod) {
        final MetaParameter mp = beanMetric.getConsolidatedMetaParameters().iterator().next();

        assertTypeIsDataBinder(mp.getType());
        dataModelType = (MetaClass) mp.getType().getParameterizedType().getTypeParameters()[0];
        dataBinderRef = inst.getInjectionContext().getInlineBeanReference(mp);
        inst.ensureMemberExposed();
      } else {
        final MetaField field = (MetaField) allInjectors.iterator().next();

        assertTypeIsDataBinder(field.getType());
        dataModelType = (MetaClass) field.getType().getParameterizedType().getTypeParameters()[0];
        dataBinderRef =
            Stmt.invokeStatic(
                inst.getInjectionContext().getProcessingContext().getBootstrapClass(),
                PrivateAccessUtil.getPrivateFieldInjectorName(field),
                Variable.get(inst.getInjector().getInstanceVarName()));
        inst.getInjectionContext().addExposedField(field, PrivateAccessType.Both);
      }
    } else {
      final MetaClass declaringClass = inst.getInjector().getInjectedType();
      for (final MetaField field : declaringClass.getFields()) {
        if (field.isAnnotationPresent(AutoBound.class)) {
          assertTypeIsDataBinder(field.getType());
          dataModelType = (MetaClass) field.getType().getParameterizedType().getTypeParameters()[0];
          dataBinderRef =
              Stmt.invokeStatic(
                  inst.getInjectionContext().getProcessingContext().getBootstrapClass(),
                  PrivateAccessUtil.getPrivateFieldInjectorName(field),
                  Variable.get(inst.getInjector().getInstanceVarName()));
          inst.getInjectionContext().addExposedField(field, PrivateAccessType.Both);
          break;
        }
      }
    }

    return (dataBinderRef != null) ? new DataBinderRef(dataModelType, dataBinderRef) : null;
  }
 private Statement defaultValueStatement(MetaClass type) {
   if (type.isPrimitive()) {
     if (type.asBoxed().isAssignableTo(Number.class)) {
       return Stmt.castTo(type, Stmt.load(0));
     } else if (type.isAssignableTo(boolean.class)) {
       return Stmt.load(false);
     } else {
       throw new UnsupportedOperationException(
           "Don't know how to make a default value for @PageState field of type "
               + type.getFullyQualifiedName());
     }
   } else {
     return Stmt.load(null);
   }
 }
Example #5
0
  /**
   * Tests that it is safe to call ensureMemberExposed() on any type of InjectionPoint. (This was an
   * actual bug that was not caught by the existing integration tests).
   */
  public void testEnsureMemberExposedWithConstructorInjectionPoint() throws Exception {
    final ClassStructureBuilder<? extends ClassStructureBuilder<?>> structureBuilder =
        ClassBuilder.define("my.FakeBootstrapper").publicScope().body();

    IOCProcessingContext processingContext =
        IOCProcessingContext.Builder.create()
            .logger(
                new TreeLogger() {
                  @Override
                  public TreeLogger branch(
                      Type type, String msg, Throwable caught, HelpInfo helpInfo) {
                    return null;
                  }

                  @Override
                  public boolean isLoggable(Type type) {
                    return false;
                  }

                  @Override
                  public void log(Type type, String msg, Throwable caught, HelpInfo helpInfo) {
                    System.out.println(type.getLabel() + ": " + msg);
                    if (caught != null) {
                      caught.printStackTrace();
                    }
                  }
                })
            .sourceWriter(new StringSourceWriter())
            .context(Context.create())
            .bootstrapClassInstance(structureBuilder.getClassDefinition())
            .bootstrapBuilder(structureBuilder)
            .blockBuilder(Stmt.do_())
            .packages(Collections.singleton(ConstructorInjectedBean.class.getPackage().getName()))
            .build();
    InjectionContext ctx =
        InjectionContext.Builder.create().processingContext(processingContext).build();
    MetaConstructor constructor =
        MetaClassFactory.get(ConstructorInjectedBean.class).getConstructor(FooService.class);
    InjectionPoint<Inject> injectionPoint =
        new InjectionPoint<Inject>(
            new Inject() {

              @Override
              public Class<? extends Annotation> annotationType() {
                return Inject.class;
              }
            },
            TaskType.Parameter,
            constructor,
            null,
            null,
            null,
            constructor.getParameters()[0],
            null,
            ctx);

    // holy crap that was a lot of setup. Here comes the actual test:

    injectionPoint.ensureMemberExposed();
  }
  /**
   * Appends the method that calls the {@code @PageHiding} method of the widget.
   *
   * @param pageImplBuilder The class builder for the implementation of PageNode we are adding the
   *     method to.
   * @param pageClass The "content type" (Widget subclass) of the page. This is the type the user
   *     annotated with {@code @Page}.
   */
  private void appendPageHidingMethod(
      AnonymousClassStructureBuilder pageImplBuilder, MetaClass pageClass) {
    BlockBuilder<?> method =
        pageImplBuilder
            .publicMethod(
                void.class,
                createMethodNameFromAnnotation(PageHiding.class),
                Parameter.of(pageClass, "widget"),
                Parameter.of(NavigationControl.class, "control"))
            .body();
    final MetaMethod pageHidingMethod =
        checkMethodAndAddPrivateAccessors(
            pageImplBuilder,
            method,
            pageClass,
            PageHiding.class,
            NavigationControl.class,
            "control");

    /*
     * If the user did not provide a control parameter, we must proceed for them after the method is invoked.
     */
    if (pageHidingMethod == null || pageHidingMethod.getParameters().length != 1) {
      method.append(Stmt.loadVariable("control").invoke("proceed"));
    }

    method.finish();
  }
  private static Statement paramFromStringStatement(MetaClass toType, Statement stringValue) {

    // make sure it's really a string
    stringValue = Stmt.castTo(String.class, stringValue);

    if (toType.isAssignableTo(String.class)) {
      return stringValue;
    } else if (toType.asBoxed().isAssignableTo(Number.class)) {
      return Stmt.invokeStatic(toType.asBoxed(), "valueOf", stringValue);
    } else if (toType.asBoxed().isAssignableTo(Boolean.class)) {
      return Stmt.invokeStatic(Boolean.class, "valueOf", stringValue);
    } else {
      throw new UnsupportedOperationException(
          "@PageState fields of type " + toType.getFullyQualifiedName() + " are not supported");
    }
  }
Example #8
0
 public static Collection<Statement> createAllPropertyBindings(final Statement proxyRef,
                                                               final Map<String, ProxyProperty> proxyProperties) {
   final List<Statement> statementList = new ArrayList<Statement>();
   for (final Map.Entry<String, ProxyProperty> entry : proxyProperties.entrySet()) {
     statementList.add(Stmt.nestedCall(proxyRef).invoke("$set_" + entry.getKey(), entry.getValue().getOriginalValueReference()));
   }
   return statementList;
 }
Example #9
0
  @Test
  public void testTryCatchFinallyBlock() {
    String s =
        StatementBuilder.create()
            .try_()
            .append(Stmt.throw_(Exception.class))
            .finish()
            .catch_(Exception.class, "e")
            .append(Stmt.throw_(RuntimeException.class, Variable.get("e")))
            .finish()
            .finally_()
            .append(Stmt.load(0).returnValue())
            .finish()
            .toJavaString();

    assertEquals("Failed to generate try catch finally block", TRY_CATCH_FINALLY_BLOCK, s);
  }
  /**
   * Appends the method that calls the {@code @PageHidden} method of the widget.
   *
   * @param pageImplBuilder The class builder for the implementation of PageNode we are adding the
   *     method to.
   * @param pageClass The "content type" (Widget subclass) of the page. This is the type the user
   *     annotated with {@code @Page}.
   */
  private void appendPageHiddenMethod(
      AnonymousClassStructureBuilder pageImplBuilder, MetaClass pageClass) {
    BlockBuilder<?> method =
        pageImplBuilder
            .publicMethod(
                void.class,
                createMethodNameFromAnnotation(PageHidden.class),
                Parameter.of(pageClass, "widget"))
            .body();
    checkMethodAndAddPrivateAccessors(
        pageImplBuilder, method, pageClass, PageHidden.class, HistoryToken.class, "state");

    if (pageClass.getAnnotation(Singleton.class) == null
        && pageClass.getAnnotation(ApplicationScoped.class) == null
        && pageClass.getAnnotation(EntryPoint.class) == null) {
      method.append(Stmt.loadVariable("bm").invoke("destroyBean", Stmt.loadVariable("widget")));
    }
    method.finish();
  }
  public void registerWithBeanManager(InjectionContext context, Statement valueRef) {
    if (InjectUtil.checkIfTypeNeedsAddingToBeanStore(context, this)) {
      QualifyingMetadata md = delegate.getQualifyingMetadata();
      if (md == null) {
        md = context.getProcessingContext().getQualifyingMetadataFactory().createDefaultMetadata();
      }

      context
          .getProcessingContext()
          .appendToEnd(
              Stmt.loadVariable(context.getProcessingContext().getContextVariableReference())
                  .invoke(
                      "addBean",
                      type,
                      Refs.get(delegate.getCreationalCallbackVarName()),
                      isSingleton() ? valueRef : null,
                      md.render()));
    }
  }
  @Override
  protected String generate(TreeLogger logger, GeneratorContext context) {
    final ClassStructureBuilder<?> classBuilder =
        Implementations.extend(NavigationGraph.class, GENERATED_CLASS_NAME);

    // accumulation of (name, pageclass) mappings for dupe detection and dot file generation
    BiMap<String, MetaClass> pageNames = HashBiMap.create();

    // accumulation UniquePageRoles for ensuring there is exactly one.
    Multimap<Class<?>, MetaClass> pageRoles = ArrayListMultimap.create();

    ConstructorBlockBuilder<?> ctor = classBuilder.publicConstructor();
    final Collection<MetaClass> pages = ClassScanner.getTypesAnnotatedWith(Page.class, context);
    for (MetaClass pageClass : pages) {
      if (!pageClass.isAssignableTo(IsWidget.class)) {
        throw new GenerationException(
            "Class "
                + pageClass.getFullyQualifiedName()
                + " is annotated with @Page, so it must implement IsWidget");
      }
      Page annotation = pageClass.getAnnotation(Page.class);
      String pageName = getPageName(pageClass);
      List<Class<? extends PageRole>> annotatedPageRoles = Arrays.asList(annotation.role());

      MetaClass prevPageWithThisName = pageNames.put(pageName, pageClass);
      if (prevPageWithThisName != null) {
        throw new GenerationException(
            "Page names must be unique, but "
                + prevPageWithThisName
                + " and "
                + pageClass
                + " are both named ["
                + pageName
                + "]");
      }
      Statement pageImplStmt = generateNewInstanceOfPageImpl(pageClass, pageName);
      if (annotatedPageRoles.contains(DefaultPage.class)) {
        // need to assign the page impl to a variable and add it to the map twice
        ctor.append(Stmt.declareFinalVariable("defaultPage", PageNode.class, pageImplStmt));
        pageImplStmt = Variable.get("defaultPage");
        ctor.append(Stmt.nestedCall(Refs.get("pagesByName")).invoke("put", "", pageImplStmt));
        ctor.append(
            Stmt.nestedCall(Refs.get("pagesByRole"))
                .invoke("put", DefaultPage.class, pageImplStmt));
      } else if (pageName.equals("")) {
        throw new GenerationException(
            "Page "
                + pageClass.getFullyQualifiedName()
                + " has an empty path. Only the"
                + " page with startingPage=true is permitted to have an empty path.");
      }

      final String fieldName = StringUtils.uncapitalize(pageClass.getName());
      ctor.append(Stmt.declareFinalVariable(fieldName, PageNode.class, pageImplStmt));
      ctor.append(
          Stmt.nestedCall(Refs.get("pagesByName")).invoke("put", pageName, Refs.get(fieldName)));

      for (Class<? extends PageRole> annotatedPageRole : annotatedPageRoles) {
        pageRoles.put(annotatedPageRole, pageClass);
        // DefaultPage is already added above.
        if (!annotatedPageRole.equals(DefaultPage.class))
          ctor.append(
              Stmt.nestedCall(Refs.get("pagesByRole"))
                  .invoke("put", annotatedPageRole, Refs.get(fieldName)));
      }
    }
    ctor.finish();

    renderNavigationToDotFile(pageNames);

    validateDefaultPagePresent(pages, pageRoles);
    validateUnique(pageRoles);

    return classBuilder.toJavaString();
  }
Example #13
0
 public static Statement closeProxy(final Statement proxyReference, final Statement beanInstance) {
   return Stmt.nestedCall(proxyReference).invoke(PROXY_BIND_METHOD, beanInstance);
 }
Example #14
0
  BuildMetaClass make(final String proxyClassName,
                      final MetaClass toProxy,
                      final String privateAccessorType) {
    final ClassStructureBuilder builder;



    final boolean renderEqualsAndHash;
    if (!toProxy.isInterface()) {
      renderEqualsAndHash = true;
      if (toProxy.isFinal()) {
        throw new UnproxyableClassException(toProxy, toProxy.getFullyQualifiedName()
            + " is an unproxiable class because it is final");
      }
      if (!toProxy.isDefaultInstantiable()) {
        throw new UnproxyableClassException(toProxy, toProxy.getFullyQualifiedName() + " must have a default " +
            "no-arg constructor");
      }

      builder = ClassBuilder.define(proxyClassName, toProxy).publicScope().body();
    }
    else {
      renderEqualsAndHash = false;
      builder = ClassBuilder.define(proxyClassName).publicScope().implementsInterface(toProxy).body();
    }

    final String proxyVar = "$$_proxy_$$";

    final Set<String> renderedMethods = new HashSet<String>();

    final Map<String, MetaType> typeVariableMap = new HashMap<String, MetaType>();
    final MetaParameterizedType metaParameterizedType = toProxy.getParameterizedType();

    if (metaParameterizedType != null) {
      int i = 0;
      for (final MetaTypeVariable metaTypeVariable : toProxy.getTypeParameters()) {
        typeVariableMap.put(metaTypeVariable.getName(), metaParameterizedType.getTypeParameters()[i++]);
      }
    }

    builder.privateField(proxyVar, toProxy).finish();

    final Set<Map.Entry<String, ProxyProperty>> entries = proxyProperties.entrySet();
    for (final Map.Entry<String, ProxyProperty> entry : entries) {
      builder.privateField(entry.getValue().getEncodedProperty(), entry.getValue().getType()).finish();
      builder.packageMethod(void.class, "$set_" + entry.getKey(), Parameter.of(entry.getValue().getType(), "o"))
          .append(Stmt.loadVariable(entry.getValue().getEncodedProperty()).assignValue(Refs.get("o")))
          .finish();
    }

    for (final MetaMethod method : toProxy.getMethods()) {
      final String methodString = GenUtil.getMethodString(method);
      if (renderedMethods.contains(methodString) || method.getName().equals("hashCode")
          || (method.getName().equals("equals") && method.getParameters().length == 1
          && method.getParameters()[0].getType().getFullyQualifiedName().equals(Object.class.getName()))) continue;

      renderedMethods.add(methodString);

      if ((!method.isPublic() && !method.isProtected()) ||
          method.isSynthetic() ||
          method.isFinal() ||
          method.isStatic() ||
          method.getDeclaringClass().getFullyQualifiedName().equals(Object.class.getName()))
        continue;

      final List<Parameter> methodParms = new ArrayList<Parameter>();
      final MetaParameter[] parameters = method.getParameters();

      for (int i = 0; i < parameters.length; i++) {
        methodParms.add(Parameter.of(parameters[i].getType().getErased(), "a" + i));
      }

      final DefParameters defParameters = DefParameters.fromParameters(methodParms);
      final BlockBuilder methBody = builder.publicMethod(method.getReturnType(), method.getName())
          .annotatedWith(OVERRIDE_ANNOTATION)
          .parameters(defParameters)
          .throws_(method.getCheckedExceptions());

      methBody.appendAll(getAroundInvokeStatements(method));
      methBody.appendAll(getBeforeStatements(method));

      final List<Parameter> parms = defParameters.getParameters();

      final Statement[] statementVars = new Statement[parms.size()];
      for (int i = 0; i < parms.size(); i++) {
        statementVars[i] = loadVariable(parms.get(i).getName());
      }

      if (!method.isPublic()) {
        PrivateAccessUtil.addPrivateAccessStubs(privateAccessorType, builder, method, new Modifier[0]);

        final Statement[] privateAccessStmts = new Statement[statementVars.length + 1];
        privateAccessStmts[0] = Refs.get(proxyVar);
        System.arraycopy(statementVars, 0, privateAccessStmts, 1, statementVars.length);

        if (method.getReturnType().isVoid()) {
          methBody._(loadVariable("this").invoke(PrivateAccessUtil.getPrivateMethodName(method), privateAccessStmts));
        }
        else {
          methBody._(loadVariable("this").invoke(PrivateAccessUtil.getPrivateMethodName(method), privateAccessStmts).returnValue());
        }
      }
      else {
        if (method.getReturnType().isVoid()) {
          methBody._(loadVariable(proxyVar).invoke(method, statementVars));
        }
        else {
          methBody._(loadVariable(proxyVar).invoke(method, statementVars).returnValue());
        }
      }

      methBody.appendAll(getAfterStatements(method));
      methBody.appendAll(getAroundInvokeStatements(method));

      methBody.finish();
    }

    if (renderEqualsAndHash) {
      // implement hashCode()
      builder.publicMethod(int.class, "hashCode")
          .annotatedWith(OVERRIDE_ANNOTATION)
          .body()
          ._(
              If.isNull(loadVariable(proxyVar))
                  ._(throw_(IllegalStateException.class, "call to hashCode() on an unclosed proxy."))
                  .finish()
                  .else_()
                  ._(Stmt.loadVariable(proxyVar).invoke("hashCode").returnValue())
                  .finish()
          )
          .finish();

      // implements equals()
      builder.publicMethod(boolean.class, "equals", Parameter.of(Object.class, "o"))
          .annotatedWith(OVERRIDE_ANNOTATION)
          .body()
          ._(
              If.isNull(loadVariable(proxyVar))
                  ._(throw_(IllegalStateException.class, "call to equals() on an unclosed proxy."))
                  .finish()
                  .else_()
                  ._(Stmt.loadVariable(proxyVar).invoke("equals", Refs.get("o")).returnValue())
                  .finish()
          )
          .finish();
    }

    builder.publicMethod(void.class, PROXY_BIND_METHOD).parameters(DefParameters.of(Parameter.of(toProxy, "proxy")))
        ._(loadVariable(proxyVar).assignValue(loadVariable("proxy"))).finish();

    return builder.getClassDefinition();
  }
  @Override
  public Statement getBeanInstance(final InjectableInstance injectableInstance) {
    final MetaClass type;
    final MetaParameterizedType pType;

    switch (injectableInstance.getTaskType()) {
      case Type:
        return null;
      case PrivateField:
      case Field:
        final MetaField field = injectableInstance.getField();
        type = field.getType();

        pType = type.getParameterizedType();
        break;

      case Parameter:
        final MetaParameter parm = injectableInstance.getParm();
        type = parm.getType();

        pType = type.getParameterizedType();
        break;

      default:
        throw new RuntimeException("illegal task type: " + injectableInstance.getEnclosingType());
    }

    final MetaType[] typeArgs = pType.getTypeParameters();
    final MetaClass[] typeArgsClasses = new MetaClass[typeArgs.length];

    for (int i = 0; i < typeArgs.length; i++) {
      final MetaType argType = typeArgs[i];

      if (argType instanceof MetaClass) {
        typeArgsClasses[i] = (MetaClass) argType;
      } else if (argType instanceof MetaParameterizedType) {
        typeArgsClasses[i] = (MetaClass) ((MetaParameterizedType) argType).getRawType();
      }
    }

    final Annotation[] qualifiers = injectableInstance.getQualifiers();

    final BlockBuilder<?> block =
        injectableInstance.getInjectionContext().getProcessingContext().getBlockBuilder();
    final MetaClass providerCreationalCallback =
        MetaClassFactory.parameterizedAs(
            CreationalCallback.class,
            MetaClassFactory.typeParametersOf(providerInjector.getInjectedType()));

    final String varName =
        InjectUtil.getVarNameFromType(
            providerInjector.getConcreteInjectedType(), injectableInstance);

    final Statement valueRef;

    if (providerInjector.isSingleton() && providerInjector.isRendered()) {
      valueRef =
          Stmt.loadVariable("beanInstance")
              .invoke("provide", typeArgsClasses, qualifiers.length != 0 ? qualifiers : null);
    } else {

      valueRef = Stmt.load(null);
    }

    block.append(
        Stmt.declareFinalVariable(
            varName,
            providerCreationalCallback,
            Stmt.newObject(providerCreationalCallback)
                .extend()
                .publicOverridesMethod(
                    "callback", Parameter.of(providerInjector.getInjectedType(), "beanInstance"))
                .append(
                    Stmt.loadVariable(InjectUtil.getVarNameFromType(type, injectableInstance))
                        .invoke("callback", valueRef))
                .append(Stmt.loadVariable("async").invoke("finish", Refs.get("this")))
                .finish()
                .publicOverridesMethod("toString")
                .append(
                    Stmt.load(providerInjector.getInjectedType()).invoke("getName").returnValue())
                .finish()
                .finish()));

    block.append(Stmt.loadVariable("async").invoke("wait", Refs.get(varName)));

    block.append(
        Stmt.loadVariable(providerInjector.getCreationalCallbackVarName())
            .invoke("getInstance", Refs.get(varName), Refs.get("context")));

    return null;
  }
  private void appendPageShowMethod(
      AnonymousClassStructureBuilder pageImplBuilder,
      MetaClass pageClass,
      Class<? extends Annotation> annotation,
      boolean addPrivateAccessors) {
    BlockBuilder<?> method =
        pageImplBuilder
            .publicMethod(
                void.class,
                createMethodNameFromAnnotation(annotation),
                Parameter.of(pageClass, "widget"),
                Parameter.of(HistoryToken.class, "state"))
            .body();

    int idx = 0;

    method.append(Stmt.declareFinalVariable("pageState", Map.class, new HashMap<String, Object>()));
    for (MetaField field : pageClass.getFieldsAnnotatedWith(PageState.class)) {
      PageState psAnno = field.getAnnotation(PageState.class);
      String fieldName = field.getName();
      String queryParamName = psAnno.value();
      if (queryParamName == null || queryParamName.trim().isEmpty()) {
        queryParamName = fieldName;
      }

      if (addPrivateAccessors) {
        PrivateAccessUtil.addPrivateAccessStubs(
            PrivateAccessType.Write, "jsni", pageImplBuilder, field, new Modifier[] {});
      }

      String injectorName = PrivateAccessUtil.getPrivateFieldInjectorName(field);

      MetaClass erasedFieldType = field.getType().getErased();
      if (erasedFieldType.isAssignableTo(Collection.class)) {
        MetaClass elementType =
            MarshallingGenUtil.getConcreteCollectionElementType(field.getType());
        if (elementType == null) {
          throw new UnsupportedOperationException(
              "Found a @PageState field with a Collection type but without a concrete type parameter. "
                  + "Collection-typed @PageState fields must specify a concrete type parameter.");
        }
        if (erasedFieldType.equals(MetaClassFactory.get(Set.class))) {
          method.append(Stmt.declareVariable(fieldName, Stmt.newObject(HashSet.class)));
        } else if (erasedFieldType.equals(MetaClassFactory.get(List.class))
            || erasedFieldType.equals(MetaClassFactory.get(Collection.class))) {
          method.append(Stmt.declareVariable(fieldName, Stmt.newObject(ArrayList.class)));
        } else {
          throw new UnsupportedOperationException(
              "Found a @PageState field which is a collection of type "
                  + erasedFieldType.getFullyQualifiedName()
                  + ". For collection-valued fields, only the exact types java.util.Collection, java.util.Set, and "
                  + "java.util.List are supported at this time.");
        }

        // for (String fv{idx} : state.get({fieldName}))
        method.append(
            Stmt.loadVariable("state")
                .invoke("getState")
                .invoke("get", queryParamName)
                .foreach("elem", Object.class)
                .append(
                    Stmt.declareVariable(
                        "fv" + idx, Stmt.castTo(String.class, Stmt.loadVariable("elem"))))
                .append(
                    Stmt.loadVariable(fieldName)
                        .invoke(
                            "add",
                            paramFromStringStatement(elementType, Stmt.loadVariable("fv" + idx))))
                .append(
                    Stmt.loadVariable("pageState")
                        .invoke("put", fieldName, Stmt.loadVariable(fieldName)))
                .finish());
        method.append(
            Stmt.loadVariable("this")
                .invoke(injectorName, Stmt.loadVariable("widget"), Stmt.loadVariable(fieldName)));
      } else {
        method.append(
            Stmt.declareFinalVariable(
                "fv" + idx,
                Collection.class,
                Stmt.loadVariable("state").invoke("getState").invoke("get", queryParamName)));
        method.append(
            If.cond(
                    Bool.or(
                        Bool.isNull(Stmt.loadVariable("fv" + idx)),
                        Stmt.loadVariable("fv" + idx).invoke("isEmpty")))
                .append(
                    Stmt.loadVariable("this")
                        .invoke(
                            injectorName,
                            Stmt.loadVariable("widget"),
                            defaultValueStatement(erasedFieldType)))
                .finish()
                .else_()
                .append(
                    Stmt.loadVariable("this")
                        .invoke(
                            injectorName,
                            Stmt.loadVariable("widget"),
                            paramFromStringStatement(
                                erasedFieldType,
                                Stmt.loadVariable("fv" + idx).invoke("iterator").invoke("next"))))
                .append(
                    Stmt.loadVariable("pageState")
                        .invoke(
                            "put",
                            fieldName,
                            Stmt.loadVariable("fv" + idx).invoke("iterator").invoke("next")))
                .finish());
      }
      idx++;
    }

    if (addPrivateAccessors) {
      method.append(
          Stmt.invokeStatic(
              CDI.class,
              "fireEvent",
              Stmt.newObject(NavigationEvent.class)
                  .withParameters(
                      Stmt.newObject(PageRequest.class)
                          .withParameters(
                              getPageName(pageClass), Stmt.loadVariable("pageState")))));
    }

    checkMethodAndAddPrivateAccessors(
        pageImplBuilder, method, pageClass, annotation, HistoryToken.class, "state");

    method.finish();
  }
  /**
   * Searches the given class for methods bearing the given annotation. Verifies that such methods
   * follow the rules (returns void; takes PageState as a parameter (or not); is the only method
   * with this annotation), creates a private accessor if required, and then appends a call to that
   * method to the given method builder.
   *
   * @param pageImplBuilder the class to add private accessors to if necessary
   * @param methodToAppendTo the method builder to append to
   * @param pageClass the class to search for annotated methods in
   * @param annotation the annotation to search for in pageClass
   * @param optionalParamType the type of the single method parameter or null if no parameter
   * @param optionalParamName the name of the variable of the optional parameter or null if no
   *     parameter.
   * @return The meta-method for which code was generated
   * @throws UnsupportedOperationException if the annotated methods in pageClass violate any of the
   *     rules
   */
  private MetaMethod checkMethodAndAddPrivateAccessors(
      AnonymousClassStructureBuilder pageImplBuilder,
      BlockBuilder<?> methodToAppendTo,
      MetaClass pageClass,
      Class<? extends Annotation> annotation,
      Class<?> optionalParamType,
      String optionalParamName) {
    List<MetaMethod> annotatedMethods = pageClass.getMethodsAnnotatedWith(annotation);
    if (annotatedMethods.size() > 1) {
      throw new UnsupportedOperationException(
          "A @Page can have at most 1 "
              + createAnnotionName(annotation)
              + " method, but "
              + pageClass
              + " has "
              + annotatedMethods.size());
    }

    for (MetaMethod metaMethod : annotatedMethods) {
      if (!metaMethod.getReturnType().equals(MetaClassFactory.get(void.class))) {
        throw new UnsupportedOperationException(
            createAnnotionName(annotation)
                + " methods must have a void return type, but "
                + metaMethod.getDeclaringClass().getFullyQualifiedName()
                + "."
                + metaMethod.getName()
                + " returns "
                + metaMethod.getReturnType().getFullyQualifiedName());
      }

      Object[] paramValues = new Object[metaMethod.getParameters().length + 1];
      paramValues[0] = Stmt.loadVariable("widget");

      // assemble parameters for private method invoker (first param is the widget instance)
      PrivateAccessUtil.addPrivateAccessStubs(
          "jsni", pageImplBuilder, metaMethod, new Modifier[] {});

      if (optionalParamType != null) {
        for (int i = 1; i < paramValues.length; i++) {
          MetaParameter paramSpec = metaMethod.getParameters()[i - 1];
          if (paramSpec.getType().equals(MetaClassFactory.get(optionalParamType))) {
            paramValues[i] = Stmt.loadVariable(optionalParamName);
          } else {
            throw new UnsupportedOperationException(
                createAnnotionName(annotation)
                    + " method "
                    + metaMethod.getDeclaringClass().getFullyQualifiedName()
                    + "."
                    + metaMethod.getName()
                    + " has an illegal parameter of type "
                    + paramSpec.getType().getFullyQualifiedName());
          }
        }

      } else {
        if (metaMethod.getParameters().length != 0) {
          throw new UnsupportedOperationException(
              createAnnotionName(annotation)
                  + " methods cannot take parameters, but "
                  + metaMethod.getDeclaringClass().getFullyQualifiedName()
                  + "."
                  + metaMethod.getName()
                  + " does.");
        }
      }

      methodToAppendTo.append(
          Stmt.loadVariable("this")
              .invoke(PrivateAccessUtil.getPrivateMethodName(metaMethod), paramValues));

      return annotatedMethods.get(0);
    }

    return null;
  }