7200341: DateFormatSymbols.hashCode() throws ArrayIndexOutOfBoundsException in some circumstances
authornaoto
Tue, 09 Oct 2012 09:59:05 -0700
changeset 14172 fad44417edf8
parent 14171 94eb36844bd7
child 14173 8a94baea7747
7200341: DateFormatSymbols.hashCode() throws ArrayIndexOutOfBoundsException in some circumstances Reviewed-by: okutsu
jdk/src/share/classes/java/text/DateFormatSymbols.java
jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.java
jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.sh
jdk/test/java/util/PluggableLocale/fooprovider.jar
jdk/test/java/util/PluggableLocale/providersrc/DateFormatSymbolsProviderImpl.java
--- a/jdk/src/share/classes/java/text/DateFormatSymbols.java	Tue Oct 09 08:58:27 2012 -0400
+++ b/jdk/src/share/classes/java/text/DateFormatSymbols.java	Tue Oct 09 09:59:05 2012 -0700
@@ -45,6 +45,7 @@
 import java.text.spi.DateFormatSymbolsProvider;
 import java.util.Arrays;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.ResourceBundle;
 import java.util.TimeZone;
 import java.util.concurrent.ConcurrentHashMap;
@@ -366,6 +367,7 @@
      */
     public void setEras(String[] newEras) {
         eras = Arrays.copyOf(newEras, newEras.length);
+        cachedHashCode = 0;
     }
 
     /**
@@ -393,6 +395,7 @@
      */
     public void setMonths(String[] newMonths) {
         months = Arrays.copyOf(newMonths, newMonths.length);
+        cachedHashCode = 0;
     }
 
     /**
@@ -420,6 +423,7 @@
      */
     public void setShortMonths(String[] newShortMonths) {
         shortMonths = Arrays.copyOf(newShortMonths, newShortMonths.length);
+        cachedHashCode = 0;
     }
 
     /**
@@ -439,6 +443,7 @@
      */
     public void setWeekdays(String[] newWeekdays) {
         weekdays = Arrays.copyOf(newWeekdays, newWeekdays.length);
+        cachedHashCode = 0;
     }
 
     /**
@@ -458,6 +463,7 @@
      */
     public void setShortWeekdays(String[] newShortWeekdays) {
         shortWeekdays = Arrays.copyOf(newShortWeekdays, newShortWeekdays.length);
+        cachedHashCode = 0;
     }
 
     /**
@@ -474,6 +480,7 @@
      */
     public void setAmPmStrings(String[] newAmpms) {
         ampms = Arrays.copyOf(newAmpms, newAmpms.length);
+        cachedHashCode = 0;
     }
 
     /**
@@ -558,6 +565,7 @@
         }
         zoneStrings = aCopy;
         isZoneStringsSet = true;
+        cachedHashCode = 0;
     }
 
     /**
@@ -576,6 +584,7 @@
     public void setLocalPatternChars(String newLocalPatternChars) {
         // Call toString() to throw an NPE in case the argument is null
         localPatternChars = newLocalPatternChars.toString();
+        cachedHashCode = 0;
     }
 
     /**
@@ -597,12 +606,23 @@
      * Override hashCode.
      * Generates a hash code for the DateFormatSymbols object.
      */
+    @Override
     public int hashCode() {
-        int hashcode = 0;
-        String[][] zoneStrings = getZoneStringsWrapper();
-        for (int index = 0; index < zoneStrings[0].length; ++index)
-            hashcode ^= zoneStrings[0][index].hashCode();
-        return hashcode;
+        int hashCode = cachedHashCode;
+        if (hashCode == 0) {
+            hashCode = 5;
+            hashCode = 11 * hashCode + Arrays.hashCode(eras);
+            hashCode = 11 * hashCode + Arrays.hashCode(months);
+            hashCode = 11 * hashCode + Arrays.hashCode(shortMonths);
+            hashCode = 11 * hashCode + Arrays.hashCode(weekdays);
+            hashCode = 11 * hashCode + Arrays.hashCode(shortWeekdays);
+            hashCode = 11 * hashCode + Arrays.hashCode(ampms);
+            hashCode = 11 * hashCode + Arrays.deepHashCode(getZoneStringsWrapper());
+            hashCode = 11 * hashCode + Objects.hashCode(localPatternChars);
+            cachedHashCode = hashCode;
+        }
+
+        return hashCode;
     }
 
     /**
@@ -641,6 +661,11 @@
 
     private transient int lastZoneIndex = 0;
 
+    /**
+     * Cached hash code
+     */
+    transient volatile int cachedHashCode = 0;
+
     private void initializeData(Locale desiredLocale) {
         locale = desiredLocale;
 
@@ -782,6 +807,7 @@
             dst.zoneStrings = null;
         }
         dst.localPatternChars = src.localPatternChars;
+        dst.cachedHashCode = 0;
     }
 
     /**
--- a/jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.java	Tue Oct 09 08:58:27 2012 -0400
+++ b/jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.java	Tue Oct 09 09:59:05 2012 -0700
@@ -44,6 +44,7 @@
     DateFormatSymbolsProviderTest() {
         availableLocalesTest();
         objectValidityTest();
+        hashCodeTest();
     }
 
     void availableLocalesTest() {
@@ -124,4 +125,17 @@
             }
         }
     }
+
+    // Bug 7200341.
+    void hashCodeTest() {
+        for (Locale target: availloc) {
+            // look for provider's object
+            DateFormatSymbols dfs = DateFormatSymbols.getInstance(target);
+            if (dfs.getClass().getSimpleName().equals("FooDateFormatSymbols")) {
+                // call its hashCode(). success if no ArrayIndexOutOfBoundsException is thrown.
+                dfs.hashCode();
+                break;
+            }
+        }
+    }
 }
--- a/jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.sh	Tue Oct 09 08:58:27 2012 -0400
+++ b/jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.sh	Tue Oct 09 09:59:05 2012 -0700
@@ -23,6 +23,6 @@
 #!/bin/sh
 #
 # @test
-# @bug 4052440
+# @bug 4052440 7200341
 # @summary DateFormatSymbolsProvider tests
 # @run shell ExecTest.sh foo DateFormatSymbolsProviderTest true
Binary file jdk/test/java/util/PluggableLocale/fooprovider.jar has changed
--- a/jdk/test/java/util/PluggableLocale/providersrc/DateFormatSymbolsProviderImpl.java	Tue Oct 09 08:58:27 2012 -0400
+++ b/jdk/test/java/util/PluggableLocale/providersrc/DateFormatSymbolsProviderImpl.java	Tue Oct 09 09:59:05 2012 -0700
@@ -221,5 +221,10 @@
         public void setAmPmStrings(String[] newAmpms) {
             ampms = newAmpms;
         }
+
+        @Override
+        public String[][] getZoneStrings() {
+            return new String[0][0];
+        }
     }
 }