Merges RenderableComposite into Composite. Version that adds a setResolver method to PotentialElement.

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


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10294 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/Composite.java b/user/src/com/google/gwt/user/client/ui/Composite.java
index c90cc4e..78cca37 100644
--- a/user/src/com/google/gwt/user/client/ui/Composite.java
+++ b/user/src/com/google/gwt/user/client/ui/Composite.java
@@ -15,6 +15,11 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.safehtml.client.SafeHtmlTemplates;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
 import com.google.gwt.event.logical.shared.AttachEvent;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Event;
@@ -23,21 +28,34 @@
  * A type of widget that can wrap another widget, hiding the wrapped widget's
  * methods. When added to a panel, a composite behaves exactly as if the widget
  * it wraps had been added.
- * 
+ *
  * <p>
  * The composite is useful for creating a single widget out of an aggregate of
  * multiple other widgets contained in a single panel.
  * </p>
- * 
+ *
  * <p>
  * <h3>Example</h3>
  * {@example com.google.gwt.examples.CompositeExample}
  * </p>
+ *
+ * TODO(rdcastro): Remove the final qualifier from IsRenderable overrides.
  */
-public abstract class Composite extends Widget {
+public abstract class Composite extends Widget implements IsRenderable {
+
+  interface HTMLTemplates extends SafeHtmlTemplates {
+    @Template("<span id=\"{0}\"></span>")
+     SafeHtml renderWithId(String id);
+  }
+  private static final HTMLTemplates TEMPLATE =
+      GWT.create(HTMLTemplates.class);
 
   private Widget widget;
 
+  private IsRenderable renderable;
+
+  private Element elementToWrap;
+
   @Override
   public boolean isAttached() {
     if (widget != null) {
@@ -55,6 +73,45 @@
     widget.onBrowserEvent(event);
   }
 
+  @Override
+  public final void performDetachedInitialization() {
+    if (renderable != null) {
+      renderable.performDetachedInitialization();
+    } else {
+      elementToWrap.getParentNode().replaceChild(widget.getElement(), elementToWrap);
+    }
+  }
+
+  @Override
+  public final SafeHtml render(String id) {
+    if (renderable != null) {
+      return renderable.render(id);
+    } else {
+      SafeHtmlBuilder builder = new SafeHtmlBuilder();
+      render(id, builder);
+      return builder.toSafeHtml();
+    }
+  }
+
+  @Override
+  public final void render(String id, SafeHtmlBuilder builder) {
+    if (renderable != null) {
+      renderable.render(id, builder);
+    } else {
+      builder.append(TEMPLATE.renderWithId(id));
+    }
+  }
+
+  @Override
+  public final void wrapElement(Element element) {
+    if (renderable != null) {
+      renderable.wrapElement(element);
+      setElement(widget.getElement());
+    } else {
+      this.elementToWrap = element;
+    }
+  }
+
   /**
    * Provides subclasses access to the topmost widget that defines this
    * composite.
@@ -79,12 +136,22 @@
           + "called once.");
     }
 
+    if (widget instanceof IsRenderable) {
+      // In case the Widget being wrapped is an IsRenderable, we save that fact.
+      this.renderable = (IsRenderable) widget;
+    }
+
     // Detach the new child.
     widget.removeFromParent();
 
     // Use the contained widget's element as the composite's element,
     // effectively merging them within the DOM.
-    setElement(widget.getElement());
+    Element elem = widget.getElement();
+    setElement(elem);
+
+    if (PotentialElement.isPotential(elem)) {
+      PotentialElement.as(elem).setResolver(this);
+    }
 
     // Logical attach.
     this.widget = widget;
@@ -126,6 +193,12 @@
     }
   }
 
+  @Override
+  protected Element resolvePotentialElement() {
+    setElement(widget.resolvePotentialElement());
+    return getElement();
+  }
+
   /**
    * This method was for initializing the Widget to be wrapped by this
    * Composite, but has been deprecated in favor of {@link #initWidget(Widget)}.
diff --git a/user/src/com/google/gwt/user/client/ui/PotentialElement.java b/user/src/com/google/gwt/user/client/ui/PotentialElement.java
index bee2484..58f5a83 100644
--- a/user/src/com/google/gwt/user/client/ui/PotentialElement.java
+++ b/user/src/com/google/gwt/user/client/ui/PotentialElement.java
@@ -37,6 +37,11 @@
  */
 public class PotentialElement extends Element {
 
+  public static PotentialElement as(Element e) {
+    assert isPotential(e);
+    return (PotentialElement) e;
+  }
+
   /**
    * Builds a new PotentialElement. This element keeps track of the
    * {@link UIObject} so that it can call
@@ -66,10 +71,7 @@
       },
       src: '',
       style: {},
-      __gwt_resolve: function() {
-        this.__gwt_resolve = @com.google.gwt.user.client.ui.PotentialElement::cannotResolveTwice();
-        return o.@com.google.gwt.user.client.ui.UIObject::resolvePotentialElement()();
-      },
+      __gwt_resolve: @com.google.gwt.user.client.ui.PotentialElement::buildResolveCallback(Lcom/google/gwt/user/client/ui/UIObject;)(o),
       title: ''
     });
   }-*/;
@@ -94,6 +96,13 @@
     return maybePotential.<PotentialElement>cast().resolve();
   }
 
+  private static native JavaScriptObject buildResolveCallback(UIObject resolver) /*-{
+    return function() {
+        this.__gwt_resolve = @com.google.gwt.user.client.ui.PotentialElement::cannotResolveTwice();
+        return resolver.@com.google.gwt.user.client.ui.UIObject::resolvePotentialElement()();
+      };
+  }-*/;
+
   private static final native void cannotResolveTwice() /*-{
     throw "A PotentialElement cannot be resolved twice.";
   }-*/;
@@ -101,6 +110,10 @@
   protected PotentialElement() {
   }
 
+  final native Element setResolver(UIObject resolver) /*-{
+    this.__gwt_resolve = @com.google.gwt.user.client.ui.PotentialElement::buildResolveCallback(Lcom/google/gwt/user/client/ui/UIObject;)(resolver);
+  }-*/;
+
   /**
    * Calls the <code>__gwt_resolve</code> method on the underlying
    * JavaScript object if it exists. On objects created via {@link #build}, this
diff --git a/user/src/com/google/gwt/user/client/ui/RenderableComposite.java b/user/src/com/google/gwt/user/client/ui/RenderableComposite.java
index fae9b06..2c32155 100644
--- a/user/src/com/google/gwt/user/client/ui/RenderableComposite.java
+++ b/user/src/com/google/gwt/user/client/ui/RenderableComposite.java
@@ -15,15 +15,6 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.logical.shared.AttachEvent;
-import com.google.gwt.safehtml.client.SafeHtmlTemplates;
-import com.google.gwt.safehtml.shared.SafeHtml;
-import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-
 /**
  * EXPERIMENTAL and subject to change. Do not use this in production code.
  * <p>
@@ -32,193 +23,9 @@
  * itself, but is still under active development.
  * The only reason why this isn't a subclass of {@link Composite} is to avoid
  * messing up it's API, since {@link Composite} is very often subclassed.
+ *
+ * TODO(rdcastro): Delete this as soon as all references have been updated to
+ * use Composite directly.
  */
-public abstract class RenderableComposite extends Widget implements IsRenderable {
-
-  interface HTMLTemplates extends SafeHtmlTemplates {
-    @Template("<span id=\"{0}\"></span>")
-     SafeHtml renderWithId(String id);
-  }
-  private static final HTMLTemplates TEMPLATE =
-      GWT.create(HTMLTemplates.class);
-
-  private Widget widget;
-
-  private IsRenderable renderable;
-
-  private Element elementToWrap;
-
-  private boolean initFinished = false;
-
-  @Override
-  public com.google.gwt.user.client.Element getElement() {
-    if (!initFinished) {
-      // if we're trying to get the element but we haven't finished the
-      // initialization, do it now.
-      initWidgetInternal();
-    }
-    return super.getElement();
-  }
-
-  @Override
-  public boolean isAttached() {
-    if (widget != null) {
-      return widget.isAttached();
-    }
-    return false;
-  }
-
-  @Override
-  public void onBrowserEvent(Event event) {
-    // Fire any handler added to the composite itself.
-    super.onBrowserEvent(event);
-
-    // Delegate events to the widget.
-    widget.onBrowserEvent(event);
-  }
-
-  @Override
-  public void performDetachedInitialization() {
-    if (renderable != null) {
-      assert (initFinished == false);
-      renderable.performDetachedInitialization();
-      initWidgetInternal();
-    } else {
-      elementToWrap.getParentNode().replaceChild(widget.getElement(), elementToWrap);
-    }
-  }
-
-  @Override
-  public SafeHtml render(String id) {
-    if (renderable != null) {
-      return renderable.render(id);
-    } else {
-      SafeHtmlBuilder builder = new SafeHtmlBuilder();
-      render(id, builder);
-      return builder.toSafeHtml();
-    }
-  }
-
-  @Override
-  public void render(String id, SafeHtmlBuilder builder) {
-    if (renderable != null) {
-      renderable.render(id, builder);
-    } else {
-      builder.append(TEMPLATE.renderWithId(id));
-    }
-  }
-
-  @Override
-  public void setStyleName(String styleName) {
-    if (this.widget == null) {
-      throw new IllegalStateException("setStyleName called before initWidget.");
-    }
-    widget.setStyleName(styleName);
-  }
-
-  @Override
-  public void wrapElement(Element element) {
-    if (renderable != null) {
-      assert (initFinished == false);
-      renderable.wrapElement(element);
-    } else {
-      this.elementToWrap = element;
-    }
-  }
-
-  /**
-   * Provides subclasses access to the topmost widget that defines this
-   * composite.
-   *
-   * @return the widget
-   */
-  protected Widget getWidget() {
-    return widget;
-  }
-
-  /**
-   * Sets the widget to be wrapped by the composite. The wrapped widget must be
-   * set before calling any {@link Widget} methods on this object, or adding it
-   * to a panel. This method may only be called once for a given composite.
-   *
-   * @param widget the widget to be wrapped
-   */
-  protected void initWidget(Widget widget) {
-    // Validate. Make sure the widget is not being set twice.
-    if (this.widget != null) {
-      throw new IllegalStateException("Composite.initWidget() may only be "
-          + "called once.");
-    }
-
-    // Detach the new child.
-    widget.removeFromParent();
-
-    // Logical attach.
-    this.widget = widget;
-
-    if (widget instanceof IsRenderable) {
-      // In case the Widget being wrapped is an IsRenderable, we delay finishing
-      // the initialization until the performDetachedInitialization() is called.
-      this.renderable = (IsRenderable) widget;
-      return;
-    }
-
-    initWidgetInternal();
-  }
-
-  @Override
-  protected void onAttach() {
-    if (!isOrWasAttached()) {
-      widget.sinkEvents(eventsToSink);
-      eventsToSink = -1;
-    }
-
-    widget.onAttach();
-
-    // Clobber the widget's call to setEventListener(), causing all events to
-    // be routed to this composite, which will delegate back to the widget by
-    // default (note: it's not necessary to clear this in onDetach(), because
-    // the widget's onDetach will do so).
-    DOM.setEventListener(getElement(), this);
-
-    // Call onLoad() directly, because we're not calling super.onAttach().
-    onLoad();
-    AttachEvent.fire(this, true);
-  }
-
-  @Override
-  protected void onDetach() {
-    try {
-      onUnload();
-      AttachEvent.fire(this, false);
-    } finally {
-      // We don't want an exception in user code to keep us from calling the
-      // super implementation (or event listeners won't get cleaned up and
-      // the attached flag will be wrong).
-      widget.onDetach();
-    }
-  }
-
-  /**
-   * This method was for initializing the Widget to be wrapped by this
-   * Composite, but has been deprecated in favor of {@link #initWidget(Widget)}.
-   *
-   * @deprecated Use {@link #initWidget(Widget)} instead
-   */
-  @Deprecated
-  protected void setWidget(Widget widget) {
-    initWidget(widget);
-  }
-
-  private void initWidgetInternal() {
-    // Use the contained widget's element as the composite's element,
-    // effectively merging them within the DOM.
-    setElement(PotentialElement.resolve(widget.getElement()));
-
-    // Adopt.
-    widget.setParent(this);
-
-    // Mark initialization as finished, as this only needs to be run once.
-    this.initFinished = true;
-  }
+public abstract class RenderableComposite extends Composite {
 }