Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/time/format/DateTimeTextProvider.java
38918 views
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
/*
27
* This file is available under and governed by the GNU General Public
28
* License version 2 only, as published by the Free Software Foundation.
29
* However, the following notice accompanied the original version of this
30
* file:
31
*
32
* Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos
33
*
34
* All rights reserved.
35
*
36
* Redistribution and use in source and binary forms, with or without
37
* modification, are permitted provided that the following conditions are met:
38
*
39
* * Redistributions of source code must retain the above copyright notice,
40
* this list of conditions and the following disclaimer.
41
*
42
* * Redistributions in binary form must reproduce the above copyright notice,
43
* this list of conditions and the following disclaimer in the documentation
44
* and/or other materials provided with the distribution.
45
*
46
* * Neither the name of JSR-310 nor the names of its contributors
47
* may be used to endorse or promote products derived from this software
48
* without specific prior written permission.
49
*
50
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
54
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
55
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
56
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
57
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61
*/
62
package java.time.format;
63
64
import static java.time.temporal.ChronoField.AMPM_OF_DAY;
65
import static java.time.temporal.ChronoField.DAY_OF_WEEK;
66
import static java.time.temporal.ChronoField.ERA;
67
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
68
69
import java.time.chrono.Chronology;
70
import java.time.chrono.IsoChronology;
71
import java.time.chrono.JapaneseChronology;
72
import java.time.temporal.ChronoField;
73
import java.time.temporal.IsoFields;
74
import java.time.temporal.TemporalField;
75
import java.util.AbstractMap.SimpleImmutableEntry;
76
import java.util.ArrayList;
77
import java.util.Calendar;
78
import java.util.Collections;
79
import java.util.Comparator;
80
import java.util.HashMap;
81
import java.util.Iterator;
82
import java.util.List;
83
import java.util.Locale;
84
import java.util.Map;
85
import java.util.Map.Entry;
86
import java.util.ResourceBundle;
87
import java.util.concurrent.ConcurrentHashMap;
88
import java.util.concurrent.ConcurrentMap;
89
90
import sun.util.locale.provider.CalendarDataUtility;
91
import sun.util.locale.provider.LocaleProviderAdapter;
92
import sun.util.locale.provider.LocaleResources;
93
94
/**
95
* A provider to obtain the textual form of a date-time field.
96
*
97
* @implSpec
98
* Implementations must be thread-safe.
99
* Implementations should cache the textual information.
100
*
101
* @since 1.8
102
*/
103
class DateTimeTextProvider {
104
105
/** Cache. */
106
private static final ConcurrentMap<Entry<TemporalField, Locale>, Object> CACHE = new ConcurrentHashMap<>(16, 0.75f, 2);
107
/** Comparator. */
108
private static final Comparator<Entry<String, Long>> COMPARATOR = new Comparator<Entry<String, Long>>() {
109
@Override
110
public int compare(Entry<String, Long> obj1, Entry<String, Long> obj2) {
111
return obj2.getKey().length() - obj1.getKey().length(); // longest to shortest
112
}
113
};
114
115
DateTimeTextProvider() {}
116
117
/**
118
* Gets the provider of text.
119
*
120
* @return the provider, not null
121
*/
122
static DateTimeTextProvider getInstance() {
123
return new DateTimeTextProvider();
124
}
125
126
/**
127
* Gets the text for the specified field, locale and style
128
* for the purpose of formatting.
129
* <p>
130
* The text associated with the value is returned.
131
* The null return value should be used if there is no applicable text, or
132
* if the text would be a numeric representation of the value.
133
*
134
* @param field the field to get text for, not null
135
* @param value the field value to get text for, not null
136
* @param style the style to get text for, not null
137
* @param locale the locale to get text for, not null
138
* @return the text for the field value, null if no text found
139
*/
140
public String getText(TemporalField field, long value, TextStyle style, Locale locale) {
141
Object store = findStore(field, locale);
142
if (store instanceof LocaleStore) {
143
return ((LocaleStore) store).getText(value, style);
144
}
145
return null;
146
}
147
148
/**
149
* Gets the text for the specified chrono, field, locale and style
150
* for the purpose of formatting.
151
* <p>
152
* The text associated with the value is returned.
153
* The null return value should be used if there is no applicable text, or
154
* if the text would be a numeric representation of the value.
155
*
156
* @param chrono the Chronology to get text for, not null
157
* @param field the field to get text for, not null
158
* @param value the field value to get text for, not null
159
* @param style the style to get text for, not null
160
* @param locale the locale to get text for, not null
161
* @return the text for the field value, null if no text found
162
*/
163
public String getText(Chronology chrono, TemporalField field, long value,
164
TextStyle style, Locale locale) {
165
if (chrono == IsoChronology.INSTANCE
166
|| !(field instanceof ChronoField)) {
167
return getText(field, value, style, locale);
168
}
169
170
int fieldIndex;
171
int fieldValue;
172
if (field == ERA) {
173
fieldIndex = Calendar.ERA;
174
if (chrono == JapaneseChronology.INSTANCE) {
175
if (value == -999) {
176
fieldValue = 0;
177
} else {
178
fieldValue = (int) value + 2;
179
}
180
} else {
181
fieldValue = (int) value;
182
}
183
} else if (field == MONTH_OF_YEAR) {
184
fieldIndex = Calendar.MONTH;
185
fieldValue = (int) value - 1;
186
} else if (field == DAY_OF_WEEK) {
187
fieldIndex = Calendar.DAY_OF_WEEK;
188
fieldValue = (int) value + 1;
189
if (fieldValue > 7) {
190
fieldValue = Calendar.SUNDAY;
191
}
192
} else if (field == AMPM_OF_DAY) {
193
fieldIndex = Calendar.AM_PM;
194
fieldValue = (int) value;
195
} else {
196
return null;
197
}
198
return CalendarDataUtility.retrieveJavaTimeFieldValueName(
199
chrono.getCalendarType(), fieldIndex, fieldValue, style.toCalendarStyle(), locale);
200
}
201
202
/**
203
* Gets an iterator of text to field for the specified field, locale and style
204
* for the purpose of parsing.
205
* <p>
206
* The iterator must be returned in order from the longest text to the shortest.
207
* <p>
208
* The null return value should be used if there is no applicable parsable text, or
209
* if the text would be a numeric representation of the value.
210
* Text can only be parsed if all the values for that field-style-locale combination are unique.
211
*
212
* @param field the field to get text for, not null
213
* @param style the style to get text for, null for all parsable text
214
* @param locale the locale to get text for, not null
215
* @return the iterator of text to field pairs, in order from longest text to shortest text,
216
* null if the field or style is not parsable
217
*/
218
public Iterator<Entry<String, Long>> getTextIterator(TemporalField field, TextStyle style, Locale locale) {
219
Object store = findStore(field, locale);
220
if (store instanceof LocaleStore) {
221
return ((LocaleStore) store).getTextIterator(style);
222
}
223
return null;
224
}
225
226
/**
227
* Gets an iterator of text to field for the specified chrono, field, locale and style
228
* for the purpose of parsing.
229
* <p>
230
* The iterator must be returned in order from the longest text to the shortest.
231
* <p>
232
* The null return value should be used if there is no applicable parsable text, or
233
* if the text would be a numeric representation of the value.
234
* Text can only be parsed if all the values for that field-style-locale combination are unique.
235
*
236
* @param chrono the Chronology to get text for, not null
237
* @param field the field to get text for, not null
238
* @param style the style to get text for, null for all parsable text
239
* @param locale the locale to get text for, not null
240
* @return the iterator of text to field pairs, in order from longest text to shortest text,
241
* null if the field or style is not parsable
242
*/
243
public Iterator<Entry<String, Long>> getTextIterator(Chronology chrono, TemporalField field,
244
TextStyle style, Locale locale) {
245
if (chrono == IsoChronology.INSTANCE
246
|| !(field instanceof ChronoField)) {
247
return getTextIterator(field, style, locale);
248
}
249
250
int fieldIndex;
251
switch ((ChronoField)field) {
252
case ERA:
253
fieldIndex = Calendar.ERA;
254
break;
255
case MONTH_OF_YEAR:
256
fieldIndex = Calendar.MONTH;
257
break;
258
case DAY_OF_WEEK:
259
fieldIndex = Calendar.DAY_OF_WEEK;
260
break;
261
case AMPM_OF_DAY:
262
fieldIndex = Calendar.AM_PM;
263
break;
264
default:
265
return null;
266
}
267
268
int calendarStyle = (style == null) ? Calendar.ALL_STYLES : style.toCalendarStyle();
269
Map<String, Integer> map = CalendarDataUtility.retrieveJavaTimeFieldValueNames(
270
chrono.getCalendarType(), fieldIndex, calendarStyle, locale);
271
if (map == null) {
272
return null;
273
}
274
List<Entry<String, Long>> list = new ArrayList<>(map.size());
275
switch (fieldIndex) {
276
case Calendar.ERA:
277
for (Map.Entry<String, Integer> entry : map.entrySet()) {
278
int era = entry.getValue();
279
if (chrono == JapaneseChronology.INSTANCE) {
280
if (era == 0) {
281
era = -999;
282
} else {
283
era -= 2;
284
}
285
}
286
list.add(createEntry(entry.getKey(), (long)era));
287
}
288
break;
289
case Calendar.MONTH:
290
for (Map.Entry<String, Integer> entry : map.entrySet()) {
291
list.add(createEntry(entry.getKey(), (long)(entry.getValue() + 1)));
292
}
293
break;
294
case Calendar.DAY_OF_WEEK:
295
for (Map.Entry<String, Integer> entry : map.entrySet()) {
296
list.add(createEntry(entry.getKey(), (long)toWeekDay(entry.getValue())));
297
}
298
break;
299
default:
300
for (Map.Entry<String, Integer> entry : map.entrySet()) {
301
list.add(createEntry(entry.getKey(), (long)entry.getValue()));
302
}
303
break;
304
}
305
return list.iterator();
306
}
307
308
private Object findStore(TemporalField field, Locale locale) {
309
Entry<TemporalField, Locale> key = createEntry(field, locale);
310
Object store = CACHE.get(key);
311
if (store == null) {
312
store = createStore(field, locale);
313
CACHE.putIfAbsent(key, store);
314
store = CACHE.get(key);
315
}
316
return store;
317
}
318
319
private static int toWeekDay(int calWeekDay) {
320
if (calWeekDay == Calendar.SUNDAY) {
321
return 7;
322
} else {
323
return calWeekDay - 1;
324
}
325
}
326
327
private Object createStore(TemporalField field, Locale locale) {
328
Map<TextStyle, Map<Long, String>> styleMap = new HashMap<>();
329
if (field == ERA) {
330
for (TextStyle textStyle : TextStyle.values()) {
331
if (textStyle.isStandalone()) {
332
// Stand-alone isn't applicable to era names.
333
continue;
334
}
335
Map<String, Integer> displayNames = CalendarDataUtility.retrieveJavaTimeFieldValueNames(
336
"gregory", Calendar.ERA, textStyle.toCalendarStyle(), locale);
337
if (displayNames != null) {
338
Map<Long, String> map = new HashMap<>();
339
for (Entry<String, Integer> entry : displayNames.entrySet()) {
340
map.put((long) entry.getValue(), entry.getKey());
341
}
342
if (!map.isEmpty()) {
343
styleMap.put(textStyle, map);
344
}
345
}
346
}
347
return new LocaleStore(styleMap);
348
}
349
350
if (field == MONTH_OF_YEAR) {
351
for (TextStyle textStyle : TextStyle.values()) {
352
Map<String, Integer> displayNames = CalendarDataUtility.retrieveJavaTimeFieldValueNames(
353
"gregory", Calendar.MONTH, textStyle.toCalendarStyle(), locale);
354
Map<Long, String> map = new HashMap<>();
355
if (displayNames != null) {
356
for (Entry<String, Integer> entry : displayNames.entrySet()) {
357
map.put((long) (entry.getValue() + 1), entry.getKey());
358
}
359
360
} else {
361
// Narrow names may have duplicated names, such as "J" for January, Jun, July.
362
// Get names one by one in that case.
363
for (int month = Calendar.JANUARY; month <= Calendar.DECEMBER; month++) {
364
String name;
365
name = CalendarDataUtility.retrieveJavaTimeFieldValueName(
366
"gregory", Calendar.MONTH, month, textStyle.toCalendarStyle(), locale);
367
if (name == null) {
368
break;
369
}
370
map.put((long) (month + 1), name);
371
}
372
}
373
if (!map.isEmpty()) {
374
styleMap.put(textStyle, map);
375
}
376
}
377
return new LocaleStore(styleMap);
378
}
379
380
if (field == DAY_OF_WEEK) {
381
for (TextStyle textStyle : TextStyle.values()) {
382
Map<String, Integer> displayNames = CalendarDataUtility.retrieveJavaTimeFieldValueNames(
383
"gregory", Calendar.DAY_OF_WEEK, textStyle.toCalendarStyle(), locale);
384
Map<Long, String> map = new HashMap<>();
385
if (displayNames != null) {
386
for (Entry<String, Integer> entry : displayNames.entrySet()) {
387
map.put((long)toWeekDay(entry.getValue()), entry.getKey());
388
}
389
390
} else {
391
// Narrow names may have duplicated names, such as "S" for Sunday and Saturday.
392
// Get names one by one in that case.
393
for (int wday = Calendar.SUNDAY; wday <= Calendar.SATURDAY; wday++) {
394
String name;
395
name = CalendarDataUtility.retrieveJavaTimeFieldValueName(
396
"gregory", Calendar.DAY_OF_WEEK, wday, textStyle.toCalendarStyle(), locale);
397
if (name == null) {
398
break;
399
}
400
map.put((long)toWeekDay(wday), name);
401
}
402
}
403
if (!map.isEmpty()) {
404
styleMap.put(textStyle, map);
405
}
406
}
407
return new LocaleStore(styleMap);
408
}
409
410
if (field == AMPM_OF_DAY) {
411
for (TextStyle textStyle : TextStyle.values()) {
412
if (textStyle.isStandalone()) {
413
// Stand-alone isn't applicable to AM/PM.
414
continue;
415
}
416
Map<String, Integer> displayNames = CalendarDataUtility.retrieveJavaTimeFieldValueNames(
417
"gregory", Calendar.AM_PM, textStyle.toCalendarStyle(), locale);
418
if (displayNames != null) {
419
Map<Long, String> map = new HashMap<>();
420
for (Entry<String, Integer> entry : displayNames.entrySet()) {
421
map.put((long) entry.getValue(), entry.getKey());
422
}
423
if (!map.isEmpty()) {
424
styleMap.put(textStyle, map);
425
}
426
}
427
}
428
return new LocaleStore(styleMap);
429
}
430
431
if (field == IsoFields.QUARTER_OF_YEAR) {
432
// The order of keys must correspond to the TextStyle.values() order.
433
final String[] keys = {
434
"QuarterNames",
435
"standalone.QuarterNames",
436
"QuarterAbbreviations",
437
"standalone.QuarterAbbreviations",
438
"QuarterNarrows",
439
"standalone.QuarterNarrows",
440
};
441
for (int i = 0; i < keys.length; i++) {
442
String[] names = getLocalizedResource(keys[i], locale);
443
if (names != null) {
444
Map<Long, String> map = new HashMap<>();
445
for (int q = 0; q < names.length; q++) {
446
map.put((long) (q + 1), names[q]);
447
}
448
styleMap.put(TextStyle.values()[i], map);
449
}
450
}
451
return new LocaleStore(styleMap);
452
}
453
454
return ""; // null marker for map
455
}
456
457
/**
458
* Helper method to create an immutable entry.
459
*
460
* @param text the text, not null
461
* @param field the field, not null
462
* @return the entry, not null
463
*/
464
private static <A, B> Entry<A, B> createEntry(A text, B field) {
465
return new SimpleImmutableEntry<>(text, field);
466
}
467
468
/**
469
* Returns the localized resource of the given key and locale, or null
470
* if no localized resource is available.
471
*
472
* @param key the key of the localized resource, not null
473
* @param locale the locale, not null
474
* @return the localized resource, or null if not available
475
* @throws NullPointerException if key or locale is null
476
*/
477
@SuppressWarnings("unchecked")
478
static <T> T getLocalizedResource(String key, Locale locale) {
479
LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased()
480
.getLocaleResources(locale);
481
ResourceBundle rb = lr.getJavaTimeFormatData();
482
return rb.containsKey(key) ? (T) rb.getObject(key) : null;
483
}
484
485
/**
486
* Stores the text for a single locale.
487
* <p>
488
* Some fields have a textual representation, such as day-of-week or month-of-year.
489
* These textual representations can be captured in this class for printing
490
* and parsing.
491
* <p>
492
* This class is immutable and thread-safe.
493
*/
494
static final class LocaleStore {
495
/**
496
* Map of value to text.
497
*/
498
private final Map<TextStyle, Map<Long, String>> valueTextMap;
499
/**
500
* Parsable data.
501
*/
502
private final Map<TextStyle, List<Entry<String, Long>>> parsable;
503
504
/**
505
* Constructor.
506
*
507
* @param valueTextMap the map of values to text to store, assigned and not altered, not null
508
*/
509
LocaleStore(Map<TextStyle, Map<Long, String>> valueTextMap) {
510
this.valueTextMap = valueTextMap;
511
Map<TextStyle, List<Entry<String, Long>>> map = new HashMap<>();
512
List<Entry<String, Long>> allList = new ArrayList<>();
513
for (Map.Entry<TextStyle, Map<Long, String>> vtmEntry : valueTextMap.entrySet()) {
514
Map<String, Entry<String, Long>> reverse = new HashMap<>();
515
for (Map.Entry<Long, String> entry : vtmEntry.getValue().entrySet()) {
516
if (reverse.put(entry.getValue(), createEntry(entry.getValue(), entry.getKey())) != null) {
517
// TODO: BUG: this has no effect
518
continue; // not parsable, try next style
519
}
520
}
521
List<Entry<String, Long>> list = new ArrayList<>(reverse.values());
522
Collections.sort(list, COMPARATOR);
523
map.put(vtmEntry.getKey(), list);
524
allList.addAll(list);
525
map.put(null, allList);
526
}
527
Collections.sort(allList, COMPARATOR);
528
this.parsable = map;
529
}
530
531
/**
532
* Gets the text for the specified field value, locale and style
533
* for the purpose of printing.
534
*
535
* @param value the value to get text for, not null
536
* @param style the style to get text for, not null
537
* @return the text for the field value, null if no text found
538
*/
539
String getText(long value, TextStyle style) {
540
Map<Long, String> map = valueTextMap.get(style);
541
return map != null ? map.get(value) : null;
542
}
543
544
/**
545
* Gets an iterator of text to field for the specified style for the purpose of parsing.
546
* <p>
547
* The iterator must be returned in order from the longest text to the shortest.
548
*
549
* @param style the style to get text for, null for all parsable text
550
* @return the iterator of text to field pairs, in order from longest text to shortest text,
551
* null if the style is not parsable
552
*/
553
Iterator<Entry<String, Long>> getTextIterator(TextStyle style) {
554
List<Entry<String, Long>> list = parsable.get(style);
555
return list != null ? list.iterator() : null;
556
}
557
}
558
}
559
560