Replace HeaderPanel's custom iterator with a new package protected FiniteWidgetIterator that iterates over children in a WidgetProvider. FiniteWidgetIterator is a generic iterator over a finite set of child widgets (versus WidgetIterator, which iterators over panel of arbitrary size). I intended to use FiniteWidgetIterator in another change then ended up not needing it, but I'm sure it will come in handy for other widgets.
Review at http://gwt-code-reviews.appspot.com/1420807
Review by: rjrjr@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10046 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/FiniteWidgetIterator.java b/user/src/com/google/gwt/user/client/ui/FiniteWidgetIterator.java
new file mode 100644
index 0000000..5253fd5
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/FiniteWidgetIterator.java
@@ -0,0 +1,93 @@
+/*
+ * 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.user.client.ui;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Iterator over a finite number of widgets, which are stored in a delegate
+ * class (usually a widget panel).
+ *
+ * <p>
+ * In order to use this class, assign each widget in the panel an arbitrary
+ * index. For example, {@link HeaderPanel} defines the header as index 0, the
+ * content as index 1, and the footer as index 2. Construct a new
+ * {@link FiniteWidgetIterator} with a {@link WidgetProvider} that provides the
+ * child widgets.
+ */
+class FiniteWidgetIterator implements Iterator<Widget> {
+
+ /**
+ * Provides widgets to the iterator.
+ */
+ public static interface WidgetProvider {
+ IsWidget get(int index);
+ }
+
+ private int index = -1;
+ private final WidgetProvider provider;
+ private final int widgetCount;
+
+ /**
+ * Construct a new {@link FiniteWidgetIterator}.
+ *
+ * <p>
+ * The widget count is the number of child widgets that the panel supports,
+ * regardless of whether or not they are set.
+ *
+ * @param provider the widget provider
+ * @param widgetCount the finite number of widgets that can be provided
+ */
+ public FiniteWidgetIterator(WidgetProvider provider, int widgetCount) {
+ this.provider = provider;
+ this.widgetCount = widgetCount;
+ }
+
+ public boolean hasNext() {
+ // Iterate over the remaining widgets until we find one.
+ for (int i = index + 1; i < widgetCount; i++) {
+ IsWidget w = provider.get(i);
+ if (w != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Widget next() {
+ // Iterate over the remaining widgets until we find one.
+ for (int i = index + 1; i < widgetCount; i++) {
+ index = i;
+ IsWidget w = provider.get(i);
+ if (w != null) {
+ return w.asWidget();
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ if (index < 0 || index >= widgetCount) {
+ throw new IllegalStateException();
+ }
+ IsWidget w = provider.get(index);
+ if (w == null) {
+ throw new IllegalStateException("Widget was already removed.");
+ }
+ w.asWidget().removeFromParent();
+ }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/HeaderPanel.java b/user/src/com/google/gwt/user/client/ui/HeaderPanel.java
index 4a58724..3ea4b14 100644
--- a/user/src/com/google/gwt/user/client/ui/HeaderPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/HeaderPanel.java
@@ -24,9 +24,9 @@
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.FiniteWidgetIterator.WidgetProvider;
import java.util.Iterator;
-import java.util.NoSuchElementException;
/**
* A panel that includes a header (top), footer (bottom), and content (middle)
@@ -35,16 +35,40 @@
*/
public class HeaderPanel extends Panel implements RequiresResize {
+ /**
+ * The widget provider for this panel.
+ *
+ * <p>
+ * Widgets are returned in the following order:
+ * <ol>
+ * <li>Header widget</li>
+ * <li>Content widget</li>
+ * <li>Footer widget</li>
+ * </ol>
+ */
+ private class WidgetProviderImpl implements WidgetProvider {
+
+ public Widget get(int index) {
+ switch (index) {
+ case 0:
+ return header;
+ case 1:
+ return content;
+ case 2:
+ return footer;
+ }
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ }
+
private Widget content;
private final Element contentContainer;
private Widget footer;
private final Element footerContainer;
- private final ResizeLayoutPanel.Impl footerImpl =
- GWT.create(ResizeLayoutPanel.Impl.class);
+ private final ResizeLayoutPanel.Impl footerImpl = GWT.create(ResizeLayoutPanel.Impl.class);
private Widget header;
private final Element headerContainer;
- private final ResizeLayoutPanel.Impl headerImpl =
- GWT.create(ResizeLayoutPanel.Impl.class);
+ private final ResizeLayoutPanel.Impl headerImpl = GWT.create(ResizeLayoutPanel.Impl.class);
private final ScheduledCommand layoutCmd = new ScheduledCommand() {
public void execute() {
layoutScheduled = false;
@@ -135,73 +159,7 @@
}
public Iterator<Widget> iterator() {
- // Return a simple iterator that iterates over the header, content, and
- // footer in order.
- return new Iterator<Widget>() {
- private int index = -1;
-
- public boolean hasNext() {
- switch (index) {
- case -1:
- if (header != null) {
- return true;
- }
- case 0: // Intentional fallthrough.
- if (content != null) {
- return true;
- }
- case 1: // Intentional fallthrough.
- if (footer != null) {
- return true;
- }
- }
- return false;
- }
-
- public Widget next() {
- switch (index) {
- case -1:
- index++;
- if (header != null) {
- return header;
- }
- case 0: // Intentional fallthrough.
- index++;
- if (content != null) {
- return content;
- }
- case 1: // Intentional fallthrough.
- index++;
- if (footer != null) {
- return footer;
- }
- }
- throw new NoSuchElementException();
- }
-
- public void remove() {
- switch (index) {
- case 0:
- doRemove(header, "Header");
- break;
- case 1:
- doRemove(content, "Content");
- break;
- case 2:
- doRemove(footer, "Footer");
- break;
- default:
- throw new IllegalStateException();
- }
- }
-
- private void doRemove(Widget widget, String position) {
- if (widget == null) {
- throw new IllegalStateException(position + " was already removed.");
- }
- HeaderPanel.this.remove(widget);
- }
- };
+ return new FiniteWidgetIterator(new WidgetProviderImpl(), 3);
}
@Override
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index 5ab2004..055968d 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -57,6 +57,7 @@
import com.google.gwt.user.client.ui.ElementWrappingTest;
import com.google.gwt.user.client.ui.FastStringMapTest;
import com.google.gwt.user.client.ui.FileUploadTest;
+import com.google.gwt.user.client.ui.FiniteWidgetIteratorTest;
import com.google.gwt.user.client.ui.FlexTableTest;
import com.google.gwt.user.client.ui.FlowPanelTest;
import com.google.gwt.user.client.ui.FocusPanelTest;
@@ -169,6 +170,7 @@
suite.addTestSuite(EventTest.class);
suite.addTestSuite(FastStringMapTest.class);
suite.addTestSuite(FileUploadTest.class);
+ suite.addTestSuite(FiniteWidgetIteratorTest.class);
suite.addTestSuite(FlexTableTest.class);
suite.addTestSuite(FlowPanelTest.class);
suite.addTestSuite(FocusPanelTest.class);
diff --git a/user/test/com/google/gwt/user/client/ui/FiniteWidgetIteratorTest.java b/user/test/com/google/gwt/user/client/ui/FiniteWidgetIteratorTest.java
new file mode 100644
index 0000000..6003146
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/FiniteWidgetIteratorTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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.user.client.ui;
+
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.ui.FiniteWidgetIterator.WidgetProvider;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+/**
+ * Test cases for {@link FiniteWidgetIterator}.
+ */
+public class FiniteWidgetIteratorTest extends GWTTestCase {
+
+ /**
+ * Implementation of {@link WidgetProvider}.
+ */
+ private static class WidgetProviderImpl implements WidgetProvider {
+
+ private final Map<Integer, Widget> widgets = new HashMap<Integer, Widget>();
+
+ /**
+ * Construct a new {@link WidgetProviderImpl} with the specified widgets.
+ */
+ public WidgetProviderImpl(Widget... widgets) {
+ if (widgets != null) {
+ for (int i = 0; i < widgets.length; i++) {
+ setWidget(i, widgets[i]);
+ }
+ }
+ }
+
+ /**
+ * Set the widget at the specified index.
+ *
+ * @param index the index
+ * @param w the widget
+ */
+ public void setWidget(int index, Widget w) {
+ widgets.put(index, w);
+ }
+
+ public Widget get(int index) {
+ if (!widgets.containsKey(index)) {
+ fail("A widget was not specified for index: " + index);
+ }
+ return widgets.get(index);
+ }
+ }
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.user.User";
+ }
+
+ public void testHasNextEmpty() {
+ FiniteWidgetIterator iterator = new FiniteWidgetIterator(new WidgetProviderImpl(), 0);
+ assertFalse(iterator.hasNext());
+ try {
+ iterator.next();
+ fail("Expected NoSuchElementException");
+ } catch (NoSuchElementException e) {
+ // Expected.
+ }
+ }
+
+ public void testNext() {
+ Widget w0 = new Widget();
+ Widget w1 = new Widget();
+ Widget w2 = new Widget();
+ FiniteWidgetIterator iterator = new FiniteWidgetIterator(new WidgetProviderImpl(w0, w1, w2), 3);
+
+ assertTrue(iterator.hasNext());
+ assertEquals(w0, iterator.next());
+
+ assertTrue(iterator.hasNext());
+ assertEquals(w1, iterator.next());
+
+ assertTrue(iterator.hasNext());
+ assertEquals(w2, iterator.next());
+
+ assertFalse(iterator.hasNext());
+ try {
+ iterator.next();
+ fail("Expected NoSuchElementException");
+ } catch (NoSuchElementException e) {
+ // Expected.
+ }
+ }
+
+ /**
+ * Test that the iterator skips null widgets at the start of the list.
+ */
+ public void testNullWidgetStart() {
+ Widget w1 = new Widget();
+ Widget w2 = new Widget();
+ FiniteWidgetIterator iterator =
+ new FiniteWidgetIterator(new WidgetProviderImpl(null, w1, w2), 3);
+
+ assertTrue(iterator.hasNext());
+ assertEquals(w1, iterator.next());
+
+ assertTrue(iterator.hasNext());
+ assertEquals(w2, iterator.next());
+
+ assertFalse(iterator.hasNext());
+ }
+
+ /**
+ * Test that the iterator skips null widgets at the end of the list.
+ */
+ public void testNullWidgetEnd() {
+ Widget w0 = new Widget();
+ Widget w1 = new Widget();
+ FiniteWidgetIterator iterator =
+ new FiniteWidgetIterator(new WidgetProviderImpl(w0, w1, null), 3);
+
+ assertTrue(iterator.hasNext());
+ assertEquals(w0, iterator.next());
+
+ assertTrue(iterator.hasNext());
+ assertEquals(w1, iterator.next());
+
+ assertFalse(iterator.hasNext());
+ }
+
+ /**
+ * Test that the iterator skips null widgets in the middle of the list.
+ */
+ public void testNullWidgetMiddle() {
+ Widget w0 = new Widget();
+ Widget w2 = new Widget();
+ FiniteWidgetIterator iterator =
+ new FiniteWidgetIterator(new WidgetProviderImpl(w0, null, w2), 3);
+
+ assertTrue(iterator.hasNext());
+ assertEquals(w0, iterator.next());
+
+ assertTrue(iterator.hasNext());
+ assertEquals(w2, iterator.next());
+
+ assertFalse(iterator.hasNext());
+ }
+
+ public void testRemove() {
+ Widget w0 = new Label();
+ Widget w1 = new Label();
+ Widget w2 = new Label();
+ WidgetProviderImpl provider = new WidgetProviderImpl(w0, w1, w2);
+ FiniteWidgetIterator iterator = new FiniteWidgetIterator(provider, 3);
+
+ // Add the widgets to a panel.
+ FlowPanel panel = new FlowPanel();
+ panel.add(w0);
+ panel.add(w1);
+ panel.add(w2);
+
+ // Remove before getting first widget.
+ try {
+ iterator.remove();
+ fail("Expected IllegalStateException");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Remove the widget.
+ assertEquals(w0, iterator.next());
+ assertEquals(panel, w0.getParent());
+ iterator.remove();
+ assertNull(w0.getParent());
+ provider.setWidget(0, null); // Update the provider.
+
+ // Try to remove again.
+ try {
+ iterator.remove();
+ fail("Expected IllegalStateException");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Get the next widget.
+ assertEquals(w1, iterator.next());
+ }
+}