1 /* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.util.locale.provider; 27 28 import android.icu.text.DateFormatSymbols; 29 import android.icu.util.ULocale; 30 31 import static java.util.Calendar.*; 32 33 import java.util.Calendar; 34 import java.util.LinkedHashMap; 35 import java.util.Locale; 36 import java.util.Map; 37 38 /** 39 * {@code CalendarDataUtility} is a utility class for getting calendar field name values. 40 * 41 * @author Masayoshi Okutsu 42 * @author Naoto Sato 43 */ 44 public class CalendarDataUtility { 45 46 private static final String ISLAMIC_CALENDAR = "islamic"; 47 private static final String GREGORIAN_CALENDAR = "gregorian"; 48 private static final String BUDDHIST_CALENDAR = "buddhist"; 49 private static final String JAPANESE_CALENDAR = "japanese"; 50 51 // No instantiation 52 private CalendarDataUtility() { 53 } 54 55 // Android-changed: Removed retrieveFirstDayOfWeek and retrieveMinimalDaysInFirstWeek. 56 // use libcore.icu.LocaleData or android.icu.util.Calendar.WeekData instead 57 58 public static String retrieveFieldValueName(String id, int field, int value, int style, 59 Locale locale) { 60 // Android-changed: delegate to ICU. 61 if (field == Calendar.ERA) { 62 // For era the field value does not always equal the index into the names array. 63 switch (normalizeCalendarType(id)) { 64 // These calendars have only one era, but represented it by the value 1. 65 case BUDDHIST_CALENDAR: 66 case ISLAMIC_CALENDAR: 67 value -= 1; 68 break; 69 case JAPANESE_CALENDAR: 70 // CLDR contains full data for historical eras, java.time only supports the 4 71 // modern eras and numbers the modern eras starting with 1 (MEIJI). There are 72 // 232 historical eras in CLDR/ICU so to get the real offset, we add 231. 73 value += 231; 74 break; 75 default: 76 // Other eras use 0-based values (e.g. 0=BCE, 1=CE for gregorian). 77 break; 78 } 79 } 80 if (value < 0) { 81 return null; 82 } 83 String[] names = getNames(id, field, style, locale); 84 if (value >= names.length) { 85 return null; 86 } 87 return names[value]; 88 } 89 90 public static String retrieveJavaTimeFieldValueName(String id, int field, int value, int style, 91 Locale locale) { 92 // Android-changed: don't distinguish between retrieve* and retrieveJavaTime* methods. 93 return retrieveFieldValueName(id, field, value, style, locale); 94 } 95 96 // ALL_STYLES implies SHORT_FORMAT and all of these values. 97 private static int[] REST_OF_STYLES = { 98 SHORT_STANDALONE, LONG_FORMAT, LONG_STANDALONE, 99 NARROW_FORMAT, NARROW_STANDALONE 100 }; 101 102 public static Map<String, Integer> retrieveFieldValueNames(String id, int field, int style, 103 Locale locale) { 104 // Android-changed: delegate to ICU. 105 Map<String, Integer> names; 106 if (style == ALL_STYLES) { 107 names = retrieveFieldValueNamesImpl(id, field, SHORT_FORMAT, locale); 108 for (int st : REST_OF_STYLES) { 109 names.putAll(retrieveFieldValueNamesImpl(id, field, st, locale)); 110 } 111 } else { 112 // specific style 113 names = retrieveFieldValueNamesImpl(id, field, style, locale); 114 } 115 return names.isEmpty() ? null : names; 116 } 117 118 private static Map<String, Integer> retrieveFieldValueNamesImpl(String id, int field, int style, 119 Locale locale) { 120 String[] names = getNames(id, field, style, locale); 121 int skipped = 0; 122 int offset = 0; 123 if (field == Calendar.ERA) { 124 // See retrieveFieldValueName() for explanation of this code and the values used. 125 switch (normalizeCalendarType(id)) { 126 case BUDDHIST_CALENDAR: 127 case ISLAMIC_CALENDAR: 128 offset = 1; 129 break; 130 case JAPANESE_CALENDAR: 131 skipped = 232; 132 offset = -231; 133 break; 134 default: 135 break; 136 } 137 } 138 Map<String, Integer> result = new LinkedHashMap<>(); 139 for (int i = skipped; i < names.length; i++) { 140 if (names[i].isEmpty()) { 141 continue; 142 } 143 144 if (result.put(names[i], i + offset) != null) { 145 // Duplicate names indicate that the names would be ambiguous. Skip this style for 146 // ALL_STYLES. In other cases this results in null being returned in 147 // retrieveValueNames(), which is required by Calendar.getDisplayNames(). 148 return new LinkedHashMap<>(); 149 } 150 } 151 return result; 152 } 153 154 public static Map<String, Integer> retrieveJavaTimeFieldValueNames(String id, int field, 155 int style, Locale locale) { 156 // Android-changed: don't distinguish between retrieve* and retrieveJavaTime* methods. 157 return retrieveFieldValueNames(id, field, style, locale); 158 } 159 160 private static String[] getNames(String id, int field, int style, Locale locale) { 161 int context = toContext(style); 162 int width = toWidth(style); 163 DateFormatSymbols symbols = getDateFormatSymbols(id, locale); 164 switch (field) { 165 case Calendar.MONTH: 166 return symbols.getMonths(context, width); 167 case Calendar.ERA: 168 switch (width) { 169 case DateFormatSymbols.NARROW: 170 return symbols.getNarrowEras(); 171 case DateFormatSymbols.ABBREVIATED: 172 return symbols.getEras(); 173 case DateFormatSymbols.WIDE: 174 return symbols.getEraNames(); 175 default: 176 throw new UnsupportedOperationException("Unknown width: " + width); 177 } 178 case Calendar.DAY_OF_WEEK: 179 return symbols.getWeekdays(context, width); 180 case Calendar.AM_PM: 181 return symbols.getAmPmStrings(); 182 default: 183 throw new UnsupportedOperationException("Unknown field: " + field); 184 } 185 } 186 187 private static DateFormatSymbols getDateFormatSymbols(String id, Locale locale) { 188 String calendarType = normalizeCalendarType(id); 189 return new DateFormatSymbols(ULocale.forLocale(locale), calendarType); 190 } 191 192 /** 193 * Transform a {@link Calendar} style constant into an ICU width value. 194 */ 195 private static int toWidth(int style) { 196 switch (style) { 197 case Calendar.SHORT_FORMAT: 198 case Calendar.SHORT_STANDALONE: 199 return DateFormatSymbols.ABBREVIATED; 200 case Calendar.NARROW_FORMAT: 201 case Calendar.NARROW_STANDALONE: 202 return DateFormatSymbols.NARROW; 203 case Calendar.LONG_FORMAT: 204 case Calendar.LONG_STANDALONE: 205 return DateFormatSymbols.WIDE; 206 default: 207 throw new IllegalArgumentException("Invalid style: " + style); 208 } 209 } 210 211 /** 212 * Transform a {@link Calendar} style constant into an ICU context value. 213 */ 214 private static int toContext(int style) { 215 switch (style) { 216 case Calendar.SHORT_FORMAT: 217 case Calendar.NARROW_FORMAT: 218 case Calendar.LONG_FORMAT: 219 return DateFormatSymbols.FORMAT; 220 case Calendar.SHORT_STANDALONE: 221 case Calendar.NARROW_STANDALONE: 222 case Calendar.LONG_STANDALONE: 223 return DateFormatSymbols.STANDALONE; 224 default: 225 throw new IllegalArgumentException("Invalid style: " + style); 226 } 227 } 228 229 private static String normalizeCalendarType(String requestID) { 230 String type; 231 // Android-changed: normalize "gregory" to "gregorian", not the other way around. 232 // See android.icu.text.DateFormatSymbols.CALENDAR_CLASSES for reference. 233 if (requestID.equals("gregory") || requestID.equals("iso8601")) { 234 type = GREGORIAN_CALENDAR; 235 } else if (requestID.startsWith(ISLAMIC_CALENDAR)) { 236 type = ISLAMIC_CALENDAR; 237 } else { 238 type = requestID; 239 } 240 return type; 241 } 242 243 // Android-changed: Removed CalendarFieldValueNameGetter, CalendarFieldValueNamesMapGetter 244 // and CalendarWeekParameterGetter 245 } 246