Add ability to include SafeHtml objects in dom based UI's if the laay widget option is being used (this is the only way that the setters will work correctly)

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

Review by: rjrjr@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10100 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java
index 562c11e..bc57c3c 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java
@@ -16,6 +16,7 @@
 package com.google.gwt.uibinder.elementparsers;
 
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.safehtml.shared.SafeHtml;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 import com.google.gwt.uibinder.rebind.XMLElement.Interpreter;
@@ -84,6 +85,11 @@
     if (writer.isWidgetElement(elem)) {
       writer.die(elem, "Found widget in an HTML context");
     }
+    if (writer.useLazyWidgetBuilders() &&
+        writer.isSubclassOf(elem, SafeHtml.class)) {
+      String childFieldName = writer.parseElementToField(elem);
+      return writer.tokenForExpression(childFieldName);
+    }
     return pipe.interpretElement(elem);
   }
 }
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index 6ca0cb8..d0572f8 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -39,6 +39,7 @@
 import com.google.gwt.uibinder.rebind.model.OwnerClass;
 import com.google.gwt.uibinder.rebind.model.OwnerField;
 import com.google.gwt.user.client.ui.Attachable;
+import com.google.gwt.user.client.ui.Widget;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -557,7 +558,7 @@
       throws UnableToCompleteException {
     String tagName = elem.getLocalName();
 
-    if (!isWidgetElement(elem)) {
+    if (!isImportedElement(elem)) {
       return findDomElementTypeForTag(tagName);
     }
 
@@ -673,9 +674,20 @@
     return uri != null && UiBinderGenerator.BINDER_URI.equals(uri);
   }
 
-  public boolean isWidgetElement(XMLElement elem) {
+  public boolean isSubclassOf(XMLElement elem, Class<?> clazz)
+      throws UnableToCompleteException {
     String uri = elem.getNamespaceUri();
-    return uri != null && uri.startsWith(PACKAGE_URI_SCHEME);
+    JClassType t = findFieldType(elem);
+    if (t == null) {
+      return false;
+    }
+    JClassType widgetType = oracle.findType(clazz.getCanonicalName());
+    return t.isAssignableTo(widgetType);
+  }
+
+  public boolean isWidgetElement(XMLElement elem)
+      throws UnableToCompleteException {
+    return isSubclassOf(elem, Widget.class);
   }
 
   /**
@@ -1024,6 +1036,11 @@
     return parsers;
   }
 
+  private boolean isImportedElement(XMLElement elem) {
+    String uri = elem.getNamespaceUri();
+    return uri != null && uri.startsWith(PACKAGE_URI_SCHEME);  
+  }
+
   /**
    * Writes a field setter if the field is not provided and the field class is
    * compatible with its respective template field.
diff --git a/user/test/com/google/gwt/uibinder/UiBinderGwtSuite.java b/user/test/com/google/gwt/uibinder/UiBinderGwtSuite.java
index e251075..b45ba38 100644
--- a/user/test/com/google/gwt/uibinder/UiBinderGwtSuite.java
+++ b/user/test/com/google/gwt/uibinder/UiBinderGwtSuite.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2009 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
@@ -18,6 +18,7 @@
 import com.google.gwt.junit.tools.GWTTestSuite;
 import com.google.gwt.uibinder.client.UiBinderUtilTest;
 import com.google.gwt.uibinder.test.client.InnerWidgetTest;
+import com.google.gwt.uibinder.test.client.LazyWidgetBuildersTest;
 import com.google.gwt.uibinder.test.client.TestParameterizedWidgets;
 import com.google.gwt.uibinder.test.client.UiBinderTest;
 
@@ -35,6 +36,7 @@
     suite.addTestSuite(UiBinderUtilTest.class);
     suite.addTestSuite(TestParameterizedWidgets.class);
     suite.addTestSuite(InnerWidgetTest.class);
+    suite.addTestSuite(LazyWidgetBuildersTest.class);
 
     return suite;
   }
diff --git a/user/test/com/google/gwt/uibinder/elementparsers/AbsolutePanelParserTest.java b/user/test/com/google/gwt/uibinder/elementparsers/AbsolutePanelParserTest.java
index 2e981f7..8cdbabd 100644
--- a/user/test/com/google/gwt/uibinder/elementparsers/AbsolutePanelParserTest.java
+++ b/user/test/com/google/gwt/uibinder/elementparsers/AbsolutePanelParserTest.java
@@ -95,7 +95,7 @@
     b.append("  <g:at left='1' top='2'>");
     b.append("    <g:Button/>");
     b.append("  </g:at>");
-    b.append("  <g:TextBox/>");
+    b.append("  <g:Label/>");
     b.append("  <g:at left='10' top='20'>");
     b.append("    <g:Label/>");
     b.append("  </g:at>");
@@ -104,7 +104,7 @@
     tester.parse(b.toString());
 
     assertStatements("fieldName.add(<g:Button>, 1, 2);",
-        "fieldName.add(<g:TextBox>);", "fieldName.add(<g:Label>, 10, 20);");
+        "fieldName.add(<g:Label>);", "fieldName.add(<g:Label>, 10, 20);");
   }
 
   private void assertStatements(String... expected) {
diff --git a/user/test/com/google/gwt/uibinder/test/LazyWidgetBuilderTest.gwt.xml b/user/test/com/google/gwt/uibinder/test/LazyWidgetBuilderTest.gwt.xml
new file mode 100644
index 0000000..87eac8b
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/LazyWidgetBuilderTest.gwt.xml
@@ -0,0 +1,19 @@
+<!-- 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.                                         -->
+
+<!-- GWT UiBinder support.                                                  -->
+<module>
+  <inherits name="com.google.gwt.user.User" />
+  <inherits name="com.google.gwt.debug.Debug"/>
+  <set-configuration-property name="UiBinder.useLazyWidgetBuilders" value="true"/>
+</module>
diff --git a/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuildersTest.DomBasedUi.ui.xml b/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuildersTest.DomBasedUi.ui.xml
new file mode 100644
index 0000000..467f090
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuildersTest.DomBasedUi.ui.xml
@@ -0,0 +1,21 @@
+<!--                                                                        -->
+<!-- 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:demo='urn:import:com.google.gwt.uibinder.test.client'
+  >
+  <div>
+    <div ui:field='div'><demo:SafeHtmlObject ui:field='safeObject' name="Bob" /></div>
+  </div>
+</ui:UiBinder>
diff --git a/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuildersTest.java b/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuildersTest.java
new file mode 100644
index 0000000..1b65ba7
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuildersTest.java
@@ -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.dom.client.DivElement;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+
+/**
+ * Tests functionality guarded by UiBinder.useLazyWidgetBuilders.
+ */
+public class LazyWidgetBuildersTest extends GWTTestCase {
+  static class DomBasedUi {
+    interface Binder extends UiBinder<Element, DomBasedUi> {
+    }
+
+    static final Binder binder = GWT.create(Binder.class);
+
+    @UiField DivElement div;
+    @UiField SafeHtmlObject safeObject;
+
+    DomBasedUi() {
+      binder.createAndBindUi(this);
+    }
+  }
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.uibinder.test.LazyWidgetBuilderTest";
+  }
+
+  public void testSafeHtml() {
+    DomBasedUi domUi = new DomBasedUi();
+    assertNotNull(domUi.safeObject);
+    assertEquals(domUi.safeObject.asString(), domUi.div.getInnerHTML());
+    assertEquals("Hello <b>Bob</b>".toLowerCase(), domUi.div.getInnerHTML().toLowerCase());
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/SafeHtmlObject.java b/user/test/com/google/gwt/uibinder/test/client/SafeHtmlObject.java
new file mode 100644
index 0000000..4f9b0d2
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/SafeHtmlObject.java
@@ -0,0 +1,35 @@
+/*
+ * 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.safehtml.shared.SafeHtml;
+
+/**
+ * Used by UiBinderTest.
+ */
+public class SafeHtmlObject implements SafeHtml {
+  private String name = "unset";
+
+  @Override
+  public String asString() {
+    return "Hello <b>" + name + "</b>";
+  }
+
+  public void setName(String name) {
+    // Bad - should escape this name
+    this.name = name;
+  }
+}