• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package java.text;
19 
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 import java.io.ObjectStreamField;
24 import java.io.Serializable;
25 import java.util.Currency;
26 import java.util.Locale;
27 import libcore.icu.ICU;
28 import libcore.icu.LocaleData;
29 
30 /**
31  * Encapsulates the set of symbols (such as the decimal separator, the grouping
32  * separator, and so on) needed by {@code DecimalFormat} to format numbers.
33  * {@code DecimalFormat} internally creates an instance of
34  * {@code DecimalFormatSymbols} from its locale data. If you need to change any
35  * of these symbols, you can get the {@code DecimalFormatSymbols} object from
36  * your {@code DecimalFormat} and modify it.
37  *
38  * @see java.util.Locale
39  * @see DecimalFormat
40  */
41 public class DecimalFormatSymbols implements Cloneable, Serializable {
42 
43     private static final long serialVersionUID = 5772796243397350300L;
44 
45     private char zeroDigit;
46     private char digit;
47     private char decimalSeparator;
48     private char groupingSeparator;
49     private char patternSeparator;
50     private char percent;
51     private char perMill;
52     private char monetarySeparator;
53     private char minusSign;
54     private String infinity, NaN, currencySymbol, intlCurrencySymbol;
55 
56     private transient Currency currency;
57     private transient Locale locale;
58     private transient String exponentSeparator;
59 
60     /**
61      * Constructs a new {@code DecimalFormatSymbols} containing the symbols for
62      * the user's default locale.
63      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
64      * Best practice is to create a {@code DecimalFormat}
65      * and then to get the {@code DecimalFormatSymbols} from that object by
66      * calling {@link DecimalFormat#getDecimalFormatSymbols()}.
67      */
DecimalFormatSymbols()68     public DecimalFormatSymbols() {
69         this(Locale.getDefault());
70     }
71 
72     /**
73      * Constructs a new DecimalFormatSymbols containing the symbols for the
74      * specified Locale.
75      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
76      * Best practice is to create a {@code DecimalFormat}
77      * and then to get the {@code DecimalFormatSymbols} from that object by
78      * calling {@link DecimalFormat#getDecimalFormatSymbols()}.
79      *
80      * @param locale
81      *            the locale.
82      */
DecimalFormatSymbols(Locale locale)83     public DecimalFormatSymbols(Locale locale) {
84         LocaleData localeData = LocaleData.get(locale);
85         this.zeroDigit = localeData.zeroDigit;
86         this.digit = '#';
87         this.decimalSeparator = localeData.decimalSeparator;
88         this.groupingSeparator = localeData.groupingSeparator;
89         this.patternSeparator = localeData.patternSeparator;
90         this.percent = localeData.percent;
91         this.perMill = localeData.perMill;
92         this.monetarySeparator = localeData.monetarySeparator;
93         this.minusSign = localeData.minusSign;
94         this.infinity = localeData.infinity;
95         this.NaN = localeData.NaN;
96         this.exponentSeparator = localeData.exponentSeparator;
97         this.locale = locale;
98         try {
99             currency = Currency.getInstance(locale);
100             currencySymbol = currency.getSymbol(locale);
101             intlCurrencySymbol = currency.getCurrencyCode();
102         } catch (IllegalArgumentException e) {
103             currency = Currency.getInstance("XXX");
104             currencySymbol = localeData.currencySymbol;
105             intlCurrencySymbol = localeData.internationalCurrencySymbol;
106         }
107     }
108 
109     /**
110      * Returns a new {@code DecimalFormatSymbols} instance for the user's default locale.
111      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
112      *
113      * @return an instance of {@code DecimalFormatSymbols}
114      * @since 1.6
115      */
getInstance()116     public static DecimalFormatSymbols getInstance() {
117         return getInstance(Locale.getDefault());
118     }
119 
120     /**
121      * Returns a new {@code DecimalFormatSymbols} for the given locale.
122      *
123      * @param locale the locale
124      * @return an instance of {@code DecimalFormatSymbols}
125      * @throws NullPointerException if {@code locale == null}
126      * @since 1.6
127      */
getInstance(Locale locale)128     public static DecimalFormatSymbols getInstance(Locale locale) {
129         if (locale == null) {
130             throw new NullPointerException();
131         }
132         return new DecimalFormatSymbols(locale);
133     }
134 
135     /**
136      * Returns an array of locales for which custom {@code DecimalFormatSymbols} instances
137      * are available.
138      * <p>Note that Android does not support user-supplied locale service providers.
139      * @since 1.6
140      */
getAvailableLocales()141     public static Locale[] getAvailableLocales() {
142         return ICU.getAvailableDecimalFormatSymbolsLocales();
143     }
144 
145     @Override
clone()146     public Object clone() {
147         try {
148             return super.clone();
149         } catch (CloneNotSupportedException e) {
150             throw new AssertionError(e);
151         }
152     }
153 
154     /**
155      * Compares the specified object to this {@code DecimalFormatSymbols} and
156      * indicates if they are equal. In order to be equal, {@code object} must be
157      * an instance of {@code DecimalFormatSymbols} and contain the same symbols.
158      *
159      * @param object
160      *            the object to compare with this object.
161      * @return {@code true} if the specified object is equal to this
162      *         {@code DecimalFormatSymbols}; {@code false} otherwise.
163      * @see #hashCode
164      */
165     @Override
equals(Object object)166     public boolean equals(Object object) {
167         if (this == object) {
168             return true;
169         }
170         if (!(object instanceof DecimalFormatSymbols)) {
171             return false;
172         }
173         DecimalFormatSymbols obj = (DecimalFormatSymbols) object;
174         return currency.equals(obj.currency) &&
175                 currencySymbol.equals(obj.currencySymbol) &&
176                 decimalSeparator == obj.decimalSeparator &&
177                 digit == obj.digit &&
178                 exponentSeparator.equals(obj.exponentSeparator) &&
179                 groupingSeparator == obj.groupingSeparator &&
180                 infinity.equals(obj.infinity) &&
181                 intlCurrencySymbol.equals(obj.intlCurrencySymbol) &&
182                 minusSign == obj.minusSign &&
183                 monetarySeparator == obj.monetarySeparator &&
184                 NaN.equals(obj.NaN) &&
185                 patternSeparator == obj.patternSeparator &&
186                 perMill == obj.perMill &&
187                 percent == obj.percent &&
188                 zeroDigit == obj.zeroDigit;
189     }
190 
191     @Override
toString()192     public String toString() {
193         return getClass().getName() +
194                 "[currency=" + currency +
195                 ",currencySymbol=" + currencySymbol +
196                 ",decimalSeparator=" + decimalSeparator +
197                 ",digit=" + digit +
198                 ",exponentSeparator=" + exponentSeparator +
199                 ",groupingSeparator=" + groupingSeparator +
200                 ",infinity=" + infinity +
201                 ",intlCurrencySymbol=" + intlCurrencySymbol +
202                 ",minusSign=" + minusSign +
203                 ",monetarySeparator=" + monetarySeparator +
204                 ",NaN=" + NaN +
205                 ",patternSeparator=" + patternSeparator +
206                 ",perMill=" + perMill +
207                 ",percent=" + percent +
208                 ",zeroDigit=" + zeroDigit +
209                 "]";
210     }
211 
212     /**
213      * Returns the currency.
214      * <p>
215      * {@code null} is returned if {@code setInternationalCurrencySymbol()} has
216      * been previously called with a value that is not a valid ISO 4217 currency
217      * code.
218      * <p>
219      *
220      * @return the currency that was set in the constructor or by calling
221      *         {@code setCurrency()} or {@code setInternationalCurrencySymbol()},
222      *         or {@code null} if an invalid currency was set.
223      * @see #setCurrency(Currency)
224      * @see #setInternationalCurrencySymbol(String)
225      */
getCurrency()226     public Currency getCurrency() {
227         return currency;
228     }
229 
230     /**
231      * Returns the international currency symbol.
232      *
233      * @return the international currency symbol as string.
234      */
getInternationalCurrencySymbol()235     public String getInternationalCurrencySymbol() {
236         return intlCurrencySymbol;
237     }
238 
239     /**
240      * Returns the currency symbol.
241      *
242      * @return the currency symbol as string.
243      */
getCurrencySymbol()244     public String getCurrencySymbol() {
245         return currencySymbol;
246     }
247 
248     /**
249      * Returns the character which represents the decimal point in a number.
250      *
251      * @return the decimal separator character.
252      */
getDecimalSeparator()253     public char getDecimalSeparator() {
254         return decimalSeparator;
255     }
256 
257     /**
258      * Returns the character which represents a single digit in a format
259      * pattern.
260      *
261      * @return the digit pattern character.
262      */
getDigit()263     public char getDigit() {
264         return digit;
265     }
266 
267     /**
268      * Returns the character used as the thousands separator in a number.
269      *
270      * @return the thousands separator character.
271      */
getGroupingSeparator()272     public char getGroupingSeparator() {
273         return groupingSeparator;
274     }
275 
276     /**
277      * Returns the string which represents infinity.
278      *
279      * @return the infinity symbol as a string.
280      */
getInfinity()281     public String getInfinity() {
282         return infinity;
283     }
284 
285     /**
286      * Returns the minus sign character.
287      *
288      * @return the minus sign as a character.
289      */
getMinusSign()290     public char getMinusSign() {
291         return minusSign;
292     }
293 
294     /**
295      * Returns the character which represents the decimal point in a monetary
296      * value.
297      *
298      * @return the monetary decimal point as a character.
299      */
getMonetaryDecimalSeparator()300     public char getMonetaryDecimalSeparator() {
301         return monetarySeparator;
302     }
303 
304     /**
305      * Returns the string which represents NaN.
306      *
307      * @return the symbol NaN as a string.
308      */
getNaN()309     public String getNaN() {
310         return NaN;
311     }
312 
313     /**
314      * Returns the character which separates the positive and negative patterns
315      * in a format pattern.
316      *
317      * @return the pattern separator character.
318      */
getPatternSeparator()319     public char getPatternSeparator() {
320         return patternSeparator;
321     }
322 
323     /**
324      * Returns the percent character.
325      *
326      * @return the percent character.
327      */
getPercent()328     public char getPercent() {
329         return percent;
330     }
331 
332     /**
333      * Returns the per mill sign character.
334      *
335      * @return the per mill sign character.
336      */
getPerMill()337     public char getPerMill() {
338         return perMill;
339     }
340 
341     /**
342      * Returns the character which represents zero.
343      *
344      * @return the zero character.
345      */
getZeroDigit()346     public char getZeroDigit() {
347         return zeroDigit;
348     }
349 
350     /*
351      * Returns the string used to separate mantissa and exponent. Typically "E", as in "1.2E3".
352      * @since 1.6
353      */
getExponentSeparator()354     public String getExponentSeparator() {
355         return exponentSeparator;
356     }
357 
358     @Override
hashCode()359     public int hashCode() {
360         int result = 17;
361         result = 31*result + zeroDigit;
362         result = 31*result + digit;
363         result = 31*result + decimalSeparator;
364         result = 31*result + groupingSeparator;
365         result = 31*result + patternSeparator;
366         result = 31*result + percent;
367         result = 31*result + perMill;
368         result = 31*result + monetarySeparator;
369         result = 31*result + minusSign;
370         result = 31*result + exponentSeparator.hashCode();
371         result = 31*result + infinity.hashCode();
372         result = 31*result + NaN.hashCode();
373         result = 31*result + currencySymbol.hashCode();
374         result = 31*result + intlCurrencySymbol.hashCode();
375         return result;
376     }
377 
378     /**
379      * Sets the currency.
380      * <p>
381      * The international currency symbol and the currency symbol are updated,
382      * but the min and max number of fraction digits stays the same.
383      * <p>
384      *
385      * @param currency
386      *            the new currency.
387      * @throws NullPointerException
388      *             if {@code currency} is {@code null}.
389      */
setCurrency(Currency currency)390     public void setCurrency(Currency currency) {
391         if (currency == null) {
392             throw new NullPointerException();
393         }
394         if (currency == this.currency) {
395             return;
396         }
397         this.currency = currency;
398         intlCurrencySymbol = currency.getCurrencyCode();
399         currencySymbol = currency.getSymbol(locale);
400     }
401 
402     /**
403      * Sets the international currency symbol.
404      * <p>
405      * The currency and currency symbol are also updated if {@code value} is a
406      * valid ISO4217 currency code.
407      * <p>
408      * The min and max number of fraction digits stay the same.
409      *
410      * @param value
411      *            the currency code.
412      */
setInternationalCurrencySymbol(String value)413     public void setInternationalCurrencySymbol(String value) {
414         if (value == null) {
415             currency = null;
416             intlCurrencySymbol = null;
417             return;
418         }
419 
420         if (value.equals(intlCurrencySymbol)) {
421             return;
422         }
423 
424         try {
425             currency = Currency.getInstance(value);
426             currencySymbol = currency.getSymbol(locale);
427         } catch (IllegalArgumentException e) {
428             currency = null;
429         }
430         intlCurrencySymbol = value;
431     }
432 
433     /**
434      * Sets the currency symbol.
435      *
436      * @param value
437      *            the currency symbol.
438      */
setCurrencySymbol(String value)439     public void setCurrencySymbol(String value) {
440         this.currencySymbol = value;
441     }
442 
443     /**
444      * Sets the character which represents the decimal point in a number.
445      *
446      * @param value
447      *            the decimal separator character.
448      */
setDecimalSeparator(char value)449     public void setDecimalSeparator(char value) {
450         this.decimalSeparator = value;
451     }
452 
453     /**
454      * Sets the character which represents a single digit in a format pattern.
455      *
456      * @param value
457      *            the digit character.
458      */
setDigit(char value)459     public void setDigit(char value) {
460         this.digit = value;
461     }
462 
463     /**
464      * Sets the character used as the thousands separator in a number.
465      *
466      * @param value
467      *            the grouping separator character.
468      */
setGroupingSeparator(char value)469     public void setGroupingSeparator(char value) {
470         this.groupingSeparator = value;
471     }
472 
473     /**
474      * Sets the string which represents infinity.
475      *
476      * @param value
477      *            the string representing infinity.
478      */
setInfinity(String value)479     public void setInfinity(String value) {
480         this.infinity = value;
481     }
482 
483     /**
484      * Sets the minus sign character.
485      *
486      * @param value
487      *            the minus sign character.
488      */
setMinusSign(char value)489     public void setMinusSign(char value) {
490         this.minusSign = value;
491     }
492 
493     /**
494      * Sets the character which represents the decimal point in a monetary
495      * value.
496      *
497      * @param value
498      *            the monetary decimal separator character.
499      */
setMonetaryDecimalSeparator(char value)500     public void setMonetaryDecimalSeparator(char value) {
501         this.monetarySeparator = value;
502     }
503 
504     /**
505      * Sets the string which represents NaN.
506      *
507      * @param value
508      *            the string representing NaN.
509      */
setNaN(String value)510     public void setNaN(String value) {
511         this.NaN = value;
512     }
513 
514     /**
515      * Sets the character which separates the positive and negative patterns in
516      * a format pattern.
517      *
518      * @param value
519      *            the pattern separator character.
520      */
setPatternSeparator(char value)521     public void setPatternSeparator(char value) {
522         this.patternSeparator = value;
523     }
524 
525     /**
526      * Sets the percent character.
527      *
528      * @param value
529      *            the percent character.
530      */
setPercent(char value)531     public void setPercent(char value) {
532         this.percent = value;
533     }
534 
535     /**
536      * Sets the per mill sign character.
537      *
538      * @param value
539      *            the per mill character.
540      */
setPerMill(char value)541     public void setPerMill(char value) {
542         this.perMill = value;
543     }
544 
545     /**
546      * Sets the character which represents zero.
547      *
548      * @param value
549      *            the zero digit character.
550      */
setZeroDigit(char value)551     public void setZeroDigit(char value) {
552         this.zeroDigit = value;
553     }
554 
555     /**
556      * Sets the string used to separate mantissa and exponent. Typically "E", as in "1.2E3".
557      * @since 1.6
558      */
setExponentSeparator(String value)559     public void setExponentSeparator(String value) {
560         if (value == null) {
561             throw new NullPointerException();
562         }
563         this.exponentSeparator = value;
564     }
565 
566     private static final ObjectStreamField[] serialPersistentFields = {
567         new ObjectStreamField("currencySymbol", String.class),
568         new ObjectStreamField("decimalSeparator", char.class),
569         new ObjectStreamField("digit", char.class),
570         new ObjectStreamField("exponential", char.class),
571         new ObjectStreamField("exponentialSeparator", String.class),
572         new ObjectStreamField("groupingSeparator", char.class),
573         new ObjectStreamField("infinity", String.class),
574         new ObjectStreamField("intlCurrencySymbol", String.class),
575         new ObjectStreamField("minusSign", char.class),
576         new ObjectStreamField("monetarySeparator", char.class),
577         new ObjectStreamField("NaN", String.class),
578         new ObjectStreamField("patternSeparator", char.class),
579         new ObjectStreamField("percent", char.class),
580         new ObjectStreamField("perMill", char.class),
581         new ObjectStreamField("serialVersionOnStream", int.class),
582         new ObjectStreamField("zeroDigit", char.class),
583         new ObjectStreamField("locale", Locale.class),
584     };
585 
writeObject(ObjectOutputStream stream)586     private void writeObject(ObjectOutputStream stream) throws IOException {
587         ObjectOutputStream.PutField fields = stream.putFields();
588         fields.put("currencySymbol", currencySymbol);
589         fields.put("decimalSeparator", getDecimalSeparator());
590         fields.put("digit", getDigit());
591         fields.put("exponential", exponentSeparator.charAt(0));
592         fields.put("exponentialSeparator", exponentSeparator);
593         fields.put("groupingSeparator", getGroupingSeparator());
594         fields.put("infinity", infinity);
595         fields.put("intlCurrencySymbol", intlCurrencySymbol);
596         fields.put("minusSign", getMinusSign());
597         fields.put("monetarySeparator", getMonetaryDecimalSeparator());
598         fields.put("NaN", NaN);
599         fields.put("patternSeparator", getPatternSeparator());
600         fields.put("percent", getPercent());
601         fields.put("perMill", getPerMill());
602         fields.put("serialVersionOnStream", 3);
603         fields.put("zeroDigit", getZeroDigit());
604         fields.put("locale", locale);
605         stream.writeFields();
606     }
607 
readObject(ObjectInputStream stream)608     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
609         ObjectInputStream.GetField fields = stream.readFields();
610         final int serialVersionOnStream = fields.get("serialVersionOnStream", 0);
611         currencySymbol = (String) fields.get("currencySymbol", "");
612         setDecimalSeparator(fields.get("decimalSeparator", '.'));
613         setDigit(fields.get("digit", '#'));
614         setGroupingSeparator(fields.get("groupingSeparator", ','));
615         infinity = (String) fields.get("infinity", "");
616         intlCurrencySymbol = (String) fields.get("intlCurrencySymbol", "");
617         setMinusSign(fields.get("minusSign", '-'));
618         NaN = (String) fields.get("NaN", "");
619         setPatternSeparator(fields.get("patternSeparator", ';'));
620         setPercent(fields.get("percent", '%'));
621         setPerMill(fields.get("perMill", '\u2030'));
622         setZeroDigit(fields.get("zeroDigit", '0'));
623         locale = (Locale) fields.get("locale", null);
624         if (serialVersionOnStream == 0) {
625             setMonetaryDecimalSeparator(getDecimalSeparator());
626         } else {
627             setMonetaryDecimalSeparator(fields.get("monetarySeparator", '.'));
628         }
629 
630         if (serialVersionOnStream == 0) {
631             // Prior to Java 1.1.6, the exponent separator wasn't configurable.
632             exponentSeparator = "E";
633         } else if (serialVersionOnStream < 3) {
634             // In Javas 1.1.6 and 1.4, there was a character field "exponential".
635             setExponentSeparator(String.valueOf(fields.get("exponential", 'E')));
636         } else {
637             // In Java 6, there's a new "exponentialSeparator" field.
638             setExponentSeparator((String) fields.get("exponentialSeparator", "E"));
639         }
640 
641         try {
642             currency = Currency.getInstance(intlCurrencySymbol);
643         } catch (IllegalArgumentException e) {
644             currency = null;
645         }
646     }
647 }
648