001: /*
002: *
003: * @(#)DecimalFormatSymbols.java 1.38 06/10/10
004: *
005: * Portions Copyright 2000-2006 Sun Microsystems, Inc. All Rights
006: * Reserved. Use is subject to license terms.
007: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
008: *
009: * This program is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU General Public License version
011: * 2 only, as published by the Free Software Foundation.
012: *
013: * This program is distributed in the hope that it will be useful, but
014: * WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * General Public License version 2 for more details (a copy is
017: * included at /legal/license.txt).
018: *
019: * You should have received a copy of the GNU General Public License
020: * version 2 along with this work; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022: * 02110-1301 USA
023: *
024: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
025: * Clara, CA 95054 or visit www.sun.com if you need additional
026: * information or have any questions.
027: */
028:
029: /*
030: * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
031: * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
032: *
033: * The original version of this source code and documentation is copyrighted
034: * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
035: * materials are provided under terms of a License Agreement between Taligent
036: * and Sun. This technology is protected by multiple US and International
037: * patents. This notice and attribution to Taligent may not be removed.
038: * Taligent is a registered trademark of Taligent, Inc.
039: *
040: */
041:
042: package java.text;
043:
044: import java.io.IOException;
045: import java.io.ObjectInputStream;
046: import java.io.Serializable;
047: import java.util.Currency;
048: import java.util.Hashtable;
049: import java.util.Locale;
050: import java.util.ResourceBundle;
051: import sun.text.resources.LocaleData;
052:
053: /**
054: * This class represents the set of symbols (such as the decimal separator,
055: * the grouping separator, and so on) needed by <code>DecimalFormat</code>
056: * to format numbers. <code>DecimalFormat</code> creates for itself an instance of
057: * <code>DecimalFormatSymbols</code> from its locale data. If you need to change any
058: * of these symbols, you can get the <code>DecimalFormatSymbols</code> object from
059: * your <code>DecimalFormat</code> and modify it.
060: *
061: * @see java.util.Locale
062: * @see DecimalFormat
063: * @version 1.39, 01/23/03
064: * @author Mark Davis
065: * @author Alan Liu
066: */
067:
068: final public class DecimalFormatSymbols implements Cloneable,
069: Serializable {
070:
071: /**
072: * Create a DecimalFormatSymbols object for the default locale.
073: */
074: public DecimalFormatSymbols() {
075: initialize(Locale.getDefault());
076: }
077:
078: /**
079: * Create a DecimalFormatSymbols object for the given locale.
080: *
081: * @exception NullPointerException if <code>locale</code> is null
082: */
083: public DecimalFormatSymbols(Locale locale) {
084: initialize(locale);
085: }
086:
087: /**
088: * Gets the character used for zero. Different for Arabic, etc.
089: */
090: public char getZeroDigit() {
091: return zeroDigit;
092: }
093:
094: /**
095: * Sets the character used for zero. Different for Arabic, etc.
096: */
097: public void setZeroDigit(char zeroDigit) {
098: this .zeroDigit = zeroDigit;
099: }
100:
101: /**
102: * Gets the character used for thousands separator. Different for French, etc.
103: */
104: public char getGroupingSeparator() {
105: return groupingSeparator;
106: }
107:
108: /**
109: * Sets the character used for thousands separator. Different for French, etc.
110: */
111: public void setGroupingSeparator(char groupingSeparator) {
112: this .groupingSeparator = groupingSeparator;
113: }
114:
115: /**
116: * Gets the character used for decimal sign. Different for French, etc.
117: */
118: public char getDecimalSeparator() {
119: return decimalSeparator;
120: }
121:
122: /**
123: * Sets the character used for decimal sign. Different for French, etc.
124: */
125: public void setDecimalSeparator(char decimalSeparator) {
126: this .decimalSeparator = decimalSeparator;
127: }
128:
129: /**
130: * Gets the character used for mille percent sign. Different for Arabic, etc.
131: */
132: public char getPerMill() {
133: return perMill;
134: }
135:
136: /**
137: * Sets the character used for mille percent sign. Different for Arabic, etc.
138: */
139: public void setPerMill(char perMill) {
140: this .perMill = perMill;
141: }
142:
143: /**
144: * Gets the character used for percent sign. Different for Arabic, etc.
145: */
146: public char getPercent() {
147: return percent;
148: }
149:
150: /**
151: * Sets the character used for percent sign. Different for Arabic, etc.
152: */
153: public void setPercent(char percent) {
154: this .percent = percent;
155: }
156:
157: /**
158: * Gets the character used for a digit in a pattern.
159: */
160: public char getDigit() {
161: return digit;
162: }
163:
164: /**
165: * Sets the character used for a digit in a pattern.
166: */
167: public void setDigit(char digit) {
168: this .digit = digit;
169: }
170:
171: /**
172: * Gets the character used to separate positive and negative subpatterns
173: * in a pattern.
174: */
175: public char getPatternSeparator() {
176: return patternSeparator;
177: }
178:
179: /**
180: * Sets the character used to separate positive and negative subpatterns
181: * in a pattern.
182: */
183: public void setPatternSeparator(char patternSeparator) {
184: this .patternSeparator = patternSeparator;
185: }
186:
187: /**
188: * Gets the string used to represent infinity. Almost always left
189: * unchanged.
190: */
191: public String getInfinity() {
192: return infinity;
193: }
194:
195: /**
196: * Sets the string used to represent infinity. Almost always left
197: * unchanged.
198: */
199: public void setInfinity(String infinity) {
200: this .infinity = infinity;
201: }
202:
203: /**
204: * Gets the string used to represent "not a number". Almost always left
205: * unchanged.
206: */
207: public String getNaN() {
208: return NaN;
209: }
210:
211: /**
212: * Sets the string used to represent "not a number". Almost always left
213: * unchanged.
214: */
215: public void setNaN(String NaN) {
216: this .NaN = NaN;
217: }
218:
219: /**
220: * Gets the character used to represent minus sign. If no explicit
221: * negative format is specified, one is formed by prefixing
222: * minusSign to the positive format.
223: */
224: public char getMinusSign() {
225: return minusSign;
226: }
227:
228: /**
229: * Sets the character used to represent minus sign. If no explicit
230: * negative format is specified, one is formed by prefixing
231: * minusSign to the positive format.
232: */
233: public void setMinusSign(char minusSign) {
234: this .minusSign = minusSign;
235: }
236:
237: /**
238: * Returns the currency symbol for the currency of these
239: * DecimalFormatSymbols in their locale.
240: * @since 1.2
241: */
242: public String getCurrencySymbol() {
243: return currencySymbol;
244: }
245:
246: /**
247: * Sets the currency symbol for the currency of these
248: * DecimalFormatSymbols in their locale.
249: * @since 1.2
250: */
251: public void setCurrencySymbol(String currency) {
252: currencySymbol = currency;
253: }
254:
255: /**
256: * Returns the ISO 4217 currency code of the currency of these
257: * DecimalFormatSymbols.
258: * @since 1.2
259: */
260: public String getInternationalCurrencySymbol() {
261: return intlCurrencySymbol;
262: }
263:
264: /**
265: * Sets the ISO 4217 currency code of the currency of these
266: * DecimalFormatSymbols.
267: * If the currency code is valid (as defined by
268: * {@link java.util.Currency#getInstance(java.lang.String) Currency.getInstance}),
269: * this also sets the currency attribute to the corresponding Currency
270: * instance and the currency symbol attribute to the currency's symbol
271: * in the DecimalFormatSymbols' locale. If the currency code is not valid,
272: * then the currency attribute is set to null and the currency symbol
273: * attribute is not modified.
274: *
275: * @see #setCurrency
276: * @see #setCurrencySymbol
277: * @since 1.2
278: */
279: public void setInternationalCurrencySymbol(String currencyCode) {
280: intlCurrencySymbol = currencyCode;
281: currency = null;
282: if (currencyCode != null) {
283: try {
284: currency = Currency.getInstance(currencyCode);
285: currencySymbol = currency.getSymbol();
286: } catch (IllegalArgumentException e) {
287: }
288: }
289: }
290:
291: /**
292: * Gets the currency of these DecimalFormatSymbols. May be null if the
293: * currency symbol attribute was previously set to a value that's not
294: * a valid ISO 4217 currency code.
295: *
296: * @return the currency used, or null
297: * @since 1.4
298: */
299: public Currency getCurrency() {
300: return currency;
301: }
302:
303: /**
304: * Sets the currency of these DecimalFormatSymbols.
305: * This also sets the currency symbol attribute to the currency's symbol
306: * in the DecimalFormatSymbols' locale, and the international currency
307: * symbol attribute to the currency's ISO 4217 currency code.
308: *
309: * @param currency the new currency to be used
310: * @exception NullPointerException if <code>currency</code> is null
311: * @since 1.4
312: * @see #setCurrencySymbol
313: * @see #setInternationalCurrencySymbol
314: */
315: public void setCurrency(Currency currency) {
316: if (currency == null) {
317: throw new NullPointerException();
318: }
319: this .currency = currency;
320: intlCurrencySymbol = currency.getCurrencyCode();
321: currencySymbol = currency.getSymbol(locale);
322: }
323:
324: /**
325: * Returns the monetary decimal separator.
326: * @since 1.2
327: */
328: public char getMonetaryDecimalSeparator() {
329: return monetarySeparator;
330: }
331:
332: /**
333: * Sets the monetary decimal separator.
334: * @since 1.2
335: */
336: public void setMonetaryDecimalSeparator(char sep) {
337: monetarySeparator = sep;
338: }
339:
340: //------------------------------------------------------------
341: // BEGIN Package Private methods ... to be made public later
342: //------------------------------------------------------------
343:
344: /**
345: * Returns the character used to separate the mantissa from the exponent.
346: */
347: char getExponentialSymbol() {
348: return exponential;
349: }
350:
351: /**
352: * Sets the character used to separate the mantissa from the exponent.
353: */
354: void setExponentialSymbol(char exp) {
355: exponential = exp;
356: }
357:
358: //------------------------------------------------------------
359: // END Package Private methods ... to be made public later
360: //------------------------------------------------------------
361:
362: /**
363: * Standard override.
364: */
365: public Object clone() {
366: try {
367: return (DecimalFormatSymbols) super .clone();
368: // other fields are bit-copied
369: } catch (CloneNotSupportedException e) {
370: throw new InternalError();
371: }
372: }
373:
374: /**
375: * Override equals.
376: */
377: public boolean equals(Object obj) {
378: if (obj == null)
379: return false;
380: if (this == obj)
381: return true;
382: if (getClass() != obj.getClass())
383: return false;
384: DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
385: return (zeroDigit == other.zeroDigit
386: && groupingSeparator == other.groupingSeparator
387: && decimalSeparator == other.decimalSeparator
388: && percent == other.percent && perMill == other.perMill
389: && digit == other.digit && minusSign == other.minusSign
390: && patternSeparator == other.patternSeparator
391: && infinity.equals(other.infinity)
392: && NaN.equals(other.NaN)
393: && currencySymbol.equals(other.currencySymbol)
394: && intlCurrencySymbol.equals(other.intlCurrencySymbol)
395: && currency == other.currency
396: && monetarySeparator == other.monetarySeparator && locale
397: .equals(other.locale));
398: }
399:
400: /**
401: * Override hashCode.
402: */
403: public int hashCode() {
404: int result = zeroDigit;
405: result = result * 37 + groupingSeparator;
406: result = result * 37 + decimalSeparator;
407: return result;
408: }
409:
410: /**
411: * Initializes the symbols from the LocaleElements resource bundle.
412: */
413: private void initialize(Locale locale) {
414: this .locale = locale;
415:
416: // get resource bundle data - try the cache first
417: boolean needCacheUpdate = false;
418: Object[] data = (Object[]) cachedLocaleData.get(locale);
419: if (data == null) { /* cache miss */
420: data = new Object[3];
421: ResourceBundle rb = LocaleData.getLocaleElements(locale);
422: data[0] = rb.getStringArray("NumberElements");
423: needCacheUpdate = true;
424: }
425:
426: String[] numberElements = (String[]) data[0];
427: ;
428:
429: decimalSeparator = numberElements[0].charAt(0);
430: groupingSeparator = numberElements[1].charAt(0);
431: patternSeparator = numberElements[2].charAt(0);
432: percent = numberElements[3].charAt(0);
433: zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
434: digit = numberElements[5].charAt(0);
435: minusSign = numberElements[6].charAt(0);
436: exponential = numberElements[7].charAt(0);
437: perMill = numberElements[8].charAt(0);
438: infinity = numberElements[9];
439: NaN = numberElements[10];
440:
441: // Try to obtain the currency used in the locale's country.
442: // Check for empty country string separately because it's a valid
443: // country ID for Locale (and used for the C locale), but not a valid
444: // ISO 3166 country code, and exceptions are expensive.
445: if (!"".equals(locale.getCountry())) {
446: try {
447: currency = Currency.getInstance(locale);
448: } catch (IllegalArgumentException e) {
449: // use default values below for compatibility
450: }
451: }
452: if (currency != null) {
453: intlCurrencySymbol = currency.getCurrencyCode();
454: if (data[1] != null && data[1] == intlCurrencySymbol) {
455: currencySymbol = (String) data[2];
456: } else {
457: currencySymbol = currency.getSymbol(locale);
458: data[1] = intlCurrencySymbol;
459: data[2] = currencySymbol;
460: needCacheUpdate = true;
461: }
462: } else {
463: // default values
464: intlCurrencySymbol = "XXX";
465: try {
466: currency = Currency.getInstance(intlCurrencySymbol);
467: } catch (IllegalArgumentException e) {
468: }
469: currencySymbol = "\u00A4";
470: }
471: // Currently the monetary decimal separator is the same as the
472: // standard decimal separator for all locales that we support.
473: // If that changes, add a new entry to NumberElements.
474: monetarySeparator = decimalSeparator;
475:
476: if (needCacheUpdate) {
477: cachedLocaleData.put(locale, data);
478: }
479: }
480:
481: /**
482: * Reads the default serializable fields, provides default values for objects
483: * in older serial versions, and initializes non-serializable fields.
484: * If <code>serialVersionOnStream</code>
485: * is less than 1, initializes <code>monetarySeparator</code> to be
486: * the same as <code>decimalSeparator</code> and <code>exponential</code>
487: * to be 'E'.
488: * If <code>serialVersionOnStream</code> is less then 2,
489: * initializes <code>locale</code>to the root locale.
490: * Sets <code>serialVersionOnStream</code> back to the maximum allowed value so that
491: * default serialization will work properly if this object is streamed out again.
492: * Initializes the currency from the intlCurrencySymbol field.
493: *
494: * @since JDK 1.1.6
495: */
496: private void readObject(ObjectInputStream stream)
497: throws IOException, ClassNotFoundException {
498: stream.defaultReadObject();
499: if (serialVersionOnStream < 1) {
500: // Didn't have monetarySeparator or exponential field;
501: // use defaults.
502: monetarySeparator = decimalSeparator;
503: exponential = 'E';
504: }
505: if (serialVersionOnStream < 2) {
506: // didn't have locale; use root locale
507: locale = new Locale("");
508: }
509: serialVersionOnStream = currentSerialVersion;
510:
511: if (intlCurrencySymbol != null) {
512: try {
513: currency = Currency.getInstance(intlCurrencySymbol);
514: } catch (IllegalArgumentException e) {
515: }
516: }
517: }
518:
519: /**
520: * Character used for zero.
521: *
522: * @serial
523: * @see #getZeroDigit
524: */
525: private char zeroDigit;
526:
527: /**
528: * Character used for thousands separator.
529: *
530: * @serial
531: * @see #getGroupingSeparator
532: */
533: private char groupingSeparator;
534:
535: /**
536: * Character used for decimal sign.
537: *
538: * @serial
539: * @see #getDecimalSeparator
540: */
541: private char decimalSeparator;
542:
543: /**
544: * Character used for mille percent sign.
545: *
546: * @serial
547: * @see #getPerMill
548: */
549: private char perMill;
550:
551: /**
552: * Character used for percent sign.
553: * @serial
554: * @see #getPercent
555: */
556: private char percent;
557:
558: /**
559: * Character used for a digit in a pattern.
560: *
561: * @serial
562: * @see #getDigit
563: */
564: private char digit;
565:
566: /**
567: * Character used to separate positive and negative subpatterns
568: * in a pattern.
569: *
570: * @serial
571: * @see #getPatternSeparator
572: */
573: private char patternSeparator;
574:
575: /**
576: * String used to represent infinity.
577: * @serial
578: * @see #getInfinity
579: */
580: private String infinity;
581:
582: /**
583: * String used to represent "not a number".
584: * @serial
585: * @see #getNaN
586: */
587: private String NaN;
588:
589: /**
590: * Character used to represent minus sign.
591: * @serial
592: * @see #getMinusSign
593: */
594: private char minusSign;
595:
596: /**
597: * String denoting the local currency, e.g. "$".
598: * @serial
599: * @see #getCurrencySymbol
600: */
601: private String currencySymbol;
602:
603: /**
604: * ISO 4217 currency code denoting the local currency, e.g. "USD".
605: * @serial
606: * @see #getInternationalCurrencySymbol
607: */
608: private String intlCurrencySymbol;
609:
610: /**
611: * The decimal separator used when formatting currency values.
612: * @serial
613: * @since JDK 1.1.6
614: * @see #getMonetaryDecimalSeparator
615: */
616: private char monetarySeparator; // Field new in JDK 1.1.6
617:
618: /**
619: * The character used to distinguish the exponent in a number formatted
620: * in exponential notation, e.g. 'E' for a number such as "1.23E45".
621: * <p>
622: * Note that the public API provides no way to set this field,
623: * even though it is supported by the implementation and the stream format.
624: * The intent is that this will be added to the API in the future.
625: *
626: * @serial
627: * @since JDK 1.1.6
628: */
629: private char exponential; // Field new in JDK 1.1.6
630:
631: /**
632: * The locale of these currency format symbols.
633: *
634: * @serial
635: * @since 1.4
636: */
637: private Locale locale;
638:
639: // currency; only the ISO code is serialized.
640: private transient Currency currency;
641:
642: // Proclaim JDK 1.1 compatibility
643: static final long serialVersionUID = 5772796243397350300L;
644:
645: // The internal serial version which says which version was written
646: // - 0 (default) for version up to JDK 1.1.5
647: // - 1 for version from JDK 1.1.6, which includes two new fields:
648: // monetarySeparator and exponential.
649: // - 2 for version from J2SE 1.4, which includes locale field.
650: private static final int currentSerialVersion = 2;
651:
652: /**
653: * Describes the version of <code>DecimalFormatSymbols</code> present on the stream.
654: * Possible values are:
655: * <ul>
656: * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
657: *
658: * <li><b>1</b>: Versions written by JDK 1.1.6 or later, which include
659: * two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
660: * <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
661: * new <code>locale</code> field.
662: * </ul>
663: * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
664: * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
665: * is always written.
666: *
667: * @serial
668: * @since JDK 1.1.6
669: */
670: private int serialVersionOnStream = currentSerialVersion;
671:
672: /**
673: * cache to hold the NumberElements and the Currency
674: * of a Locale.
675: */
676: private static final Hashtable cachedLocaleData = new Hashtable(3);
677: }
|