• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package libcore.icu;
18 
19 import com.android.icu.util.ExtendedCalendar;
20 import java.text.DateFormat;
21 import java.text.SimpleDateFormat;
22 import java.util.Locale;
23 import java.util.Objects;
24 import java.util.concurrent.ConcurrentHashMap;
25 
26 /**
27  * Pattern cache for {@link SimpleDateFormat}
28  *
29  * @hide
30  */
31 public class SimpleDateFormatData {
32 
33     // TODO(http://b/217881004): Replace this with a LRU cache.
34     private static final ConcurrentHashMap<String, SimpleDateFormatData> CACHE =
35             new ConcurrentHashMap<>(/* initialCapacity */ 3);
36 
37     private final Locale locale;
38 
39     private final String fullTimeFormat;
40     private final String longTimeFormat;
41     private final String mediumTimeFormat;
42     private final String shortTimeFormat;
43 
44     private final String fullDateFormat;
45     private final String longDateFormat;
46     private final String mediumDateFormat;
47     private final String shortDateFormat;
48 
SimpleDateFormatData(Locale locale)49     private SimpleDateFormatData(Locale locale) {
50         this.locale = locale;
51 
52         // libcore's java.text supports Gregorian calendar only.
53         ExtendedCalendar extendedCalendar = ICU.getExtendedCalendar(locale, "gregorian");
54 
55         String tmpFullTimeFormat = getDateTimeFormatString(extendedCalendar,
56                 android.icu.text.DateFormat.NONE, android.icu.text.DateFormat.FULL);
57 
58         // Fix up a couple of patterns.
59         if (tmpFullTimeFormat != null) {
60             // There are some full time format patterns in ICU that use the pattern character 'v'.
61             // Java doesn't accept this, so we replace it with 'z' which has about the same result
62             // as 'v', the timezone name.
63             // 'v' -> "PT", 'z' -> "PST", v is the generic timezone and z the standard tz
64             // "vvvv" -> "Pacific Time", "zzzz" -> "Pacific Standard Time"
65             tmpFullTimeFormat = tmpFullTimeFormat.replace('v', 'z');
66         }
67         fullTimeFormat = tmpFullTimeFormat;
68 
69         longTimeFormat = getDateTimeFormatString(extendedCalendar,
70                 android.icu.text.DateFormat.NONE, android.icu.text.DateFormat.LONG);
71         mediumTimeFormat = getDateTimeFormatString(extendedCalendar,
72                 android.icu.text.DateFormat.NONE, android.icu.text.DateFormat.MEDIUM);
73         shortTimeFormat = getDateTimeFormatString(extendedCalendar,
74                 android.icu.text.DateFormat.NONE, android.icu.text.DateFormat.SHORT);
75         fullDateFormat = getDateTimeFormatString(extendedCalendar,
76                 android.icu.text.DateFormat.FULL, android.icu.text.DateFormat.NONE);
77         longDateFormat = getDateTimeFormatString(extendedCalendar,
78                 android.icu.text.DateFormat.LONG, android.icu.text.DateFormat.NONE);
79         mediumDateFormat = getDateTimeFormatString(extendedCalendar,
80                 android.icu.text.DateFormat.MEDIUM, android.icu.text.DateFormat.NONE);
81         shortDateFormat = getDateTimeFormatString(extendedCalendar,
82                 android.icu.text.DateFormat.SHORT, android.icu.text.DateFormat.NONE);
83     }
84 
85     /**
86      * Returns an instance.
87      *
88      * @param locale can't be null
89      * @throws NullPointerException if {@code locale} is null
90      * @return a {@link SimpleDateFormatData} instance
91      */
getInstance(Locale locale)92     public static SimpleDateFormatData getInstance(Locale locale) {
93         Objects.requireNonNull(locale, "locale can't be null");
94 
95         locale = LocaleData.getCompatibleLocaleForBug159514442(locale);
96 
97         final String languageTag = locale.toLanguageTag();
98 
99         SimpleDateFormatData data = CACHE.get(languageTag);
100         if (data != null) {
101             return data;
102         }
103 
104         data = new SimpleDateFormatData(locale);
105         SimpleDateFormatData prev = CACHE.putIfAbsent(languageTag, data);
106         if (prev != null) {
107             return prev;
108         }
109         return data;
110     }
111 
112     /**
113      * Ensure that we pull in the locale data for the root locale, en_US, and the user's default
114      * locale. All devices must support the root locale and en_US, and they're used for various
115      * system things. Pre-populating the cache is especially useful on Android because
116      * we'll share this via the Zygote.
117      */
initializeCacheInZygote()118     public static void initializeCacheInZygote() {
119         getInstance(Locale.ROOT);
120         getInstance(Locale.US);
121         getInstance(Locale.getDefault());
122     }
123 
124     /**
125      * @throws AssertionError if style is not one of the 4 styles specified in {@link DateFormat}
126      * @return a date pattern string
127      */
getDateFormat(int style)128     public String getDateFormat(int style) {
129         switch (style) {
130             case DateFormat.SHORT:
131                 return shortDateFormat;
132             case DateFormat.MEDIUM:
133                 return mediumDateFormat;
134             case DateFormat.LONG:
135                 return longDateFormat;
136             case DateFormat.FULL:
137                 return fullDateFormat;
138         }
139         // TODO: fix this legacy behavior of throwing AssertionError introduced in
140         //  the commit 6ca85c4.
141         throw new AssertionError();
142     }
143 
144     /**
145      * @throws AssertionError if style is not one of the 4 styles specified in {@link DateFormat}
146      * @return a time pattern string
147      */
getTimeFormat(int style)148     public String getTimeFormat(int style) {
149         // Do not cache ICU.getTimePattern() return value in the LocaleData instance
150         // because most users do not enable this setting, hurts performance in critical path,
151         // e.g. b/161846393, and ICU.getBestDateTimePattern will cache it in  ICU.CACHED_PATTERNS
152         // on demand.
153         switch (style) {
154             case DateFormat.SHORT:
155                 if (DateFormat.is24Hour == null) {
156                     return shortTimeFormat;
157                 } else {
158                     return ICU.getTimePattern(locale, DateFormat.is24Hour, false);
159                 }
160             case DateFormat.MEDIUM:
161                 if (DateFormat.is24Hour == null) {
162                     return mediumTimeFormat;
163                 } else {
164                     return ICU.getTimePattern(locale, DateFormat.is24Hour, true);
165                 }
166             case DateFormat.LONG:
167                 // CLDR doesn't really have anything we can use to obey the 12-/24-hour preference.
168                 return longTimeFormat;
169             case DateFormat.FULL:
170                 // CLDR doesn't really have anything we can use to obey the 12-/24-hour preference.
171                 return fullTimeFormat;
172         }
173         // TODO: fix this legacy behavior of throwing AssertionError introduced in
174         //  the commit 6ca85c4.
175         throw new AssertionError();
176     }
177 
getDateTimeFormatString(ExtendedCalendar extendedCalendar, int dateStyle, int timeStyle)178     private static String getDateTimeFormatString(ExtendedCalendar extendedCalendar,
179             int dateStyle, int timeStyle) {
180         return ICU.transformIcuDateTimePattern_forJavaText(
181                 extendedCalendar.getDateTimePattern(dateStyle, timeStyle));
182     }
183 
184 }
185