1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3 /*
4  *******************************************************************************
5  * Copyright (C) 2009,2016 International Business Machines Corporation and
6  * others. All Rights Reserved.
7  *******************************************************************************
8  */
9 package com.ibm.icu.impl;
10 
11 import java.util.Locale;
12 import java.util.Map;
13 import java.util.MissingResourceException;
14 import java.util.TreeMap;
15 
16 import com.ibm.icu.util.ULocale;
17 import com.ibm.icu.util.UResourceBundle;
18 
19 /**
20  * Calendar utilities.
21  *
22  * Date/time format service classes in com.ibm.icu.text packages
23  * sometimes need to access calendar internal APIs.  But calendar
24  * classes are in com.ibm.icu.util package, so the package local
25  * cannot be used.  This class is added in com.ibm.icu.impl
26  * package for sharing some calendar internal code for calendar
27  * and date format.
28  */
29 public final class CalendarUtil {
30     private static final String CALKEY = "calendar";
31     private static final String DEFCAL = "gregorian";
32 
33     /**
34      * Returns a calendar type for the given locale.
35      * When the given locale has calendar keyword, the
36      * value of calendar keyword is returned.  Otherwise,
37      * the default calendar type for the locale is returned.
38      * @param loc The locale
39      * @return Calendar type string, such as "gregorian"
40      */
getCalendarType(ULocale loc)41     public static String getCalendarType(ULocale loc) {
42         String calType = loc.getKeywordValue(CALKEY);
43         if (calType != null) {
44             // Convert to lower case, because getKeywordValue does not
45             // canonicalize keyword value.
46             return calType.toLowerCase(Locale.ROOT);
47         }
48 
49         // Canonicalize, so grandfathered variant will be transformed to keywords
50         ULocale canonical = ULocale.createCanonical(loc.toString());
51         calType = canonical.getKeywordValue(CALKEY);
52         if (calType != null) {
53             return calType;
54         }
55 
56         // When calendar keyword is not available, use the locale's
57         // region to get the default calendar type
58         String region = ULocale.getRegionForSupplementalData(canonical, true);
59         return CalendarPreferences.INSTANCE.getCalendarTypeForRegion(region);
60     }
61 
62     private static final class CalendarPreferences extends UResource.Sink {
63         private static final CalendarPreferences INSTANCE = new CalendarPreferences();
64         // A TreeMap should be good because we expect very few entries.
65         Map<String, String> prefs = new TreeMap<String, String>();
66 
CalendarPreferences()67         CalendarPreferences() {
68             try {
69                 ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(
70                         ICUData.ICU_BASE_NAME, "supplementalData");
71                 rb.getAllItemsWithFallback("calendarPreferenceData", this);
72             } catch (MissingResourceException mre) {
73                 // Always use "gregorian".
74             }
75         }
76 
getCalendarTypeForRegion(String region)77         String getCalendarTypeForRegion(String region) {
78             String type = prefs.get(region);
79             return type == null ? DEFCAL : type;
80         }
81 
82         @Override
put(UResource.Key key, UResource.Value value, boolean noFallback)83         public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
84             UResource.Table calendarPreferenceData = value.getTable();
85             for (int i = 0; calendarPreferenceData.getKeyAndValue(i, key, value); ++i) {
86                 UResource.Array types = value.getArray();
87                 // The first calendar type is the default for the region.
88                 if (types.getValue(0, value)) {
89                     String type = value.getString();
90                     if (!type.equals(DEFCAL)) {
91                         prefs.put(key.toString(), type);
92                     }
93                 }
94             }
95         }
96     }
97 }
98