1. /*
  2. * @(#)DecimalFormatSymbols.java 1.39 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. /*
  8. * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
  9. * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
  10. *
  11. * The original version of this source code and documentation is copyrighted
  12. * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  13. * materials are provided under terms of a License Agreement between Taligent
  14. * and Sun. This technology is protected by multiple US and International
  15. * patents. This notice and attribution to Taligent may not be removed.
  16. * Taligent is a registered trademark of Taligent, Inc.
  17. *
  18. */
  19. package java.text;
  20. import java.io.IOException;
  21. import java.io.ObjectInputStream;
  22. import java.io.Serializable;
  23. import java.util.Currency;
  24. import java.util.Hashtable;
  25. import java.util.Locale;
  26. import java.util.ResourceBundle;
  27. import sun.text.resources.LocaleData;
  28. /**
  29. * This class represents the set of symbols (such as the decimal separator,
  30. * the grouping separator, and so on) needed by <code>DecimalFormat</code>
  31. * to format numbers. <code>DecimalFormat</code> creates for itself an instance of
  32. * <code>DecimalFormatSymbols</code> from its locale data. If you need to change any
  33. * of these symbols, you can get the <code>DecimalFormatSymbols</code> object from
  34. * your <code>DecimalFormat</code> and modify it.
  35. *
  36. * @see java.util.Locale
  37. * @see DecimalFormat
  38. * @version 1.39, 01/23/03
  39. * @author Mark Davis
  40. * @author Alan Liu
  41. */
  42. final public class DecimalFormatSymbols implements Cloneable, Serializable {
  43. /**
  44. * Create a DecimalFormatSymbols object for the default locale.
  45. */
  46. public DecimalFormatSymbols() {
  47. initialize( Locale.getDefault() );
  48. }
  49. /**
  50. * Create a DecimalFormatSymbols object for the given locale.
  51. *
  52. * @exception NullPointerException if <code>locale</code> is null
  53. */
  54. public DecimalFormatSymbols( Locale locale ) {
  55. initialize( locale );
  56. }
  57. /**
  58. * Gets the character used for zero. Different for Arabic, etc.
  59. */
  60. public char getZeroDigit() {
  61. return zeroDigit;
  62. }
  63. /**
  64. * Sets the character used for zero. Different for Arabic, etc.
  65. */
  66. public void setZeroDigit(char zeroDigit) {
  67. this.zeroDigit = zeroDigit;
  68. }
  69. /**
  70. * Gets the character used for thousands separator. Different for French, etc.
  71. */
  72. public char getGroupingSeparator() {
  73. return groupingSeparator;
  74. }
  75. /**
  76. * Sets the character used for thousands separator. Different for French, etc.
  77. */
  78. public void setGroupingSeparator(char groupingSeparator) {
  79. this.groupingSeparator = groupingSeparator;
  80. }
  81. /**
  82. * Gets the character used for decimal sign. Different for French, etc.
  83. */
  84. public char getDecimalSeparator() {
  85. return decimalSeparator;
  86. }
  87. /**
  88. * Sets the character used for decimal sign. Different for French, etc.
  89. */
  90. public void setDecimalSeparator(char decimalSeparator) {
  91. this.decimalSeparator = decimalSeparator;
  92. }
  93. /**
  94. * Gets the character used for mille percent sign. Different for Arabic, etc.
  95. */
  96. public char getPerMill() {
  97. return perMill;
  98. }
  99. /**
  100. * Sets the character used for mille percent sign. Different for Arabic, etc.
  101. */
  102. public void setPerMill(char perMill) {
  103. this.perMill = perMill;
  104. }
  105. /**
  106. * Gets the character used for percent sign. Different for Arabic, etc.
  107. */
  108. public char getPercent() {
  109. return percent;
  110. }
  111. /**
  112. * Sets the character used for percent sign. Different for Arabic, etc.
  113. */
  114. public void setPercent(char percent) {
  115. this.percent = percent;
  116. }
  117. /**
  118. * Gets the character used for a digit in a pattern.
  119. */
  120. public char getDigit() {
  121. return digit;
  122. }
  123. /**
  124. * Sets the character used for a digit in a pattern.
  125. */
  126. public void setDigit(char digit) {
  127. this.digit = digit;
  128. }
  129. /**
  130. * Gets the character used to separate positive and negative subpatterns
  131. * in a pattern.
  132. */
  133. public char getPatternSeparator() {
  134. return patternSeparator;
  135. }
  136. /**
  137. * Sets the character used to separate positive and negative subpatterns
  138. * in a pattern.
  139. */
  140. public void setPatternSeparator(char patternSeparator) {
  141. this.patternSeparator = patternSeparator;
  142. }
  143. /**
  144. * Gets the string used to represent infinity. Almost always left
  145. * unchanged.
  146. */
  147. public String getInfinity() {
  148. return infinity;
  149. }
  150. /**
  151. * Sets the string used to represent infinity. Almost always left
  152. * unchanged.
  153. */
  154. public void setInfinity(String infinity) {
  155. this.infinity = infinity;
  156. }
  157. /**
  158. * Gets the string used to represent "not a number". Almost always left
  159. * unchanged.
  160. */
  161. public String getNaN() {
  162. return NaN;
  163. }
  164. /**
  165. * Sets the string used to represent "not a number". Almost always left
  166. * unchanged.
  167. */
  168. public void setNaN(String NaN) {
  169. this.NaN = NaN;
  170. }
  171. /**
  172. * Gets the character used to represent minus sign. If no explicit
  173. * negative format is specified, one is formed by prefixing
  174. * minusSign to the positive format.
  175. */
  176. public char getMinusSign() {
  177. return minusSign;
  178. }
  179. /**
  180. * Sets the character used to represent minus sign. If no explicit
  181. * negative format is specified, one is formed by prefixing
  182. * minusSign to the positive format.
  183. */
  184. public void setMinusSign(char minusSign) {
  185. this.minusSign = minusSign;
  186. }
  187. /**
  188. * Returns the currency symbol for the currency of these
  189. * DecimalFormatSymbols in their locale.
  190. * @since 1.2
  191. */
  192. public String getCurrencySymbol()
  193. {
  194. return currencySymbol;
  195. }
  196. /**
  197. * Sets the currency symbol for the currency of these
  198. * DecimalFormatSymbols in their locale.
  199. * @since 1.2
  200. */
  201. public void setCurrencySymbol(String currency)
  202. {
  203. currencySymbol = currency;
  204. }
  205. /**
  206. * Returns the ISO 4217 currency code of the currency of these
  207. * DecimalFormatSymbols.
  208. * @since 1.2
  209. */
  210. public String getInternationalCurrencySymbol()
  211. {
  212. return intlCurrencySymbol;
  213. }
  214. /**
  215. * Sets the ISO 4217 currency code of the currency of these
  216. * DecimalFormatSymbols.
  217. * If the currency code is valid (as defined by
  218. * {@link java.util.Currency#getInstance(java.lang.String) Currency.getInstance}),
  219. * this also sets the currency attribute to the corresponding Currency
  220. * instance and the currency symbol attribute to the currency's symbol
  221. * in the DecimalFormatSymbols' locale. If the currency code is not valid,
  222. * then the currency attribute is set to null and the currency symbol
  223. * attribute is not modified.
  224. *
  225. * @see #setCurrency
  226. * @see #setCurrencySymbol
  227. * @since 1.2
  228. */
  229. public void setInternationalCurrencySymbol(String currencyCode)
  230. {
  231. intlCurrencySymbol = currencyCode;
  232. currency = null;
  233. if (currencyCode != null) {
  234. try {
  235. currency = Currency.getInstance(currencyCode);
  236. currencySymbol = currency.getSymbol();
  237. } catch (IllegalArgumentException e) {
  238. }
  239. }
  240. }
  241. /**
  242. * Gets the currency of these DecimalFormatSymbols. May be null if the
  243. * currency symbol attribute was previously set to a value that's not
  244. * a valid ISO 4217 currency code.
  245. *
  246. * @return the currency used, or null
  247. * @since 1.4
  248. */
  249. public Currency getCurrency() {
  250. return currency;
  251. }
  252. /**
  253. * Sets the currency of these DecimalFormatSymbols.
  254. * This also sets the currency symbol attribute to the currency's symbol
  255. * in the DecimalFormatSymbols' locale, and the international currency
  256. * symbol attribute to the currency's ISO 4217 currency code.
  257. *
  258. * @param currency the new currency to be used
  259. * @exception NullPointerException if <code>currency</code> is null
  260. * @since 1.4
  261. * @see #setCurrencySymbol
  262. * @see #setInternationalCurrencySymbol
  263. */
  264. public void setCurrency(Currency currency) {
  265. if (currency == null) {
  266. throw new NullPointerException();
  267. }
  268. this.currency = currency;
  269. intlCurrencySymbol = currency.getCurrencyCode();
  270. currencySymbol = currency.getSymbol(locale);
  271. }
  272. /**
  273. * Returns the monetary decimal separator.
  274. * @since 1.2
  275. */
  276. public char getMonetaryDecimalSeparator()
  277. {
  278. return monetarySeparator;
  279. }
  280. /**
  281. * Sets the monetary decimal separator.
  282. * @since 1.2
  283. */
  284. public void setMonetaryDecimalSeparator(char sep)
  285. {
  286. monetarySeparator = sep;
  287. }
  288. //------------------------------------------------------------
  289. // BEGIN Package Private methods ... to be made public later
  290. //------------------------------------------------------------
  291. /**
  292. * Returns the character used to separate the mantissa from the exponent.
  293. */
  294. char getExponentialSymbol()
  295. {
  296. return exponential;
  297. }
  298. /**
  299. * Sets the character used to separate the mantissa from the exponent.
  300. */
  301. void setExponentialSymbol(char exp)
  302. {
  303. exponential = exp;
  304. }
  305. //------------------------------------------------------------
  306. // END Package Private methods ... to be made public later
  307. //------------------------------------------------------------
  308. /**
  309. * Standard override.
  310. */
  311. public Object clone() {
  312. try {
  313. return (DecimalFormatSymbols)super.clone();
  314. // other fields are bit-copied
  315. } catch (CloneNotSupportedException e) {
  316. throw new InternalError();
  317. }
  318. }
  319. /**
  320. * Override equals.
  321. */
  322. public boolean equals(Object obj) {
  323. if (obj == null) return false;
  324. if (this == obj) return true;
  325. if (getClass() != obj.getClass()) return false;
  326. DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
  327. return (zeroDigit == other.zeroDigit &&
  328. groupingSeparator == other.groupingSeparator &&
  329. decimalSeparator == other.decimalSeparator &&
  330. percent == other.percent &&
  331. perMill == other.perMill &&
  332. digit == other.digit &&
  333. minusSign == other.minusSign &&
  334. patternSeparator == other.patternSeparator &&
  335. infinity.equals(other.infinity) &&
  336. NaN.equals(other.NaN) &&
  337. currencySymbol.equals(other.currencySymbol) &&
  338. intlCurrencySymbol.equals(other.intlCurrencySymbol) &&
  339. currency == other.currency &&
  340. monetarySeparator == other.monetarySeparator &&
  341. locale.equals(other.locale));
  342. }
  343. /**
  344. * Override hashCode.
  345. */
  346. public int hashCode() {
  347. int result = zeroDigit;
  348. result = result * 37 + groupingSeparator;
  349. result = result * 37 + decimalSeparator;
  350. return result;
  351. }
  352. /**
  353. * Initializes the symbols from the LocaleElements resource bundle.
  354. */
  355. private void initialize( Locale locale ) {
  356. this.locale = locale;
  357. // get resource bundle data - try the cache first
  358. boolean needCacheUpdate = false;
  359. Object[] data = (Object[]) cachedLocaleData.get(locale);
  360. if (data == null) { /* cache miss */
  361. data = new Object[3];
  362. ResourceBundle rb = LocaleData.getLocaleElements(locale);
  363. data[0] = rb.getStringArray("NumberElements");
  364. needCacheUpdate = true;
  365. }
  366. String[] numberElements = (String[]) data[0];;
  367. decimalSeparator = numberElements[0].charAt(0);
  368. groupingSeparator = numberElements[1].charAt(0);
  369. patternSeparator = numberElements[2].charAt(0);
  370. percent = numberElements[3].charAt(0);
  371. zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
  372. digit = numberElements[5].charAt(0);
  373. minusSign = numberElements[6].charAt(0);
  374. exponential = numberElements[7].charAt(0);
  375. perMill = numberElements[8].charAt(0);
  376. infinity = numberElements[9];
  377. NaN = numberElements[10];
  378. // Try to obtain the currency used in the locale's country.
  379. // Check for empty country string separately because it's a valid
  380. // country ID for Locale (and used for the C locale), but not a valid
  381. // ISO 3166 country code, and exceptions are expensive.
  382. if (!"".equals(locale.getCountry())) {
  383. try {
  384. currency = Currency.getInstance(locale);
  385. } catch (IllegalArgumentException e) {
  386. // use default values below for compatibility
  387. }
  388. }
  389. if (currency != null) {
  390. intlCurrencySymbol = currency.getCurrencyCode();
  391. if (data[1] != null && data[1] == intlCurrencySymbol) {
  392. currencySymbol = (String) data[2];
  393. } else {
  394. currencySymbol = currency.getSymbol(locale);
  395. data[1] = intlCurrencySymbol;
  396. data[2] = currencySymbol;
  397. needCacheUpdate = true;
  398. }
  399. } else {
  400. // default values
  401. intlCurrencySymbol = "XXX";
  402. try {
  403. currency = Currency.getInstance(intlCurrencySymbol);
  404. } catch (IllegalArgumentException e) {
  405. }
  406. currencySymbol = "\u00A4";
  407. }
  408. // Currently the monetary decimal separator is the same as the
  409. // standard decimal separator for all locales that we support.
  410. // If that changes, add a new entry to NumberElements.
  411. monetarySeparator = decimalSeparator;
  412. if (needCacheUpdate) {
  413. cachedLocaleData.put(locale, data);
  414. }
  415. }
  416. /**
  417. * Reads the default serializable fields, provides default values for objects
  418. * in older serial versions, and initializes non-serializable fields.
  419. * If <code>serialVersionOnStream</code>
  420. * is less than 1, initializes <code>monetarySeparator</code> to be
  421. * the same as <code>decimalSeparator</code> and <code>exponential</code>
  422. * to be 'E'.
  423. * If <code>serialVersionOnStream</code> is less then 2,
  424. * initializes <code>locale</code>to the root locale.
  425. * Sets <code>serialVersionOnStream</code> back to the maximum allowed value so that
  426. * default serialization will work properly if this object is streamed out again.
  427. * Initializes the currency from the intlCurrencySymbol field.
  428. *
  429. * @since JDK 1.1.6
  430. */
  431. private void readObject(ObjectInputStream stream)
  432. throws IOException, ClassNotFoundException {
  433. stream.defaultReadObject();
  434. if (serialVersionOnStream < 1) {
  435. // Didn't have monetarySeparator or exponential field;
  436. // use defaults.
  437. monetarySeparator = decimalSeparator;
  438. exponential = 'E';
  439. }
  440. if (serialVersionOnStream < 2) {
  441. // didn't have locale; use root locale
  442. locale = new Locale("");
  443. }
  444. serialVersionOnStream = currentSerialVersion;
  445. if (intlCurrencySymbol != null) {
  446. try {
  447. currency = Currency.getInstance(intlCurrencySymbol);
  448. } catch (IllegalArgumentException e) {
  449. }
  450. }
  451. }
  452. /**
  453. * Character used for zero.
  454. *
  455. * @serial
  456. * @see #getZeroDigit
  457. */
  458. private char zeroDigit;
  459. /**
  460. * Character used for thousands separator.
  461. *
  462. * @serial
  463. * @see #getGroupingSeparator
  464. */
  465. private char groupingSeparator;
  466. /**
  467. * Character used for decimal sign.
  468. *
  469. * @serial
  470. * @see #getDecimalSeparator
  471. */
  472. private char decimalSeparator;
  473. /**
  474. * Character used for mille percent sign.
  475. *
  476. * @serial
  477. * @see #getPerMill
  478. */
  479. private char perMill;
  480. /**
  481. * Character used for percent sign.
  482. * @serial
  483. * @see #getPercent
  484. */
  485. private char percent;
  486. /**
  487. * Character used for a digit in a pattern.
  488. *
  489. * @serial
  490. * @see #getDigit
  491. */
  492. private char digit;
  493. /**
  494. * Character used to separate positive and negative subpatterns
  495. * in a pattern.
  496. *
  497. * @serial
  498. * @see #getPatternSeparator
  499. */
  500. private char patternSeparator;
  501. /**
  502. * String used to represent infinity.
  503. * @serial
  504. * @see #getInfinity
  505. */
  506. private String infinity;
  507. /**
  508. * String used to represent "not a number".
  509. * @serial
  510. * @see #getNaN
  511. */
  512. private String NaN;
  513. /**
  514. * Character used to represent minus sign.
  515. * @serial
  516. * @see #getMinusSign
  517. */
  518. private char minusSign;
  519. /**
  520. * String denoting the local currency, e.g. "$".
  521. * @serial
  522. * @see #getCurrencySymbol
  523. */
  524. private String currencySymbol;
  525. /**
  526. * ISO 4217 currency code denoting the local currency, e.g. "USD".
  527. * @serial
  528. * @see #getInternationalCurrencySymbol
  529. */
  530. private String intlCurrencySymbol;
  531. /**
  532. * The decimal separator used when formatting currency values.
  533. * @serial
  534. * @since JDK 1.1.6
  535. * @see #getMonetaryDecimalSeparator
  536. */
  537. private char monetarySeparator; // Field new in JDK 1.1.6
  538. /**
  539. * The character used to distinguish the exponent in a number formatted
  540. * in exponential notation, e.g. 'E' for a number such as "1.23E45".
  541. * <p>
  542. * Note that the public API provides no way to set this field,
  543. * even though it is supported by the implementation and the stream format.
  544. * The intent is that this will be added to the API in the future.
  545. *
  546. * @serial
  547. * @since JDK 1.1.6
  548. */
  549. private char exponential; // Field new in JDK 1.1.6
  550. /**
  551. * The locale of these currency format symbols.
  552. *
  553. * @serial
  554. * @since 1.4
  555. */
  556. private Locale locale;
  557. // currency; only the ISO code is serialized.
  558. private transient Currency currency;
  559. // Proclaim JDK 1.1 FCS compatibility
  560. static final long serialVersionUID = 5772796243397350300L;
  561. // The internal serial version which says which version was written
  562. // - 0 (default) for version up to JDK 1.1.5
  563. // - 1 for version from JDK 1.1.6, which includes two new fields:
  564. // monetarySeparator and exponential.
  565. // - 2 for version from J2SE 1.4, which includes locale field.
  566. private static final int currentSerialVersion = 2;
  567. /**
  568. * Describes the version of <code>DecimalFormatSymbols</code> present on the stream.
  569. * Possible values are:
  570. * <ul>
  571. * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
  572. *
  573. * <li><b>1</b>: Versions written by JDK 1.1.6 or later, which include
  574. * two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
  575. * <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
  576. * new <code>locale</code> field.
  577. * </ul>
  578. * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
  579. * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
  580. * is always written.
  581. *
  582. * @serial
  583. * @since JDK 1.1.6
  584. */
  585. private int serialVersionOnStream = currentSerialVersion;
  586. /**
  587. * cache to hold the NumberElements and the Currency
  588. * of a Locale.
  589. */
  590. private static final Hashtable cachedLocaleData = new Hashtable(3);
  591. }