1 /*
2  * Copyright (c) 2012, 2019, 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 static java.util.Calendar.*;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.spi.CalendarDataProvider;
32 import java.util.spi.CalendarNameProvider;
33 
34 /**
35  * {@code CalendarDataUtility} is a utility class for calling the
36  * {@link CalendarDataProvider} methods.
37  *
38  * @author Masayoshi Okutsu
39  * @author Naoto Sato
40  */
41 public class CalendarDataUtility {
42     public static final String FIRST_DAY_OF_WEEK = "firstDayOfWeek";
43     public static final String MINIMAL_DAYS_IN_FIRST_WEEK = "minimalDaysInFirstWeek";
44     private static final Locale.Builder OVERRIDE_BUILDER = new Locale.Builder();
45 
46     // No instantiation
CalendarDataUtility()47     private CalendarDataUtility() {
48     }
49 
retrieveFirstDayOfWeek(Locale locale)50     public static int retrieveFirstDayOfWeek(Locale locale) {
51         // Look for the Unicode Extension in the locale parameter
52         if (locale.hasExtensions()) {
53             String fw = locale.getUnicodeLocaleType("fw");
54             if (fw != null) {
55                 switch (fw.toLowerCase(Locale.ROOT)) {
56                     case "mon":
57                         return MONDAY;
58                     case "tue":
59                         return TUESDAY;
60                     case "wed":
61                         return WEDNESDAY;
62                     case "thu":
63                         return THURSDAY;
64                     case "fri":
65                         return FRIDAY;
66                     case "sat":
67                         return SATURDAY;
68                     case "sun":
69                         return SUNDAY;
70                 }
71             }
72         }
73 
74         LocaleServiceProviderPool pool =
75                 LocaleServiceProviderPool.getPool(CalendarDataProvider.class);
76         Integer value = pool.getLocalizedObject(CalendarWeekParameterGetter.INSTANCE,
77                                                 findRegionOverride(locale),
78                                                 true, FIRST_DAY_OF_WEEK);
79         return (value != null && (value >= SUNDAY && value <= SATURDAY)) ? value : SUNDAY;
80     }
81 
retrieveMinimalDaysInFirstWeek(Locale locale)82     public static int retrieveMinimalDaysInFirstWeek(Locale locale) {
83         LocaleServiceProviderPool pool =
84                 LocaleServiceProviderPool.getPool(CalendarDataProvider.class);
85         Integer value = pool.getLocalizedObject(CalendarWeekParameterGetter.INSTANCE,
86                                                 findRegionOverride(locale),
87                                                 true, MINIMAL_DAYS_IN_FIRST_WEEK);
88         return (value != null && (value >= 1 && value <= 7)) ? value : 1;
89     }
90 
retrieveFieldValueName(String id, int field, int value, int style, Locale locale)91     public static String retrieveFieldValueName(String id, int field, int value, int style, Locale locale) {
92         LocaleServiceProviderPool pool =
93                 LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
94         return pool.getLocalizedObject(CalendarFieldValueNameGetter.INSTANCE, locale, normalizeCalendarType(id),
95                                        field, value, style, false);
96     }
97 
retrieveJavaTimeFieldValueName(String id, int field, int value, int style, Locale locale)98     public static String retrieveJavaTimeFieldValueName(String id, int field, int value, int style, Locale locale) {
99         LocaleServiceProviderPool pool =
100                 LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
101         String name;
102         name = pool.getLocalizedObject(CalendarFieldValueNameGetter.INSTANCE, locale, normalizeCalendarType(id),
103                                        field, value, style, true);
104         if (name == null) {
105             name = pool.getLocalizedObject(CalendarFieldValueNameGetter.INSTANCE, locale, normalizeCalendarType(id),
106                                            field, value, style, false);
107         }
108         return name;
109     }
110 
retrieveFieldValueNames(String id, int field, int style, Locale locale)111     public static Map<String, Integer> retrieveFieldValueNames(String id, int field, int style, Locale locale) {
112         LocaleServiceProviderPool pool =
113             LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
114         return pool.getLocalizedObject(CalendarFieldValueNamesMapGetter.INSTANCE, locale,
115                                        normalizeCalendarType(id), field, style, false);
116     }
117 
retrieveJavaTimeFieldValueNames(String id, int field, int style, Locale locale)118     public static Map<String, Integer> retrieveJavaTimeFieldValueNames(String id, int field, int style, Locale locale) {
119         LocaleServiceProviderPool pool =
120             LocaleServiceProviderPool.getPool(CalendarNameProvider.class);
121         Map<String, Integer> map;
122         map = pool.getLocalizedObject(CalendarFieldValueNamesMapGetter.INSTANCE, locale,
123                                        normalizeCalendarType(id), field, style, true);
124         if (map == null) {
125             map = pool.getLocalizedObject(CalendarFieldValueNamesMapGetter.INSTANCE, locale,
126                                            normalizeCalendarType(id), field, style, false);
127         }
128         return map;
129     }
130 
131     /**
132      * Utility to look for a region override extension.
133      * If no region override is found, returns the original locale.
134      */
findRegionOverride(Locale l)135     public static Locale findRegionOverride(Locale l) {
136         String rg = l.getUnicodeLocaleType("rg");
137         Locale override = l;
138 
139         if (rg != null && rg.length() == 6) {
140             // UN M.49 code should not be allowed here
141             // cannot use regex here, as it could be a recursive call
142             rg = rg.toUpperCase(Locale.ROOT);
143             if (rg.charAt(0) >= 0x0041 &&
144                 rg.charAt(0) <= 0x005A &&
145                 rg.charAt(1) >= 0x0041 &&
146                 rg.charAt(1) <= 0x005A &&
147                 rg.substring(2).equals("ZZZZ")) {
148                 override = OVERRIDE_BUILDER
149                     .clear()
150                     .setLocale(l)
151                     .setRegion(rg.substring(0, 2))
152                     .build();
153             }
154         }
155 
156         return override;
157     }
158 
normalizeCalendarType(String requestID)159     static String normalizeCalendarType(String requestID) {
160         String type;
161         if (requestID.equals("gregorian") || requestID.equals("iso8601")) {
162             type = "gregory";
163         } else if (requestID.startsWith("islamic")) {
164             type = "islamic";
165         } else {
166             type = requestID;
167         }
168         return type;
169     }
170 
171     /**
172      * Obtains a localized field value string from a CalendarDataProvider
173      * implementation.
174      */
175     private static class CalendarFieldValueNameGetter
176         implements LocaleServiceProviderPool.LocalizedObjectGetter<CalendarNameProvider,
177                                                                    String> {
178         private static final CalendarFieldValueNameGetter INSTANCE =
179             new CalendarFieldValueNameGetter();
180 
181         @Override
getObject(CalendarNameProvider calendarNameProvider, Locale locale, String requestID, Object... params)182         public String getObject(CalendarNameProvider calendarNameProvider,
183                                 Locale locale,
184                                 String requestID, // calendarType
185                                 Object... params) {
186             assert params.length == 4;
187             int field = (int) params[0];
188             int value = (int) params[1];
189             int style = (int) params[2];
190             boolean javatime = (boolean) params[3];
191 
192             // If javatime is true, resources from CLDR have precedence over JRE
193             // native resources.
194             if (javatime && calendarNameProvider instanceof CalendarNameProviderImpl) {
195                 String name;
196                 name = ((CalendarNameProviderImpl)calendarNameProvider)
197                         .getJavaTimeDisplayName(requestID, field, value, style, locale);
198                 return name;
199             }
200             return calendarNameProvider.getDisplayName(requestID, field, value, style, locale);
201         }
202     }
203 
204     /**
205      * Obtains a localized field-value pairs from a CalendarDataProvider
206      * implementation.
207      */
208     private static class CalendarFieldValueNamesMapGetter
209         implements LocaleServiceProviderPool.LocalizedObjectGetter<CalendarNameProvider,
210                                                                    Map<String, Integer>> {
211         private static final CalendarFieldValueNamesMapGetter INSTANCE =
212             new CalendarFieldValueNamesMapGetter();
213 
214         @Override
getObject(CalendarNameProvider calendarNameProvider, Locale locale, String requestID, Object... params)215         public Map<String, Integer> getObject(CalendarNameProvider calendarNameProvider,
216                                               Locale locale,
217                                               String requestID, // calendarType
218                                               Object... params) {
219             assert params.length == 3;
220             int field = (int) params[0];
221             int style = (int) params[1];
222             boolean javatime = (boolean) params[2];
223 
224             // If javatime is true, resources from CLDR have precedence over JRE
225             // native resources.
226             if (javatime && calendarNameProvider instanceof CalendarNameProviderImpl) {
227                 Map<String, Integer> map;
228                 map = ((CalendarNameProviderImpl)calendarNameProvider)
229                         .getJavaTimeDisplayNames(requestID, field, style, locale);
230                 return map;
231             }
232             return calendarNameProvider.getDisplayNames(requestID, field, style, locale);
233         }
234     }
235 
236     private static class CalendarWeekParameterGetter
237         implements LocaleServiceProviderPool.LocalizedObjectGetter<CalendarDataProvider,
238                                                                    Integer> {
239         private static final CalendarWeekParameterGetter INSTANCE =
240             new CalendarWeekParameterGetter();
241 
242         @Override
getObject(CalendarDataProvider calendarDataProvider, Locale locale, String requestID, Object... params)243         public Integer getObject(CalendarDataProvider calendarDataProvider,
244                                  Locale locale,
245                                  String requestID,    // resource key
246                                  Object... params) {
247             assert params.length == 0;
248             int value;
249             switch (requestID) {
250             case FIRST_DAY_OF_WEEK:
251                 value = calendarDataProvider.getFirstDayOfWeek(locale);
252                 if (value == 0) {
253                     value = MONDAY; // default for the world ("001")
254                 }
255                 break;
256             case MINIMAL_DAYS_IN_FIRST_WEEK:
257                 value = calendarDataProvider.getMinimalDaysInFirstWeek(locale);
258                 if (value == 0) {
259                     value = 1; // default for the world ("001")
260                 }
261                 break;
262             default:
263                 throw new InternalError("invalid requestID: " + requestID);
264             }
265 
266             assert value != 0;
267             return value;
268         }
269     }
270 }
271