Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright 2013 JBoss, by Red Hat, Inc
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *    http://www.apache.org/licenses/LICENSE-2.0
   *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 package org.jboss.errai.ui.rebind;
 
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 
 
Generates the code required for org.jboss.errai.ui.shared.api.annotations.Templated classes.

Author(s):
Lincoln Baxter, III
 
   private static final String CONSTRUCTED_TEMPLATE_SET_KEY = "constructedTemplate";
 
   private static final Logger logger = LoggerFactory.getLogger(TemplatedCodeDecorator.class);
 
   public TemplatedCodeDecorator(final Class<TemplateddecoratesWith) {
     super(decoratesWith);
   }
 
   @Override
   public List<? extends StatementgenerateDecorator(final InjectableInstance<Templatedctx) {
    final MetaClass declaringClass = ctx.getEnclosingType();
    if (!declaringClass.isAssignableTo(Composite.class)) {
      throw new GenerationException("@Templated class [" + declaringClass.getFullyQualifiedName()
          + "] must extend base class [" + Composite.class.getName() + "].");
    }
    for (final MetaField field : declaringClass.getFields()) {
      if (field.isAnnotationPresent(DataField.class)) {
      }
    }
    final List<StatementinitStmts = new ArrayList<Statement>();
    /*
     * Do the work
     */
    generateTemplatedInitialization(ctxinitStmts);
    if (declaringClass.isAnnotationPresent(EntryPoint.class)) {
      initStmts.add(Stmt.invokeStatic(RootPanel.class"get").invoke("add", Refs.get("obj")));
    }
    List<Statementstmts = new ArrayList<Statement>();
    final Statement initCallback = InjectUtil.createInitializationCallback(declaringClass"obj"initStmts);
    stmts.add(Stmt.loadVariable("context").invoke("addInitializationCallback",
        Refs.get(ctx.getInjector().getInstanceVarName()), initCallback));
    Statement destructionCallback = generateTemplateDestruction(ctx);
    if (destructionCallback != null) {
      stmts.add(destructionCallback);
    }
    return stmts;
  }

  
Generates a org.jboss.errai.ioc.client.container.DestructionCallback for the org.jboss.errai.ui.shared.api.annotations.Templated component.

Returns:
statement representing the template destruction logic.
    List<StatementdestructionStatements = new ArrayList<Statement>();
    final Map<StringStatementdataFields = DataFieldCodeDecorator.aggregateDataFieldMap(ctxctx.getEnclosingType());
    final Map<StringMetaClassdataFieldTypes =
      DataFieldCodeDecorator.aggregateDataFieldTypeMap(ctxctx.getEnclosingType());
    for (final String fieldName : dataFields.keySet()) {
      Statement field = dataFields.get(fieldName);
      MetaClass fieldType = dataFieldTypes.get(fieldName);
      if (fieldType.isAssignableTo(Element.class)) {
        destructionStatements.add(Stmt.invokeStatic(ElementWrapperWidget.class"removeWidget"field));
      }
    }
    if (destructionStatements.isEmpty())
      return null;
    Statement destructionLogic =
      Stmt.loadVariable("context").invoke("addDestructionCallback",
        Refs.get(ctx.getInjector().getInstanceVarName()),
        InjectUtil.createDestructionCallback(ctx.getEnclosingType(), "obj"destructionStatements));
    return destructionLogic;
  }

  
Generate the actual construction logic for our org.jboss.errai.ui.shared.api.annotations.Templated component
  @SuppressWarnings("serial")
                                               final List<StatementinitStmts) {
    final Map<MetaClassBuildMetaClassconstructed = getConstructedTemplateTypes(ctx);
    final MetaClass declaringClass = ctx.getEnclosingType();
    if (!constructed.containsKey(declaringClass)) {
      /*
       * Generate this component's ClientBundle resource if necessary
       */
      generateTemplateResourceInterface(ctxdeclaringClass);
      /*
       * Instantiate the ClientBundle Template resource
       */
      final String templateVarName = InjectUtil.getUniqueVarName();
      initStmts.add(Stmt
          .declareVariable(getConstructedTemplateTypes(ctx).get(declaringClass))
          .named(templateVarName)
          .initializeWith(
              Stmt.invokeStatic(GWT.class"create"getConstructedTemplateTypes(ctx).get(declaringClass))));
      /*
       * Get root Template Element
       */
      final String rootTemplateElementVarName = InjectUtil.getUniqueVarName();
      initStmts.add(Stmt
          .declareVariable(Element.class)
          .named(rootTemplateElementVarName)
          .initializeWith(
              Stmt.invokeStatic(TemplateUtil.class"getRootTemplateElement", Stmt
                  .loadVariable(templateVarName).invoke("getContents").invoke("getText"),
                  getTemplateFragmentName(declaringClass))));
      final Statement rootTemplateElement = Stmt.loadVariable(rootTemplateElementVarName);
      /*
       * If i18n is enabled for this module, translate the root template element here
       */
      translateTemplate(ctxinitStmtsrootTemplateElement);
      /*
       * Get a reference to the actual Composite component being created
       */
      final Statement component = Refs.get(ctx.getInjector().getInstanceVarName());
      /*
       * Get all of the data-field Elements from the Template
       */
      final String dataFieldElementsVarName = InjectUtil.getUniqueVarName();
      initStmts.add(Stmt.declareVariable(dataFieldElementsVarName,
          new TypeLiteral<Map<StringElement>>() {},
          Stmt.invokeStatic(TemplateUtil.class"getDataFieldElements"rootTemplateElement))
      );
      /*
       * Attach Widget field children Elements to the Template DOM
       */
      final String fieldsMapVarName = InjectUtil.getUniqueVarName();
      /*
       * The Map<String, Widget> to store actual component field references.
       */
      initStmts.add(Stmt.declareVariable(fieldsMapVarNamenew TypeLiteral<Map<StringWidget>>() {},
          Stmt.newObject(new TypeLiteral<LinkedHashMap<StringWidget>>() {}))
      );
      final Statement fieldsMap = Stmt.loadVariable(fieldsMapVarName);
      generateComponentCompositions(ctxinitStmtscomponentrootTemplateElement,
          Stmt.loadVariable(dataFieldElementsVarName), fieldsMap);
      generateEventHandlerMethodClasses(ctxinitStmtscomponentdataFieldElementsVarNamefieldsMap);
    }
  }
                                                 final List<StatementinitStmtsfinal Statement component,
                                                 final String dataFieldElementsVarNamefinal Statement fieldsMap) {
    final Map<StringMetaClassdataFieldTypes = DataFieldCodeDecorator.aggregateDataFieldTypeMap(ctxctx.getEnclosingType());
    dataFieldTypes.put("this"ctx.getEnclosingType());
    final MetaClass declaringClass = ctx.getEnclosingType();
    /* Ensure that no @DataFields are handled more than once when used in combination with @SyncNative */
    final Set<StringprocessedNativeHandlers = new HashSet<String>();
    final Set<StringprocessedEventHandlers = new HashSet<String>();
    for (final MetaMethod method : declaringClass.getMethodsAnnotatedWith(EventHandler.class)) {
      final String[] targetDataFieldNames = method.getAnnotation(EventHandler.class).value();
      if (targetDataFieldNames.length == 0) {
        throw new GenerationException("@EventHandler annotation on method ["
            + declaringClass.getFullyQualifiedName()
            + "." + method.getName() + "] must specify at least one data-field target.");
      }
      final MetaClass eventType = (method.getParameters().length == 1) ? method.getParameters()[0].getType() : null;
      if (eventType == null || (!eventType.isAssignableTo(Event.class)) && !eventType.isAssignableTo(DomEvent.class)) {
        throw new GenerationException("@EventHandler method [" + method.getName() + "] in class ["
            + declaringClass.getFullyQualifiedName()
            + "] must have exactly one parameter of a type extending either ["
            + DomEvent.class.getName() + "] or [" + NativeEvent.class.getName() + "].");
      }
      if (eventType.isAssignableTo(Event.class)) {
        /*
         * Generate native DOM event handlers.
         */
        final MetaClass handlerType = MetaClassFactory.get(EventListener.class);
        final BlockBuilder<AnonymousClassStructureBuilderlistenerBuiler = ObjectBuilder.newInstanceOf(handlerType)
            .extend()
            .publicOverridesMethod(handlerType.getMethods()[0].getName(), Parameter.of(eventType"event"));
        listenerBuiler.append(InjectUtil.invokePublicOrPrivateMethod(ctx.getInjectionContext(), component,
            method, Stmt.loadVariable("event")));
        final ObjectBuilder listenerInstance = listenerBuiler.finish().finish();
        int eventsToSink =
        if (method.isAnnotationPresent(SinkNative.class)) {
          eventsToSink = method.getAnnotation(SinkNative.class).value();
        }
        for (final String name : targetDataFieldNames) {
          if (processedNativeHandlers.contains(name) || processedEventHandlers.contains(name)) {
            throw new GenerationException(
                "Cannot specify more than one @EventHandler method when @SyncNative is used for data-field ["
                    + name + "] in class ["
                    + declaringClass.getFullyQualifiedName()
                    + "].");
          }
          else {
            processedNativeHandlers.add(name);
          }
          if (dataFieldTypes.containsKey(name)) {
            final MetaClass dataFieldType = dataFieldTypes.get(name);
            if (!dataFieldType.isAssignableTo(Element.class)) {
              /*
               * We have a GWT or other Widget type.
               */
              throw new GenerationException("@DataField [" + name + "] of type [" + dataFieldType.getName()
                  + "] in class [" + declaringClass.getFullyQualifiedName() + "] is not assignable to ["
                  + Element.class.getName() + "] specified by @EventHandler method " + method.getName()
                  + "("
                  + eventType.getName() + ")]");
            }
            else {
              /*
               * We have a wrapped native Element reference
               */
              throw new GenerationException("Cannot attach native DOM events to @DataField [" + name
                  + "] of type ["
                  + dataFieldType.getName() + "] in class [" + declaringClass.getFullyQualifiedName()
                  + "] specified by @EventHandler method " + method.getName() + "(" + eventType.getName()
                  + ")] - Use the corresponding GWT 'EventHandler' types instead.");
            }
          }
          else {
            /*
             * We are completely native and have no reference to this data-field
             * Element in Java
             */
            initStmts.add(Stmt.invokeStatic(TemplateUtil.class"setupNativeEventListener"component,
                Stmt.loadVariable(dataFieldElementsVarName).invoke("get"name), listenerInstance,
                eventsToSink));
          }
        }
      }
      else {
        /*
         * We have a GWT Widget type
         */
        final MetaClass handlerType;
        try {
          handlerType = getHandlerForEvent(eventType);
        }
        catch (GenerationException e) {
          /*
           *  see ERRAI-373 for details on this crazy inference (without this message, the cause of the
           *  problem is nearly impossible to diagnose)
           */
          if (declaringClass.getClass() == JavaReflectionClass.class) {
            throw new GenerationException(
                "The type " + declaringClass.getFullyQualifiedName() + " looks like a client-side" +
                    " @Templated class, but it is not known to GWT. This probably means that " +
                    declaringClass.getName() + " or one of its supertypes contains non-translatable code." +
                    " Run the GWT compiler with logLevel=DEBUG to pinpoint the problem."e);
          }
          throw e;
        }
        final BlockBuilder<AnonymousClassStructureBuilderlistenerBuiler = ObjectBuilder.newInstanceOf(handlerType)
            .extend()
            .publicOverridesMethod(handlerType.getMethods()[0].getName(), Parameter.of(eventType"event"));
        listenerBuiler.append(InjectUtil.invokePublicOrPrivateMethod(ctx.getInjectionContext(),
            componentmethod, Stmt.loadVariable("event")));
        final ObjectBuilder listenerInstance = listenerBuiler.finish().finish();
        final MetaClass hasHandlerType = MetaClassFactory.get("com.google.gwt.event.dom.client.Has"
            + handlerType.getName()
            + "s");
        for (final String name : targetDataFieldNames) {
          final MetaClass dataFieldType = dataFieldTypes.get(name);
          if (dataFieldType == null) {
            throw new GenerationException("@EventHandler method [" + method.getName() + "] in class ["
                + declaringClass.getFullyQualifiedName()
                + "] handles a GWT event type but the specified @DataField [" + name + "] was not found.");
          }
          if (processedNativeHandlers.contains(name)) {
            throw new GenerationException(
                "Cannot specify more than one @EventHandler method when @SinkNative is used for data-field ["
                    + name + "] in class [" + declaringClass.getFullyQualifiedName()
                    + "].");
          }
          processedEventHandlers.add(name);
          // Where will the event come from? It could be a @DataField member, or it could be the templated widget itself!
          final Statement eventSource;
          if ("this".equals(name)) {
            eventSource = Stmt.loadVariable("obj");
          }
          else {
            eventSource = Stmt.nestedCall(fieldsMap).invoke("get"name);
          }
          if (dataFieldType.isAssignableTo(Element.class)) {
            initStmts.add(Stmt.invokeStatic(TemplateUtil.class"setupWrappedElementEventHandler"component,
                eventSourcelistenerInstance,
                Stmt.invokeStatic(eventType"getType")));
          }
          else if (dataFieldType.isAssignableTo(hasHandlerType)) {
            final Statement widget = Cast.to(hasHandlerTypeeventSource);
            initStmts.add(Stmt.nestedCall(widget).invoke("add" + handlerType.getName(),
                Cast.to(handlerTypelistenerInstance)));
          }
          else if (dataFieldType.isAssignableTo(Widget.class)) {
            final Statement widget = Cast.to(Widget.classeventSource);
            initStmts.add(Stmt.nestedCall(widget).invoke("addDomHandler",
                listenerInstance, Stmt.invokeStatic(eventType"getType")));
          }
          else {
            throw new GenerationException("@DataField [" + name + "] of type [" + dataFieldType.getName()
                + "] in class [" + declaringClass.getFullyQualifiedName()
                + "] does not implement required interface [" + hasHandlerType.getName()
                + "] specified by @EventHandler method " + method.getName() + "(" + eventType.getName()
                + ")]");
          }
        }
      }
    }
  }
  private MetaClass getHandlerForEvent(final MetaClass eventType) {
    /*
     * All handlers event must have an overrided method getAssociatedType(). We
     * take advantage of this information to get the associated handler. Ex:
     * com.google.gwt.event.dom.client.ClickEvent --->
     * com.google.gwt.event.dom.client.ClickHandler
     *
     * com.google.gwt.event.dom.client.BlurEvent --->
     * com.google.gwt.event.dom.client.BlurHandler
     */
    if (eventType == null) {
      return null;
    }
    MetaMethod method = eventType.getBestMatchingMethod("getAssociatedType"Type.class);
    if (method == null) {
      for (final MetaMethod m : eventType.getMethods()) {
        if ("getAssociatedType".equals(m.getName())) {
          method = m;
          break;
        }
      }
    }
    if (method == null) {
      throw new GenerationException("Method 'getAssociatedType()' could not be found in the event ["
          + eventType.getName() + "]");
    }
    final MetaType returnType = method.getGenericReturnType();
    if (returnType == null) {
      throw new GenerationException("The method 'getAssociatedType()' in the event [" + eventType.getName()
          + "] returns void.");
    }
    .debug("eventType: " + eventType.getClass() + " -- " + eventType);
    .debug("method: " + method.getClass() + " -- " + method);
    .debug("genericReturnType: " + returnType.getClass() + " -- " + returnType);
    if (!(returnType instanceof MetaParameterizedType)) {
      throw new GenerationException("The method 'getAssociatedType()' in the event [" + eventType.getName()
          + "] does not return Type<? extends EventHandler>..");
    }
    final MetaParameterizedType parameterizedType = (MetaParameterizedTypereturnType;
    .debug("parameterizedType: " + parameterizedType.getClass() + " -- " + parameterizedType);
    final MetaType[] argTypes = parameterizedType.getTypeParameters();
    if ((argTypes.length != 1) && argTypes[0] instanceof MetaClass
        && !((MetaClassargTypes[0]).isAssignableTo(EventHandler.class)) {
      throw new GenerationException("The method 'getAssociatedType()' in the event [" + eventType.getName()
          + "] does not return Type<? extends EventHandler>..");
    }
    return (MetaClassargTypes[0];
  }

  
Translates the template using the module's i18n message bundle (only if i18n is enabled for the module).

Parameters:
ctx
initStmts
rootTemplateElement
  private void translateTemplate(InjectableInstance<TemplatedctxList<StatementinitStmts,
          Statement rootTemplateElement) {
    initStmts.add(
            Stmt.invokeStatic(
                    TemplateUtil.class,
                    "translateTemplate",
                    getTemplateFileName(ctx.getEnclosingType()),
                    rootTemplateElement
                    ));
  }
                                             final List<StatementinitStmts,
                                             final Statement component,
                                             final Statement rootTemplateElement,
                                             final Statement dataFieldElements,
                                             final Statement fieldsMap) {
    /*
     * Merge each field's Widget Element into the DOM in place of the
     * corresponding data-field
     */
    final Map<StringStatementdataFields = DataFieldCodeDecorator.aggregateDataFieldMap(ctxctx.getEnclosingType());
    for (final Entry<StringStatementfield : dataFields.entrySet()) {
      initStmts.add(Stmt.invokeStatic(TemplateUtil.class"compositeComponentReplace"ctx.getEnclosingType()
          .getFullyQualifiedName(), getTemplateFileName(ctx.getEnclosingType()), Cast.to(Widget.classfield.getValue()),
          dataFieldElementsfield.getKey()));
    }
    /*
     * Add each field to the Collection of children of the new Composite
     * Template
     */
    for (final Entry<StringStatementfield : dataFields.entrySet()) {
      initStmts.add(Stmt.nestedCall(fieldsMap).invoke("put"field.getKey(), field.getValue()));
    }
    /*
     * Attach the Template to the Component, and set up the GWT Widget hierarchy
     * to preserve Handlers and DOM events.
     */
    initStmts.add(Stmt.invokeStatic(TemplateUtil.class"initWidget"componentrootTemplateElement,
        Stmt.nestedCall(fieldsMap).invoke("values")));
  }

  
Create an inner interface for the given org.jboss.errai.ui.shared.Template class and its HTML corresponding resource
  private void generateTemplateResourceInterface(final InjectableInstance<Templatedctxfinal MetaClass type) {
    final ClassStructureBuilder<?> componentTemplateResource = ClassBuilder.define(getTemplateTypeName(type)).publicScope()
        .body()
        .publicMethod(TextResource.class"getContents").annotatedWith(new Source() {
          @Override
          public Class<? extends AnnotationannotationType() {
            return Source.class;
          }
          @Override
          public String[] value() {
            return new String[]{getTemplateFileName(type)};
          }
        }).finish();
        .addInnerClass(new InnerClass(componentTemplateResource.getClassDefinition()));
    getConstructedTemplateTypes(ctx).put(typecomponentTemplateResource.getClassDefinition());
  }

  
Get a map of all previously constructed org.jboss.errai.ui.shared.Template object types
  @SuppressWarnings("unchecked")
    if (result == null) {
      result = new LinkedHashMap<MetaClassBuildMetaClass>();
    }
    return result;
  }
  /*
   * Non-generation utility methods.
   */

  
  private String getTemplateTypeName(final MetaClass type) {
    return type.getFullyQualifiedName().replaceAll("\\.""_") + "TemplateResource";
  }

  
Get the name of the org.jboss.errai.ui.shared.Template HTML file of the given org.jboss.errai.codegen.meta.MetaClass component type
  public static String getTemplateFileName(final MetaClass type) {
    String resource = type.getFullyQualifiedName().replaceAll("\\.""/") + ".html";
    if (type.isAnnotationPresent(Templated.class)) {
      final String source = canonicalizeTemplateSourceSyntax(typetype.getAnnotation(Templated.class).value());
      final Matcher matcher = Pattern.compile("^([^#]+)#?.*$").matcher(source);
      if (matcher.matches()) {
        resource = (matcher.group(1) == null ? resource : matcher.group(1));
        if (resource.matches("\\S+\\.html")) {
          if (resource.startsWith("/")) {
            resource = resource.substring(1);
          }
          else {
            resource = type.getPackageName().replaceAll("\\.""/") + "/" + resource;
          }
        }
      }
    }
    return resource;
  }

  
Get the name of the org.jboss.errai.ui.shared.Template HTML fragment (Element subtree) to be used as the template root of the given org.jboss.errai.codegen.meta.MetaClass component type
  public static String getTemplateFragmentName(final MetaClass type) {
    String fragment = "";
    if (type.isAnnotationPresent(Templated.class)) {
      final String source = canonicalizeTemplateSourceSyntax(typetype.getAnnotation(Templated.class).value());
      final Matcher matcher = Pattern.compile("^.*#([^#]+)$").matcher(source);
      if (matcher.matches()) {
        fragment = (matcher.group(1) == null ? fragment : matcher.group(1));
      }
    }
    return fragment;
  }

  
Throw an exception if the template source syntax is invalid
  private static String canonicalizeTemplateSourceSyntax(final MetaClass componentfinal String source) {
    final String result = Strings.nullToEmpty(source).trim();
    if (result.matches(".*#.*#.*")) {
      throw new IllegalArgumentException("Invalid syntax: @" + Templated.class.getSimpleName() + "(" + source
          + ") on component " + component.getFullyQualifiedName()
          + ". Multiple '#' found, where only one fragment is permitted.");
    }
    return result;
  }
New to GrepCode? Check out our FAQ X