Add support for FlowPanels to have custom tags.

This is useful for translating HTML code to UiBinder, where a "panel"
needs to have, per the mockup's HTML/CSS, an "li" tag or "p" tag.

Previously this required using HTMLPanel, but HTMLPanel doesn't
implement InsertPanel, which has a useful add(Widget, index) method.

Change-Id: Ic43bba2c115c7b531b75ed9f7a5d81371e231893
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/FlowPanelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/FlowPanelParser.java
new file mode 100644
index 0000000..19d5af4
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/elementparsers/FlowPanelParser.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 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.elementparsers;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.uibinder.rebind.UiBinderWriter;
+import com.google.gwt.uibinder.rebind.XMLElement;
+import com.google.gwt.uibinder.rebind.model.OwnerField;
+
+/**
+ * Parses {@link com.google.gwt.user.client.ui.FlowPanel} widgets.
+ */
+public class FlowPanelParser implements ElementParser {
+
+  public void parse(XMLElement elem, String fieldName, JClassType type,
+      final UiBinderWriter writer) throws UnableToCompleteException {
+    String customTag = elem.consumeStringAttribute("tag", null);
+    if (null != customTag) {
+      OwnerField uiField = writer.getOwnerClass().getUiField(fieldName);
+      if (uiField != null && uiField.isProvided()) {
+        writer.die("UiField %s for FlowPanel cannot set tag when it is also provided.", fieldName);
+      }
+      writer.setFieldInitializerAsConstructor(fieldName, customTag);
+    }
+  }
+
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index 850c808..41414dd 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -1394,6 +1394,7 @@
     addWidgetParser("HasTreeItems");
     addWidgetParser("HasWidgets");
     addWidgetParser("HTMLPanel");
+    addWidgetParser("FlowPanel");
     addWidgetParser("AbsolutePanel");
     addWidgetParser("DockPanel");
     addWidgetParser("StackPanel");
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 0179a41..899aa10 100644
--- a/user/src/com/google/gwt/user/client/ui/FlowPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/FlowPanel.java
@@ -15,13 +15,14 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Node;
-import com.google.gwt.user.client.DOM;
 
 /**
  * A panel that formats its child widgets using the default HTML layout
  * behavior.
- * 
+ *
  * <p>
  * <img class='gallery' src='doc-files/FlowPanel.png'/>
  * </p>
@@ -31,12 +32,19 @@
    * Creates an empty flow panel.
    */
   public FlowPanel() {
-    setElement(DOM.createDiv());
+    this(DivElement.TAG);
+  }
+
+  /**
+   * Creates an empty flow panel with a custom tag.
+   */
+  public FlowPanel(String tag) {
+    setElement(Document.get().createElement(tag));
   }
 
   /**
    * Adds a new child widget to the panel.
-   * 
+   *
    * @param w the widget to be added
    */
   @Override
@@ -58,18 +66,20 @@
     }
   }
 
+  @Override
   public void insert(IsWidget w, int beforeIndex) {
     insert(asWidgetOrNull(w), beforeIndex);
   }
 
   /**
    * Inserts a widget before the specified index.
-   * 
+   *
    * @param w the widget to be inserted
    * @param beforeIndex the index before which it will be inserted
    * @throws IndexOutOfBoundsException if <code>beforeIndex</code> is out of
    *           range
    */
+  @Override
   public void insert(Widget w, int beforeIndex) {
     insert(w, getElement(), beforeIndex, true);
   }
diff --git a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
index 3b31b7d..d5897ba 100644
--- a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
+++ b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
@@ -647,7 +647,7 @@
   public void testUrlResource() {
     assertEquals(new FakeBundle().aUrl(), widgetUi.myImage.getSrc());
   }
-  
+
   public void testUiTextWithSafeHtml() {
     assertEquals("<b>this text should be bold!</b>",
         StringCase.toLower(widgetUi.htmlWithComputedSafeHtml.getHTML()));
@@ -656,7 +656,11 @@
     assertEquals("<b>this text won't be bold!</b>",
         StringCase.toLower(widgetUi.labelWithComputedText.getText()));
   }
-  
+
+  public void testFlowPanelWithTag() {
+    assertEquals("P", widgetUi.flowPanelWithTag.getElement().getTagName());
+  }
+
   /**
    * Assert that the expect strings are found in body, and in the order given.
    * WARNING: both body and expected are normalized to lower case, to get around
diff --git a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java
index e389f98..1580b50 100644
--- a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java
+++ b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java
@@ -46,6 +46,7 @@
 import com.google.gwt.user.client.ui.DisclosurePanel;
 import com.google.gwt.user.client.ui.DockPanel;
 import com.google.gwt.user.client.ui.DoubleBox;
+import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.Grid;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.HTMLPanel;
@@ -214,6 +215,7 @@
   @UiField HTML htmlWithComputedSafeHtml;
   @UiField HTML htmlWithComputedText;
   @UiField Label labelWithComputedText;
+  @UiField FlowPanel flowPanelWithTag;
 
   ValueChangeEvent<Double> doubleValueChangeEvent;
   @UiHandler("myDoubleBox")
diff --git a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
index 8ef7529..12805f5 100644
--- a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
+++ b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
@@ -611,6 +611,8 @@
       <gwt:Button ui:field="btnGo" debugId="myButton" addStyleNames="buttonStyle">Go</gwt:Button>
     </gwt:FlowPanel>
 
+    <gwt:FlowPanel ui:field="flowPanelWithTag" tag="p" />
+
     <demo:HandlerDemo />
     <demo:I18nMessageTest />