Revision: 10345
Author:   rchan...@google.com
Date:     Thu Jun 16 09:38:05 2011
Log:      Re-roll issue 1442804: SafeHtmlRenderer code gen for UiBinder

Review at http://gwt-code-reviews.appspot.com/1453812

Review by: rj...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=10345

Added:
 /trunk/user/src/com/google/gwt/text/shared/UiRenderer.java
 /trunk/user/test/com/google/gwt/uibinder/test/client/UiRendererTest.java
 /trunk/user/test/com/google/gwt/uibinder/test/client/UiRendererTestApp.java
 /trunk/user/test/com/google/gwt/uibinder/test/client/UiRendererUi.css
 /trunk/user/test/com/google/gwt/uibinder/test/client/UiRendererUi.java
 /trunk/user/test/com/google/gwt/uibinder/test/client/UiRendererUi.ui.xml
Modified:
 /trunk/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml
/trunk/user/src/com/google/gwt/uibinder/elementparsers/CustomButtonParser.java
 /trunk/user/src/com/google/gwt/uibinder/elementparsers/DialogBoxParser.java
/trunk/user/src/com/google/gwt/uibinder/elementparsers/DomElementParser.java
 /trunk/user/src/com/google/gwt/uibinder/elementparsers/GridParser.java
 /trunk/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java
 /trunk/user/src/com/google/gwt/uibinder/elementparsers/HasHTMLParser.java
/trunk/user/src/com/google/gwt/uibinder/elementparsers/RenderablePanelParser.java /trunk/user/src/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParser.java /trunk/user/src/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParser.java
 /trunk/user/src/com/google/gwt/uibinder/elementparsers/TabPanelParser.java
 /trunk/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
 /trunk/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
 /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
 /trunk/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplate.java
 /trunk/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java
/trunk/user/test/com/google/gwt/uibinder/elementparsers/DialogBoxParserTest.java
 /trunk/user/test/com/google/gwt/uibinder/elementparsers/GridParserTest.java
/trunk/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java /trunk/user/test/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParserTest.java /trunk/user/test/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParserTest.java /trunk/user/test/com/google/gwt/uibinder/elementparsers/TabPanelParserTest.java
 /trunk/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java

=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/text/shared/UiRenderer.java Thu Jun 16 09:38:05 2011
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 Google 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 com.google.gwt.text.shared;
+
+/**
+ * Marker interface for SafeHtmlRenderer implementations to be code generated
+ * from ui.xml files.
+ *
+ * @param <T> the type to render
+ */
+public interface UiRenderer<T> extends SafeHtmlRenderer<T> {
+}
=======================================
--- /dev/null
+++ /trunk/user/test/com/google/gwt/uibinder/test/client/UiRendererTest.java Thu Jun 16 09:38:05 2011
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2011 Google 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 com.google.gwt.uibinder.test.client;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.LabelElement;
+import com.google.gwt.dom.client.Node;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.safehtml.shared.SafeHtml;
+
+/**
+ * Functional test of UiBinder.
+ */
+public class UiRendererTest extends GWTTestCase {
+  private UiRendererUi safeHtmlUi;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.uibinder.test.LazyWidgetBuilderSuite";
+  }
+
+  @Override
+  public void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    UiRendererTestApp app = UiRendererTestApp.getInstance();
+    safeHtmlUi = app.getSafeHtmlUi();
+  }
+
+  public void testSafeHtmlRendererText() {
+    SafeHtml render = safeHtmlUi.render();
+
+    LabelElement renderedHtml = Document.get().createLabelElement();
+    renderedHtml.setInnerHTML(render.asString());
+
+    Node innerDiv = renderedHtml.getFirstChild();
+
+    // Was the first span rendered as a "HTML-safe" text string?
+    Node spanWithConstantTextNode = innerDiv.getChild(0);
+ assertEquals("span", spanWithConstantTextNode.getNodeName().toLowerCase()); + assertEquals(Node.TEXT_NODE, spanWithConstantTextNode.getFirstChild().getNodeType());
+    assertEquals("<b>This text won't be bold!</b>",
+        spanWithConstantTextNode.getFirstChild().getNodeValue());
+
+    Node firstRawTextNode = innerDiv.getChild(1);
+    assertEquals(Node.TEXT_NODE, firstRawTextNode.getNodeType());
+    assertEquals(" Hello, ", firstRawTextNode.getNodeValue());
+
+    // Fields not present in owning class produce no content
+    Node firstFieldNode = innerDiv.getChild(2);
+    assertEquals(Node.ELEMENT_NODE, firstFieldNode.getNodeType());
+    assertEquals("span", firstFieldNode.getNodeName().toLowerCase());
+    assertFalse(firstFieldNode.hasChildNodes());
+
+    // ui:msg tags get rendered but the "<ui:msg>" tag is not
+    Node secondRawTextNode = innerDiv.getChild(3);
+    assertEquals(Node.TEXT_NODE, secondRawTextNode.getNodeType());
+    assertEquals(". How goes it? ", secondRawTextNode.getNodeValue());
+  }
+}
=======================================
--- /dev/null
+++ /trunk/user/test/com/google/gwt/uibinder/test/client/UiRendererTestApp.java Thu Jun 16 09:38:05 2011
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2011 Google 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 com.google.gwt.uibinder.test.client;
+
+import com.google.gwt.core.client.EntryPoint;
+
+/**
+ * Demonstration of templated UI. Used by UiRendererTest
+ */
+public class UiRendererTestApp implements EntryPoint {
+  private static UiRendererTestApp instance;
+
+  /**
+   * Ensure the singleton instance has installed its UI, and return it.
+   */
+  public static UiRendererTestApp getInstance() {
+    if (instance == null) {
+      setAndInitInstance(new UiRendererTestApp());
+    }
+
+    return instance;
+  }
+
+  private static void setAndInitInstance(UiRendererTestApp newInstance) {
+    instance = newInstance;
+    instance.safeHtmlUi = new UiRendererUi();
+  }
+
+  private UiRendererUi safeHtmlUi;
+
+  private UiRendererTestApp() {
+  }
+
+  public UiRendererUi getSafeHtmlUi() {
+    return safeHtmlUi;
+  }
+
+  /**
+   * Entry point method, called only when this is run as an application.
+   */
+  public void onModuleLoad() {
+    setAndInitInstance(this);
+  }
+}
=======================================
--- /dev/null
+++ /trunk/user/test/com/google/gwt/uibinder/test/client/UiRendererUi.css Thu Jun 16 09:38:05 2011
@@ -0,0 +1,8 @@
+.bodyColor {
+  color: indigo;
+}
+
+.bodyFont {
+  font-family: Helvetica, Arial, sans-serif;
+  font-size: small;
+}
=======================================
--- /dev/null
+++ /trunk/user/test/com/google/gwt/uibinder/test/client/UiRendererUi.java Thu Jun 16 09:38:05 2011
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 Google 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 com.google.gwt.uibinder.test.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.text.shared.UiRenderer;
+
+/**
+ * Sample use of a {@code SafeHtmlRenderer} with no dependency on
+ * com.google.gwt.user.
+ */
+public class UiRendererUi {
+  /**
+   * Resources for this template.
+   */
+  public interface Resources extends ClientBundle {
+    @Source("UiRendererUi.css")
+    Style style();
+  }
+
+  /**
+   * CSS for this template.
+   */
+  public interface Style extends CssResource {
+    String bodyColor();
+    String bodyFont();
+  }
+
+  interface HtmlRenderer extends UiRenderer<String> { }
+ private static final HtmlRenderer renderer = GWT.create(HtmlRenderer.class);
+
+  public UiRendererUi() {
+  }
+
+  public SafeHtml render() {
+    return renderer.render(null);
+  }
+}
=======================================
--- /dev/null
+++ /trunk/user/test/com/google/gwt/uibinder/test/client/UiRendererUi.ui.xml Thu Jun 16 09:38:05 2011
@@ -0,0 +1,47 @@
+<!-- --> +<!-- Copyright 2011 Google 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 --> +<!-- 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. License for the specific language governing permissions and --> +<!-- limitations under the License. -->
+<ui:UiBinder
+  xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:res='urn:with:com.google.gwt.uibinder.test.client.DomBasedUi.Resources'
+  >
+ <ui:with field="constants" type="com.google.gwt.uibinder.test.client.Constants"/>
+  <div ui:field='root' res:class="style.bodyColor style.bodyFont"
+      title="The title of this div is localizable">
+    <ui:attribute name='title'/>
+    <span><ui:text from="{constants.getText}" /></span>
+    Hello, <span ui:field="nameSpan" />.
+    <ui:msg>How goes it?</ui:msg>
+ <h2 res:class="style.bodyColor style.bodyFont">Placeholders in localizables</h2> + <p><ui:msg>When you mark up your text for translation, there will be bits
+      that you don't want the translators to mess with. You can protect
+      these with <span style="font-weight:bold"
+ ui:ph="boldSpan">placeholders</span><ui:ph name="tm"><sup ui:field="tmElement">TM</sup></ui:ph>.</ui:msg></p>
+  <table>
+    <col ui:field='narrowColumn' width='0%'></col>
+    <col width='100%'></col>
+    <tr ui:field='tr'>
+      <th ui:field='th1'>Tables with col elements</th>
+      <th ui:field='th2' align='left'>are</th>
+      <th ui:field='th3' align='left'>tricky</th>
+    </tr>
+  </table>
+  <table>
+    <tbody ui:field='tbody'>
+    <tr ui:field='tr2'>
+      <th ui:field='th4'>Tables with tbody elements too</th>
+    </tr>
+    </tbody>
+  </table>
+  </div>
+</ui:UiBinder>
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml Thu Jun 16 09:38:05 2011
@@ -33,6 +33,10 @@
<define-configuration-property name="UiBinder.useLazyWidgetBuilders" is-multi-valued="false"/> <set-configuration-property name="UiBinder.useLazyWidgetBuilders" value="false"/>

+  <generate-with class="com.google.gwt.uibinder.rebind.UiBinderGenerator">
+    <when-type-assignable class="com.google.gwt.text.shared.UiRenderer"/>
+  </generate-with>
+
   <generate-with class="com.google.gwt.uibinder.rebind.UiBinderGenerator">
     <when-type-assignable class="com.google.gwt.uibinder.client.UiBinder"/>
   </generate-with>
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/CustomButtonParser.java Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/CustomButtonParser.java Thu Jun 16 09:38:05 2011
@@ -69,7 +69,8 @@
         String innerHtml = child.consumeInnerHtml(interpreter);
         if (innerHtml.length() > 0) {
           writer.addStatement("%s.%s().setHTML(%s);", fieldName,
- faceNameGetter(faceName), writer.declareTemplateCall(innerHtml)); + faceNameGetter(faceName), writer.declareTemplateCall(innerHtml,
+                  fieldName));
         }

         if (child.hasAttribute("image")) {
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/DialogBoxParser.java Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/DialogBoxParser.java Thu Jun 16 09:38:05 2011
@@ -77,7 +77,8 @@
     handleConstructorArgs(elem, fieldName, type, writer, customCaption);

     if (caption != null) {
- writer.addStatement("%s.setHTML(%s);", fieldName, writer.declareTemplateCall(caption));
+      writer.addStatement("%s.setHTML(%s);", fieldName,
+          writer.declareTemplateCall(caption, fieldName));
     }
     if (body != null) {
       writer.addStatement("%s.setWidget(%s);", fieldName, body);
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/DomElementParser.java Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/DomElementParser.java Thu Jun 16 09:38:05 2011
@@ -42,6 +42,6 @@

     writer.setFieldInitializer(fieldName, String.format(
         "(%1$s) UiBinderUtil.fromHtml(%2$s)",
-        type.getQualifiedSourceName(), writer.declareTemplateCall(html)));
+ type.getQualifiedSourceName(), writer.declareTemplateCall(html, fieldName)));
   }
 }
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/GridParser.java Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/GridParser.java Thu Jun 16 09:38:05 2011
@@ -128,7 +128,7 @@
             writer.addStatement("%s.setHTML(%s, %s, %s);", fieldName,
                 Integer.toString(matrix.indexOf(row)),
                 Integer.toString(row.getColumns().indexOf(column)),
-                writer.declareTemplateCall(column.getContent()));
+ writer.declareTemplateCall(column.getContent(), fieldName));
           }
           if (column.getTagName().equals(CUSTOMCELL_TAG)) {
             writer.addStatement("%s.setWidget(%s, %s, %s);", fieldName,
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java Wed Jun 15 11:05:55 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java Thu Jun 16 09:38:05 2011
@@ -67,10 +67,11 @@
     String customTag = elem.consumeStringAttribute("tag", null);

     if (null == customTag) {
- writer.setFieldInitializerAsConstructor(fieldName, type, writer.declareTemplateCall(html));
+      writer.setFieldInitializerAsConstructor(fieldName, type,
+          writer.declareTemplateCall(html, fieldName));
     } else {
       writer.setFieldInitializerAsConstructor(fieldName, type, customTag,
-          writer.declareTemplateCall(html));
+          writer.declareTemplateCall(html, fieldName));
     }
   }

=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/HasHTMLParser.java Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/HasHTMLParser.java Thu Jun 16 09:38:05 2011
@@ -35,7 +35,8 @@
     writer.endAttachedSection();
     // TODO(jgw): throw an error if there's a conflicting 'html' attribute.
     if (html.trim().length() > 0) {
- writer.genPropertySet(fieldName, "HTML", writer.declareTemplateCall(html)); + writer.genPropertySet(fieldName, "HTML", writer.declareTemplateCall(html,
+          fieldName));
     }
   }
 }
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/RenderablePanelParser.java Wed Jun 15 06:24:57 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/RenderablePanelParser.java Thu Jun 16 09:38:05 2011
@@ -85,7 +85,8 @@
           "RenderablePanel does not support custom root elements yet.");
     }

- writer.setFieldInitializerAsConstructor(fieldName, type, writer.declareTemplateCall(html)); + writer.setFieldInitializerAsConstructor(fieldName, type, writer.declareTemplateCall(html,
+        fieldName));
   }

   /**
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParser.java Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParser.java Thu Jun 16 09:38:05 2011
@@ -76,7 +76,7 @@
String size = children.header.consumeRequiredDoubleAttribute("size");
         String html = children.header.consumeInnerHtml(htmlInt);
         writer.addStatement("%s.add(%s, %s, true, %s);", fieldName,
-            childFieldName, writer.declareTemplateCall(html), size);
+ childFieldName, writer.declareTemplateCall(html, fieldName), size);
       } else if (children.customHeader != null) {
XMLElement headerElement = children.customHeader.consumeSingleChildElement(); String size = children.customHeader.consumeRequiredDoubleAttribute("size");
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParser.java Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParser.java Thu Jun 16 09:38:05 2011
@@ -79,7 +79,7 @@
             writer, fieldName);
         String html = children.header.consumeInnerHtml(htmlInt);
         writer.addStatement("%s.add(%s, %s, true);", fieldName,
-            childFieldName, writer.declareTemplateCall(html));
+            childFieldName, writer.declareTemplateCall(html, fieldName));
       } else if (children.customHeader != null) {
XMLElement headerElement = children.customHeader.consumeSingleChildElement();

=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/TabPanelParser.java Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/TabPanelParser.java Thu Jun 16 09:38:05 2011
@@ -70,7 +70,7 @@
       }
       if (tabHTML != null) {
         writer.addStatement("%1$s.add(%2$s, %3$s, true);", fieldName,
-            childFieldName, writer.declareTemplateCall(tabHTML));
+ childFieldName, writer.declareTemplateCall(tabHTML, fieldName));
       } else if (tabCaption != null) {
writer.addStatement("%1$s.add(%2$s, %3$s);", fieldName, childFieldName,
             tabCaption);
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java Tue May 24 10:37:15 2011 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java Thu Jun 16 09:38:05 2011
@@ -60,6 +60,7 @@
   private int buildPrecedence;
   private final MortalLogger logger;
   private final FieldWriterType fieldType;
+  private String html;

public AbstractFieldWriter(String name, FieldWriterType fieldType, MortalLogger logger) {
     if (name == null) {
@@ -95,6 +96,10 @@
   public FieldWriterType getFieldType() {
     return fieldType;
   }
+
+  public String getHtml() {
+    return html + ".asString()";
+  }

   public String getInitializer() {
     return initializer;
@@ -114,6 +119,10 @@
     List<String> pathList = Arrays.asList(path).subList(1, path.length);
     return getReturnType(getAssignableType(), pathList, logger);
   }
+
+  public String getSafeHtml() {
+    return html;
+  }

   public void needs(FieldWriter f) {
     needs.add(f);
@@ -123,6 +132,10 @@
   public void setBuildPrecedence(int precedence) {
     this.buildPrecedence = precedence;
   }
+
+  public void setHtml(String html) {
+    this.html = html;
+  }

   public void setInitializer(String initializer) {
     this.initializer = initializer;
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java Thu Jun 16 09:38:05 2011
@@ -98,6 +98,11 @@
    */
   FieldWriterType getFieldType();

+  /**
+   * Returns the string html representation of the field.
+   */
+  String getHtml();
+
   /**
* Returns the custom initializer for this field, or null if it is not set.
    */
@@ -126,6 +131,11 @@
    */
   JType getReturnType(String[] path, MonitoredLogger logger);

+  /**
+   * Returns the string SafeHtml representation of the field.
+   */
+  String getSafeHtml();
+
   /**
    * Declares that the receiver depends upon the given field.
    */
@@ -139,6 +149,11 @@
    */
   void setBuildPrecedence(int precedence);

+  /**
+   * Sets the html representation of the field for applicable field types.
+   */
+  void setHtml(String html);
+
   /**
    * Used to provide an initializer string to use instead of a
* {@link com.google.gwt.core.client.GWT#create} call. Note that this is an
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java Mon Jun 6 06:47:21 2011 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java Thu Jun 16 09:38:05 2011
@@ -23,6 +23,7 @@
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dom.client.TagName;
 import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.text.shared.UiRenderer;
 import com.google.gwt.uibinder.attributeparsers.AttributeParser;
 import com.google.gwt.uibinder.attributeparsers.AttributeParsers;
 import com.google.gwt.uibinder.attributeparsers.BundleAttributeParser;
@@ -242,6 +243,7 @@
   private final UiBinderContext uiBinderCtx;

   private final String binderUri;
+  private final boolean isRenderer;

   public UiBinderWriter(JClassType baseClass, String implClassName,
       String templatePath, TypeOracle oracle, MortalLogger logger,
@@ -279,13 +281,42 @@
     JClassType uiBinderType = uiBinderTypes[0];

     JClassType[] typeArgs = uiBinderType.isParameterized().getTypeArgs();
-    if (typeArgs.length < 2) {
-      throw new RuntimeException(
-          "Root and owner type parameters are required for type %s"
-              + uiBinderType.getName());
-    }
-    uiRootType = typeArgs[0];
-    uiOwnerType = typeArgs[1];
+
+    String binderType = uiBinderType.getName();
+
+ JClassType uiRendererClass = getOracle().findType(UiRenderer.class.getName());
+    if (uiBinderType.isAssignableTo(uibinderItself)) {
+      if (typeArgs.length < 2) {
+        throw new RuntimeException(
+            "Root and owner type parameters are required for type %s"
+                + binderType);
+      }
+      uiRootType = typeArgs[0];
+      uiOwnerType = typeArgs[1];
+      isRenderer = false;
+    } else if (uiBinderType.isAssignableTo(uiRendererClass)) {
+      if (typeArgs.length < 1) {
+        throw new RuntimeException(
+            "Owner type parameter is required for type %s"
+                + binderType);
+      }
+      if (!useSafeHtmlTemplates) {
+        die("Configuration property UiBinder.useSafeHtmlTemplates\n"
+            + "  must be set to true to generate a UiRenderer");
+      }
+      if (!useLazyWidgetBuilders) {
+        die("Configuration property UiBinder.useLazyWidgetBuilders\n"
+            + "  must be set to true to generate a UiRenderer");
+      }
+
+      uiOwnerType = typeArgs[0];
+      uiRootType = null;
+      isRenderer = true;
+    } else {
+      die(baseClass.getName() + " must implement UiBinder or UiRenderer");
+ // This is unreachable in practice, but silences not initialized errors
+      throw new UnableToCompleteException();
+    }

isRenderableClassType = oracle.findType(IsRenderable.class.getCanonicalName()); lazyDomElementClass = oracle.findType(LazyDomElement.class.getCanonicalName());
@@ -485,13 +516,14 @@
* @return The invocation of the SafeHtml template function with the arguments
    * filled in
    */
-  public String declareTemplateCall(String html)
+  public String declareTemplateCall(String html, String fieldName)
     throws IllegalArgumentException {
     if (!useSafeHtmlTemplates) {
       return '"' + html + '"';
     }
-
-    return htmlTemplates.addSafeHtmlTemplate(html, tokenator);
+    FieldWriter w = fieldManager.lookup(fieldName);
+    w.setHtml(htmlTemplates.addSafeHtmlTemplate(html, tokenator));
+    return w.getHtml();
   }

   /**
@@ -940,11 +972,6 @@
    */
   void parseDocument(Document doc, PrintWriter printWriter)
       throws UnableToCompleteException {
- JClassType uiBinderClass = getOracle().findType(UiBinder.class.getName());
-    if (!baseClass.isAssignableTo(uiBinderClass)) {
-      die(baseClass.getName() + " must implement UiBinder");
-    }
-
     Element documentElement = doc.getDocumentElement();
     gwtPrefix = documentElement.lookupPrefix(binderUri);

@@ -1201,7 +1228,9 @@
     IndentedWriter niceWriter = new IndentedWriter(
         new PrintWriter(stringWriter));

-    if (useLazyWidgetBuilders) {
+    if (isRenderer) {
+      writeRenderer(niceWriter, rootField);
+    } else if (useLazyWidgetBuilders) {
       for (ImplicitCssResource css : bundleClass.getCssMethods()) {
         String fieldName = css.getName();
         FieldWriter cssField = fieldManager.require(fieldName);
@@ -1211,7 +1240,6 @@
     } else {
       writeBinder(niceWriter, rootField);
     }
-
     ensureAttachmentCleanedUp();
     return stringWriter.toString();
   }
@@ -1408,10 +1436,16 @@
   }

   private void writeClassOpen(IndentedWriter w) {
- w.write("public class %s implements UiBinder<%s, %s>, %s {", implClassName,
-        uiRootType.getParameterizedQualifiedSourceName(),
-        uiOwnerType.getParameterizedQualifiedSourceName(),
-        baseClass.getParameterizedQualifiedSourceName());
+    if (!isRenderer) {
+ w.write("public class %s implements UiBinder<%s, %s>, %s {", implClassName,
+          uiRootType.getParameterizedQualifiedSourceName(),
+          uiOwnerType.getParameterizedQualifiedSourceName(),
+          baseClass.getParameterizedQualifiedSourceName());
+    } else {
+ w.write("public class %s extends AbstractSafeHtmlRenderer<%s> implements %s {", implClassName,
+          uiOwnerType.getParameterizedQualifiedSourceName(),
+          baseClass.getParameterizedQualifiedSourceName());
+    }
     w.indent();
   }

@@ -1455,7 +1489,6 @@
       }
     }

-    // Write gwt field declarations.
fieldManager.writeGwtFieldsDeclaration(niceWriter, uiOwnerType.getName());
   }

@@ -1473,11 +1506,18 @@
       w.write("import com.google.gwt.safehtml.client.SafeHtmlTemplates;");
       w.write("import com.google.gwt.safehtml.shared.SafeHtml;");
       w.write("import com.google.gwt.safehtml.shared.SafeHtmlUtils;");
-    }
-    w.write("import com.google.gwt.uibinder.client.UiBinder;");
-    w.write("import com.google.gwt.uibinder.client.UiBinderUtil;");
-    w.write("import %s.%s;", uiRootType.getPackage().getName(),
-        uiRootType.getName());
+      w.write("import com.google.gwt.safehtml.shared.SafeHtmlBuilder;");
+      w.write("import com.google.gwt.uibinder.client.UiBinderUtil;");
+    }
+
+    if (!isRenderer) {
+      w.write("import com.google.gwt.uibinder.client.UiBinder;");
+      w.write("import com.google.gwt.uibinder.client.UiBinderUtil;");
+      w.write("import %s.%s;", uiRootType.getPackage().getName(),
+          uiRootType.getName());
+    } else {
+ w.write("import com.google.gwt.text.shared.AbstractSafeHtmlRenderer;");
+    }
   }

   /**
@@ -1538,6 +1578,76 @@
       w.newline();
     }
   }
+
+  /**
+   * Writes the SafeHtmlRenderer's source for the renderable
+   * strategy.
+   */
+  private void writeRenderer(
+ IndentedWriter w, String rootField) throws UnableToCompleteException {
+    writePackage(w);
+
+    writeImports(w);
+    w.newline();
+
+    writeClassOpen(w);
+    writeStatics(w);
+    w.newline();
+
+    // Create SafeHtml Template
+    writeSafeHtmlTemplates(w);
+
+    w.newline();
+
+    w.write("public SafeHtml render(final %s owner) {",
+        uiOwnerType.getParameterizedQualifiedSourceName());
+    w.indent();
+    w.newline();
+
+    w.write("return new Widgets(owner).getSafeHtml();");
+    w.outdent();
+
+    w.write("}");
+    w.newline();
+
+    // Writes the inner class Widgets.
+    w.newline();
+    w.write("/**");
+    w.write(" * Encapsulates the access to all inner widgets");
+    w.write(" */");
+    w.write("class Widgets {");
+    w.indent();
+
+ String ownerClassType = uiOwnerType.getParameterizedQualifiedSourceName();
+    w.write("private final %s owner;", ownerClassType);
+    w.newline();
+
+    w.write("public Widgets(final %s owner) {", ownerClassType);
+    w.indent();
+    w.write("this.owner = owner;");
+    fieldManager.initializeWidgetsInnerClass(w, getOwnerClass());
+    w.outdent();
+    w.write("}");
+    w.newline();
+
+    w.write("public SafeHtml getSafeHtml() {");
+    w.indent();
+    // TODO Find a better way to get the root field name
+ String safeHtml = fieldManager.lookup(rootField.substring(4, rootField.length() - 2)).getSafeHtml();
+    w.write("return %s;", safeHtml);
+    w.outdent();
+    w.write("}");
+
+    fieldManager.writeFieldDefinitions(
+        w, getOracle(), getOwnerClass(), getDesignTime());
+
+    w.outdent();
+    w.write("}");
+
+    // Close class
+    w.outdent();
+    w.write("}");
+  }

   /**
* Write statements created by {@link HtmlTemplates#addSafeHtmlTemplate}. This
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplate.java Tue May 24 07:34:42 2011 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplate.java Thu Jun 16 09:38:05 2011
@@ -77,7 +77,7 @@
    */
   public String writeTemplateCall() {
     return "template." + methodName + "(" + getSafeHtmlArgs()
-      + ").asString()";
+      + ")";
   }

   /**
=======================================
--- /trunk/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java Wed May 4 09:28:35 2011 +++ /trunk/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java Thu Jun 16 09:38:05 2011
@@ -17,6 +17,7 @@

 import com.google.gwt.junit.tools.GWTTestSuite;
 import com.google.gwt.uibinder.test.client.SafeHtmlAsComponentsTest;
+import com.google.gwt.uibinder.test.client.UiRendererTest;

 import junit.framework.Test;

@@ -29,6 +30,7 @@
         "Tests that rely on the useLazyWidgetBuilders switch");

     suite.addTestSuite(SafeHtmlAsComponentsTest.class);
+    suite.addTestSuite(UiRendererTest.class);

     return suite;
   }
=======================================
--- /trunk/user/test/com/google/gwt/uibinder/elementparsers/DialogBoxParserTest.java Tue May 24 07:34:42 2011 +++ /trunk/user/test/com/google/gwt/uibinder/elementparsers/DialogBoxParserTest.java Thu Jun 16 09:38:05 2011
@@ -77,7 +77,8 @@
     b.append("</g:DialogBox> ");

     String[] expected = {
-        "fieldName.setHTML(\"@mockToken-Hello, I <b>caption</b>you.\");",
+        "fieldName.setHTML(\"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-Hello, I <b>caption</b>you.\");",
         "fieldName.setWidget(<g:Label>);",};

     FieldWriter w = tester.parse(b.toString());
@@ -342,7 +343,8 @@
     b.append("</ui:UiBinder>");

     String[] expected = {
-        "fieldName.setHTML(\"@mockToken-Hello, I <b>caption</b>you.\");",
+        "fieldName.setHTML(\"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-Hello, I <b>caption</b>you.\");",
         "fieldName.setWidget(<g:Label>);",};

parser.parse(tester.getElem(b.toString(), "my:MyDialogBox"), "fieldName",
=======================================
--- /trunk/user/test/com/google/gwt/uibinder/elementparsers/GridParserTest.java Tue May 24 07:34:42 2011 +++ /trunk/user/test/com/google/gwt/uibinder/elementparsers/GridParserTest.java Thu Jun 16 09:38:05 2011
@@ -132,12 +132,16 @@

     String[] expected = {"fieldName.resize(2, 2);",
         "fieldName.getRowFormatter().setStyleName(0, \"rowHeaderStyle\");",
-        "fieldName.setHTML(0, 0, \"@mockToken-foo\");",
+ "fieldName.setHTML(0, 0, \"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-foo\");",
"fieldName.getCellFormatter().setStyleName(0, 0, \"headerStyle\");",
-        "fieldName.setHTML(0, 1, \"@mockToken-bar\");",
+ "fieldName.setHTML(0, 1, \"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-bar\");",
"fieldName.getCellFormatter().setStyleName(0, 1, \"headerStyle\");",
-        "fieldName.setHTML(1, 0, \"@mockToken-foo\");",
-        "fieldName.setHTML(1, 1, \"@mockToken-bar\");"};
+ "fieldName.setHTML(1, 0, \"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-foo\");",
+ "fieldName.setHTML(1, 1, \"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-bar\");"};

     FieldWriter w = tester.parse(b.toString());

@@ -172,8 +176,10 @@

     String[] expected = {
         "fieldName.resize(2, 2);",
- "fieldName.setHTML(0, 0, \"@mockToken-<div>foo HTML element</div>\");", - "fieldName.setHTML(0, 1, \"@mockToken-<div>bar HTML element</div>\");", + "fieldName.setHTML(0, 0, \"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-<div>foo HTML element</div>\");",
+ "fieldName.setHTML(0, 1, \"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-<div>bar HTML element</div>\");",
         "fieldName.setWidget(1, 0, <g:Label>);",
         "fieldName.setWidget(1, 1, <g:Label>);"};

@@ -209,7 +215,8 @@

     String[] expected = {
         "fieldName.resize(2, 2);",
- "fieldName.setHTML(0, 0, \"@mockToken-<div>foo HTML element</div>\");", + "fieldName.setHTML(0, 0, \"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-<div>foo HTML element</div>\");",
         "fieldName.setWidget(1, 0, <g:Label>);",
         "fieldName.setWidget(1, 1, <g:Label>);"};

=======================================
--- /trunk/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java Tue May 24 10:01:44 2011 +++ /trunk/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java Thu Jun 16 09:38:05 2011
@@ -45,11 +45,12 @@
   }

   /**
-   * Mocked out version of the template declaration. Returns the template
-   * prefixed with "@mockToken-"
+ * Mocked out version of the template declaration. Returns the fieldName and
+   * template separated with a dash, all prefixed with "@mockToken-"
    */
-  public String declareTemplateCall(String html) {
-    return "\"@mockToken-" + html + "\"";
+  @Override
+  public String declareTemplateCall(String html, String fieldName) {
+    return "\"@mockToken-" + fieldName + "-" + html + "\"";
   }

   @Override
=======================================
--- /trunk/user/test/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParserTest.java Tue May 24 07:34:42 2011 +++ /trunk/user/test/com/google/gwt/uibinder/elementparsers/StackLayoutPanelParserTest.java Thu Jun 16 09:38:05 2011
@@ -165,7 +165,8 @@
         + "(com.google.gwt.dom.client.Style.Unit.PX)", w.getInitializer());

     assertStatements(
- "fieldName.add(<g:Label id='able'>, \"@mockToken-Re<b>mark</b>able\", true, 3);", + "fieldName.add(<g:Label id='able'>, \"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-Re<b>mark</b>able\", true, 3);",
"fieldName.add(<g:Label id='baker'>, " + "<g:Label id='custom'>, 3);");
   }

=======================================
--- /trunk/user/test/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParserTest.java Tue May 24 07:34:42 2011 +++ /trunk/user/test/com/google/gwt/uibinder/elementparsers/TabLayoutPanelParserTest.java Thu Jun 16 09:38:05 2011
@@ -168,7 +168,8 @@
     b.append("</g:TabLayoutPanel>");

     String[] expected = {
- "fieldName.add(<g:Label id='able'>, \"@mockToken-Re<b>mark</b>able\", true);", + "fieldName.add(<g:Label id='able'>, \"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-Re<b>mark</b>able\", true);",
"fieldName.add(<g:Label id='baker'>, " + "<g:Label id='custom'>);",};

     FieldWriter w = tester.parse(b.toString());
=======================================
--- /trunk/user/test/com/google/gwt/uibinder/elementparsers/TabPanelParserTest.java Tue May 24 07:34:42 2011 +++ /trunk/user/test/com/google/gwt/uibinder/elementparsers/TabPanelParserTest.java Thu Jun 16 09:38:05 2011
@@ -146,7 +146,8 @@
     tester.parse(b.toString());

     assertStatements("fieldName.add(<g:Label id='0'>, \"Foo\");",
- "fieldName.add(<g:Label id='1'>, \"@mockToken-B<b>a</b>r\", true);"); + "fieldName.add(<g:Label id='1'>, \"@mockToken-" + ElementParserTester.FIELD_NAME
+            + "-B<b>a</b>r\", true);");
   }

   private void assertStatements(String... expected) {
=======================================
--- /trunk/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java Wed May 25 09:31:31 2011 +++ /trunk/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java Thu Jun 16 09:38:05 2011
@@ -22,8 +22,8 @@
 import com.google.gwt.dom.client.SpanElement;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.ImageResource;
 import com.google.gwt.resources.client.CssResource.NotStrict;
+import com.google.gwt.resources.client.ImageResource;
 import com.google.gwt.uibinder.test.client.EnumeratedLabel.Suffix;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.ui.AbsolutePanel;

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to