8008576: Calendar mismatch using Host LocaleProviderAdapter
authornaoto
Thu, 14 Mar 2013 11:29:16 -0700
changeset 16481 8e30386cc014
parent 16480 fcad3b6ec812
child 16482 cbc9c93df1fd
child 16492 4cd9aa345503
8008576: Calendar mismatch using Host LocaleProviderAdapter Reviewed-by: okutsu
jdk/make/java/java/FILES_java.gmk
jdk/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java
jdk/src/share/classes/java/util/Calendar.java
jdk/src/share/classes/sun/util/locale/LanguageTag.java
jdk/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java
jdk/src/share/classes/sun/util/locale/provider/CalendarProviderImpl.java
jdk/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java
jdk/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java
jdk/src/share/classes/sun/util/spi/CalendarProvider.java
jdk/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java
jdk/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c
--- a/jdk/make/java/java/FILES_java.gmk	Thu Mar 14 16:59:06 2013 +0000
+++ b/jdk/make/java/java/FILES_java.gmk	Thu Mar 14 11:29:16 2013 -0700
@@ -209,6 +209,7 @@
             sun/util/locale/provider/CalendarDataProviderImpl.java \
             sun/util/locale/provider/CalendarDataUtility.java \
             sun/util/locale/provider/CalendarNameProviderImpl.java \
+            sun/util/locale/provider/CalendarProviderImpl.java \
             sun/util/locale/provider/CollationRules.java \
             sun/util/locale/provider/CollatorProviderImpl.java \
             sun/util/locale/provider/CurrencyNameProviderImpl.java \
@@ -232,6 +233,7 @@
             sun/util/locale/provider/SPILocaleProviderAdapter.java \
             sun/util/locale/provider/TimeZoneNameProviderImpl.java \
             sun/util/locale/provider/TimeZoneNameUtility.java \
+	sun/util/spi/CalendarProvider.java \
     java/util/LocaleISOData.java \
         sun/util/cldr/CLDRLocaleProviderAdapter.java \
     java/util/MissingResourceException.java \
--- a/jdk/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java	Thu Mar 14 16:59:06 2013 +0000
+++ b/jdk/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java	Thu Mar 14 11:29:16 2013 -0700
@@ -32,11 +32,13 @@
 import java.text.spi.DecimalFormatSymbolsProvider;
 import java.text.spi.NumberFormatProvider;
 import java.util.Collections;
+import java.util.Calendar;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.ResourceBundle.Control;
 import java.util.Set;
+import java.util.TimeZone;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicReferenceArray;
@@ -45,6 +47,7 @@
 import java.util.spi.CurrencyNameProvider;
 import java.util.spi.LocaleNameProvider;
 import java.util.spi.TimeZoneNameProvider;
+import sun.util.spi.CalendarProvider;
 
 /**
  * LocaleProviderAdapter implementation for the Mac OS X locale data
@@ -94,17 +97,56 @@
 
     private static final Set<Locale> supportedLocaleSet;
     static {
-        Set<Locale> tmpSet = new HashSet<Locale>();
+        Set<Locale> tmpSet = new HashSet<>();
         // Assuming the default locales do not include any extensions, so
         // no stripping is needed here.
-        Locale l = Locale.forLanguageTag(getDefaultLocale(CAT_FORMAT).replaceAll("_","-"));
+        Locale l = convertMacOSXLocaleToJavaLocale(getDefaultLocale(CAT_FORMAT));
         tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
-        l = Locale.forLanguageTag(getDefaultLocale(CAT_DISPLAY).replaceAll("_","-"));
+        l = convertMacOSXLocaleToJavaLocale(getDefaultLocale(CAT_DISPLAY));
         tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
         supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
     }
     private final static Locale[] supportedLocale = supportedLocaleSet.toArray(new Locale[0]);
 
+    @SuppressWarnings("fallthrough")
+    private static Locale convertMacOSXLocaleToJavaLocale(String macosxloc) {
+        // MacOSX may return ICU notation, here is the quote from CFLocale doc:
+        // "The corresponding value is a CFString containing the POSIX locale
+        // identifier as used by ICU, such as "ja_JP". If you have a variant
+        // locale or a different currency or calendar, it can be as complex as
+        // "en_US_POSIX@calendar=japanese;currency=EUR" or
+        // "az_Cyrl_AZ@calendar=buddhist;currency=JPY".
+        String[] tmp = macosxloc.split("@");
+        String langTag = tmp[0].replace('_', '-');
+        if (tmp.length > 1) {
+            String[] ext = tmp[1].split(";");
+            for (String keyval : ext) {
+                // We are only interested in "calendar" value for now.
+                if (keyval.startsWith("calendar=")) {
+                    String calid = keyval.substring(keyval.indexOf('=')+1);
+                    switch (calid) {
+                        case "gregorian":
+                            langTag += "-u-ca-gregory";
+                            break;
+                        case "japanese":
+                            // Tweak for ja_JP_JP
+                            if (tmp[0].equals("ja_JP")) {
+                                return JRELocaleConstants.JA_JP_JP;
+                            }
+
+                            // fall through
+
+                        default:
+                            langTag += "-u-ca-" + calid;
+                            break;
+                    }
+                }
+            }
+        }
+
+        return Locale.forLanguageTag(langTag);
+    }
+
     public static DateFormatProvider getDateFormatProvider() {
         return new DateFormatProvider() {
 
@@ -170,9 +212,8 @@
                 if (isSupportedLocale(Locale.getDefault(Locale.Category.FORMAT))) {
                     return supportedLocale;
                 }
-
-                        return new Locale[0];
-                    }
+                return new Locale[0];
+            }
 
             @Override
             public boolean isSupportedLocale(Locale locale) {
@@ -362,6 +403,30 @@
         };
     }
 
+    public static CalendarProvider getCalendarProvider() {
+        return new CalendarProvider() {
+            @Override
+            public Locale[] getAvailableLocales() {
+                return getSupportedCalendarLocales();
+            }
+
+            @Override
+            public boolean isSupportedLocale(Locale locale) {
+                return isSupportedCalendarLocale(locale);
+            }
+
+            @Override
+            public Calendar getInstance(TimeZone zone, Locale locale) {
+                return new Calendar.Builder()
+                             .setLocale(locale)
+                             .setCalendarType(getCalendarID(locale.toLanguageTag()))
+                             .setTimeZone(zone)
+                             .setInstant(System.currentTimeMillis())
+                             .build();
+            }
+        };
+    }
+
     public static CurrencyNameProvider getCurrencyNameProvider() {
         return new CurrencyNameProvider() {
             @Override
@@ -455,23 +520,20 @@
     }
 
     private static boolean isSupportedCalendarLocale(Locale locale) {
-        // special case for ja_JP_JP
-        if (JRELocaleConstants.JA_JP_JP.equals(locale)) {
-            return isJapaneseCalendar();
-        }
-
         Locale base = locale.stripExtensions();
         if (!supportedLocaleSet.contains(base)) {
             return false;
         }
 
-        String caltype = locale.getUnicodeLocaleType("ca");
-        if (caltype == null) {
-            return true;
+        String requestedCalType = locale.getUnicodeLocaleType("ca");
+        String nativeCalType =
+            getCalendarID(locale.toLanguageTag()).replaceFirst("gregorian", "gregory");
+
+        if (requestedCalType == null) {
+            return Calendar.getAvailableCalendarTypes().contains(nativeCalType);
+        } else {
+            return requestedCalType.equals(nativeCalType);
         }
-
-        return caltype.replaceFirst("gregory", "gregorian").equals(
-                getCalendarID(locale.toLanguageTag()));
     }
 
     private static boolean isJapaneseCalendar() {
@@ -479,18 +541,15 @@
     }
 
     private static Locale getCalendarLocale(Locale locale) {
-        Locale.Builder lb = new Locale.Builder().setLocale(locale);
-        String calid = getCalendarID(locale.toLanguageTag());
-        switch (calid) {
-            case "gregorian":
-                calid = "gregory";
-                // FALL THROUGH!
-            case "japanese":
-            case "buddhist":
-                lb.setUnicodeLocaleKeyword("ca", calid);
-                return lb.build();
-            default:
-                return locale;
+        String nativeCalType = getCalendarID(locale.toLanguageTag())
+                     .replaceFirst("gregorian", "gregory");
+        if (Calendar.getAvailableCalendarTypes().contains(nativeCalType)) {
+            return new Locale.Builder()
+                           .setLocale(locale)
+                           .setUnicodeLocaleKeyword("ca", nativeCalType)
+                           .build();
+        } else {
+            return locale;
         }
     }
 
--- a/jdk/src/share/classes/java/util/Calendar.java	Thu Mar 14 16:59:06 2013 +0000
+++ b/jdk/src/share/classes/java/util/Calendar.java	Thu Mar 14 11:29:16 2013 -0700
@@ -57,6 +57,8 @@
 import sun.util.BuddhistCalendar;
 import sun.util.calendar.ZoneInfo;
 import sun.util.locale.provider.CalendarDataUtility;
+import sun.util.locale.provider.LocaleProviderAdapter;
+import sun.util.spi.CalendarProvider;
 
 /**
  * The <code>Calendar</code> class is an abstract class that provides methods
@@ -1608,9 +1610,7 @@
      */
     public static Calendar getInstance()
     {
-        Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
-        cal.sharedZone = true;
-        return cal;
+        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
     }
 
     /**
@@ -1637,9 +1637,7 @@
      */
     public static Calendar getInstance(Locale aLocale)
     {
-        Calendar cal = createCalendar(TimeZone.getDefaultRef(), aLocale);
-        cal.sharedZone = true;
-        return cal;
+        return createCalendar(TimeZone.getDefault(), aLocale);
     }
 
     /**
@@ -1660,6 +1658,17 @@
     private static Calendar createCalendar(TimeZone zone,
                                            Locale aLocale)
     {
+        CalendarProvider provider =
+            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
+                                 .getCalendarProvider();
+        if (provider != null) {
+            try {
+                return provider.getInstance(zone, aLocale);
+            } catch (IllegalArgumentException iae) {
+                // fall back to the default instantiation
+            }
+        }
+
         Calendar cal = null;
 
         if (aLocale.hasExtensions()) {
--- a/jdk/src/share/classes/sun/util/locale/LanguageTag.java	Thu Mar 14 16:59:06 2013 +0000
+++ b/jdk/src/share/classes/sun/util/locale/LanguageTag.java	Thu Mar 14 11:29:16 2013 -0700
@@ -134,7 +134,7 @@
     }
 
     /*
-     * BNF in RFC5464
+     * BNF in RFC5646
      *
      * Language-Tag  = langtag             ; normal language tags
      *               / privateuse          ; private use tag
--- a/jdk/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java	Thu Mar 14 16:59:06 2013 +0000
+++ b/jdk/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java	Thu Mar 14 11:29:16 2013 -0700
@@ -43,6 +43,7 @@
 import java.util.spi.LocaleNameProvider;
 import java.util.spi.LocaleServiceProvider;
 import java.util.spi.TimeZoneNameProvider;
+import sun.util.spi.CalendarProvider;
 
 /**
  * An abstract parent class for the
@@ -140,6 +141,14 @@
         return getLocaleServiceProvider(CalendarNameProvider.class);
     }
 
+    /**
+     * Getter methods for sun.util.spi.* providers
+     */
+    @Override
+    public CalendarProvider getCalendarProvider() {
+        return getLocaleServiceProvider(CalendarProvider.class);
+    }
+
     @Override
     public LocaleResources getLocaleResources(Locale locale) {
         return null;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/util/locale/provider/CalendarProviderImpl.java	Thu Mar 14 11:29:16 2013 -0700
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.util.locale.provider;
+
+import java.util.Calendar;
+import java.util.Calendar.Builder;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TimeZone;
+import sun.util.spi.CalendarProvider;
+
+/**
+ * Concrete implementation of the  {@link sun.util.spi.CalendarProvider
+ * CalendarProvider} class for the JRE LocaleProviderAdapter.
+ *
+ * @author Naoto Sato
+ */
+public class CalendarProviderImpl extends CalendarProvider implements AvailableLanguageTags {
+    private final LocaleProviderAdapter.Type type;
+    private final Set<String> langtags;
+
+    public CalendarProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {
+        this.type = type;
+        this.langtags = langtags;
+    }
+
+    /**
+     * Returns an array of all locales for which this locale service provider
+     * can provide localized objects or names.
+     *
+     * @return An array of all locales for which this locale service provider
+     * can provide localized objects or names.
+     */
+    @Override
+    public Locale[] getAvailableLocales() {
+        return LocaleProviderAdapter.toLocaleArray(langtags);
+    }
+
+    @Override
+    public boolean isSupportedLocale(Locale locale) {
+        // Support any locales.
+        return true;
+    }
+
+    /**
+     * Returns a new <code>Calendar</code> instance for the
+     * specified locale.
+     *
+     * @param zone the time zone
+     * @param locale the desired locale
+     * @exception NullPointerException if <code>locale</code> is null
+     * @exception IllegalArgumentException if <code>locale</code> isn't
+     *     one of the locales returned from
+     *     {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
+     *     getAvailableLocales()}.
+     * @return a <code>Calendar</code> instance.
+     * @see java.util.Calendar#getInstance(java.util.Locale)
+     */
+    @Override
+    public Calendar getInstance(TimeZone zone, Locale locale) {
+        return new Calendar.Builder()
+                     .setLocale(locale)
+                     .setTimeZone(zone)
+                     .setInstant(System.currentTimeMillis())
+                     .build();
+    }
+
+    @Override
+    public Set<String> getAvailableLanguageTags() {
+        return langtags;
+    }
+}
--- a/jdk/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java	Thu Mar 14 16:59:06 2013 +0000
+++ b/jdk/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java	Thu Mar 14 11:29:16 2013 -0700
@@ -34,10 +34,12 @@
 import java.text.spi.DateFormatSymbolsProvider;
 import java.text.spi.DecimalFormatSymbolsProvider;
 import java.text.spi.NumberFormatProvider;
+import java.util.Calendar;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.TimeZone;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.spi.CalendarDataProvider;
@@ -47,6 +49,7 @@
 import java.util.spi.LocaleServiceProvider;
 import java.util.spi.TimeZoneNameProvider;
 import sun.util.resources.LocaleData;
+import sun.util.spi.CalendarProvider;
 
 /**
  * LocaleProviderAdapter implementation for the legacy JRE locale data.
@@ -104,6 +107,8 @@
             return (P) getCalendarDataProvider();
         case "CalendarNameProvider":
             return (P) getCalendarNameProvider();
+        case "CalendarProvider":
+            return (P) getCalendarProvider();
         default:
             throw new InternalError("should not come down here");
         }
@@ -122,6 +127,8 @@
     private volatile CalendarDataProvider calendarDataProvider = null;
     private volatile CalendarNameProvider calendarNameProvider = null;
 
+    private volatile CalendarProvider calendarProvider = null;
+
     /*
      * Getter methods for java.text.spi.* providers
      */
@@ -283,6 +290,23 @@
         return calendarNameProvider;
     }
 
+    /**
+     * Getter methods for sun.util.spi.* providers
+     */
+    @Override
+    public CalendarProvider getCalendarProvider() {
+        if (calendarProvider == null) {
+            CalendarProvider provider = new CalendarProviderImpl(getAdapterType(),
+                                                    getLanguageTagSet("CalendarData"));
+            synchronized (this) {
+                if (calendarProvider == null) {
+                    calendarProvider = provider;
+                }
+            }
+        }
+        return calendarProvider;
+    }
+
     @Override
     public LocaleResources getLocaleResources(Locale locale) {
         LocaleResources lr = localeResourcesMap.get(locale);
--- a/jdk/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java	Thu Mar 14 16:59:06 2013 +0000
+++ b/jdk/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java	Thu Mar 14 11:29:16 2013 -0700
@@ -47,6 +47,7 @@
 import java.util.spi.LocaleServiceProvider;
 import java.util.spi.TimeZoneNameProvider;
 import sun.util.cldr.CLDRLocaleProviderAdapter;
+import sun.util.spi.CalendarProvider;
 
 /**
  * The LocaleProviderAdapter abstract class.
@@ -295,7 +296,10 @@
         }
         if (type == Type.JRE) {
             String oldname = locale.toString().replace('_', '-');
-            return langtags.contains(oldname);
+            return langtags.contains(oldname) ||
+                   "ja-JP-JP".equals(oldname) ||
+                   "th-TH-TH".equals(oldname) ||
+                   "no-NO-NY".equals(oldname);
         }
         return false;
     }
@@ -422,6 +426,14 @@
      */
     public abstract CalendarNameProvider getCalendarNameProvider();
 
+    /**
+     * Returns a CalendarProvider for this LocaleProviderAdapter, or null if no
+     * CalendarProvider is available.
+     *
+     * @return a CalendarProvider
+     */
+    public abstract CalendarProvider getCalendarProvider();
+
     public abstract LocaleResources getLocaleResources(Locale locale);
 
     public abstract Locale[] getAvailableLocales();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/util/spi/CalendarProvider.java	Thu Mar 14 11:29:16 2013 -0700
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.util.spi;
+
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.spi.LocaleServiceProvider;
+
+/**
+ * An abstract class for service providers that
+ * provide instances of the
+ * {@link java.util.Calendar Calendar} class.
+ *
+ * @since        1.8
+ */
+public abstract class CalendarProvider extends LocaleServiceProvider {
+
+    /**
+     * Sole constructor.  (For invocation by subclass constructors, typically
+     * implicit.)
+     */
+    protected CalendarProvider() {
+    }
+
+    /**
+     * Returns a new <code>Calendar</code> instance for the
+     * specified locale.
+     *
+     * @param zone the time zone
+     * @param locale the desired locale
+     * @exception NullPointerException if <code>locale</code> is null
+     * @exception IllegalArgumentException if <code>locale</code> isn't
+     *     one of the locales returned from
+     *     {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
+     *     getAvailableLocales()}.
+     * @return a <code>Calendar</code> instance.
+     * @see java.util.Calendar#getInstance(java.util.Locale)
+     */
+    public abstract Calendar getInstance(TimeZone zone, Locale locale);
+}
--- a/jdk/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java	Thu Mar 14 16:59:06 2013 +0000
+++ b/jdk/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java	Thu Mar 14 11:29:16 2013 -0700
@@ -35,17 +35,20 @@
 import java.text.spi.DateFormatSymbolsProvider;
 import java.text.spi.DecimalFormatSymbolsProvider;
 import java.text.spi.NumberFormatProvider;
+import java.util.Calendar;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.ResourceBundle.Control;
 import java.util.Set;
+import java.util.TimeZone;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicReferenceArray;
 import java.util.spi.CalendarDataProvider;
 import java.util.spi.CalendarNameProvider;
+import sun.util.spi.CalendarProvider;
 
 /**
  * LocaleProviderdapter implementation for the Windows locale data.
@@ -98,9 +101,9 @@
         if (initialize()) {
             // Assuming the default locales do not include any extensions, so
             // no stripping is needed here.
-            Locale l = Locale.forLanguageTag(getDefaultLocale(CAT_FORMAT).replaceAll("_","-"));
+            Locale l = Locale.forLanguageTag(getDefaultLocale(CAT_FORMAT).replace('_', '-'));
             tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
-            l = Locale.forLanguageTag(getDefaultLocale(CAT_DISPLAY).replaceAll("_","-"));
+            l = Locale.forLanguageTag(getDefaultLocale(CAT_DISPLAY).replace('_', '-'));
             tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l));
         }
         supportedLocaleSet = Collections.unmodifiableSet(tmpSet);
@@ -173,24 +176,12 @@
 
             @Override
             public Locale[] getAvailableLocales() {
-                if (isSupportedLocale(Locale.getDefault(Locale.Category.FORMAT))) {
-                    return supportedLocale;
-                }
-
-                return new Locale[0];
+                return getSupportedCalendarLocales();
             }
 
             @Override
             public boolean isSupportedLocale(Locale locale) {
-                // Only supports the locale with Gregorian calendar
-                if (supportedLocale.length != 0) {
-                    int calid = getCalendarID(locale.toLanguageTag());
-                    if (calid > 0 && calid < calIDToLDML.length) {
-                        return calIDToLDML[calid].startsWith("gregory");
-                    }
-                }
-
-                return false;
+                return isSupportedCalendarLocale(locale);
             }
 
             @Override
@@ -380,6 +371,29 @@
         };
     }
 
+    public static CalendarProvider getCalendarProvider() {
+        return new CalendarProvider() {
+            @Override
+            public Locale[] getAvailableLocales() {
+                return getSupportedCalendarLocales();
+            }
+
+            @Override
+            public boolean isSupportedLocale(Locale locale) {
+                return isSupportedCalendarLocale(locale);
+            }
+
+            @Override
+            public Calendar getInstance(TimeZone zone, Locale locale) {
+                return new Calendar.Builder()
+                             .setLocale(getCalendarLocale(locale))
+                             .setTimeZone(zone)
+                             .setInstant(System.currentTimeMillis())
+                             .build();
+            }
+        };
+    }
+
     private static String convertDateTimePattern(String winPattern) {
         String ret = winPattern.replaceAll("dddd", "EEEE");
         ret = ret.replaceAll("ddd", "EEE");
@@ -401,24 +415,21 @@
     }
 
     private static boolean isSupportedCalendarLocale(Locale locale) {
-        // special case for ja_JP_JP
-        if (JRELocaleConstants.JA_JP_JP.equals(locale)) {
-            return isJapaneseCalendar();
-        }
-
         Locale base = locale.stripExtensions();
         if (!supportedLocaleSet.contains(base)) {
             return false;
         }
 
-        String caltype = locale.getUnicodeLocaleType("ca");
-        if (caltype == null) {
-            return true;
+        String requestedCalType = locale.getUnicodeLocaleType("ca");
+        String nativeCalType =
+                calIDToLDML[getCalendarID(locale.toLanguageTag())]
+                .replaceFirst("_.*", ""); // remove locale part.
+
+        if (requestedCalType == null) {
+            return Calendar.getAvailableCalendarTypes().contains(nativeCalType);
+        } else {
+            return requestedCalType.equals(nativeCalType);
         }
-
-        return caltype.equals(
-            calIDToLDML[getCalendarID(locale.toLanguageTag())]
-            .replaceFirst("_.*", ""));
     }
 
     private static Locale[] getSupportedNativeDigitLocales() {
--- a/jdk/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c	Thu Mar 14 16:59:06 2013 +0000
+++ b/jdk/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c	Thu Mar 14 11:29:16 2013 -0700
@@ -611,7 +611,12 @@
 
 int getLocaleInfoWrapper(const jchar *langtag, LCTYPE type, LPWSTR data, int buflen) {
     if (pGetLocaleInfoEx) {
-        return pGetLocaleInfoEx((LPWSTR)langtag, type, data, buflen);
+        if (wcscmp(L"und", (LPWSTR)langtag) == 0) {
+            // defaults to "en"
+            return pGetLocaleInfoEx(L"en", type, data, buflen);
+        } else {
+            return pGetLocaleInfoEx((LPWSTR)langtag, type, data, buflen);
+        }
     } else {
         // If we ever wanted to support WinXP, we will need extra module from
         // MS...
@@ -622,7 +627,12 @@
 
 int getCalendarInfoWrapper(const jchar *langtag, CALID id, LPCWSTR reserved, CALTYPE type, LPWSTR data, int buflen, LPDWORD val) {
     if (pGetCalendarInfoEx) {
-        return pGetCalendarInfoEx((LPWSTR)langtag, id, reserved, type, data, buflen, val);
+        if (wcscmp(L"und", (LPWSTR)langtag) == 0) {
+            // defaults to "en"
+            return pGetCalendarInfoEx(L"en", id, reserved, type, data, buflen, val);
+        } else {
+            return pGetCalendarInfoEx((LPWSTR)langtag, id, reserved, type, data, buflen, val);
+        }
     } else {
         // If we ever wanted to support WinXP, we will need extra module from
         // MS...