Home | History | Annotate | Download | only in provider
      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