Introduces EnumAttributeParser, makes XMLElement use it, and makes the layout
panel parsers use that.

Review by jgw

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6598 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java
index a5ab01f..71dd0ac 100644
--- a/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java
@@ -1,12 +1,12 @@
 /*
  * 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
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
@@ -48,31 +49,16 @@
     DOCK_NAMES.put("center", "add");
   }
 
-  /**
-   * TODO(jgw): This will be moved into EnumAttributeParser as soon as I get
-   * around to building it.
-   */
-  static String getFullyQualifiedEnumName(Enum<?> e) {
-    Class<?> cls = e.getClass();
-    String clsName = cls.getCanonicalName();
-    if (clsName == null) {
-      // A synthesized enum subtype (e.g., Unit$3) will have no canonical name
-      // (yet will not be marked as synthetic). Its superclass will be the
-      // one we want (e.g., Unit).
-      clsName = cls.getSuperclass().getCanonicalName();
-    }
-    return clsName + "." + e.name();
-  }
-
   public void parse(XMLElement elem, String fieldName, JClassType type,
       UiBinderWriter writer) throws UnableToCompleteException {
     // Generate instantiation (requires a 'unit' ctor param).
     // (Don't generate a ctor for the SplitLayoutPanel; it's implicitly PX).
     if (type != getSplitLayoutPanelType(writer)) {
-      Unit unit = elem.consumeEnumAttribute("unit", Unit.class);
+      JEnumType unitEnumType = writer.getOracle().findType(
+          Unit.class.getCanonicalName()).isEnum();
+      String unit = elem.consumeEnumAttribute("unit", unitEnumType);
       writer.setFieldInitializerAsConstructor(fieldName,
-          writer.getOracle().findType(DockLayoutPanel.class.getName()),
-          getFullyQualifiedEnumName(unit));
+          writer.getOracle().findType(DockLayoutPanel.class.getName()), unit);
     }
 
     // Parse children.
diff --git a/user/src/com/google/gwt/uibinder/parsers/EnumAttributeParser.java b/user/src/com/google/gwt/uibinder/parsers/EnumAttributeParser.java
new file mode 100644
index 0000000..19ebbb1
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/parsers/EnumAttributeParser.java
@@ -0,0 +1,48 @@
+/*
+ * 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.uibinder.parsers;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JEnumConstant;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
+import com.google.gwt.uibinder.rebind.MortalLogger;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Parses an enum attribute.
+ */
+public class EnumAttributeParser extends StrictAttributeParser {
+  private final Map<String, JEnumConstant> values = new HashMap<String, JEnumConstant>();
+
+  public EnumAttributeParser(JEnumType enumType) {
+    JEnumConstant[] constants = enumType.getEnumConstants();
+    for (JEnumConstant c : constants) {
+      values.put(c.getName(), c);
+    }
+  }
+
+  public String parse(String value, MortalLogger logger)
+      throws UnableToCompleteException {
+    JEnumConstant c = values.get(value);
+    if (c != null) {
+      return String.format("%s.%s", c.getEnclosingType().getQualifiedSourceName(),
+          value);
+    }
+    return super.parse(value, logger);
+  }
+}
diff --git a/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java
index 1e93be7..00395c9 100644
--- a/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
@@ -32,11 +33,12 @@
 
   public void parse(XMLElement elem, String fieldName, JClassType type,
       UiBinderWriter writer) throws UnableToCompleteException {
-    // StackLayoutPanel requires a unit ctor.
-    Unit unit = elem.consumeEnumAttribute("unit", Unit.class);
+    JEnumType unitEnumType = writer.getOracle().findType(
+        Unit.class.getCanonicalName()).isEnum();
+    String unit = elem.consumeEnumAttribute("unit", unitEnumType);
     writer.setFieldInitializerAsConstructor(fieldName,
         writer.getOracle().findType(StackLayoutPanel.class.getName()),
-        DockLayoutPanelParser.getFullyQualifiedEnumName(unit));
+        unit);
 
     // Parse children.
     for (XMLElement child : elem.consumeChildElements()) {
diff --git a/user/src/com/google/gwt/uibinder/parsers/TabLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/parsers/TabLayoutPanelParser.java
index 3d0e42c..a5b3343 100644
--- a/user/src/com/google/gwt/uibinder/parsers/TabLayoutPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/parsers/TabLayoutPanelParser.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
@@ -39,15 +40,17 @@
 
   public void parse(XMLElement panelElem, String fieldName, JClassType type,
       UiBinderWriter writer) throws UnableToCompleteException {
+    JEnumType unitEnumType = writer.getOracle().findType(
+        Unit.class.getCanonicalName()).isEnum();
+
     // TabLayoutPanel requires tabBar size and unit ctor args.
     String size = panelElem.consumeDoubleAttribute("barHeight");
-    Unit unit = panelElem.consumeEnumAttribute("barUnit", Unit.class);
+    String unit = panelElem.consumeEnumAttribute("barUnit", unitEnumType);
 
-    String enumName = DockLayoutPanelParser.getFullyQualifiedEnumName(unit);
     JClassType tlpType = writer.getOracle().findType(
         TabLayoutPanel.class.getName());
     writer.setFieldInitializerAsConstructor(fieldName, tlpType,
-        size, enumName);
+        size, unit);
 
     // Parse children.
     for (XMLElement tabElem : panelElem.consumeChildElements()) {
diff --git a/user/src/com/google/gwt/uibinder/rebind/AttributeParsers.java b/user/src/com/google/gwt/uibinder/rebind/AttributeParsers.java
index b5c65d8..3b5963d 100644
--- a/user/src/com/google/gwt/uibinder/rebind/AttributeParsers.java
+++ b/user/src/com/google/gwt/uibinder/rebind/AttributeParsers.java
@@ -15,8 +15,11 @@
  */
 package com.google.gwt.uibinder.rebind;
 
+import com.google.gwt.core.ext.typeinfo.JEnumType;
 import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.uibinder.parsers.AttributeParser;
+import com.google.gwt.uibinder.parsers.EnumAttributeParser;
 import com.google.gwt.uibinder.parsers.StrictAttributeParser;
 
 import java.util.HashMap;
@@ -49,8 +52,11 @@
    * keyed by method parameter signatures.
    */
   private final Map<String, String> parsers = new HashMap<String, String>();
+  private final TypeOracle oracle;
 
-  public AttributeParsers() {
+  public AttributeParsers(TypeOracle oracle) {
+    this.oracle = oracle;
+    
     addAttributeParser(BOOLEAN,
         "com.google.gwt.uibinder.parsers.BooleanAttributeParser");
 
@@ -73,11 +79,23 @@
 
   public AttributeParser get(JType... types) {
     AttributeParser rtn = getForKey(getParametersKey(types));
-    if (rtn == null && types.length == 1) {
-      rtn = new StrictAttributeParser();
+    if (rtn != null || types.length > 1) {
+      return rtn;
+    }
+    
+    /* A couple of special cases when we're asked to handle a single type */
+    
+    /* Maybe it's an enum */
+    JEnumType enumType = types[0].isEnum();
+    if (enumType != null) {
+      return new EnumAttributeParser(enumType);
     }
 
-    return rtn;
+    /*
+     * Dunno what it is, so let a StrictAttributeParser look for
+     * a {field.reference}
+     */
+    return new StrictAttributeParser();
   }
 
   public AttributeParser getBooleanParser() {
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
index 7f4045b..5bd718a 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
@@ -96,15 +96,15 @@
     PrintWriter printWriter = writers.tryToMakePrintWriterFor(implName);
 
     if (printWriter != null) {
-      generateOnce(interfaceType, implName, packageName, printWriter, logger,
-          oracle, writers);
+      generateOnce(interfaceType, implName, printWriter, logger, oracle,
+          writers);
     }
     return packageName + "." + implName;
   }
 
   private void generateOnce(JClassType interfaceType, String implName,
-      String packageName, PrintWriter binderPrintWrier, TreeLogger treeLogger,
-      TypeOracle oracle, PrintWriterManager writerManager)
+      PrintWriter binderPrintWrier, TreeLogger treeLogger, TypeOracle oracle,
+      PrintWriterManager writerManager)
       throws UnableToCompleteException {
 
     MortalLogger logger = new MortalLogger(treeLogger);
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index e094e7e..4a767df 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -271,7 +271,7 @@
     handlerEvaluator = new HandlerEvaluator(ownerClass, logger, oracle);
     fieldManager = new FieldManager(logger);
 
-    attributeParsers = new AttributeParsers();
+    attributeParsers = new AttributeParsers(oracle);
     bundleParsers = new BundleAttributeParsers(oracle, gwtPrefix, logger,
         getOwnerClass(), templatePath, uiOwnerType);
   }
diff --git a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
index 6e9babc..fcced5a 100644
--- a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
+++ b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
@@ -16,6 +16,7 @@
 package com.google.gwt.uibinder.rebind;
 
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
@@ -234,26 +235,10 @@
    * @return the attribute's value
    * @throws UnableToCompleteException
    */
-  public <T extends Enum<T>> T consumeEnumAttribute(String attr, Class<T> type)
+  public String consumeEnumAttribute(String name, JEnumType enumType)
       throws UnableToCompleteException {
-    String strValue = consumeRawAttribute(attr);
-
-    // Get the enum value. Enum.valueOf() throws IAE if the specified string is
-    // not valid.
-    T value = null;
-    try {
-      // Enum.valueOf() doesn't accept null arguments.
-      if (strValue != null) {
-        value = Enum.valueOf(type, strValue);
-      }
-    } catch (IllegalArgumentException e) {
-    }
-
-    if (value == null) {
-      logger.die(String.format("Error parsing \"%s\" attribute of \"%s\" "
-          + "as a %s enum", attr, this, type.getSimpleName()));
-    }
-    return value;
+    String value = consumeRawAttribute(name);
+    return attributeParsers.get(enumType).parse(value, logger);
   }
 
   /**
diff --git a/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java b/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java
index 28f20c9..8f224b4 100644
--- a/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java
+++ b/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java
@@ -226,7 +226,7 @@
     item = (Element) doc.getDocumentElement().getElementsByTagName("elm").item(
         0);
 
-    elemProvider = new XMLElementProviderImpl(new AttributeParsers(), null,
+    elemProvider = new XMLElementProviderImpl(new AttributeParsers(null), null,
         new DummyMortalLogger());
     elm = elemProvider.get(item);
   }
diff --git a/user/test/com/google/gwt/uibinder/test/client/EnumeratedLabel.java b/user/test/com/google/gwt/uibinder/test/client/EnumeratedLabel.java
new file mode 100644
index 0000000..790cb2c
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/EnumeratedLabel.java
@@ -0,0 +1,25 @@
+package com.google.gwt.uibinder.test.client;
+
+import com.google.gwt.user.client.ui.Label;
+
+public class EnumeratedLabel extends Label {
+  public enum Suffix { ending, suffix, tail}
+
+  private Suffix suffix = Suffix.ending;
+  private String value = "";
+  
+  @Override
+  public void setText(String text) {
+    this.value = text;
+    update();
+  }
+  
+  public void setSuffix(Suffix suffix) {
+    this.suffix = suffix;
+    update();
+  }
+
+  private void update() {
+    super.setText(value + ": " + suffix);
+  }
+}
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 465ff75..d40617a 100644
--- a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
+++ b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
@@ -26,6 +26,7 @@
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.resources.client.ClientBundle;
 import com.google.gwt.resources.client.CssResource.NotStrict;
+import com.google.gwt.uibinder.test.client.EnumeratedLabel.Suffix;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.ui.DisclosurePanel;
 import com.google.gwt.user.client.ui.DockPanel;
@@ -81,10 +82,10 @@
   }
 
   public void testBraceEscaping() {
-    assertEquals("blah di blah {foo: \"bar\"} di blah", 
+    assertEquals("blah di blah {foo: \"bar\"} di blah",
         widgetUi.bracedParagraph.getAttribute("fnord"));
   }
-  
+
   public void testBundle() {
     assertEquals(getCenter(), widgetUi.bundledLabel.getParent());
     assertEquals(new FakeBundle().helloText(), widgetUi.bundledLabel.getText());
@@ -168,6 +169,12 @@
         + "translation: funny characters \" \" ' ' & < > > { }", t);
   }
 
+  public void testEnums() {
+    Suffix expected = EnumeratedLabel.Suffix.tail;
+    assertTrue("Should end with suffix \"" + expected + "\"",
+        widgetUi.enumLabel.getText().endsWith(expected.toString()));
+  }
+
   public void testProtectedDomTextMessageWithFunnyChars() {
     String t = widgetUi.funnyCharsProtectedMessageParagraph.getInnerText();
     assertEquals("Don't forget about protected untranslatable blocks: "
@@ -236,7 +243,7 @@
   }
 
   interface Bundle extends ClientBundle {
-    @Source({"WidgetBasedUi.css", "Menu.css"})
+    @Source( {"WidgetBasedUi.css", "Menu.css"})
     @NotStrict
     WidgetBasedUi.Style style();
   }
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 39500f2..3ca675e 100644
--- a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java
+++ b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java
@@ -22,7 +22,6 @@
 import com.google.gwt.dom.client.OListElement;
 import com.google.gwt.dom.client.ParagraphElement;
 import com.google.gwt.dom.client.SpanElement;
-import com.google.gwt.dom.client.StyleInjector;
 import com.google.gwt.dom.client.TableElement;
 import com.google.gwt.resources.client.CssResource;
 import com.google.gwt.resources.client.DataResource;
@@ -63,13 +62,11 @@
   }
   private static final Binder binder = GWT.create(Binder.class);
 
-  private static boolean stylesInjected = false;
+  @UiField(provided = true)
+  final WidgetBasedUiExternalResources external = GWT.create(WidgetBasedUiExternalResources.class);
 
   @UiField(provided = true)
-  final WidgetBasedUiExternalResources external;
-
-  @UiField(provided = true)
-  final Label bundledLabel;
+  final Label bundledLabel =  new Label();
 
   @UiField Style myStyle;
   @UiField ClickyLink customLinkWidget;
@@ -123,17 +120,10 @@
   @UiField DataResource heartCursorResource;
   @UiField CssImportScopeSample cssImportScopeSample;
   @UiField ParagraphElement bracedParagraph;
+  @UiField EnumeratedLabel enumLabel;
 
   public WidgetBasedUi() {
-    this.bundledLabel = new Label();
-    this.external = GWT.create(WidgetBasedUiExternalResources.class);
-
-    // Inject only once.
-    if (!stylesInjected) {
-      StyleInjector.injectStylesheet(external.style().getText());
-      stylesInjected = true;
-    }
-
+    external.style().ensureInjected();
     initWidget(binder.createAndBindUi(this));
   }
 
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 9c3ed83..7bb97de 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
@@ -495,6 +495,7 @@
       <p><demo:NeedlesslyAnnotatedLabel ui:field="needlessLabel"
         text="Sometimes people do things they don't need to do. Don't punish them."/></p>
       <demo:FooLabel ui:field='theFoo' pojo="{values.pojo}"/>
+      <demo:EnumeratedLabel ui:field='enumLabel' suffix='tail'>This label uses an enum for its</demo:EnumeratedLabel>
 
     <h2>How to use the debugId, addStyleNames, addDependentStyleNames attributes</h2>
     <p>You can use the <strong>debugId</strong>, <strong>addStyleNames</strong>, and <strong>addDependentStyleNames</strong>