Implements Enum.valueOf(Class,String), which is also required for Sun's JDK
to correctly compile code using enums.

Patch by: tobyr
Review by: scottb



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1749 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/super/com/google/gwt/emul/java/lang/Enum.java b/user/super/com/google/gwt/emul/java/lang/Enum.java
index c0efe0b..e632fa5 100644
--- a/user/super/com/google/gwt/emul/java/lang/Enum.java
+++ b/user/super/com/google/gwt/emul/java/lang/Enum.java
@@ -27,6 +27,33 @@
 public abstract class Enum<E extends Enum<E>> implements Comparable<E>,
     Serializable {
 
+  public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
+
+    // Have to explicitly test for null, otherwise we won't get 
+    // NullPointerExceptions in web mode
+    if (enumType == null || name == null) {
+      throw new NullPointerException("enumType and name must not be null.");
+    }
+
+    // TODO(scottb) Work some compiler magic to improve this from a linear
+    // search to a map lookup.
+    T[] constants = enumType.getEnumConstants();
+
+    if (constants == null) {
+      throw new IllegalArgumentException(
+          enumType.getName() + " is not an enum.");
+    }
+
+    for (T constant : constants) {
+      if (constant.name().equals(name)) {
+        return constant;
+      }
+    }
+
+    throw new IllegalArgumentException(enumType.getName()
+        + " does not have an enum constant named, " + name + ".");
+  }
+
   protected static <T extends Enum<T>> T valueOf(JavaScriptObject map,
       String name) {
     T result = Enum.<T> valueOf0(map, "_" + name);
diff --git a/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java b/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java
index 1a67d32..086f7bc 100644
--- a/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java
@@ -197,6 +197,40 @@
       fail("Subclassing.valueOf(\"D\") -- expected IllegalArgumentException");
     } catch (IllegalArgumentException e) {
     }
+    
+    enumValuesTest(Basic.class);
+    enumValuesTest(Complex.class);
+    enumValuesTest(Subclassing.class);
+    
+    try {
+      Enum.valueOf(Basic.class, "foo");
+      fail("Passed an invalid enum constant name to Enum.valueOf; expected "
+          + "IllegalArgumentException");
+    } catch (IllegalArgumentException e) {
+    }
+    
+    try {
+      Class fakeEnumClass = String.class;
+      Enum.valueOf(fakeEnumClass, "foo");
+      fail("Passed a non enum class to Enum.valueOf; expected " 
+              + "IllegalArgumentException");
+    } catch (IllegalArgumentException e) {       
+    }
+     
+    try {
+      Class<Basic> nullEnumClass = null;
+      Enum.valueOf(nullEnumClass, "foo");
+      fail("Passed a null enum class to Enum.valueOf; expected "
+          + "NullPointerException");
+    } catch (NullPointerException e) {
+    }
+      
+    try {
+      Enum.valueOf(Basic.class, null);
+      fail("Passed a null enum constant to Enum.valueOf; expected "
+          + "NullPointerException");
+    } catch (NullPointerException e) {
+    }
   }
 
   public void testValues() {
@@ -218,4 +252,11 @@
     assertEquals(Subclassing.B, subs[1]);
     assertEquals(Subclassing.C, subs[2]);
   }
+  
+  private <T extends Enum<T>> void enumValuesTest(Class<T> enumClass) {
+    T[] constants = enumClass.getEnumConstants();
+    for (T constant : constants) {
+      assertEquals(constant, Enum.valueOf(enumClass, constant.name()));
+    }
+  }
 }