2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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.
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
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
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.
41 import java.io.IOException;
42 import java.io.ObjectInputStream;
43 import java.io.Serializable;
44 import java.util.Currency;
45 import java.util.Locale;
46 import java.util.ResourceBundle;
47 import java.util.concurrent.ConcurrentHashMap;
50 * This class represents the set of symbols (such as the decimal separator,
51 * the grouping separator, and so on) needed by <code>DecimalFormat</code>
52 * to format numbers. <code>DecimalFormat</code> creates for itself an instance of
53 * <code>DecimalFormatSymbols</code> from its locale data. If you need to change any
54 * of these symbols, you can get the <code>DecimalFormatSymbols</code> object from
55 * your <code>DecimalFormat</code> and modify it.
57 * @see java.util.Locale
63 public class DecimalFormatSymbols implements Cloneable, Serializable {
66 * Create a DecimalFormatSymbols object for the default locale.
67 * This constructor can only construct instances for the locales
68 * supported by the Java runtime environment, not for those
69 * supported by installed
70 * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
71 * implementations. For full locale coverage, use the
72 * {@link #getInstance(Locale) getInstance} method.
74 public DecimalFormatSymbols() {
75 initialize( Locale.getDefault(Locale.Category.FORMAT) );
79 * Create a DecimalFormatSymbols object for the given locale.
80 * This constructor can only construct instances for the locales
81 * supported by the Java runtime environment, not for those
82 * supported by installed
83 * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
84 * implementations. For full locale coverage, use the
85 * {@link #getInstance(Locale) getInstance} method.
87 * @exception NullPointerException if <code>locale</code> is null
89 public DecimalFormatSymbols( Locale locale ) {
94 * Returns an array of all locales for which the
95 * <code>getInstance</code> methods of this class can return
96 * localized instances.
97 * The returned array represents the union of locales supported by the Java
98 * runtime and by installed
99 * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
100 * implementations. It must contain at least a <code>Locale</code>
101 * instance equal to {@link java.util.Locale#US Locale.US}.
103 * @return An array of locales for which localized
104 * <code>DecimalFormatSymbols</code> instances are available.
107 public static Locale[] getAvailableLocales() {
108 return new Locale[] { Locale.US };
109 // LocaleServiceProviderPool pool =
110 // LocaleServiceProviderPool.getPool(DecimalFormatSymbolsProvider.class);
111 // return pool.getAvailableLocales();
115 * Gets the <code>DecimalFormatSymbols</code> instance for the default
116 * locale. This method provides access to <code>DecimalFormatSymbols</code>
117 * instances for locales supported by the Java runtime itself as well
118 * as for those supported by installed
119 * {@link java.text.spi.DecimalFormatSymbolsProvider
120 * DecimalFormatSymbolsProvider} implementations.
121 * @return a <code>DecimalFormatSymbols</code> instance.
124 public static final DecimalFormatSymbols getInstance() {
125 return getInstance(Locale.getDefault(Locale.Category.FORMAT));
129 * Gets the <code>DecimalFormatSymbols</code> instance for the specified
130 * locale. This method provides access to <code>DecimalFormatSymbols</code>
131 * instances for locales supported by the Java runtime itself as well
132 * as for those supported by installed
133 * {@link java.text.spi.DecimalFormatSymbolsProvider
134 * DecimalFormatSymbolsProvider} implementations.
135 * @param locale the desired locale.
136 * @return a <code>DecimalFormatSymbols</code> instance.
137 * @exception NullPointerException if <code>locale</code> is null
140 public static final DecimalFormatSymbols getInstance(Locale locale) {
142 // Check whether a provider can provide an implementation that's closer
143 // to the requested locale than what the Java runtime itself can provide.
144 LocaleServiceProviderPool pool =
145 LocaleServiceProviderPool.getPool(DecimalFormatSymbolsProvider.class);
146 if (pool.hasProviders()) {
147 DecimalFormatSymbols providersInstance = pool.getLocalizedObject(
148 DecimalFormatSymbolsGetter.INSTANCE, locale);
149 if (providersInstance != null) {
150 return providersInstance;
154 return new DecimalFormatSymbols(locale);
158 * Gets the character used for zero. Different for Arabic, etc.
160 public char getZeroDigit() {
165 * Sets the character used for zero. Different for Arabic, etc.
167 public void setZeroDigit(char zeroDigit) {
168 this.zeroDigit = zeroDigit;
172 * Gets the character used for thousands separator. Different for French, etc.
174 public char getGroupingSeparator() {
175 return groupingSeparator;
179 * Sets the character used for thousands separator. Different for French, etc.
181 public void setGroupingSeparator(char groupingSeparator) {
182 this.groupingSeparator = groupingSeparator;
186 * Gets the character used for decimal sign. Different for French, etc.
188 public char getDecimalSeparator() {
189 return decimalSeparator;
193 * Sets the character used for decimal sign. Different for French, etc.
195 public void setDecimalSeparator(char decimalSeparator) {
196 this.decimalSeparator = decimalSeparator;
200 * Gets the character used for per mille sign. Different for Arabic, etc.
202 public char getPerMill() {
207 * Sets the character used for per mille sign. Different for Arabic, etc.
209 public void setPerMill(char perMill) {
210 this.perMill = perMill;
214 * Gets the character used for percent sign. Different for Arabic, etc.
216 public char getPercent() {
221 * Sets the character used for percent sign. Different for Arabic, etc.
223 public void setPercent(char percent) {
224 this.percent = percent;
228 * Gets the character used for a digit in a pattern.
230 public char getDigit() {
235 * Sets the character used for a digit in a pattern.
237 public void setDigit(char digit) {
242 * Gets the character used to separate positive and negative subpatterns
245 public char getPatternSeparator() {
246 return patternSeparator;
250 * Sets the character used to separate positive and negative subpatterns
253 public void setPatternSeparator(char patternSeparator) {
254 this.patternSeparator = patternSeparator;
258 * Gets the string used to represent infinity. Almost always left
261 public String getInfinity() {
266 * Sets the string used to represent infinity. Almost always left
269 public void setInfinity(String infinity) {
270 this.infinity = infinity;
274 * Gets the string used to represent "not a number". Almost always left
277 public String getNaN() {
282 * Sets the string used to represent "not a number". Almost always left
285 public void setNaN(String NaN) {
290 * Gets the character used to represent minus sign. If no explicit
291 * negative format is specified, one is formed by prefixing
292 * minusSign to the positive format.
294 public char getMinusSign() {
299 * Sets the character used to represent minus sign. If no explicit
300 * negative format is specified, one is formed by prefixing
301 * minusSign to the positive format.
303 public void setMinusSign(char minusSign) {
304 this.minusSign = minusSign;
308 * Returns the currency symbol for the currency of these
309 * DecimalFormatSymbols in their locale.
312 public String getCurrencySymbol()
314 return currencySymbol;
318 * Sets the currency symbol for the currency of these
319 * DecimalFormatSymbols in their locale.
322 public void setCurrencySymbol(String currency)
324 currencySymbol = currency;
328 * Returns the ISO 4217 currency code of the currency of these
329 * DecimalFormatSymbols.
332 public String getInternationalCurrencySymbol()
334 return intlCurrencySymbol;
338 * Sets the ISO 4217 currency code of the currency of these
339 * DecimalFormatSymbols.
340 * If the currency code is valid (as defined by
341 * {@link java.util.Currency#getInstance(java.lang.String) Currency.getInstance}),
342 * this also sets the currency attribute to the corresponding Currency
343 * instance and the currency symbol attribute to the currency's symbol
344 * in the DecimalFormatSymbols' locale. If the currency code is not valid,
345 * then the currency attribute is set to null and the currency symbol
346 * attribute is not modified.
349 * @see #setCurrencySymbol
352 public void setInternationalCurrencySymbol(String currencyCode)
354 intlCurrencySymbol = currencyCode;
356 if (currencyCode != null) {
358 currency = Currency.getInstance(currencyCode);
359 currencySymbol = currency.getSymbol();
360 } catch (IllegalArgumentException e) {
366 * Gets the currency of these DecimalFormatSymbols. May be null if the
367 * currency symbol attribute was previously set to a value that's not
368 * a valid ISO 4217 currency code.
370 * @return the currency used, or null
373 public Currency getCurrency() {
378 * Sets the currency of these DecimalFormatSymbols.
379 * This also sets the currency symbol attribute to the currency's symbol
380 * in the DecimalFormatSymbols' locale, and the international currency
381 * symbol attribute to the currency's ISO 4217 currency code.
383 * @param currency the new currency to be used
384 * @exception NullPointerException if <code>currency</code> is null
386 * @see #setCurrencySymbol
387 * @see #setInternationalCurrencySymbol
389 public void setCurrency(Currency currency) {
390 if (currency == null) {
391 throw new NullPointerException();
393 this.currency = currency;
394 intlCurrencySymbol = currency.getCurrencyCode();
395 currencySymbol = currency.getSymbol(locale);
400 * Returns the monetary decimal separator.
403 public char getMonetaryDecimalSeparator()
405 return monetarySeparator;
409 * Sets the monetary decimal separator.
412 public void setMonetaryDecimalSeparator(char sep)
414 monetarySeparator = sep;
417 //------------------------------------------------------------
418 // BEGIN Package Private methods ... to be made public later
419 //------------------------------------------------------------
422 * Returns the character used to separate the mantissa from the exponent.
424 char getExponentialSymbol()
429 * Returns the string used to separate the mantissa from the exponent.
430 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
432 * @return the exponent separator string
433 * @see #setExponentSeparator(java.lang.String)
436 public String getExponentSeparator()
438 return exponentialSeparator;
442 * Sets the character used to separate the mantissa from the exponent.
444 void setExponentialSymbol(char exp)
450 * Sets the string used to separate the mantissa from the exponent.
451 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
453 * @param exp the exponent separator string
454 * @exception NullPointerException if <code>exp</code> is null
455 * @see #getExponentSeparator()
458 public void setExponentSeparator(String exp)
461 throw new NullPointerException();
463 exponentialSeparator = exp;
467 //------------------------------------------------------------
468 // END Package Private methods ... to be made public later
469 //------------------------------------------------------------
474 public Object clone() {
476 return (DecimalFormatSymbols)super.clone();
477 // other fields are bit-copied
478 } catch (CloneNotSupportedException e) {
479 throw new InternalError();
486 public boolean equals(Object obj) {
487 if (obj == null) return false;
488 if (this == obj) return true;
489 if (getClass() != obj.getClass()) return false;
490 DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
491 return (zeroDigit == other.zeroDigit &&
492 groupingSeparator == other.groupingSeparator &&
493 decimalSeparator == other.decimalSeparator &&
494 percent == other.percent &&
495 perMill == other.perMill &&
496 digit == other.digit &&
497 minusSign == other.minusSign &&
498 patternSeparator == other.patternSeparator &&
499 infinity.equals(other.infinity) &&
500 NaN.equals(other.NaN) &&
501 currencySymbol.equals(other.currencySymbol) &&
502 intlCurrencySymbol.equals(other.intlCurrencySymbol) &&
503 currency == other.currency &&
504 monetarySeparator == other.monetarySeparator &&
505 exponentialSeparator.equals(other.exponentialSeparator) &&
506 locale.equals(other.locale));
512 public int hashCode() {
513 int result = zeroDigit;
514 result = result * 37 + groupingSeparator;
515 result = result * 37 + decimalSeparator;
520 * Initializes the symbols from the FormatData resource bundle.
522 private void initialize( Locale locale ) {
523 this.locale = locale;
525 // get resource bundle data - try the cache first
526 boolean needCacheUpdate = false;
527 Object[] data = cachedLocaleData.get(locale);
528 if (data == null) { /* cache miss */
529 // When numbering system is thai (Locale's extension contains u-nu-thai),
530 // we read the data from th_TH_TH.
531 Locale lookupLocale = locale;
532 String numberType = locale.getUnicodeLocaleType("nu");
533 if (numberType != null && numberType.equals("thai")) {
534 lookupLocale = new Locale("th", "TH", "TH");
536 data = new Object[3];
537 // ResourceBundle rb = LocaleData.getNumberFormatData(lookupLocale);
538 // data[0] = rb.getStringArray("NumberElements");
539 needCacheUpdate = true;
542 String[] numberElements = (String[]) data[0];
544 decimalSeparator = numberElements[0].charAt(0);
545 groupingSeparator = numberElements[1].charAt(0);
546 patternSeparator = numberElements[2].charAt(0);
547 percent = numberElements[3].charAt(0);
548 zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
549 digit = numberElements[5].charAt(0);
550 minusSign = numberElements[6].charAt(0);
551 exponential = numberElements[7].charAt(0);
552 exponentialSeparator = numberElements[7]; //string representation new since 1.6
553 perMill = numberElements[8].charAt(0);
554 infinity = numberElements[9];
555 NaN = numberElements[10];
557 // Try to obtain the currency used in the locale's country.
558 // Check for empty country string separately because it's a valid
559 // country ID for Locale (and used for the C locale), but not a valid
560 // ISO 3166 country code, and exceptions are expensive.
561 if (!"".equals(locale.getCountry())) {
563 currency = Currency.getInstance(locale);
564 } catch (IllegalArgumentException e) {
565 // use default values below for compatibility
568 if (currency != null) {
569 intlCurrencySymbol = currency.getCurrencyCode();
570 if (data[1] != null && data[1] == intlCurrencySymbol) {
571 currencySymbol = (String) data[2];
573 currencySymbol = currency.getSymbol(locale);
574 data[1] = intlCurrencySymbol;
575 data[2] = currencySymbol;
576 needCacheUpdate = true;
580 intlCurrencySymbol = "XXX";
582 currency = Currency.getInstance(intlCurrencySymbol);
583 } catch (IllegalArgumentException e) {
585 currencySymbol = "\u00A4";
587 // Currently the monetary decimal separator is the same as the
588 // standard decimal separator for all locales that we support.
589 // If that changes, add a new entry to NumberElements.
590 monetarySeparator = decimalSeparator;
592 if (needCacheUpdate) {
593 cachedLocaleData.putIfAbsent(locale, data);
598 * Reads the default serializable fields, provides default values for objects
599 * in older serial versions, and initializes non-serializable fields.
600 * If <code>serialVersionOnStream</code>
601 * is less than 1, initializes <code>monetarySeparator</code> to be
602 * the same as <code>decimalSeparator</code> and <code>exponential</code>
604 * If <code>serialVersionOnStream</code> is less than 2,
605 * initializes <code>locale</code>to the root locale, and initializes
606 * If <code>serialVersionOnStream</code> is less than 3, it initializes
607 * <code>exponentialSeparator</code> using <code>exponential</code>.
608 * Sets <code>serialVersionOnStream</code> back to the maximum allowed value so that
609 * default serialization will work properly if this object is streamed out again.
610 * Initializes the currency from the intlCurrencySymbol field.
614 private void readObject(ObjectInputStream stream)
615 throws IOException, ClassNotFoundException {
616 stream.defaultReadObject();
617 if (serialVersionOnStream < 1) {
618 // Didn't have monetarySeparator or exponential field;
620 monetarySeparator = decimalSeparator;
623 if (serialVersionOnStream < 2) {
624 // didn't have locale; use root locale
625 locale = Locale.ROOT;
627 if (serialVersionOnStream < 3) {
628 // didn't have exponentialSeparator. Create one using exponential
629 exponentialSeparator = Character.toString(exponential);
631 serialVersionOnStream = currentSerialVersion;
633 if (intlCurrencySymbol != null) {
635 currency = Currency.getInstance(intlCurrencySymbol);
636 } catch (IllegalArgumentException e) {
642 * Character used for zero.
647 private char zeroDigit;
650 * Character used for thousands separator.
653 * @see #getGroupingSeparator
655 private char groupingSeparator;
658 * Character used for decimal sign.
661 * @see #getDecimalSeparator
663 private char decimalSeparator;
666 * Character used for per mille sign.
671 private char perMill;
674 * Character used for percent sign.
678 private char percent;
681 * Character used for a digit in a pattern.
689 * Character used to separate positive and negative subpatterns
693 * @see #getPatternSeparator
695 private char patternSeparator;
698 * String used to represent infinity.
702 private String infinity;
705 * String used to represent "not a number".
712 * Character used to represent minus sign.
716 private char minusSign;
719 * String denoting the local currency, e.g. "$".
721 * @see #getCurrencySymbol
723 private String currencySymbol;
726 * ISO 4217 currency code denoting the local currency, e.g. "USD".
728 * @see #getInternationalCurrencySymbol
730 private String intlCurrencySymbol;
733 * The decimal separator used when formatting currency values.
736 * @see #getMonetaryDecimalSeparator
738 private char monetarySeparator; // Field new in JDK 1.1.6
741 * The character used to distinguish the exponent in a number formatted
742 * in exponential notation, e.g. 'E' for a number such as "1.23E45".
744 * Note that the public API provides no way to set this field,
745 * even though it is supported by the implementation and the stream format.
746 * The intent is that this will be added to the API in the future.
751 private char exponential; // Field new in JDK 1.1.6
754 * The string used to separate the mantissa from the exponent.
755 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
757 * If both <code>exponential</code> and <code>exponentialSeparator</code>
758 * exist, this <code>exponentialSeparator</code> has the precedence.
763 private String exponentialSeparator; // Field new in JDK 1.6
766 * The locale of these currency format symbols.
771 private Locale locale;
773 // currency; only the ISO code is serialized.
774 private transient Currency currency;
776 // Proclaim JDK 1.1 FCS compatibility
777 static final long serialVersionUID = 5772796243397350300L;
779 // The internal serial version which says which version was written
780 // - 0 (default) for version up to JDK 1.1.5
781 // - 1 for version from JDK 1.1.6, which includes two new fields:
782 // monetarySeparator and exponential.
783 // - 2 for version from J2SE 1.4, which includes locale field.
784 // - 3 for version from J2SE 1.6, which includes exponentialSeparator field.
785 private static final int currentSerialVersion = 3;
788 * Describes the version of <code>DecimalFormatSymbols</code> present on the stream.
789 * Possible values are:
791 * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
793 * <li><b>1</b>: Versions written by JDK 1.1.6 or later, which include
794 * two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
795 * <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
796 * new <code>locale</code> field.
797 * <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a
798 * new <code>exponentialSeparator</code> field.
800 * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
801 * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
807 private int serialVersionOnStream = currentSerialVersion;
810 * cache to hold the NumberElements and the Currency
813 private static final ConcurrentHashMap<Locale, Object[]> cachedLocaleData = new ConcurrentHashMap<Locale, Object[]>(3);
816 * Obtains a DecimalFormatSymbols instance from a DecimalFormatSymbolsProvider
818 private static class DecimalFormatSymbolsGetter
819 implements LocaleServiceProviderPool.LocalizedObjectGetter<DecimalFormatSymbolsProvider,
820 DecimalFormatSymbols> {
821 private static final DecimalFormatSymbolsGetter INSTANCE =
822 new DecimalFormatSymbolsGetter();
824 public DecimalFormatSymbols getObject(
825 DecimalFormatSymbolsProvider decimalFormatSymbolsProvider,
829 assert params.length == 0;
830 return decimalFormatSymbolsProvider.getInstance(locale);