Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java
41159 views
1
/*
2
* Copyright (c) 2018, 2020, 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.cldr;
27
28
import static sun.util.locale.provider.LocaleProviderAdapter.Type;
29
30
import java.text.MessageFormat;
31
import java.util.Arrays;
32
import java.util.Locale;
33
import java.util.Objects;
34
import java.util.ResourceBundle;
35
import java.util.Set;
36
import java.util.TimeZone;
37
import sun.util.calendar.ZoneInfoFile;
38
import sun.util.locale.provider.LocaleProviderAdapter;
39
import sun.util.locale.provider.LocaleResources;
40
import sun.util.locale.provider.TimeZoneNameProviderImpl;
41
import sun.util.locale.provider.TimeZoneNameUtility;
42
43
/**
44
* Concrete implementation of the
45
* {@link java.util.spi.TimeZoneNameProvider TimeZoneNameProvider} class
46
* for the CLDR LocaleProviderAdapter.
47
*
48
* @author Naoto Sato
49
*/
50
public class CLDRTimeZoneNameProviderImpl extends TimeZoneNameProviderImpl {
51
52
private static final String NO_INHERITANCE_MARKER = "\u2205\u2205\u2205";
53
private static class AVAILABLE_IDS {
54
static final String[] INSTANCE =
55
Arrays.stream(ZoneInfoFile.getZoneIds())
56
.sorted()
57
.toArray(String[]::new);
58
}
59
60
// display name array indexes
61
private static final int INDEX_TZID = 0;
62
private static final int INDEX_STD_LONG = 1;
63
private static final int INDEX_STD_SHORT = 2;
64
private static final int INDEX_DST_LONG = 3;
65
private static final int INDEX_DST_SHORT = 4;
66
private static final int INDEX_GEN_LONG = 5;
67
private static final int INDEX_GEN_SHORT = 6;
68
69
public CLDRTimeZoneNameProviderImpl(Type type, Set<String> langtags) {
70
super(type, langtags);
71
}
72
73
@Override
74
protected String[] getDisplayNameArray(String id, Locale locale) {
75
String[] namesSuper = super.getDisplayNameArray(id, locale);
76
77
if (namesSuper == null) {
78
// try canonical id instead
79
namesSuper = super.getDisplayNameArray(
80
TimeZoneNameUtility.canonicalTZID(id).orElse(id),
81
locale);
82
}
83
84
if (namesSuper != null) {
85
// CLDR's resource bundle has an translated entry for this id.
86
// Fix up names if needed, either missing or no-inheritance
87
namesSuper[INDEX_TZID] = id;
88
89
for(int i = INDEX_STD_LONG; i < namesSuper.length; i++) { // index 0 is the 'id' itself
90
switch (namesSuper[i]) {
91
case "":
92
// Fill in empty elements
93
deriveFallbackName(namesSuper, i, locale,
94
!TimeZone.getTimeZone(id).useDaylightTime());
95
break;
96
case NO_INHERITANCE_MARKER:
97
// CLDR's "no inheritance marker"
98
namesSuper[i] = toGMTFormat(id, i == INDEX_DST_LONG || i == INDEX_DST_SHORT,
99
locale);
100
break;
101
default:
102
break;
103
}
104
}
105
return namesSuper;
106
} else {
107
// Derive the names for this id. Validate the id first.
108
if (Arrays.binarySearch(AVAILABLE_IDS.INSTANCE, id) >= 0) {
109
String[] names = new String[INDEX_GEN_SHORT + 1];
110
names[INDEX_TZID] = id;
111
deriveFallbackNames(names, locale);
112
return names;
113
}
114
}
115
116
return null;
117
}
118
119
@Override
120
protected String[][] getZoneStrings(Locale locale) {
121
String[][] ret = super.getZoneStrings(locale);
122
123
// Fill in for the empty names.
124
for (int zoneIndex = 0; zoneIndex < ret.length; zoneIndex++) {
125
deriveFallbackNames(ret[zoneIndex], locale);
126
}
127
return ret;
128
}
129
130
// Derive fallback time zone name according to LDML's logic
131
private void deriveFallbackNames(String[] names, Locale locale) {
132
boolean noDST = !TimeZone.getTimeZone(names[0]).useDaylightTime();
133
134
for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) {
135
deriveFallbackName(names, i, locale, noDST);
136
}
137
}
138
139
private void deriveFallbackName(String[] names, int index, Locale locale, boolean noDST) {
140
String id = names[INDEX_TZID];
141
142
if (exists(names, index)) {
143
if (names[index].equals(NO_INHERITANCE_MARKER)) {
144
// CLDR's "no inheritance marker"
145
names[index] = toGMTFormat(id,
146
index == INDEX_DST_LONG || index == INDEX_DST_SHORT,
147
locale);
148
}
149
return;
150
}
151
152
// Check parent locale first
153
if (!exists(names, index)) {
154
CLDRLocaleProviderAdapter clpa = (CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(Type.CLDR);
155
var cands = clpa.getCandidateLocales("", locale);
156
if (cands.size() > 1) {
157
var parentLoc = cands.get(1); // immediate parent locale
158
String[] parentNames = super.getDisplayNameArray(id, parentLoc);
159
if (parentNames != null && !parentNames[index].isEmpty()) {
160
names[index] = parentNames[index];
161
return;
162
}
163
}
164
}
165
166
// Check if COMPAT can substitute the name
167
if (LocaleProviderAdapter.getAdapterPreference().contains(Type.JRE)) {
168
String[] compatNames = (String[])LocaleProviderAdapter.forJRE()
169
.getLocaleResources(mapChineseLocale(locale))
170
.getTimeZoneNames(id);
171
if (compatNames != null) {
172
for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) {
173
// Assumes COMPAT has no empty slots
174
if (i == index || !exists(names, i)) {
175
names[i] = compatNames[i];
176
}
177
}
178
return;
179
}
180
}
181
182
// Region Fallback
183
if (regionFormatFallback(names, index, locale)) {
184
return;
185
}
186
187
// Type Fallback
188
if (noDST && typeFallback(names, index)) {
189
return;
190
}
191
192
// last resort
193
names[index] = toGMTFormat(id,
194
index == INDEX_DST_LONG || index == INDEX_DST_SHORT,
195
locale);
196
// aliases of "GMT" timezone.
197
if ((exists(names, INDEX_STD_LONG)) && (id.startsWith("Etc/")
198
|| id.startsWith("GMT") || id.startsWith("Greenwich"))) {
199
switch (id) {
200
case "Etc/GMT":
201
case "Etc/GMT-0":
202
case "Etc/GMT+0":
203
case "Etc/GMT0":
204
case "GMT+0":
205
case "GMT-0":
206
case "GMT0":
207
case "Greenwich":
208
names[INDEX_DST_LONG] = names[INDEX_GEN_LONG] = names[INDEX_STD_LONG];
209
break;
210
}
211
}
212
}
213
214
private boolean exists(String[] names, int index) {
215
return Objects.nonNull(names)
216
&& Objects.nonNull(names[index])
217
&& !names[index].isEmpty();
218
}
219
220
private boolean typeFallback(String[] names, int index) {
221
// check generic
222
int genIndex = INDEX_GEN_SHORT - index % 2;
223
if (!exists(names, index) && exists(names, genIndex) && !names[genIndex].startsWith("GMT")) {
224
names[index] = names[genIndex];
225
} else {
226
// check standard
227
int stdIndex = INDEX_STD_SHORT - index % 2;
228
if (!exists(names, index) && exists(names, stdIndex) && !names[stdIndex].startsWith("GMT")) {
229
names[index] = names[stdIndex];
230
}
231
}
232
233
return exists(names, index);
234
}
235
236
private boolean regionFormatFallback(String[] names, int index, Locale l) {
237
String id = names[INDEX_TZID];
238
LocaleResources lr = LocaleProviderAdapter.forType(Type.CLDR).getLocaleResources(l);
239
ResourceBundle fd = lr.getJavaTimeFormatData();
240
241
id = TimeZoneNameUtility.canonicalTZID(id).orElse(id);
242
String rgn = (String) lr.getTimeZoneNames("timezone.excity." + id);
243
if (rgn == null && !id.startsWith("Etc") && !id.startsWith("SystemV")) {
244
int slash = id.lastIndexOf('/');
245
if (slash > 0) {
246
rgn = id.substring(slash + 1).replaceAll("_", " ");
247
}
248
}
249
250
if (rgn != null) {
251
String fmt = "";
252
switch (index) {
253
case INDEX_STD_LONG:
254
fmt = fd.getString("timezone.regionFormat.standard");
255
break;
256
case INDEX_DST_LONG:
257
fmt = fd.getString("timezone.regionFormat.daylight");
258
break;
259
case INDEX_GEN_LONG:
260
fmt = fd.getString("timezone.regionFormat");
261
break;
262
}
263
if (!fmt.isEmpty()) {
264
names[index] = MessageFormat.format(fmt, rgn);
265
}
266
}
267
268
return exists(names, index);
269
}
270
271
private String toGMTFormat(String id, boolean daylight, Locale l) {
272
TimeZone tz = ZoneInfoFile.getZoneInfo(id);
273
int offset = (tz.getRawOffset() + (daylight ? tz.getDSTSavings() : 0)) / 60000;
274
LocaleResources lr = LocaleProviderAdapter.forType(Type.CLDR).getLocaleResources(l);
275
ResourceBundle fd = lr.getJavaTimeFormatData();
276
277
if (offset == 0) {
278
return fd.getString("timezone.gmtZeroFormat");
279
} else {
280
String gmtFormat = fd.getString("timezone.gmtFormat");
281
String hourFormat = fd.getString("timezone.hourFormat");
282
283
if (offset > 0) {
284
hourFormat = hourFormat.substring(0, hourFormat.indexOf(";"));
285
} else {
286
hourFormat = hourFormat.substring(hourFormat.indexOf(";") + 1);
287
offset = -offset;
288
}
289
hourFormat = hourFormat
290
.replaceFirst("H+", "\\%1\\$02d")
291
.replaceFirst("m+", "\\%2\\$02d");
292
return MessageFormat.format(gmtFormat,
293
String.format(l, hourFormat, offset / 60, offset % 60));
294
}
295
}
296
297
// Mapping CLDR's Simplified/Traditional Chinese resources
298
// to COMPAT's zh-CN/TW
299
private Locale mapChineseLocale(Locale locale) {
300
if (locale.getLanguage() == "zh") {
301
switch (locale.getScript()) {
302
case "Hans":
303
return Locale.CHINA;
304
case "Hant":
305
return Locale.TAIWAN;
306
case "":
307
// no script, guess from country code.
308
switch (locale.getCountry()) {
309
case "":
310
case "CN":
311
case "SG":
312
return Locale.CHINA;
313
case "HK":
314
case "MO":
315
case "TW":
316
return Locale.TAIWAN;
317
}
318
break;
319
}
320
}
321
322
// no need to map
323
return locale;
324
}
325
}
326
327