Provides an integration test for IsRenderable

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


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10540 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/IsRenderable.java b/user/src/com/google/gwt/user/client/ui/IsRenderable.java
index d4bf0fe..b7b065e 100644
--- a/user/src/com/google/gwt/user/client/ui/IsRenderable.java
+++ b/user/src/com/google/gwt/user/client/ui/IsRenderable.java
@@ -31,7 +31,8 @@
 
   /**
    * Replace the previous contents of the receiver with the given element,
-   * presumed to have been created via a previous call to {@link #render}.
+   * presumed to have been created and stamped via a previous call to
+   * {@link #render}.
    */
   void claimElement(Element element);
 
diff --git a/user/src/com/google/gwt/user/client/ui/RenderableStamper.java b/user/src/com/google/gwt/user/client/ui/RenderableStamper.java
index 3393271..f22a72e 100644
--- a/user/src/com/google/gwt/user/client/ui/RenderableStamper.java
+++ b/user/src/com/google/gwt/user/client/ui/RenderableStamper.java
@@ -89,8 +89,4 @@
     elementBuilder.id(token);
     return elementBuilder;
   }
-
-  private String getToken() {
-    return token;
-  }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/UIObject.java b/user/src/com/google/gwt/user/client/ui/UIObject.java
index b6b9ec1..10985d3 100644
--- a/user/src/com/google/gwt/user/client/ui/UIObject.java
+++ b/user/src/com/google/gwt/user/client/ui/UIObject.java
@@ -851,7 +851,7 @@
    * <p>
    * Note that this method is normally called only on the top element
    * of an IsRenderable tree. Children instead will receive {@link
-   * IsRenderable#render} and {@link IsRenderable#wrap}.
+   * IsRenderable#render} and {@link IsRenderable#claimElement(Element)}.
    *
    * @see PotentialElement
    * @see IsRenderable
diff --git a/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java b/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java
index a5f1303..23342b5 100644
--- a/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java
+++ b/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java
@@ -16,6 +16,7 @@
 package com.google.gwt.uibinder;
 
 import com.google.gwt.junit.tools.GWTTestSuite;
+import com.google.gwt.uibinder.test.client.IsRenderableIntegrationTest;
 import com.google.gwt.uibinder.test.client.SafeHtmlAsComponentsTest;
 import com.google.gwt.uibinder.test.client.UiRendererTest;
 
@@ -29,6 +30,7 @@
     GWTTestSuite suite = new GWTTestSuite(
         "Tests that rely on the useLazyWidgetBuilders switch");
 
+    suite.addTestSuite(IsRenderableIntegrationTest.class);
     suite.addTestSuite(SafeHtmlAsComponentsTest.class);
     suite.addTestSuite(UiRendererTest.class);
 
diff --git a/user/test/com/google/gwt/uibinder/test/client/IsRenderableIntegrationTest.Deep.ui.xml b/user/test/com/google/gwt/uibinder/test/client/IsRenderableIntegrationTest.Deep.ui.xml
new file mode 100644
index 0000000..90df8ab
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/IsRenderableIntegrationTest.Deep.ui.xml
@@ -0,0 +1,45 @@
+<!--                                                                        -->
+<!-- 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:gwt="urn:import:com.google.gwt.user.client.ui"
+  xmlns:test="urn:import:com.google.gwt.uibinder.test.client"
+  >
+  <gwt:RenderablePanel ui:field="top" styleName="test">
+    <div ui:field="outerDiv" class="ble">Outer div</div>
+    <test:SimpleRenderable ui:field="outerRenderable">Outer renderable</test:SimpleRenderable>
+    <gwt:Label ui:field="outerLabel">Outer label</gwt:Label>
+    <test:RenderableComposite ui:field="outerComposite"/>
+
+    <ui:msg description="A photo promo">
+      Outer i18n image
+      <gwt:Image ui:field="outerI18nImage" styleName="ble" />
+      while browsing your photos.
+    </ui:msg>
+
+    <gwt:RenderablePanel ui:field="childPanel" styleName="test">
+      <div ui:field="innerDiv" class="ble">Inner div</div>
+      <test:SimpleRenderable ui:field="innerRenderable">Inner renderable</test:SimpleRenderable>
+      <gwt:Label ui:field="innerLabel">Inner label</gwt:Label>
+      <test:RenderableComposite ui:field="innerComposite"/>
+
+      <ui:msg description="A photo promo">
+        Inner i18n image
+        <gwt:Image ui:field="innerI18nImage" styleName="ble" />
+        while browsing your photos.
+      </ui:msg>
+    </gwt:RenderablePanel>
+
+  </gwt:RenderablePanel>
+</ui:UiBinder>
diff --git a/user/test/com/google/gwt/uibinder/test/client/IsRenderableIntegrationTest.Shallow.ui.xml b/user/test/com/google/gwt/uibinder/test/client/IsRenderableIntegrationTest.Shallow.ui.xml
new file mode 100644
index 0000000..a9fb250
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/IsRenderableIntegrationTest.Shallow.ui.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.                                         -->
+<ui:UiBinder
+  xmlns:ui="urn:ui:com.google.gwt.uibinder"
+  xmlns:test="urn:import:com.google.gwt.uibinder.test.client"
+  >
+  <test:SimpleRenderable ui:field="widget">Tip-top level renderable</test:SimpleRenderable>
+</ui:UiBinder>
diff --git a/user/test/com/google/gwt/uibinder/test/client/IsRenderableIntegrationTest.java b/user/test/com/google/gwt/uibinder/test/client/IsRenderableIntegrationTest.java
new file mode 100644
index 0000000..9de281b
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/IsRenderableIntegrationTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.IsRenderable;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.PotentialElement;
+import com.google.gwt.user.client.ui.RenderablePanel;
+import com.google.gwt.user.client.ui.RootPanel;
+
+/**
+ * Integration test for {@link com.google.gwt.user.client.ui.IsRenderable
+ * IsRenderable}.
+ */
+public class IsRenderableIntegrationTest extends GWTTestCase {
+  static class Deep {
+    interface Binder extends UiBinder<IsRenderable, Deep> {
+    }
+
+    static final Binder binder = GWT.create(Binder.class);
+
+    @UiField
+    RenderablePanel top;
+    @UiField
+    DivElement outerDiv;
+    @UiField
+    DivElement innerDiv;
+    @UiField
+    SimpleRenderable outerRenderable;
+    @UiField
+    Label outerLabel;
+    @UiField
+    Label innerLabel;
+    @UiField
+    SimpleRenderable innerRenderable;
+    @UiField
+    Image outerI18nImage;
+    @UiField
+    Image innerI18nImage;
+    @UiField
+    RenderableComposite outerComposite;
+    @UiField
+    RenderableComposite innerComposite;
+
+    Object receivedValue;
+
+    Deep() {
+      binder.createAndBindUi(this);
+    }
+
+    @UiHandler("outerRenderable")
+    void onValueChange(ValueChangeEvent<Object> event) {
+      receivedValue = event.getValue();
+    }
+  }
+
+  static class Shallow {
+    interface Binder extends UiBinder<IsRenderable, Shallow> {
+    }
+
+    static final Binder binder = GWT.create(Binder.class);
+
+    @UiField
+    SimpleRenderable widget;
+
+    Shallow() {
+      binder.createAndBindUi(this);
+    }
+  }
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.uibinder.test.LazyWidgetBuilderSuite";
+  }
+
+  public void testDeep() {
+    Deep deep = new Deep();
+    assertTrue(PotentialElement.isPotential(deep.top.getElement()));
+    assertTrue(PotentialElement.isPotential(deep.outerRenderable.getElement()));
+    assertTrue(PotentialElement.isPotential(deep.innerRenderable.getElement()));
+
+    /*
+     * This is pretty surprising. I don't think it's a good idea to be filling
+     * in the owner's fields at some arbitrary later date. I doubt it's a real
+     * speed up, since the things will still have to be instantiated before the
+     * user sees them, and it seems guaranteed to be a source of confusion.
+     * 
+     * Hmm. What keeps the object that fills in the the fields at attach time
+     * from being gc'd? And are we sure it's not leaking?
+     * 
+     * Although I suppose we have no choice with dom children. Interesting
+     * problem.
+     */
+    assertNull(deep.outerI18nImage);
+    assertNull(deep.innerI18nImage);
+    assertNull(deep.outerLabel);
+    assertNull(deep.innerLabel);
+    assertNull(deep.innerLabel);
+    assertNull(deep.outerDiv);
+    assertNull(deep.innerDiv);
+
+    // Oh dear, we're not even consistent about it. That means the above is a
+    // bug, period.
+    assertNotNull(deep.outerComposite);
+    assertNotNull(deep.innerComposite);
+    assertTrue(PotentialElement.isPotential(deep.outerComposite.getWidget().getElement()));
+    assertTrue(PotentialElement.isPotential(deep.innerComposite.getWidget().getElement()));
+
+    try {
+      RootPanel.get().add(deep.top);
+
+      assertEquals("Outer div", deep.outerDiv.getInnerText());
+      assertEquals("Inner div", deep.innerDiv.getInnerText());
+
+      assertEquals("[string built]Outer renderable",
+          deep.outerRenderable.getElement().getInnerText());
+      assertEquals("[string built]Inner renderable",
+          deep.innerRenderable.getElement().getInnerText());
+
+      assertNotNull(deep.outerI18nImage);
+      assertNotNull(deep.innerI18nImage);
+
+      assertEquals("Outer label", deep.outerLabel.getText());
+      assertEquals("Inner label", deep.innerLabel.getText());
+
+      assertEquals("[string built]Renderable",
+          deep.outerComposite.getWidget().getElement().getInnerText());
+      assertEquals("[string built]Renderable",
+          deep.innerComposite.getWidget().getElement().getInnerText());
+
+      // Test event handling. It is cool as hell that this works!
+      deep.outerRenderable.setValue("foo");
+      assertEquals("foo", deep.receivedValue);
+
+      /*
+       * This is really a test of SimpleRenderable itself, but what the hell.
+       * That class might be useful some day.
+       */
+      deep.outerRenderable.setText("fnord");
+      assertEquals("[updated]fnord", deep.outerRenderable.getElement().getInnerText());
+
+    } finally {
+      deep.top.removeFromParent();
+    }
+  }
+
+  public void testLegacyComposite() {
+    LegacyComposite legacyComposite = new LegacyComposite();
+    assertEquals("span", legacyComposite.span.getInnerText());
+  }
+
+  public void testNestedRenderableComposite() {
+    RenderableComposite.Meta meta = new RenderableComposite.Meta();
+    assertTrue(PotentialElement.isPotential(meta.getElement()));
+
+    try {
+      RootPanel.get().add(meta);
+      assertEquals("[dom built]Renderable",
+          meta.getWidget().getElement().getInnerText());
+    } finally {
+      meta.removeFromParent();
+    }
+  }
+  
+  public void testRenderableComposite() {
+    RenderableComposite renderableComposite = new RenderableComposite();
+    assertTrue(PotentialElement.isPotential(renderableComposite.getElement()));
+
+    // TODO(rdcastro) This results in an NPE 
+    // assertEquals("something useful", renderableComposite.toString());
+
+    try {
+      RootPanel.get().add(renderableComposite);
+      assertEquals("[dom built]Renderable",
+          renderableComposite.getWidget().getElement().getInnerText());
+    } finally {
+      renderableComposite.removeFromParent();
+    }
+  }
+
+
+  public void testShallow() {
+    Shallow shallow = new Shallow();
+    assertTrue(PotentialElement.isPotential(shallow.widget.getElement()));
+    try {
+      RootPanel.get().add(shallow.widget);
+      assertEquals("[dom built]" + shallow.widget.getText(),
+          shallow.widget.getElement().getInnerText());
+    } finally {
+      shallow.widget.removeFromParent();
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/LegacyComposite.java b/user/test/com/google/gwt/uibinder/test/client/LegacyComposite.java
new file mode 100644
index 0000000..b677d5a
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/LegacyComposite.java
@@ -0,0 +1,43 @@
+/*
+ * 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.SpanElement;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Used by {@link IsRenderableIntegrationTest}.
+ */
+public class LegacyComposite extends Composite {
+  interface Binder extends UiBinder<Widget, LegacyComposite> {
+  }
+  private static final Binder BINDER = GWT.create(Binder.class);
+  
+  @UiField SpanElement span;
+  
+  public LegacyComposite() {
+    initWidget(BINDER.createAndBindUi(this));
+  }
+  
+  @Override
+  public Widget getWidget() {
+    return super.getWidget();
+  }
+}
\ No newline at end of file
diff --git a/user/test/com/google/gwt/uibinder/test/client/LegacyComposite.ui.xml b/user/test/com/google/gwt/uibinder/test/client/LegacyComposite.ui.xml
new file mode 100644
index 0000000..20d1071
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/LegacyComposite.ui.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.                                         -->
+<ui:UiBinder
+  xmlns:ui="urn:ui:com.google.gwt.uibinder"
+  xmlns:gwt="urn:import:com.google.gwt.user.client.ui"
+  >
+  <gwt:HTML>When does this <span ui:field='span'>span</span> get filled?</gwt:HTML>
+</ui:UiBinder>
diff --git a/user/test/com/google/gwt/uibinder/test/client/RenderableComposite.java b/user/test/com/google/gwt/uibinder/test/client/RenderableComposite.java
new file mode 100644
index 0000000..125d482
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/RenderableComposite.java
@@ -0,0 +1,49 @@
+/*
+ * 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.uibinder.client.UiBinder;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Used by {@link IsRenderableIntegrationTest}.
+ */
+public class RenderableComposite extends Composite {
+  interface Binder extends UiBinder<Widget, RenderableComposite> {
+  }
+  private static final Binder BINDER = GWT.create(Binder.class);
+  
+  static class Meta extends Composite {
+    Meta() {
+      initWidget(new RenderableComposite());
+    }
+    @Override
+    public Widget getWidget() {
+      return super.getWidget();
+    }
+  }
+  
+  public RenderableComposite() {
+    initWidget(BINDER.createAndBindUi(this));
+  }
+  
+  @Override
+  public Widget getWidget() {
+    return super.getWidget();
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/RenderableComposite.ui.xml b/user/test/com/google/gwt/uibinder/test/client/RenderableComposite.ui.xml
new file mode 100644
index 0000000..ce33402
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/RenderableComposite.ui.xml
@@ -0,0 +1,20 @@
+<!--                                                                        -->
+<!-- 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:gwt="urn:import:com.google.gwt.user.client.ui"
+  xmlns:test="urn:import:com.google.gwt.uibinder.test.client"
+  >
+  <test:SimpleRenderable>Renderable</test:SimpleRenderable>
+</ui:UiBinder>
diff --git a/user/test/com/google/gwt/uibinder/test/client/SimpleRenderable.java b/user/test/com/google/gwt/uibinder/test/client/SimpleRenderable.java
new file mode 100644
index 0000000..de8fc30
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/SimpleRenderable.java
@@ -0,0 +1,157 @@
+/*
+ * 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.builder.client.DomBuilderFactory;
+import com.google.gwt.dom.builder.shared.DivBuilder;
+import com.google.gwt.dom.builder.shared.ElementBuilderBase;
+import com.google.gwt.dom.builder.shared.ElementBuilderFactory;
+import com.google.gwt.dom.builder.shared.HtmlBuilderFactory;
+import com.google.gwt.dom.builder.shared.HtmlElementBuilderBase;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
+import com.google.gwt.user.client.ui.HasText;
+import com.google.gwt.user.client.ui.HasValue;
+import com.google.gwt.user.client.ui.IsRenderable;
+import com.google.gwt.user.client.ui.PotentialElement;
+import com.google.gwt.user.client.ui.RenderableStamper;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Used by {@link IsRenderableIntegrationTest}. A simple implementation of
+ * IsRenderable. Note that the actual building for both string based and dom
+ * based versions happens in one place, {@link #build(ElementBuilderFactory)}.
+ * <p>
+ * Note also that that the widget's contents are simply re-rendered when the
+ * widget's state changes, in this case in {@link #setText}. This is a bad idea
+ * for large widgets, but for little leaf widgets like this one is probably just
+ * fine.
+ * <p>
+ * It would probably be a good refactor a production super class out of this
+ * once the IsRenderable interface settles down.
+ * <p>
+ * It's HasValue aspect is just meant for unit testing, not anything to
+ * generalize.
+ */
+public class SimpleRenderable extends Widget implements IsRenderable, HasText, HasValue<Object> {
+
+  private String text = "";
+  private Object value;
+
+  public SimpleRenderable() {
+    setElement(PotentialElement.build(this));
+  }
+
+  @Override
+  public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Object> handler) {
+    return addHandler(handler, ValueChangeEvent.getType());
+  }
+
+  @Override
+  public void claimElement(Element element) {
+    setElement(element);
+  }
+
+  public String getText() {
+    return text;
+  }
+
+  @Override
+  public Object getValue() {
+    return value;
+  }
+
+  @Override
+  public void initializeClaimedElement() {
+  }
+
+  @Override
+  public SafeHtml render(RenderableStamper stamper) {
+    HtmlBuilderFactory factory = HtmlBuilderFactory.get();
+    String realText = text;
+    text = "[string built]" + text;
+    HtmlElementBuilderBase<?> builder = (HtmlElementBuilderBase<?>) build(factory);
+    text = realText;
+    return stamper.stamp(builder.asSafeHtml());
+  }
+
+  @Override
+  public void render(RenderableStamper stamper, SafeHtmlBuilder safeHtmlBuilder) {
+    safeHtmlBuilder.append(render(stamper));
+  }
+
+  @Override
+  public Element resolvePotentialElement() {
+    String realText = text;
+    text = "[dom built]" + text;
+    ElementBuilderBase<?> builder = build(DomBuilderFactory.get());
+    text = realText;
+    setElement(builder.finish());
+    return getElement();
+  }
+
+  @Override
+  public void setText(String text) {
+    this.text = text;
+    updateElement();
+  }
+
+  @Override
+  public void setValue(Object value) {
+    setValue(value, false);
+  }
+
+  @Override
+  public void setValue(Object newValue, boolean fireEvents) {
+    Object oldValue = value;
+    value = newValue;
+    ValueChangeEvent.fireIfNotEqual(this, oldValue, newValue);
+  }
+
+  private ElementBuilderBase<?> build(ElementBuilderFactory factory) {
+    DivBuilder builder = factory.createDivBuilder();
+    builder.text(text).end();
+    return builder;
+  }
+
+  private boolean domIsReal() {
+    return !PotentialElement.isPotential(getElement());
+  }
+
+  private void updateElement() {
+    if (domIsReal()) {
+      String realText = text;
+      text = "[updated]" + text;
+      /*
+       * Sleazey. If this is to get real, should split rendering into the outer
+       * builder and the inner builder. Update can just call the inner builder.
+       * But we'd still have to make a bunch of dom calls to tear down the old
+       * guts, something like while (el.firstChild) {
+       * el.removeChild(el.firstChild); }
+       * 
+       * Have to wonder if that actually would be any faster than rebuilding the
+       * guts as string
+       */
+      ElementBuilderBase<?> builder = build(DomBuilderFactory.get());
+      getElement().setInnerHTML(builder.finish().getInnerHTML());
+      text = realText;
+    }
+  }
+}