Add java.nio.charset.Charset and java.nio.charset.StandardCharsets

Backported Guava-GWT's java.nio.Charset implementation and added
ISO-8859-1, which is currently supported by GWT.

Change-Id: I1243baec2c46aaa79c3b8e497eda16cbf20a87b5
Review-Link: https://gwt-review.googlesource.com/#/c/13260/
diff --git a/tools/api-checker/config/gwt27_28userApi.conf b/tools/api-checker/config/gwt27_28userApi.conf
index 99ebf6b..bdf843e 100644
--- a/tools/api-checker/config/gwt27_28userApi.conf
+++ b/tools/api-checker/config/gwt27_28userApi.conf
@@ -167,3 +167,7 @@
 java.lang.Number::floatRegex MISSING
 java.math.BigDecimal::unscaledRegex MISSING
 
+# Add java.nio.charset.Charset and java.nio.charset.StandardCharsets
+java.lang.String::String([BIILjava/lang/String;) OVERLOADED_METHOD_CALL
+java.lang.String::String([BLjava/lang/String;) OVERLOADED_METHOD_CALL
+java.lang.String::getBytes(Ljava/lang/String;) OVERLOADED_METHOD_CALL
diff --git a/user/super/com/google/gwt/emul/java/lang/String.java b/user/super/com/google/gwt/emul/java/lang/String.java
index f540688..e027d48 100644
--- a/user/super/com/google/gwt/emul/java/lang/String.java
+++ b/user/super/com/google/gwt/emul/java/lang/String.java
@@ -18,6 +18,7 @@
 
 import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
 import java.util.Comparator;
 import java.util.Locale;
 
@@ -214,6 +215,14 @@
   /**
    * @skip
    */
+  static String _String(byte[] bytes, int ofs, int len, Charset charset)
+      throws UnsupportedEncodingException {
+    return _String(bytes, ofs, len, charset.name());
+  }
+
+  /**
+   * @skip
+   */
   static String _String(byte[] bytes, String charsetName)
       throws UnsupportedEncodingException {
     return _String(bytes, 0, bytes.length, charsetName);
@@ -222,6 +231,14 @@
   /**
    * @skip
    */
+  static String _String(byte[] bytes, Charset charset)
+      throws UnsupportedEncodingException {
+    return _String(bytes, 0, bytes.length, charset.name());
+  }
+
+  /**
+   * @skip
+   */
   static String _String(char value[]) {
     return valueOf(value);
   }
@@ -454,12 +471,24 @@
     _String(bytes, ofs, len, charsetName);
   }
 
+  public String(byte[] bytes, int ofs, int len, Charset charset)
+      throws UnsupportedEncodingException {
+    // magic delegation to _String
+    _String(bytes, ofs, len, charset);
+  }
+
   public String(byte[] bytes, String charsetName)
       throws UnsupportedEncodingException {
     // magic delegation to _String
     _String(bytes, charsetName);
   }
 
+  public String(byte[] bytes, Charset charset)
+      throws UnsupportedEncodingException {
+    // magic delegation to _String
+    _String(bytes, charset);
+  }
+
   public String(char value[]) {
     // magic delegation to _String
     _String(value);
@@ -572,6 +601,10 @@
     throw new UnsupportedEncodingException(charSet + " is not supported");
   }
 
+  public byte[] getBytes(Charset charSet) throws UnsupportedEncodingException {
+    return getBytes(charSet.name());
+  }
+
   public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) {
     for (int srcIdx = srcBegin; srcIdx < srcEnd; ++srcIdx) {
       dst[dstBegin++] = charAt(srcIdx);
diff --git a/user/super/com/google/gwt/emul/java/nio/charset/Charset.java b/user/super/com/google/gwt/emul/java/nio/charset/Charset.java
new file mode 100644
index 0000000..3633424
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/nio/charset/Charset.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 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 java.nio.charset;
+
+import java.util.Collections;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * A minimal GWT emulation of {@link Charset}.
+ */
+public abstract class Charset implements Comparable<Charset> {
+  static final Charset ISO_8859_1 = new Charset("ISO-8859-1") { };
+  static final Charset UTF_8 = new Charset("UTF-8") { };
+
+  private static final class AvailableCharsets {
+    private static final SortedMap<String, Charset> CHARSETS;
+    static {
+      SortedMap<String, Charset> map = new TreeMap<String, Charset>() { };
+      map.put(ISO_8859_1.name(), ISO_8859_1);
+      map.put(UTF_8.name(), UTF_8);
+      CHARSETS = Collections.unmodifiableSortedMap(map);
+    }
+  }
+
+  public static SortedMap<String, Charset> availableCharsets() {
+    return AvailableCharsets.CHARSETS;
+  }
+
+  public static Charset forName(String charsetName) {
+    if (charsetName == null) {
+      throw new IllegalArgumentException("Null charset name");
+    } else if (!isLegalCharsetName(charsetName)) {
+      throw new IllegalCharsetNameException(charsetName);
+    }
+    Charset charset = AvailableCharsets.CHARSETS.get(charsetName.toUpperCase());
+    if (charset == null) {
+      throw new UnsupportedCharsetException(charsetName);
+    }
+    return charset;
+  }
+
+  private static native boolean isLegalCharsetName(String name) /*-{
+    return /^[A-Za-z0-9][\w-:\.\+]*$/.test(name);
+  }-*/;
+
+  private final String name;
+
+  private Charset(String name) {
+    this.name = name;
+  }
+
+  public final String name() {
+    return name;
+  }
+
+  @Override
+  public final int compareTo(Charset that) {
+    return this.name.compareToIgnoreCase(that.name);
+  }
+
+  @Override
+  public final int hashCode() {
+    return name.hashCode();
+  }
+
+  @Override
+  public final boolean equals(Object o) {
+    if (o == this) {
+      return true;
+    }
+    if (!(o instanceof Charset)) {
+      return false;
+    }
+    Charset that = (Charset) o;
+    return this.name.equals(that.name);
+  }
+
+  @Override
+  public final String toString() {
+    return name;
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/nio/charset/IllegalCharsetNameException.java b/user/super/com/google/gwt/emul/java/nio/charset/IllegalCharsetNameException.java
new file mode 100644
index 0000000..5b2d261
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/nio/charset/IllegalCharsetNameException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 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 java.nio.charset;
+
+/**
+ * GWT emulation of {@link IllegalCharsetNameException}.
+ */
+public class IllegalCharsetNameException extends IllegalArgumentException {
+  private final String charsetName;
+
+  public IllegalCharsetNameException(String charsetName) {
+    super(String.valueOf(charsetName));
+    this.charsetName = charsetName;
+  }
+
+  public String getCharsetName() {
+    return charsetName;
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/nio/charset/StandardCharsets.java b/user/super/com/google/gwt/emul/java/nio/charset/StandardCharsets.java
new file mode 100644
index 0000000..b7e0079
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/nio/charset/StandardCharsets.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 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 java.nio.charset;
+
+/**
+ * Constant definitions for the standard Charsets.
+ */
+public final class StandardCharsets {
+  public static final Charset ISO_8859_1 = Charset.ISO_8859_1;
+  public static final Charset UTF_8 = Charset.UTF_8;
+}
diff --git a/user/super/com/google/gwt/emul/java/nio/charset/UnsupportedCharsetException.java b/user/super/com/google/gwt/emul/java/nio/charset/UnsupportedCharsetException.java
new file mode 100644
index 0000000..b8fe90c
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/nio/charset/UnsupportedCharsetException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 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 java.nio.charset;
+
+/**
+ * GWT emulation of {@link UnsupportedCharsetException}.
+ */
+public class UnsupportedCharsetException extends IllegalArgumentException {
+  private final String charsetName;
+
+  public UnsupportedCharsetException(String charsetName) {
+    super(String.valueOf(charsetName));
+    this.charsetName = charsetName;
+  }
+
+  public String getCharsetName() {
+    return charsetName;
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/EmulSuite.java b/user/test/com/google/gwt/emultest/EmulSuite.java
index d1ad5bf..9a59baa 100644
--- a/user/test/com/google/gwt/emultest/EmulSuite.java
+++ b/user/test/com/google/gwt/emultest/EmulSuite.java
@@ -38,6 +38,8 @@
 import com.google.gwt.emultest.java.math.MathContextTest;
 import com.google.gwt.emultest.java.math.MathContextWithObfuscatedEnumsTest;
 import com.google.gwt.emultest.java.math.RoundingModeTest;
+import com.google.gwt.emultest.java.nio.charset.CharsetTest;
+import com.google.gwt.emultest.java.nio.charset.StandardCharsetsTest;
 import com.google.gwt.emultest.java.security.MessageDigestTest;
 import com.google.gwt.emultest.java.sql.SqlDateTest;
 import com.google.gwt.emultest.java.sql.SqlTimeTest;
@@ -87,6 +89,10 @@
     suite.addTestSuite(MathContextTest.class);
     suite.addTestSuite(MathContextWithObfuscatedEnumsTest.class);
 
+    //-- java.nio
+    suite.addTestSuite(CharsetTest.class);
+    suite.addTestSuite(StandardCharsetsTest.class);
+
     //-- java.security
     suite.addTestSuite(MessageDigestTest.class);
 
diff --git a/user/test/com/google/gwt/emultest/java/lang/StringTest.java b/user/test/com/google/gwt/emultest/java/lang/StringTest.java
index 0558f8b..db7efd2 100644
--- a/user/test/com/google/gwt/emultest/java/lang/StringTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/StringTest.java
@@ -21,6 +21,7 @@
 import com.google.gwt.testing.TestUtils;
 
 import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
 import java.util.Locale;
 
 /**
@@ -203,6 +204,21 @@
       assertTrue("Should have thrown IOOB in Development Mode", GWT.isScript());
     } catch (IndexOutOfBoundsException expected) {
     }
+    try {
+      new String(bytes, 1, 6, Charset.forName(encoding));
+      assertTrue("Should have thrown IOOB in Development Mode", GWT.isScript());
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      new String(bytes, -1, 2, Charset.forName(encoding));
+      assertTrue("Should have thrown IOOB in Development Mode", GWT.isScript());
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      new String(bytes, 6, 2, Charset.forName(encoding));
+      assertTrue("Should have thrown IOOB in Development Mode", GWT.isScript());
+    } catch (IndexOutOfBoundsException expected) {
+    }
   }
 
   public void testConstructorUtf8() throws UnsupportedEncodingException {
@@ -234,6 +250,21 @@
       assertTrue("Should have thrown IOOB in Development Mode", GWT.isScript());
     } catch (IndexOutOfBoundsException expected) {
     }
+    try {
+      new String(bytes, 2, 12, Charset.forName(encoding));
+      assertTrue("Should have thrown IOOB in Development Mode", GWT.isScript());
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      new String(bytes, -1, 2, Charset.forName(encoding));
+      assertTrue("Should have thrown IOOB in Development Mode", GWT.isScript());
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      new String(bytes, 12, 2, Charset.forName(encoding));
+      assertTrue("Should have thrown IOOB in Development Mode", GWT.isScript());
+    } catch (IndexOutOfBoundsException expected) {
+    }
   }
 
   /*
diff --git a/user/test/com/google/gwt/emultest/java/nio/charset/CharsetTest.java b/user/test/com/google/gwt/emultest/java/nio/charset/CharsetTest.java
new file mode 100644
index 0000000..ad6f887
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/nio/charset/CharsetTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2015 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.emultest.java.nio.charset;
+
+import com.google.gwt.emultest.java.util.EmulTestBase;
+
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+
+/**
+ * Unit test for the {@link java.nio.charset.Charset} emulated class.
+ */
+public class CharsetTest extends EmulTestBase {
+
+  public void testIso88591() {
+    assertEquals("ISO-8859-1", Charset.forName("ISO-8859-1").name());
+    assertEquals("ISO-8859-1", Charset.forName("iso-8859-1").name());
+  }
+
+  public void testUtf8() {
+    assertEquals("UTF-8", Charset.forName("UTF-8").name());
+    assertEquals("UTF-8", Charset.forName("utf-8").name());
+  }
+
+  public void testForName_null() {
+    try {
+      Charset.forName(null);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testForName_illegal() {
+    try {
+      Charset.forName("");
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+    try {
+      Charset.forName("!@#$");
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+    try {
+      Charset.forName("_UTF_8");
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+    try {
+      Charset.forName("UTF_8#");
+      fail();
+    } catch (IllegalCharsetNameException expected) {
+    }
+  }
+
+  public void testForName_unsupported() {
+    try {
+      Charset.forName("qwer");
+      fail();
+    } catch (UnsupportedCharsetException expected) {
+    }
+    try {
+      Charset.forName("A:.+-:_Aa0");
+      fail();
+    } catch (UnsupportedCharsetException expected) {
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java/nio/charset/StandardCharsetsTest.java b/user/test/com/google/gwt/emultest/java/nio/charset/StandardCharsetsTest.java
new file mode 100644
index 0000000..a354fc6
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/nio/charset/StandardCharsetsTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 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.emultest.java.nio.charset;
+
+import com.google.gwt.emultest.java.util.EmulTestBase;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Unit test for the {@link java.nio.charset.StandardCharsets} emulated class.
+ */
+public class StandardCharsetsTest extends EmulTestBase {
+
+  public void testIso88591() {
+    assertEquals(Charset.forName("ISO-8859-1"), StandardCharsets.ISO_8859_1);
+  }
+
+  public void testUtf8() {
+    assertEquals(Charset.forName("UTF-8"), StandardCharsets.UTF_8);
+  }
+}