diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/java.base/share/classes/sun/util/locale/provider/CalendarNameProviderImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/util/locale/provider/CalendarNameProviderImpl.java Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2012, 2017, 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 static java.util.Calendar.*; +import java.util.Comparator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.spi.CalendarNameProvider; +import sun.util.calendar.CalendarSystem; +import sun.util.calendar.Era; + +/** + * Concrete implementation of the {@link java.util.spi.CalendarDataProvider + * CalendarDataProvider} class for the JRE LocaleProviderAdapter. + * + * @author Masayoshi Okutsu + * @author Naoto Sato + */ +public class CalendarNameProviderImpl extends CalendarNameProvider implements AvailableLanguageTags { + private final LocaleProviderAdapter.Type type; + private final Set langtags; + + public CalendarNameProviderImpl(LocaleProviderAdapter.Type type, Set langtags) { + this.type = type; + this.langtags = langtags; + } + + @Override + public String getDisplayName(String calendarType, int field, int value, int style, Locale locale) { + return getDisplayNameImpl(calendarType, field, value, style, locale, false); + } + + public String getJavaTimeDisplayName(String calendarType, int field, int value, int style, Locale locale) { + return getDisplayNameImpl(calendarType, field, value, style, locale, true); + } + + public String getDisplayNameImpl(String calendarType, int field, int value, int style, Locale locale, boolean javatime) { + String name = null; + String key = getResourceKey(calendarType, field, style, javatime); + if (key != null) { + LocaleResources lr = LocaleProviderAdapter.forType(type).getLocaleResources(locale); + String[] strings = javatime ? lr.getJavaTimeNames(key) : lr.getCalendarNames(key); + + // If standalone names are requested and no "standalone." resources are found, + // try the default ones instead. + if (strings == null && key.indexOf("standalone.") != -1) { + key = key.replaceFirst("standalone.", ""); + strings = javatime ? lr.getJavaTimeNames(key) : lr.getCalendarNames(key); + } + + if (strings != null && strings.length > 0) { + if (field == DAY_OF_WEEK || field == YEAR) { + --value; + } + if (value < 0 || value > strings.length) { + return null; + } else if (value == strings.length) { + if (field == ERA && "japanese".equals(calendarType)) { + // get the supplemental era, if any, specified through + // the property "jdk.calendar.japanese.supplemental.era" + // which is always the last element. + Era[] jeras = CalendarSystem.forName("japanese").getEras(); + if (jeras.length == value) { + Era supEra = jeras[value - 1]; // 0-based index + if (javatime) { + return getBaseStyle(style) == NARROW_FORMAT ? + supEra.getAbbreviation() : + supEra.getName(); + } else { + return (style & LONG) != 0 ? + supEra.getName() : + supEra.getAbbreviation(); + } + } + } + return null; + } + name = strings[value]; + // If name is empty in standalone, try its `format' style. + if (name.length() == 0 + && (style == SHORT_STANDALONE || style == LONG_STANDALONE + || style == NARROW_STANDALONE)) { + name = getDisplayName(calendarType, field, value, + getBaseStyle(style), + locale); + } + } + } + return name; + } + + private static int[] REST_OF_STYLES = { + SHORT_STANDALONE, LONG_FORMAT, LONG_STANDALONE, + NARROW_FORMAT, NARROW_STANDALONE + }; + + @Override + public Map getDisplayNames(String calendarType, int field, int style, Locale locale) { + Map names; + if (style == ALL_STYLES) { + names = getDisplayNamesImpl(calendarType, field, SHORT_FORMAT, locale, false); + for (int st : REST_OF_STYLES) { + names.putAll(getDisplayNamesImpl(calendarType, field, st, locale, false)); + } + } else { + // specific style + names = getDisplayNamesImpl(calendarType, field, style, locale, false); + } + return names.isEmpty() ? null : names; + } + + // NOTE: This method should be used ONLY BY JSR 310 classes. + public Map getJavaTimeDisplayNames(String calendarType, int field, int style, Locale locale) { + Map names; + names = getDisplayNamesImpl(calendarType, field, style, locale, true); + return names.isEmpty() ? null : names; + } + + private Map getDisplayNamesImpl(String calendarType, int field, + int style, Locale locale, boolean javatime) { + String key = getResourceKey(calendarType, field, style, javatime); + Map map = new TreeMap<>(LengthBasedComparator.INSTANCE); + if (key != null) { + LocaleResources lr = LocaleProviderAdapter.forType(type).getLocaleResources(locale); + String[] strings = javatime ? lr.getJavaTimeNames(key) : lr.getCalendarNames(key); + + // If standalone names are requested and no "standalone." resources are found, + // try the default ones instead. + if (strings == null && key.indexOf("standalone.") != -1) { + key = key.replaceFirst("standalone.", ""); + strings = javatime ? lr.getJavaTimeNames(key) : lr.getCalendarNames(key); + } + + if (strings != null) { + if (!hasDuplicates(strings)) { + if (field == YEAR) { + if (strings.length > 0) { + map.put(strings[0], 1); + } + } else { + int base = (field == DAY_OF_WEEK) ? 1 : 0; + for (int i = 0; i < strings.length; i++) { + String name = strings[i]; + // Ignore any empty string (some standalone month names + // are not defined) + if (name.length() == 0) { + continue; + } + map.put(name, base + i); + } + } + } + } + } + return map; + } + + private int getBaseStyle(int style) { + return style & ~(SHORT_STANDALONE - SHORT_FORMAT); + } + + /** + * Comparator implementation for TreeMap which iterates keys from longest + * to shortest. + */ + private static class LengthBasedComparator implements Comparator { + private static final LengthBasedComparator INSTANCE = new LengthBasedComparator(); + + private LengthBasedComparator() { + } + + @Override + public int compare(String o1, String o2) { + int n = o2.length() - o1.length(); + return (n == 0) ? o1.compareTo(o2) : n; + } + } + + @Override + public Locale[] getAvailableLocales() { + return LocaleProviderAdapter.toLocaleArray(langtags); + } + + @Override + public boolean isSupportedLocale(Locale locale) { + if (Locale.ROOT.equals(locale)) { + return true; + } + String calendarType = null; + if (locale.hasExtensions()) { + calendarType = locale.getUnicodeLocaleType("ca"); + locale = locale.stripExtensions(); + } + + if (calendarType != null) { + switch (calendarType) { + case "buddhist": + case "japanese": + case "gregory": + case "islamic": + case "roc": + break; + default: + // Unknown calendar type + return false; + } + } + if (langtags.contains(locale.toLanguageTag())) { + return true; + } + if (type == LocaleProviderAdapter.Type.JRE) { + String oldname = locale.toString().replace('_', '-'); + return langtags.contains(oldname); + } + return false; + } + + @Override + public Set getAvailableLanguageTags() { + return langtags; + } + + private boolean hasDuplicates(String[] strings) { + int len = strings.length; + for (int i = 0; i < len - 1; i++) { + String a = strings[i]; + if (a != null) { + for (int j = i + 1; j < len; j++) { + if (a.equals(strings[j])) { + return true; + } + } + } + } + return false; + } + + private String getResourceKey(String type, int field, int style, boolean javatime) { + int baseStyle = getBaseStyle(style); + boolean isStandalone = (style != baseStyle); + + if ("gregory".equals(type)) { + type = null; + } + boolean isNarrow = (baseStyle == NARROW_FORMAT); + StringBuilder key = new StringBuilder(); + // If javatime is true, use prefix "java.time.". + if (javatime) { + key.append("java.time."); + } + switch (field) { + case ERA: + if (type != null) { + key.append(type).append('.'); + } + if (isNarrow) { + key.append("narrow."); + } else { + // JRE and CLDR use different resource key conventions + // due to historical reasons. (JRE DateFormatSymbols.getEras returns + // abbreviations while other getShort*() return abbreviations.) + if (this.type == LocaleProviderAdapter.Type.JRE) { + if (javatime) { + if (baseStyle == LONG) { + key.append("long."); + } + } + if (baseStyle == SHORT) { + key.append("short."); + } + } else { // this.type == LocaleProviderAdapter.Type.CLDR + if (baseStyle == LONG) { + key.append("long."); + } + } + } + key.append("Eras"); + break; + + case YEAR: + if (!isNarrow) { + key.append(type).append(".FirstYear"); + } + break; + + case MONTH: + if ("islamic".equals(type)) { + key.append(type).append('.'); + } + if (isStandalone) { + key.append("standalone."); + } + key.append("Month").append(toStyleName(baseStyle)); + break; + + case DAY_OF_WEEK: + // support standalone day names + if (isStandalone) { + key.append("standalone."); + } + key.append("Day").append(toStyleName(baseStyle)); + break; + + case AM_PM: + if (isNarrow) { + key.append("narrow."); + } + key.append("AmPmMarkers"); + break; + } + return key.length() > 0 ? key.toString() : null; + } + + private String toStyleName(int baseStyle) { + switch (baseStyle) { + case SHORT: + return "Abbreviations"; + case NARROW_FORMAT: + return "Narrows"; + } + return "Names"; + } +}