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/text/ChoiceFormat.java
38829 views
1
/*
2
* Copyright (c) 1996, 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
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28
* (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29
*
30
* The original version of this source code and documentation is copyrighted
31
* and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32
* materials are provided under terms of a License Agreement between Taligent
33
* and Sun. This technology is protected by multiple US and International
34
* patents. This notice and attribution to Taligent may not be removed.
35
* Taligent is a registered trademark of Taligent, Inc.
36
*
37
*/
38
39
package java.text;
40
41
import java.io.InvalidObjectException;
42
import java.io.IOException;
43
import java.io.ObjectInputStream;
44
import java.util.Arrays;
45
46
/**
47
* A <code>ChoiceFormat</code> allows you to attach a format to a range of numbers.
48
* It is generally used in a <code>MessageFormat</code> for handling plurals.
49
* The choice is specified with an ascending list of doubles, where each item
50
* specifies a half-open interval up to the next item:
51
* <blockquote>
52
* <pre>
53
* X matches j if and only if limit[j] &le; X &lt; limit[j+1]
54
* </pre>
55
* </blockquote>
56
* If there is no match, then either the first or last index is used, depending
57
* on whether the number (X) is too low or too high. If the limit array is not
58
* in ascending order, the results of formatting will be incorrect. ChoiceFormat
59
* also accepts <code>&#92;u221E</code> as equivalent to infinity(INF).
60
*
61
* <p>
62
* <strong>Note:</strong>
63
* <code>ChoiceFormat</code> differs from the other <code>Format</code>
64
* classes in that you create a <code>ChoiceFormat</code> object with a
65
* constructor (not with a <code>getInstance</code> style factory
66
* method). The factory methods aren't necessary because <code>ChoiceFormat</code>
67
* doesn't require any complex setup for a given locale. In fact,
68
* <code>ChoiceFormat</code> doesn't implement any locale specific behavior.
69
*
70
* <p>
71
* When creating a <code>ChoiceFormat</code>, you must specify an array of formats
72
* and an array of limits. The length of these arrays must be the same.
73
* For example,
74
* <ul>
75
* <li>
76
* <em>limits</em> = {1,2,3,4,5,6,7}<br>
77
* <em>formats</em> = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"}
78
* <li>
79
* <em>limits</em> = {0, 1, ChoiceFormat.nextDouble(1)}<br>
80
* <em>formats</em> = {"no files", "one file", "many files"}<br>
81
* (<code>nextDouble</code> can be used to get the next higher double, to
82
* make the half-open interval.)
83
* </ul>
84
*
85
* <p>
86
* Here is a simple example that shows formatting and parsing:
87
* <blockquote>
88
* <pre>{@code
89
* double[] limits = {1,2,3,4,5,6,7};
90
* String[] dayOfWeekNames = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"};
91
* ChoiceFormat form = new ChoiceFormat(limits, dayOfWeekNames);
92
* ParsePosition status = new ParsePosition(0);
93
* for (double i = 0.0; i <= 8.0; ++i) {
94
* status.setIndex(0);
95
* System.out.println(i + " -> " + form.format(i) + " -> "
96
* + form.parse(form.format(i),status));
97
* }
98
* }</pre>
99
* </blockquote>
100
* Here is a more complex example, with a pattern format:
101
* <blockquote>
102
* <pre>{@code
103
* double[] filelimits = {0,1,2};
104
* String[] filepart = {"are no files","is one file","are {2} files"};
105
* ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
106
* Format[] testFormats = {fileform, null, NumberFormat.getInstance()};
107
* MessageFormat pattform = new MessageFormat("There {0} on {1}");
108
* pattform.setFormats(testFormats);
109
* Object[] testArgs = {null, "ADisk", null};
110
* for (int i = 0; i < 4; ++i) {
111
* testArgs[0] = new Integer(i);
112
* testArgs[2] = testArgs[0];
113
* System.out.println(pattform.format(testArgs));
114
* }
115
* }</pre>
116
* </blockquote>
117
* <p>
118
* Specifying a pattern for ChoiceFormat objects is fairly straightforward.
119
* For example:
120
* <blockquote>
121
* <pre>{@code
122
* ChoiceFormat fmt = new ChoiceFormat(
123
* "-1#is negative| 0#is zero or fraction | 1#is one |1.0<is 1+ |2#is two |2<is more than 2.");
124
* System.out.println("Formatter Pattern : " + fmt.toPattern());
125
*
126
* System.out.println("Format with -INF : " + fmt.format(Double.NEGATIVE_INFINITY));
127
* System.out.println("Format with -1.0 : " + fmt.format(-1.0));
128
* System.out.println("Format with 0 : " + fmt.format(0));
129
* System.out.println("Format with 0.9 : " + fmt.format(0.9));
130
* System.out.println("Format with 1.0 : " + fmt.format(1));
131
* System.out.println("Format with 1.5 : " + fmt.format(1.5));
132
* System.out.println("Format with 2 : " + fmt.format(2));
133
* System.out.println("Format with 2.1 : " + fmt.format(2.1));
134
* System.out.println("Format with NaN : " + fmt.format(Double.NaN));
135
* System.out.println("Format with +INF : " + fmt.format(Double.POSITIVE_INFINITY));
136
* }</pre>
137
* </blockquote>
138
* And the output result would be like the following:
139
* <blockquote>
140
* <pre>{@code
141
* Format with -INF : is negative
142
* Format with -1.0 : is negative
143
* Format with 0 : is zero or fraction
144
* Format with 0.9 : is zero or fraction
145
* Format with 1.0 : is one
146
* Format with 1.5 : is 1+
147
* Format with 2 : is two
148
* Format with 2.1 : is more than 2.
149
* Format with NaN : is negative
150
* Format with +INF : is more than 2.
151
* }</pre>
152
* </blockquote>
153
*
154
* <h3><a name="synchronization">Synchronization</a></h3>
155
*
156
* <p>
157
* Choice formats are not synchronized.
158
* It is recommended to create separate format instances for each thread.
159
* If multiple threads access a format concurrently, it must be synchronized
160
* externally.
161
*
162
*
163
* @see DecimalFormat
164
* @see MessageFormat
165
* @author Mark Davis
166
*/
167
public class ChoiceFormat extends NumberFormat {
168
169
// Proclaim serial compatibility with 1.1 FCS
170
private static final long serialVersionUID = 1795184449645032964L;
171
172
/**
173
* Sets the pattern.
174
* @param newPattern See the class description.
175
*/
176
public void applyPattern(String newPattern) {
177
StringBuffer[] segments = new StringBuffer[2];
178
for (int i = 0; i < segments.length; ++i) {
179
segments[i] = new StringBuffer();
180
}
181
double[] newChoiceLimits = new double[30];
182
String[] newChoiceFormats = new String[30];
183
int count = 0;
184
int part = 0;
185
double startValue = 0;
186
double oldStartValue = Double.NaN;
187
boolean inQuote = false;
188
for (int i = 0; i < newPattern.length(); ++i) {
189
char ch = newPattern.charAt(i);
190
if (ch=='\'') {
191
// Check for "''" indicating a literal quote
192
if ((i+1)<newPattern.length() && newPattern.charAt(i+1)==ch) {
193
segments[part].append(ch);
194
++i;
195
} else {
196
inQuote = !inQuote;
197
}
198
} else if (inQuote) {
199
segments[part].append(ch);
200
} else if (ch == '<' || ch == '#' || ch == '\u2264') {
201
if (segments[0].length() == 0) {
202
throw new IllegalArgumentException();
203
}
204
try {
205
String tempBuffer = segments[0].toString();
206
if (tempBuffer.equals("\u221E")) {
207
startValue = Double.POSITIVE_INFINITY;
208
} else if (tempBuffer.equals("-\u221E")) {
209
startValue = Double.NEGATIVE_INFINITY;
210
} else {
211
startValue = Double.valueOf(segments[0].toString()).doubleValue();
212
}
213
} catch (Exception e) {
214
throw new IllegalArgumentException();
215
}
216
if (ch == '<' && startValue != Double.POSITIVE_INFINITY &&
217
startValue != Double.NEGATIVE_INFINITY) {
218
startValue = nextDouble(startValue);
219
}
220
if (startValue <= oldStartValue) {
221
throw new IllegalArgumentException();
222
}
223
segments[0].setLength(0);
224
part = 1;
225
} else if (ch == '|') {
226
if (count == newChoiceLimits.length) {
227
newChoiceLimits = doubleArraySize(newChoiceLimits);
228
newChoiceFormats = doubleArraySize(newChoiceFormats);
229
}
230
newChoiceLimits[count] = startValue;
231
newChoiceFormats[count] = segments[1].toString();
232
++count;
233
oldStartValue = startValue;
234
segments[1].setLength(0);
235
part = 0;
236
} else {
237
segments[part].append(ch);
238
}
239
}
240
// clean up last one
241
if (part == 1) {
242
if (count == newChoiceLimits.length) {
243
newChoiceLimits = doubleArraySize(newChoiceLimits);
244
newChoiceFormats = doubleArraySize(newChoiceFormats);
245
}
246
newChoiceLimits[count] = startValue;
247
newChoiceFormats[count] = segments[1].toString();
248
++count;
249
}
250
choiceLimits = new double[count];
251
System.arraycopy(newChoiceLimits, 0, choiceLimits, 0, count);
252
choiceFormats = new String[count];
253
System.arraycopy(newChoiceFormats, 0, choiceFormats, 0, count);
254
}
255
256
/**
257
* Gets the pattern.
258
*
259
* @return the pattern string
260
*/
261
public String toPattern() {
262
StringBuffer result = new StringBuffer();
263
for (int i = 0; i < choiceLimits.length; ++i) {
264
if (i != 0) {
265
result.append('|');
266
}
267
// choose based upon which has less precision
268
// approximate that by choosing the closest one to an integer.
269
// could do better, but it's not worth it.
270
double less = previousDouble(choiceLimits[i]);
271
double tryLessOrEqual = Math.abs(Math.IEEEremainder(choiceLimits[i], 1.0d));
272
double tryLess = Math.abs(Math.IEEEremainder(less, 1.0d));
273
274
if (tryLessOrEqual < tryLess) {
275
result.append(""+choiceLimits[i]);
276
result.append('#');
277
} else {
278
if (choiceLimits[i] == Double.POSITIVE_INFINITY) {
279
result.append("\u221E");
280
} else if (choiceLimits[i] == Double.NEGATIVE_INFINITY) {
281
result.append("-\u221E");
282
} else {
283
result.append(""+less);
284
}
285
result.append('<');
286
}
287
// Append choiceFormats[i], using quotes if there are special characters.
288
// Single quotes themselves must be escaped in either case.
289
String text = choiceFormats[i];
290
boolean needQuote = text.indexOf('<') >= 0
291
|| text.indexOf('#') >= 0
292
|| text.indexOf('\u2264') >= 0
293
|| text.indexOf('|') >= 0;
294
if (needQuote) result.append('\'');
295
if (text.indexOf('\'') < 0) result.append(text);
296
else {
297
for (int j=0; j<text.length(); ++j) {
298
char c = text.charAt(j);
299
result.append(c);
300
if (c == '\'') result.append(c);
301
}
302
}
303
if (needQuote) result.append('\'');
304
}
305
return result.toString();
306
}
307
308
/**
309
* Constructs with limits and corresponding formats based on the pattern.
310
*
311
* @param newPattern the new pattern string
312
* @see #applyPattern
313
*/
314
public ChoiceFormat(String newPattern) {
315
applyPattern(newPattern);
316
}
317
318
/**
319
* Constructs with the limits and the corresponding formats.
320
*
321
* @param limits limits in ascending order
322
* @param formats corresponding format strings
323
* @see #setChoices
324
*/
325
public ChoiceFormat(double[] limits, String[] formats) {
326
setChoices(limits, formats);
327
}
328
329
/**
330
* Set the choices to be used in formatting.
331
* @param limits contains the top value that you want
332
* parsed with that format, and should be in ascending sorted order. When
333
* formatting X, the choice will be the i, where
334
* limit[i] &le; X {@literal <} limit[i+1].
335
* If the limit array is not in ascending order, the results of formatting
336
* will be incorrect.
337
* @param formats are the formats you want to use for each limit.
338
* They can be either Format objects or Strings.
339
* When formatting with object Y,
340
* if the object is a NumberFormat, then ((NumberFormat) Y).format(X)
341
* is called. Otherwise Y.toString() is called.
342
*/
343
public void setChoices(double[] limits, String formats[]) {
344
if (limits.length != formats.length) {
345
throw new IllegalArgumentException(
346
"Array and limit arrays must be of the same length.");
347
}
348
choiceLimits = Arrays.copyOf(limits, limits.length);
349
choiceFormats = Arrays.copyOf(formats, formats.length);
350
}
351
352
/**
353
* Get the limits passed in the constructor.
354
* @return the limits.
355
*/
356
public double[] getLimits() {
357
double[] newLimits = Arrays.copyOf(choiceLimits, choiceLimits.length);
358
return newLimits;
359
}
360
361
/**
362
* Get the formats passed in the constructor.
363
* @return the formats.
364
*/
365
public Object[] getFormats() {
366
Object[] newFormats = Arrays.copyOf(choiceFormats, choiceFormats.length);
367
return newFormats;
368
}
369
370
// Overrides
371
372
/**
373
* Specialization of format. This method really calls
374
* <code>format(double, StringBuffer, FieldPosition)</code>
375
* thus the range of longs that are supported is only equal to
376
* the range that can be stored by double. This will never be
377
* a practical limitation.
378
*/
379
public StringBuffer format(long number, StringBuffer toAppendTo,
380
FieldPosition status) {
381
return format((double)number, toAppendTo, status);
382
}
383
384
/**
385
* Returns pattern with formatted double.
386
* @param number number to be formatted and substituted.
387
* @param toAppendTo where text is appended.
388
* @param status ignore no useful status is returned.
389
*/
390
public StringBuffer format(double number, StringBuffer toAppendTo,
391
FieldPosition status) {
392
// find the number
393
int i;
394
for (i = 0; i < choiceLimits.length; ++i) {
395
if (!(number >= choiceLimits[i])) {
396
// same as number < choiceLimits, except catchs NaN
397
break;
398
}
399
}
400
--i;
401
if (i < 0) i = 0;
402
// return either a formatted number, or a string
403
return toAppendTo.append(choiceFormats[i]);
404
}
405
406
/**
407
* Parses a Number from the input text.
408
* @param text the source text.
409
* @param status an input-output parameter. On input, the
410
* status.index field indicates the first character of the
411
* source text that should be parsed. On exit, if no error
412
* occurred, status.index is set to the first unparsed character
413
* in the source text. On exit, if an error did occur,
414
* status.index is unchanged and status.errorIndex is set to the
415
* first index of the character that caused the parse to fail.
416
* @return A Number representing the value of the number parsed.
417
*/
418
public Number parse(String text, ParsePosition status) {
419
// find the best number (defined as the one with the longest parse)
420
int start = status.index;
421
int furthest = start;
422
double bestNumber = Double.NaN;
423
double tempNumber = 0.0;
424
for (int i = 0; i < choiceFormats.length; ++i) {
425
String tempString = choiceFormats[i];
426
if (text.regionMatches(start, tempString, 0, tempString.length())) {
427
status.index = start + tempString.length();
428
tempNumber = choiceLimits[i];
429
if (status.index > furthest) {
430
furthest = status.index;
431
bestNumber = tempNumber;
432
if (furthest == text.length()) break;
433
}
434
}
435
}
436
status.index = furthest;
437
if (status.index == start) {
438
status.errorIndex = furthest;
439
}
440
return new Double(bestNumber);
441
}
442
443
/**
444
* Finds the least double greater than {@code d}.
445
* If {@code NaN}, returns same value.
446
* <p>Used to make half-open intervals.
447
*
448
* @param d the reference value
449
* @return the least double value greather than {@code d}
450
* @see #previousDouble
451
*/
452
public static final double nextDouble (double d) {
453
return nextDouble(d,true);
454
}
455
456
/**
457
* Finds the greatest double less than {@code d}.
458
* If {@code NaN}, returns same value.
459
*
460
* @param d the reference value
461
* @return the greatest double value less than {@code d}
462
* @see #nextDouble
463
*/
464
public static final double previousDouble (double d) {
465
return nextDouble(d,false);
466
}
467
468
/**
469
* Overrides Cloneable
470
*/
471
public Object clone()
472
{
473
ChoiceFormat other = (ChoiceFormat) super.clone();
474
// for primitives or immutables, shallow clone is enough
475
other.choiceLimits = choiceLimits.clone();
476
other.choiceFormats = choiceFormats.clone();
477
return other;
478
}
479
480
/**
481
* Generates a hash code for the message format object.
482
*/
483
public int hashCode() {
484
int result = choiceLimits.length;
485
if (choiceFormats.length > 0) {
486
// enough for reasonable distribution
487
result ^= choiceFormats[choiceFormats.length-1].hashCode();
488
}
489
return result;
490
}
491
492
/**
493
* Equality comparision between two
494
*/
495
public boolean equals(Object obj) {
496
if (obj == null) return false;
497
if (this == obj) // quick check
498
return true;
499
if (getClass() != obj.getClass())
500
return false;
501
ChoiceFormat other = (ChoiceFormat) obj;
502
return (Arrays.equals(choiceLimits, other.choiceLimits)
503
&& Arrays.equals(choiceFormats, other.choiceFormats));
504
}
505
506
/**
507
* After reading an object from the input stream, do a simple verification
508
* to maintain class invariants.
509
* @throws InvalidObjectException if the objects read from the stream is invalid.
510
*/
511
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
512
in.defaultReadObject();
513
if (choiceLimits.length != choiceFormats.length) {
514
throw new InvalidObjectException(
515
"limits and format arrays of different length.");
516
}
517
}
518
519
// ===============privates===========================
520
521
/**
522
* A list of lower bounds for the choices. The formatter will return
523
* <code>choiceFormats[i]</code> if the number being formatted is greater than or equal to
524
* <code>choiceLimits[i]</code> and less than <code>choiceLimits[i+1]</code>.
525
* @serial
526
*/
527
private double[] choiceLimits;
528
529
/**
530
* A list of choice strings. The formatter will return
531
* <code>choiceFormats[i]</code> if the number being formatted is greater than or equal to
532
* <code>choiceLimits[i]</code> and less than <code>choiceLimits[i+1]</code>.
533
* @serial
534
*/
535
private String[] choiceFormats;
536
537
/*
538
static final long SIGN = 0x8000000000000000L;
539
static final long EXPONENT = 0x7FF0000000000000L;
540
static final long SIGNIFICAND = 0x000FFFFFFFFFFFFFL;
541
542
private static double nextDouble (double d, boolean positive) {
543
if (Double.isNaN(d) || Double.isInfinite(d)) {
544
return d;
545
}
546
long bits = Double.doubleToLongBits(d);
547
long significand = bits & SIGNIFICAND;
548
if (bits < 0) {
549
significand |= (SIGN | EXPONENT);
550
}
551
long exponent = bits & EXPONENT;
552
if (positive) {
553
significand += 1;
554
// FIXME fix overflow & underflow
555
} else {
556
significand -= 1;
557
// FIXME fix overflow & underflow
558
}
559
bits = exponent | (significand & ~EXPONENT);
560
return Double.longBitsToDouble(bits);
561
}
562
*/
563
564
static final long SIGN = 0x8000000000000000L;
565
static final long EXPONENT = 0x7FF0000000000000L;
566
static final long POSITIVEINFINITY = 0x7FF0000000000000L;
567
568
/**
569
* Finds the least double greater than {@code d} (if {@code positive} is
570
* {@code true}), or the greatest double less than {@code d} (if
571
* {@code positive} is {@code false}).
572
* If {@code NaN}, returns same value.
573
*
574
* Does not affect floating-point flags,
575
* provided these member functions do not:
576
* Double.longBitsToDouble(long)
577
* Double.doubleToLongBits(double)
578
* Double.isNaN(double)
579
*
580
* @param d the reference value
581
* @param positive {@code true} if the least double is desired;
582
* {@code false} otherwise
583
* @return the least or greater double value
584
*/
585
public static double nextDouble (double d, boolean positive) {
586
587
/* filter out NaN's */
588
if (Double.isNaN(d)) {
589
return d;
590
}
591
592
/* zero's are also a special case */
593
if (d == 0.0) {
594
double smallestPositiveDouble = Double.longBitsToDouble(1L);
595
if (positive) {
596
return smallestPositiveDouble;
597
} else {
598
return -smallestPositiveDouble;
599
}
600
}
601
602
/* if entering here, d is a nonzero value */
603
604
/* hold all bits in a long for later use */
605
long bits = Double.doubleToLongBits(d);
606
607
/* strip off the sign bit */
608
long magnitude = bits & ~SIGN;
609
610
/* if next double away from zero, increase magnitude */
611
if ((bits > 0) == positive) {
612
if (magnitude != POSITIVEINFINITY) {
613
magnitude += 1;
614
}
615
}
616
/* else decrease magnitude */
617
else {
618
magnitude -= 1;
619
}
620
621
/* restore sign bit and return */
622
long signbit = bits & SIGN;
623
return Double.longBitsToDouble (magnitude | signbit);
624
}
625
626
private static double[] doubleArraySize(double[] array) {
627
int oldSize = array.length;
628
double[] newArray = new double[oldSize * 2];
629
System.arraycopy(array, 0, newArray, 0, oldSize);
630
return newArray;
631
}
632
633
private String[] doubleArraySize(String[] array) {
634
int oldSize = array.length;
635
String[] newArray = new String[oldSize * 2];
636
System.arraycopy(array, 0, newArray, 0, oldSize);
637
return newArray;
638
}
639
640
}
641
642