Fixes issue 3148 by improving the performance of FlowPanel.clear() by a hundred to one on IE.
Review by:jlabanca

git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@4468 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/SpeedForClear.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/SpeedForClear.java
new file mode 100644
index 0000000..558aeed
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/SpeedForClear.java
@@ -0,0 +1,129 @@
+/*

+ * 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

+ * License for the specific language governing permissions and limitations under

+ * the License.

+ */

+package com.google.gwt.museum.client.defaultmuseum;

+

+import com.google.gwt.core.client.Duration;

+import com.google.gwt.event.dom.client.ClickEvent;

+import com.google.gwt.event.dom.client.ClickHandler;

+import com.google.gwt.museum.client.common.AbstractIssue;

+import com.google.gwt.museum.client.common.ControlInputPanel;

+import com.google.gwt.museum.client.common.SimpleLogger;

+import com.google.gwt.museum.client.common.ControlInputPanel.IntegerInput;

+import com.google.gwt.user.client.ui.Button;

+import com.google.gwt.user.client.ui.FlowPanel;

+import com.google.gwt.user.client.ui.Label;

+import com.google.gwt.user.client.ui.Panel;

+import com.google.gwt.user.client.ui.RootPanel;

+import com.google.gwt.user.client.ui.VerticalPanel;

+import com.google.gwt.user.client.ui.Widget;

+

+import java.util.ArrayList;

+import java.util.List;

+

+/*

+ * Originally, on IE, about three seconds to distroy

+ */

+/**

+ * Tests bad behavior for clear.

+ * 

+ * <pre>

+ * ff -- 1000 flow panels, aprox 500  millis

+ * ie -- 1000 flow panels, aprox 3000 millis

+ * 

+ * in new version

+ * ie -- 1000 flow panels, aprox 30-80 millis

+ * ff -- 1000 flow panels, aprox 13-50 millis

+ * </pre>

+ * <p>

+ * <img class='gallery' src='FlowPanel.png'/>

+ * </p>

+ */

+public class SpeedForClear extends AbstractIssue {

+  private Panel target;

+  private List<Widget> children = new ArrayList<Widget>();

+  private SimpleLogger log = new SimpleLogger();

+

+  @Override

+  public Widget createIssue() {

+    VerticalPanel v = new VerticalPanel();

+    ControlInputPanel p = new ControlInputPanel();

+    v.add(p);

+    v.add(log);

+    final IntegerInput size = new IntegerInput("flowpanel", 10, p);

+    Button create = new Button("create widget", new ClickHandler() {

+

+      public void onClick(ClickEvent event) {

+        createLargeFlowPanel(size.getValue());

+      }

+    });

+

+    Button distroy = new Button("time the removal", new ClickHandler() {

+      public void onClick(ClickEvent event) {

+        Duration d = new Duration();

+        target.clear();

+

+        log.report("Took " + d.elapsedMillis() + " milliseconds to clear "

+            + size.getValue() + " widgets from a flow panel");

+        for (Widget child : children) {

+          if (child.getElement().getPropertyString("__listener") != null) {

+            throw new IllegalStateException(

+                "each child should no longer have a listener");

+          }

+        }

+      }

+    });

+    v.add(create);

+    v.add(distroy);

+    return v;

+  }

+

+  @Override

+  public String getInstructions() {

+    return "check the speed of clear methods";

+  }

+

+  @Override

+  public String getSummary() {

+    return "clear() speed check";

+  }

+

+  @Override

+  public boolean hasCSS() {

+    return false;

+  }

+

+  private void createLargeFlowPanel(int size) {

+

+    if (target != null) {

+      target.removeFromParent();

+    }

+    target = new FlowPanel();

+

+    for (int i = 0; i < size; i++) {

+      Widget w = new Label("widget-" + i);

+      target.add(w);

+      children.add(w);

+    }

+

+    RootPanel.get().add(target);

+    for (Widget child : target) {

+      if (child.getElement().getPropertyString("__listener") == null) {

+        throw new IllegalStateException("each child should now have a listener");

+      }

+    }

+  }

+

+}

diff --git a/user/src/com/google/gwt/user/client/ui/ComplexPanel.java b/user/src/com/google/gwt/user/client/ui/ComplexPanel.java
index aa26def..b7b1d52 100644
--- a/user/src/com/google/gwt/user/client/ui/ComplexPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/ComplexPanel.java
@@ -111,8 +111,8 @@
   }
 
   /**
-   * Checks that <code>index</code> is in the range [0, getWidgetCount()),
-   * which is the valid range on accessible indexes.
+   * Checks that <code>index</code> is in the range [0, getWidgetCount()), which
+   * is the valid range on accessible indexes.
    * 
    * @param index the index being accessed
    */
@@ -123,8 +123,8 @@
   }
 
   /**
-   * Checks that <code>index</code> is in the range [0, getWidgetCount()],
-   * which is the valid range for indexes on an insertion.
+   * Checks that <code>index</code> is in the range [0, getWidgetCount()], which
+   * is the valid range for indexes on an insertion.
    * 
    * @param index the index where insertion will occur
    */
@@ -197,4 +197,12 @@
     // Adopt.
     adopt(child);
   }
+
+  void doLogicalClear() {
+    int size = children.size();
+    for (int i = 0; i < size; i++) {
+      orphan(children.get(i));
+    }
+    children = new WidgetCollection(this);
+  }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/FlowPanel.java b/user/src/com/google/gwt/user/client/ui/FlowPanel.java
index 601bac7..44f2153 100644
--- a/user/src/com/google/gwt/user/client/ui/FlowPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/FlowPanel.java
@@ -44,6 +44,12 @@
     add(w, getElement());
   }
 
+  @Override
+  public void clear() {
+    super.doLogicalClear();
+    this.getElement().setInnerHTML("");
+  }
+
   /**
    * Inserts a widget before the specified index.
    * 
diff --git a/user/test/com/google/gwt/user/client/ui/FlowPanelTest.java b/user/test/com/google/gwt/user/client/ui/FlowPanelTest.java
index d1c8b34..441ac03 100644
--- a/user/test/com/google/gwt/user/client/ui/FlowPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/FlowPanelTest.java
@@ -17,6 +17,9 @@
 
 import com.google.gwt.junit.client.GWTTestCase;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Tests the FlowPanel widget.
  */
@@ -29,4 +32,30 @@
   public void testAttachDetachOrder() {
     HasWidgetsTester.testAll(new FlowPanel());
   }
+
+  public void testClear() {
+    int size = 10;
+    FlowPanel target;
+    List<Widget> children = new ArrayList<Widget>();
+
+    target = new FlowPanel();
+
+    for (int i = 0; i < size; i++) {
+      Widget w = new Label("widget-" + i);
+      target.add(w);
+      children.add(w);
+    }
+
+    RootPanel.get().add(target);
+    for (Widget child : target) {
+      assertNotNull(child.getElement().getPropertyString("__listener"));
+    }
+    assertEquals(10, target.getWidgetCount());
+    target.clear();
+    assertEquals(0, target.getWidgetCount());
+
+    for (Widget child : target) {
+      assertNull(child.getElement().getPropertyString("__listener") == null);
+    }
+  }
 }