--- old/src/java.base/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java 2016-12-01 16:43:11.000000000 +0530 +++ new/src/java.base/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java 2016-12-01 16:43:11.000000000 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, 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 @@ -47,6 +47,7 @@ import java.util.spi.CurrencyNameProvider; import java.util.spi.LocaleNameProvider; import java.util.spi.TimeZoneNameProvider; +import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.spi.CalendarProvider; /** @@ -147,6 +148,165 @@ return Locale.forLanguageTag(langTag); } + public static JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() { + return new JavaTimeDateTimePatternProvider() { + @Override + public Locale[] getAvailableLocales() { + return getSupportedCalendarLocales(); + } + + @Override + public boolean isSupportedLocale(Locale locale) { + return isSupportedCalendarLocale(locale); + } + + @Override + public String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType, Locale locale) { + return toJavaTimeDateTimePattern(calType, getDateTimePattern(dateStyle, timeStyle, locale)); + + } + + private String getDateTimePattern(int dateStyle, int timeStyle, Locale locale) { + AtomicReferenceArray dateFormatPatterns; + SoftReference> ref = dateFormatPatternsMap.get(locale); + + if (ref == null || (dateFormatPatterns = ref.get()) == null) { + dateFormatPatterns = new AtomicReferenceArray<>(5 * 5); + ref = new SoftReference<>(dateFormatPatterns); + dateFormatPatternsMap.put(locale, ref); + } + int index = (dateStyle + 1) * 5 + timeStyle + 1; + String pattern = dateFormatPatterns.get(index); + if (pattern == null) { + String langTag = locale.toLanguageTag(); + pattern = translateDateFormatLetters(getCalendarID(langTag), + getDateTimePatternNative(dateStyle, timeStyle, langTag)); + if (!dateFormatPatterns.compareAndSet(index, null, pattern)) { + pattern = dateFormatPatterns.get(index); + } + } + return pattern; + } + + /** + * This method will convert JRE Date/time Pattern String to JSR310 + * type Date/Time Pattern + */ + private String toJavaTimeDateTimePattern(String calendarType, String jrePattern) { + int length = jrePattern.length(); + StringBuilder sb = new StringBuilder(length); + boolean inQuote = false; + int count = 0; + char lastLetter = 0; + for (int i = 0; i < length; i++) { + char c = jrePattern.charAt(i); + if (c == '\'') { + // '' is treated as a single quote regardless of being + // in a quoted section. + if ((i + 1) < length) { + char nextc = jrePattern.charAt(i + 1); + if (nextc == '\'') { + i++; + if (count != 0) { + convert(calendarType, lastLetter, count, sb); + lastLetter = 0; + count = 0; + } + sb.append("''"); + continue; + } + } + if (!inQuote) { + if (count != 0) { + convert(calendarType, lastLetter, count, sb); + lastLetter = 0; + count = 0; + } + inQuote = true; + } else { + inQuote = false; + } + sb.append(c); + continue; + } + if (inQuote) { + sb.append(c); + continue; + } + if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) { + if (count != 0) { + convert(calendarType, lastLetter, count, sb); + lastLetter = 0; + count = 0; + } + sb.append(c); + continue; + } + if (lastLetter == 0 || lastLetter == c) { + lastLetter = c; + count++; + continue; + } + convert(calendarType, lastLetter, count, sb); + lastLetter = c; + count = 1; + } + if (inQuote) { + // should not come here. + // returning null so that FALLBACK provider will kick in. + return null; + } + if (count != 0) { + convert(calendarType, lastLetter, count, sb); + } + return sb.toString(); + } + + private void convert(String calendarType, char letter, int count, StringBuilder sb) { + switch (letter) { + case 'G': + if (calendarType.equals("japanese")) { + if (count >= 4) { + count = 1; + } else { + count = 5; + } + } else if (!calendarType.equals("iso8601")) { + // Gregorian calendar is iso8601 for java.time + // Adjust the number of 'G's + if (count >= 4) { + // JRE full -> JavaTime full + count = 4; + } else { + // JRE short -> JavaTime short + count = 1; + } + } + break; + case 'y': + if (calendarType.equals("japanese") && count >= 4) { + // JRE specific "gan-nen" support + count = 1; + } + break; + default: + // JSR 310 and CLDR define 5-letter patterns for narrow text. + if (count > 4) { + count = 4; + } + break; + } + appendN(letter, count, sb); + } + + private void appendN(char c, int n, StringBuilder sb) { + for (int i = 0; i < n; i++) { + sb.append(c); + } + } + }; + } + public static DateFormatProvider getDateFormatProvider() { return new DateFormatProvider() { @@ -163,20 +323,20 @@ @Override public DateFormat getDateInstance(int style, Locale locale) { return new SimpleDateFormat(getDateTimePattern(style, -1, locale), - getCalendarLocale(locale)); + getCalendarLocale(locale)); } @Override public DateFormat getTimeInstance(int style, Locale locale) { return new SimpleDateFormat(getDateTimePattern(-1, style, locale), - getCalendarLocale(locale)); + getCalendarLocale(locale)); } @Override public DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) { return new SimpleDateFormat(getDateTimePattern(dateStyle, timeStyle, locale), - getCalendarLocale(locale)); + getCalendarLocale(locale)); } private String getDateTimePattern(int dateStyle, int timeStyle, Locale locale) { --- old/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java 2016-12-01 16:43:12.000000000 +0530 +++ new/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java 2016-12-01 16:43:12.000000000 +0530 @@ -119,6 +119,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.LocaleResources; import sun.util.locale.provider.TimeZoneNameUtility; @@ -212,9 +213,10 @@ if (dateStyle == null && timeStyle == null) { throw new IllegalArgumentException("Either dateStyle or timeStyle must be non-null"); } - LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(locale); - String pattern = lr.getJavaTimeDateTimePattern( - convertStyle(timeStyle), convertStyle(dateStyle), chrono.getCalendarType()); + LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(JavaTimeDateTimePatternProvider.class, locale); + JavaTimeDateTimePatternProvider provider = adapter.getJavaTimeDateTimePatternProvider(); + String pattern = provider.getJavaTimeDateTimePattern(convertStyle(timeStyle), + convertStyle(dateStyle), chrono.getCalendarType(), locale); return pattern; } --- old/src/java.base/share/classes/module-info.java 2016-12-01 16:43:13.000000000 +0530 +++ new/src/java.base/share/classes/module-info.java 2016-12-01 16:43:13.000000000 +0530 @@ -306,6 +306,7 @@ // JDK-internal service types uses jdk.internal.logger.DefaultLoggerFinder; uses sun.security.ssl.ClientKeyExchangeService; + uses sun.text.spi.JavaTimeDateTimePatternProvider; uses sun.util.spi.CalendarProvider; uses sun.util.locale.provider.LocaleDataMetaInfo; uses sun.util.resources.LocaleData.CommonResourceBundleProvider; --- old/src/java.base/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java 2016-12-01 16:43:14.000000000 +0530 +++ new/src/java.base/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java 2016-12-01 16:43:13.000000000 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, 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 @@ -45,6 +45,7 @@ import java.util.spi.LocaleNameProvider; import java.util.spi.LocaleServiceProvider; import java.util.spi.TimeZoneNameProvider; +import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.spi.CalendarProvider; /** @@ -156,6 +157,11 @@ return null; } + @Override + public JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() { + return getLocaleServiceProvider(JavaTimeDateTimePatternProvider.class); + } + private static Locale[] availableLocales = null; @Override --- old/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java 2016-12-01 16:43:15.000000000 +0530 +++ new/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java 2016-12-01 16:43:14.000000000 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, 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 @@ -50,6 +50,7 @@ import java.util.spi.LocaleNameProvider; import java.util.spi.LocaleServiceProvider; import java.util.spi.TimeZoneNameProvider; +import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.resources.LocaleData; import sun.util.spi.CalendarProvider; @@ -109,6 +110,8 @@ return (P) getCalendarNameProvider(); case "CalendarProvider": return (P) getCalendarProvider(); + case "JavaTimeDateTimePatternProvider": + return (P) getJavaTimeDateTimePatternProvider(); default: throw new InternalError("should not come down here"); } @@ -128,6 +131,7 @@ private volatile CalendarNameProvider calendarNameProvider; private volatile CalendarProvider calendarProvider; + private volatile JavaTimeDateTimePatternProvider javaTimeDateTimePatternProvider; /* * Getter methods for java.text.spi.* providers @@ -354,6 +358,27 @@ return calendarProvider; } + /** + * Getter methods for sun.text.spi.JavaTimeDateTimePatternProvider provider + */ + @Override + public JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() { + if (javaTimeDateTimePatternProvider == null) { + JavaTimeDateTimePatternProvider provider = AccessController.doPrivileged( + (PrivilegedAction) () + -> new JavaTimeDateTimePatternImpl( + getAdapterType(), + getLanguageTagSet("FormatData"))); + + synchronized (this) { + if (javaTimeDateTimePatternProvider == null) { + javaTimeDateTimePatternProvider = provider; + } + } + } + return javaTimeDateTimePatternProvider; + } + @Override public LocaleResources getLocaleResources(Locale locale) { LocaleResources lr = localeResourcesMap.get(locale); --- old/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java 2016-12-01 16:43:15.000000000 +0530 +++ new/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java 2016-12-01 16:43:15.000000000 +0530 @@ -47,6 +47,7 @@ import java.util.spi.LocaleServiceProvider; import java.util.spi.TimeZoneNameProvider; import sun.security.action.GetPropertyAction; +import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.spi.CalendarProvider; /** @@ -428,6 +429,14 @@ */ public abstract CalendarProvider getCalendarProvider(); + /** + * Returns a JavaTimeDateTimePatternProvider for this LocaleProviderAdapter, + * or null if no JavaTimeDateTimePatternProvider is available. + * + * @return a JavaTimeDateTimePatternProvider + */ + public abstract JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider(); + public abstract LocaleResources getLocaleResources(Locale locale); public abstract Locale[] getAvailableLocales(); --- old/src/java.base/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java 2016-12-01 16:43:16.000000000 +0530 +++ new/src/java.base/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java 2016-12-01 16:43:16.000000000 +0530 @@ -52,6 +52,7 @@ import java.util.spi.CalendarNameProvider; import java.util.spi.CurrencyNameProvider; import java.util.spi.LocaleNameProvider; +import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.spi.CalendarProvider; /** @@ -525,6 +526,167 @@ }; } + public static JavaTimeDateTimePatternProvider getJavaTimeDateTimePatternProvider() { + return new JavaTimeDateTimePatternProvider() { + @Override + public Locale[] getAvailableLocales() { + return getSupportedCalendarLocales(); + } + + @Override + public boolean isSupportedLocale(Locale locale) { + return isSupportedCalendarLocale(locale); + } + + @Override + public String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType, Locale locale) { + AtomicReferenceArray patterns = getDateTimePatterns(locale); + String pattern = new StringBuilder(patterns.get(dateStyle / 2)) + .append(" ") + .append(patterns.get(timeStyle / 2 + 2)) + .toString(); + return toJavaTimeDateTimePattern(calType, pattern); + + } + + private AtomicReferenceArray getDateTimePatterns(Locale locale) { + AtomicReferenceArray patterns; + SoftReference> ref = dateFormatCache.get(locale); + + if (ref == null || (patterns = ref.get()) == null) { + String langtag = removeExtensions(locale).toLanguageTag(); + patterns = new AtomicReferenceArray<>(4); + patterns.compareAndSet(0, null, convertDateTimePattern( + getDateTimePattern(DateFormat.LONG, -1, langtag))); + patterns.compareAndSet(1, null, convertDateTimePattern( + getDateTimePattern(DateFormat.SHORT, -1, langtag))); + patterns.compareAndSet(2, null, convertDateTimePattern( + getDateTimePattern(-1, DateFormat.LONG, langtag))); + patterns.compareAndSet(3, null, convertDateTimePattern( + getDateTimePattern(-1, DateFormat.SHORT, langtag))); + ref = new SoftReference<>(patterns); + dateFormatCache.put(locale, ref); + } + return patterns; + } + /** + * This method will convert JRE Date/time Pattern String to JSR310 + * type Date/Time Pattern + */ + private String toJavaTimeDateTimePattern(String calendarType, String jrePattern) { + int length = jrePattern.length(); + StringBuilder sb = new StringBuilder(length); + boolean inQuote = false; + int count = 0; + char lastLetter = 0; + for (int i = 0; i < length; i++) { + char c = jrePattern.charAt(i); + if (c == '\'') { + // '' is treated as a single quote regardless of being + // in a quoted section. + if ((i + 1) < length) { + char nextc = jrePattern.charAt(i + 1); + if (nextc == '\'') { + i++; + if (count != 0) { + convert(calendarType, lastLetter, count, sb); + lastLetter = 0; + count = 0; + } + sb.append("''"); + continue; + } + } + if (!inQuote) { + if (count != 0) { + convert(calendarType, lastLetter, count, sb); + lastLetter = 0; + count = 0; + } + inQuote = true; + } else { + inQuote = false; + } + sb.append(c); + continue; + } + if (inQuote) { + sb.append(c); + continue; + } + if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) { + if (count != 0) { + convert(calendarType, lastLetter, count, sb); + lastLetter = 0; + count = 0; + } + sb.append(c); + continue; + } + if (lastLetter == 0 || lastLetter == c) { + lastLetter = c; + count++; + continue; + } + convert(calendarType, lastLetter, count, sb); + lastLetter = c; + count = 1; + } + if (inQuote) { + // should not come here. + // returning null so that FALLBACK provider will kick in. + return null; + } + if (count != 0) { + convert(calendarType, lastLetter, count, sb); + } + return sb.toString(); + } + + private void convert(String calendarType, char letter, int count, StringBuilder sb) { + switch (letter) { + case 'G': + if (calendarType.equals("japanese")) { + if (count >= 4) { + count = 1; + } else { + count = 5; + } + } else if (!calendarType.equals("iso8601")) { + // Adjust the number of 'G's + // Gregorian calendar is iso8601 for java.time + if (count >= 4) { + // JRE full -> JavaTime full + count = 4; + } else { + // JRE short -> JavaTime short + count = 1; + } + } + break; + case 'y': + if (calendarType.equals("japanese") && count >= 4) { + // JRE specific "gan-nen" support + count = 1; + } + break; + default: + // JSR 310 and CLDR define 5-letter patterns for narrow text. + if (count > 4) { + count = 4; + } + break; + } + appendN(letter, count, sb); + } + + private void appendN(char c, int n, StringBuilder sb) { + for (int i = 0; i < n; i++) { + sb.append(c); + } + } + }; + } private static String convertDateTimePattern(String winPattern) { String ret = winPattern.replaceAll("dddd", "EEEE"); --- /dev/null 2016-12-01 16:43:17.000000000 +0530 +++ new/src/java.base/share/classes/sun/text/spi/JavaTimeDateTimePatternProvider.java 2016-12-01 16:43:17.000000000 +0530 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016, 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.text.spi; + +import java.util.Locale; +import java.util.spi.LocaleServiceProvider; + +/** + * Service Provider Interface for retrieving DateTime patterns from + * specified Locale provider for java.time. + */ + +public abstract class JavaTimeDateTimePatternProvider extends LocaleServiceProvider { + + protected JavaTimeDateTimePatternProvider() { + } + + /** + * Gets the formatting pattern for a timeStyle + * dateStyle, calendarType and locale. + * Concrete implementation of this method will retrieve + * a java.time specific dateTime Pattern from selected Locale Provider. + * + * @param timeStyle an {@code int} value representing FormatStyle constant, -1 + * for date-only pattern + * @param dateStyle an {@code int} value,representing FormatStyle constant, -1 + * for time-only pattern + * @param locale {@code locale}, non-null + * @param calType a {@code String},non-null representing CalendarType such as "japanese", + * "iso8601" + * @return formatting pattern {@code String} + * @see java.time.format.DateTimeFormatterBuilder#convertStyle(java.time.format.FormatStyle) + * @since 9 + */ + public abstract String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType, Locale locale); +} --- /dev/null 2016-12-01 16:43:18.000000000 +0530 +++ new/src/java.base/share/classes/sun/util/locale/provider/JavaTimeDateTimePatternImpl.java 2016-12-01 16:43:17.000000000 +0530 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016, 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.Locale; +import java.util.Set; +import sun.text.spi.JavaTimeDateTimePatternProvider; + +/** + * Concrete implementation of the {@link sun.text.spi.JavaTimeDateTimePatternProvider + * } class for the JRE LocaleProviderAdapter. + * + */ +public class JavaTimeDateTimePatternImpl extends JavaTimeDateTimePatternProvider implements AvailableLanguageTags { + + private final LocaleProviderAdapter.Type type; + private final Set langtags; + + public JavaTimeDateTimePatternImpl(LocaleProviderAdapter.Type type, Set 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) { + return LocaleProviderAdapter.forType(type).isSupportedProviderLocale(locale, langtags); + } + + @Override + public String getJavaTimeDateTimePattern(int timeStyle, int dateStyle, String calType, Locale locale) { + LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(locale); + String pattern = lr.getJavaTimeDateTimePattern( + timeStyle, dateStyle, calType); + return pattern; + + } + + @Override + public Set getAvailableLanguageTags() { + return langtags; + } +}