--- old/make/java/java/FILES_java.gmk Fri Mar 8 15:04:23 2013 +++ new/make/java/java/FILES_java.gmk Fri Mar 8 15:04:22 2013 @@ -232,6 +232,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 \ --- old/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Fri Mar 8 15:04:29 2013 +++ new/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Fri Mar 8 15:04:28 2013 @@ -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 supportedLocaleSet; static { - Set tmpSet = new HashSet(); + Set 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 = convertPosixLocaleToJavaLocale(getDefaultLocale(CAT_FORMAT)); tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l)); - l = Locale.forLanguageTag(getDefaultLocale(CAT_DISPLAY).replaceAll("_","-")); + l = convertPosixLocaleToJavaLocale(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 convertPosixLocaleToJavaLocale(String posix) { + // 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 = posix.split("@"); + String langTag = tmp[0].replaceAll("_","-"); + 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,10 +212,9 @@ if (isSupportedLocale(Locale.getDefault(Locale.Category.FORMAT))) { return supportedLocale; } + return new Locale[0]; + } - return new Locale[0]; - } - @Override public boolean isSupportedLocale(Locale locale) { // Only supports the locale with Gregorian calendar @@ -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()) + .replaceFirst("gregorian", "gregory")) + .setTimeZone(zone) + .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"); - return caltype.replaceFirst("gregory", "gregorian").equals( - getCalendarID(locale.toLanguageTag())); + if (requestedCalType == null) { + return Calendar.getAvailableCalendarTypes().contains(nativeCalType); + } else { + return requestedCalType.equals(nativeCalType); + } } 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; } } --- old/src/share/classes/java/util/Calendar.java Fri Mar 8 15:04:35 2013 +++ new/src/share/classes/java/util/Calendar.java Fri Mar 8 15:04:34 2013 @@ -57,6 +57,9 @@ import sun.util.BuddhistCalendar; import sun.util.calendar.ZoneInfo; import sun.util.locale.provider.CalendarDataUtility; +import sun.util.locale.provider.JRELocaleProviderAdapter; +import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.spi.CalendarProvider; /** * The Calendar class is an abstract class that provides methods @@ -1660,6 +1663,18 @@ private static Calendar createCalendar(TimeZone zone, Locale aLocale) { + LocaleProviderAdapter adapter = + LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale); + // Kludge Alert: JRE's CalendarProvider should instantiate Calendar instances + // in it, but it's not possible since JapaneseImperialCalendar is package + // private. + if (!(adapter instanceof JRELocaleProviderAdapter)) { + CalendarProvider provider = adapter.getCalendarProvider(); + if (provider != null) { + return provider.getInstance(zone, aLocale); + } + } + Calendar cal = null; if (aLocale.hasExtensions()) { --- old/src/share/classes/sun/util/locale/LanguageTag.java Fri Mar 8 15:04:42 2013 +++ new/src/share/classes/sun/util/locale/LanguageTag.java Fri Mar 8 15:04:40 2013 @@ -134,7 +134,7 @@ } /* - * BNF in RFC5464 + * BNF in RFC5646 * * Language-Tag = langtag ; normal language tags * / privateuse ; private use tag --- old/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java Fri Mar 8 15:04:48 2013 +++ new/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java Fri Mar 8 15:04:46 2013 @@ -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; --- old/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java Fri Mar 8 15:04:53 2013 +++ new/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java Fri Mar 8 15:04:52 2013 @@ -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,40 @@ return calendarNameProvider; } + /** + * Getter methods for sun.util.spi.* providers + */ + @Override + public CalendarProvider getCalendarProvider() { + // Return a dummy here to pretend supporting this provider. + // Real instantiation happens in Calendar.createCalendar(). + if (calendarProvider == null) { + CalendarProvider provider; + provider = new CalendarProvider() { + @Override + public Locale[] getAvailableLocales() { + return null; + } + + @Override + public boolean isSupportedLocale(Locale l) { + return true; + } + + @Override + public Calendar getInstance(TimeZone tz, Locale l) { + return null; + } + }; + synchronized (this) { + if (calendarProvider == null) { + calendarProvider = provider; + } + } + } + return calendarProvider; + } + @Override public LocaleResources getLocaleResources(Locale locale) { LocaleResources lr = localeResourcesMap.get(locale); --- old/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java Fri Mar 8 15:04:59 2013 +++ new/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java Fri Mar 8 15:04:58 2013 @@ -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(); --- old/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Fri Mar 8 15:05:05 2013 +++ new/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java Fri Mar 8 15:05:04 2013 @@ -35,6 +35,7 @@ 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; @@ -41,11 +42,13 @@ 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. @@ -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,28 @@ }; } + 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) + .build(); + } + }; + } + private static String convertDateTimePattern(String winPattern) { String ret = winPattern.replaceAll("dddd", "EEEE"); ret = ret.replaceAll("ddd", "EEE"); @@ -401,24 +414,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. - return caltype.equals( - calIDToLDML[getCalendarID(locale.toLanguageTag())] - .replaceFirst("_.*", "")); + if (requestedCalType == null) { + return Calendar.getAvailableCalendarTypes().contains(nativeCalType); + } else { + return requestedCalType.equals(nativeCalType); + } } private static Locale[] getSupportedNativeDigitLocales() { --- old/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c Fri Mar 8 15:05:11 2013 +++ new/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c Fri Mar 8 15:05:10 2013 @@ -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... --- /dev/null Fri Mar 8 15:05:17 2013 +++ new/src/share/classes/sun/util/spi/CalendarProvider.java Fri Mar 8 15:05:16 2013 @@ -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 Calendar instance for the + * specified locale. + * + * @param zone the time zone + * @param locale the desired locale + * @exception NullPointerException if locale is null + * @exception IllegalArgumentException if locale isn't + * one of the locales returned from + * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() + * getAvailableLocales()}. + * @return a Calendar instance. + * @see java.util.Calendar#getInstance(java.util.Locale) + */ + public abstract Calendar getInstance(TimeZone zone, Locale locale); +}