Source for java.text.DecimalFormatSymbols

   1: /* DecimalFormatSymbols.java -- Format symbols used by DecimalFormat
   2:    Copyright (C) 1999, 2000, 2001, 2004, 2007 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10:  
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package java.text;
  40: 
  41: import gnu.java.locale.LocaleHelper;
  42: 
  43: import java.io.IOException;
  44: import java.io.ObjectInputStream;
  45: import java.io.Serializable;
  46: 
  47: import java.text.spi.DecimalFormatSymbolsProvider;
  48: 
  49: import java.util.Currency;
  50: import java.util.Locale;
  51: import java.util.MissingResourceException;
  52: import java.util.ResourceBundle;
  53: import java.util.ServiceLoader;
  54: 
  55: /**
  56:  * This class is a container for the symbols used by 
  57:  * <code>DecimalFormat</code> to format numbers and currency.  These are
  58:  * normally handled automatically, but an application can override
  59:  * values as desired using this class.
  60:  *
  61:  * @author Tom Tromey (tromey@cygnus.com)
  62:  * @author Aaron M. Renn (arenn@urbanophile.com)
  63:  * @date February 24, 1999
  64:  */
  65: /* Written using "Java Class Libraries", 2nd edition, plus online
  66:  * API docs for JDK 1.2 from http://www.javasoft.com.
  67:  * Status:  Believed complete and correct to 1.2.
  68:  */
  69: public final class DecimalFormatSymbols implements Cloneable, Serializable
  70: {
  71:   public Object clone ()
  72:   {
  73:     try
  74:       {
  75:     return super.clone ();
  76:       }
  77:     catch(CloneNotSupportedException e)
  78:       {
  79:     return null;
  80:       }
  81:   }
  82: 
  83:   /**
  84:    * This method initializes a new instance of
  85:    * <code>DecimalFormatSymbols</code> for the default locale.
  86:    * This constructor only obtains instances using the runtime's resources;
  87:    * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances,
  88:    * call {@link #getInstance()} instead.
  89:    *
  90:    * @see #getInstance()
  91:    */
  92:   public DecimalFormatSymbols ()
  93:   {
  94:     this (Locale.getDefault());
  95:   }
  96: 
  97:   private String safeGetString(ResourceBundle bundle,
  98:                                String name, String def)
  99:   {
 100:     if (bundle != null)
 101:       {
 102:     try
 103:       {
 104:         return bundle.getString(name);
 105:       }
 106:     catch (MissingResourceException x)
 107:       {
 108:       }
 109:       }
 110:     return def;
 111:   }
 112: 
 113:   private char safeGetChar(ResourceBundle bundle,
 114:                            String name, char def)
 115:   {
 116:     String r = null;
 117:     if (bundle != null)
 118:       {
 119:     try
 120:       {
 121:         r = bundle.getString(name);
 122:       }
 123:     catch (MissingResourceException x)
 124:       {
 125:       }
 126:       }
 127:     if (r == null || r.length() < 1)
 128:       return def;
 129:     return r.charAt(0);
 130:   }
 131: 
 132:   /**
 133:    * This method initializes a new instance of
 134:    * <code>DecimalFormatSymbols</code> for the specified locale.
 135:    * <strong>Note</strong>: if the locale does not have an associated
 136:    * <code>Currency</code> instance, the currency symbol and
 137:    * international currency symbol will be set to the strings "?"
 138:    * and "XXX" respectively.  This generally happens with language
 139:    * locales (those with no specified country), such as
 140:    * <code>Locale.ENGLISH</code>.  This constructor only obtains
 141:    * instances using the runtime's resources; to also include
 142:    * {@link java.text.spi.DecimalFormatSymbolsProvider} instances,
 143:    * call {@link #getInstance(java.util.Locale)} instead.
 144:    *
 145:    * @param loc The local to load symbols for.
 146:    * @throws NullPointerException if the locale is null.
 147:    * @see #getInstance(java.util.Locale)
 148:    */
 149:   public DecimalFormatSymbols (Locale loc)
 150:   {
 151:     ResourceBundle res;
 152:     try
 153:       {
 154:     res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 155:         loc, ClassLoader.getSystemClassLoader());
 156:       }
 157:     catch (MissingResourceException x)
 158:       {
 159:     res = null;
 160:       }
 161:     currency = Currency.getInstance("XXX");
 162:     currencySymbol = "?";
 163:     intlCurrencySymbol = "XXX";
 164:     try
 165:       {
 166:     Currency localeCurrency = Currency.getInstance(loc);
 167:     if (localeCurrency != null)
 168:       {
 169:         setCurrency(localeCurrency);
 170:       }
 171:       }
 172:     catch(IllegalArgumentException exception)
 173:       {
 174:     /* Locale has an invalid currency */
 175:       }
 176:     decimalSeparator = safeGetChar (res, "decimalSeparator", '.');
 177:     digit = safeGetChar (res, "digit", '#');
 178:     exponential = safeGetChar (res, "exponential", 'E');
 179:     groupingSeparator = safeGetChar (res, "groupingSeparator", ',');
 180:     infinity = safeGetString (res, "infinity", "\u221e");
 181:     try
 182:       {
 183:     monetarySeparator = safeGetChar (res, "monetarySeparator", '.');
 184:       }
 185:     catch (MissingResourceException x)
 186:       {
 187:     monetarySeparator = decimalSeparator;
 188:       }
 189:     minusSign = safeGetChar (res, "minusSign", '-');
 190:     NaN = safeGetString (res, "NaN", "\ufffd");
 191:     patternSeparator = safeGetChar (res, "patternSeparator", ';');
 192:     percent = safeGetChar (res, "percent", '%');
 193:     perMill = safeGetChar (res, "perMill", '\u2030');
 194:     zeroDigit = safeGetChar (res, "zeroDigit", '0');
 195:     locale = loc;
 196:   }
 197: 
 198:   /**
 199:    * This method this this object for equality against the specified object.
 200:    * This will be true if and only if the following criteria are met with
 201:    * regard to the specified object:
 202:    * <p>
 203:    * <ul>
 204:    * <li>It is not <code>null</code>.</li>
 205:    * <li>It is an instance of <code>DecimalFormatSymbols</code>.</li>
 206:    * <li>All of its symbols are identical to the symbols in this object.</li>
 207:    * </ul>
 208:    *
 209:    * @return <code>true</code> if the specified object is equal to this
 210:    * object, <code>false</code> otherwise.
 211:    */
 212:   public boolean equals (Object obj)
 213:   {
 214:     if (! (obj instanceof DecimalFormatSymbols))
 215:       return false;
 216:     DecimalFormatSymbols dfs = (DecimalFormatSymbols) obj;
 217:     return (currencySymbol.equals(dfs.currencySymbol)
 218:         && decimalSeparator == dfs.decimalSeparator
 219:         && digit == dfs.digit
 220:         && exponential == dfs.exponential
 221:         && groupingSeparator == dfs.groupingSeparator
 222:         && infinity.equals(dfs.infinity)
 223:         && intlCurrencySymbol.equals(dfs.intlCurrencySymbol)
 224:         && minusSign == dfs.minusSign
 225:         && monetarySeparator == dfs.monetarySeparator
 226:         && NaN.equals(dfs.NaN)
 227:         && patternSeparator == dfs.patternSeparator
 228:         && percent == dfs.percent
 229:         && perMill == dfs.perMill
 230:         && zeroDigit == dfs.zeroDigit);
 231:   }
 232: 
 233:   /**
 234:    * Returns the currency corresponding to the currency symbol stored
 235:    * in the instance of <code>DecimalFormatSymbols</code>.
 236:    *
 237:    * @return An instance of <code>Currency</code> which matches
 238:    *         the currency used, or null if there is no corresponding
 239:    *         instance.
 240:    */
 241:   public Currency getCurrency ()
 242:   {
 243:     return currency;
 244:   }
 245: 
 246:   /**
 247:    * This method returns the currency symbol in local format.  For example,
 248:    * "$" for Canadian dollars.
 249:    *
 250:    * @return The currency symbol in local format.
 251:    */
 252:   public String getCurrencySymbol ()
 253:   {
 254:     return currencySymbol;
 255:   }
 256: 
 257:   /**
 258:    * This method returns the character used as the decimal point.
 259:    *
 260:    * @return The character used as the decimal point.
 261:    */
 262:   public char getDecimalSeparator ()
 263:   {
 264:     return decimalSeparator;
 265:   }
 266: 
 267:   /**
 268:    * This method returns the character used to represent a digit in a
 269:    * format pattern string.
 270:    *
 271:    * @return The character used to represent a digit in a format
 272:    * pattern string. 
 273:    */
 274:   public char getDigit ()
 275:   {
 276:     return digit;
 277:   }
 278: 
 279:   // This is our own extension.
 280:   char getExponential ()
 281:   {
 282:     return exponential;
 283:   }
 284: 
 285:   /**
 286:    * This method sets the character used to separate groups of digits.  For
 287:    * example, the United States uses a comma (,) to separate thousands in
 288:    * a number.
 289:    *
 290:    * @return The character used to separate groups of digits.
 291:    */
 292:   public char getGroupingSeparator ()
 293:   {
 294:     return groupingSeparator;
 295:   }
 296: 
 297:   /**
 298:    * This method returns the character used to represent infinity.
 299:    *
 300:    * @return The character used to represent infinity.
 301:    */
 302:   public String getInfinity ()
 303:   {
 304:     return infinity;
 305:   }
 306: 
 307:   /**
 308:    * This method returns the currency symbol in international format.  For
 309:    * example, "C$" for Canadian dollars.
 310:    *
 311:    * @return The currency symbol in international format.
 312:    */
 313:   public String getInternationalCurrencySymbol ()
 314:   {
 315:     return intlCurrencySymbol;
 316:   }
 317: 
 318:   /**
 319:    * This method returns the character used to represent the minus sign.
 320:    *
 321:    * @return The character used to represent the minus sign.
 322:    */
 323:   public char getMinusSign ()
 324:   {
 325:     return minusSign;
 326:   }
 327: 
 328:   /**
 329:    * This method returns the character used to represent the decimal
 330:    * point for currency values.
 331:    *
 332:    * @return The decimal point character used in currency values.
 333:    */
 334:   public char getMonetaryDecimalSeparator ()
 335:   {
 336:     return monetarySeparator;
 337:   }
 338: 
 339:   /**
 340:    * This method returns the string used to represent the NaN (not a number)
 341:    * value.
 342:    *
 343:    * @return The string used to represent NaN
 344:    */
 345:   public String getNaN ()
 346:   {
 347:     return NaN;
 348:   }
 349: 
 350:   /**
 351:    * This method returns the character used to separate positive and negative
 352:    * subpatterns in a format pattern.
 353:    *
 354:    * @return The character used to separate positive and negative subpatterns
 355:    * in a format pattern.
 356:    */
 357:   public char getPatternSeparator ()
 358:   {
 359:     return patternSeparator;
 360:   }
 361: 
 362:   /**
 363:    * This method returns the character used as the percent sign.
 364:    *
 365:    * @return The character used as the percent sign.
 366:    */
 367:   public char getPercent ()
 368:   {
 369:     return percent;
 370:   }
 371: 
 372:   /**
 373:    * This method returns the character used as the per mille character.
 374:    *
 375:    * @return The per mille character.
 376:    */
 377:   public char getPerMill ()
 378:   {
 379:     return perMill;
 380:   }
 381: 
 382:   /**
 383:    * This method returns the character used to represent the digit zero.
 384:    *
 385:    * @return The character used to represent the digit zero.
 386:    */
 387:   public char getZeroDigit ()
 388:   {
 389:     return zeroDigit;
 390:   }
 391: 
 392:   /**
 393:    * This method returns a hash value for this object.
 394:    *
 395:    * @return A hash value for this object.
 396:    */
 397:   public int hashCode ()
 398:   {
 399:     // Compute based on zero digit, grouping separator, and decimal
 400:     // separator -- JCL book.  This probably isn't a very good hash
 401:     // code.
 402:     return zeroDigit << 16 + groupingSeparator << 8 + decimalSeparator;
 403:   }
 404: 
 405:   /**
 406:    * This method sets the currency symbol and ISO 4217 currency
 407:    * code to the values obtained from the supplied currency.
 408:    *
 409:    * @param currency the currency from which to obtain the values.
 410:    * @throws NullPointerException if the currency is null.
 411:    */
 412:   public void setCurrency (Currency currency)
 413:   {
 414:     setCurrencySymbol (currency.getSymbol());
 415:     this.currency = currency;
 416:   }
 417: 
 418:   /**
 419:    * This method sets the currency symbol to the specified value.
 420:    *
 421:    * @param currency The new currency symbol
 422:    */
 423:   public void setCurrencySymbol (String currency)
 424:   {
 425:     currencySymbol = currency;
 426:   }
 427: 
 428:   /**
 429:    * This method sets the decimal point character to the specified value.
 430:    *
 431:    * @param decimalSep The new decimal point character
 432:    */
 433:   public void setDecimalSeparator (char decimalSep)
 434:   {
 435:     decimalSeparator = decimalSep;
 436:   }
 437: 
 438:   /**
 439:    * This method sets the character used to represents a digit in a format
 440:    * string to the specified value.
 441:    *
 442:    * @param digit The character used to represent a digit in a format pattern.
 443:    */
 444:   public void setDigit (char digit)
 445:   {
 446:     this.digit = digit;
 447:   }
 448: 
 449:   // This is our own extension.
 450:   void setExponential (char exp)
 451:   {
 452:     exponential = exp;
 453:   }
 454: 
 455:   /**
 456:    * This method sets the character used to separate groups of digits.
 457:    *
 458:    * @param groupSep The character used to separate groups of digits.
 459:    */
 460:   public void setGroupingSeparator (char groupSep)
 461:   {
 462:     groupingSeparator = groupSep;
 463:   }
 464: 
 465:   /**
 466:    * This method sets the string used to represents infinity.
 467:    *
 468:    * @param infinity The string used to represent infinity.
 469:    */
 470:   public void setInfinity (String infinity)
 471:   {
 472:     this.infinity = infinity;
 473:   }
 474: 
 475:   /**
 476:    * This method sets the international currency symbol to the
 477:    * specified value. If a valid <code>Currency</code> instance
 478:    * exists for the international currency code, then this is
 479:    * used for the currency attribute, and the currency symbol
 480:    * is set to the corresponding value from this instance.
 481:    * Otherwise, the currency attribute is set to null and the
 482:    * symbol is left unmodified. 
 483:    *
 484:    * @param currencyCode The new international currency symbol.
 485:    */
 486:   public void setInternationalCurrencySymbol (String currencyCode)
 487:   {
 488:     intlCurrencySymbol = currencyCode;
 489:     try
 490:       {
 491:     currency = Currency.getInstance(currencyCode);
 492:       }
 493:     catch (IllegalArgumentException exception)
 494:       {
 495:     currency = null;
 496:       }
 497:     if (currency != null)
 498:       {
 499:         setCurrencySymbol(currency.getSymbol(locale));
 500:       }
 501:   }
 502: 
 503:   /**
 504:    * This method sets the character used to represent the minus sign.
 505:    *
 506:    * @param minusSign The character used to represent the minus sign.
 507:    */
 508:   public void setMinusSign (char minusSign)
 509:   {
 510:     this.minusSign = minusSign;
 511:   }
 512: 
 513:   /**
 514:    * This method sets the character used for the decimal point in currency
 515:    * values.
 516:    *
 517:    * @param decimalSep The decimal point character used in currency values. 
 518:    */
 519:   public void setMonetaryDecimalSeparator (char decimalSep)
 520:   {
 521:     monetarySeparator = decimalSep;
 522:   }
 523: 
 524:   /**
 525:    * This method sets the string used to represent the NaN (not a
 526:    * number) value. 
 527:    *
 528:    * @param nan The string used to represent NaN
 529:    */
 530:   public void setNaN (String nan)
 531:   {
 532:     NaN = nan;
 533:   }
 534: 
 535:   /**
 536:    * This method sets the character used to separate positive and negative
 537:    * subpatterns in a format pattern.
 538:    *
 539:    * @param patternSep The character used to separate positive and
 540:    * negative subpatterns in a format pattern.
 541:    */
 542:   public void setPatternSeparator (char patternSep)
 543:   {
 544:     patternSeparator = patternSep;
 545:   }
 546: 
 547:   /**
 548:    * This method sets the character used as the percent sign.
 549:    *
 550:    * @param percent  The character used as the percent sign.
 551:    */
 552:   public void setPercent (char percent)
 553:   {
 554:     this.percent = percent;
 555:   }
 556: 
 557:   /**
 558:    * This method sets the character used as the per mille character.
 559:    *
 560:    * @param perMill The per mille character.
 561:    */
 562:   public void setPerMill (char perMill)
 563:   {
 564:     this.perMill = perMill;
 565:   }
 566: 
 567:   /**
 568:    * This method sets the character used to represent the digit zero.
 569:    *
 570:    * @param zeroDigit The character used to represent the digit zero.
 571:    */
 572:   public void setZeroDigit (char zeroDigit)
 573:   {
 574:     this.zeroDigit = zeroDigit;
 575:   }
 576: 
 577:   /**
 578:    * @serial A string used for the local currency
 579:    */
 580:   private String currencySymbol;
 581:   /**
 582:    * @serial The <code>char</code> used to separate decimals in a number.
 583:    */
 584:   private char decimalSeparator;
 585:   /**
 586:    * @serial This is the <code>char</code> used to represent a digit in
 587:    * a format specification.
 588:    */
 589:   private char digit;
 590:   /**
 591:    * @serial This is the <code>char</code> used to represent the exponent
 592:    * separator in exponential notation.
 593:    */
 594:   private char exponential;
 595:   /**
 596:    * @serial This separates groups of thousands in numbers.
 597:    */
 598:   private char groupingSeparator;
 599:   /**
 600:    * @serial This string represents infinity.
 601:    */
 602:   private String infinity;
 603:   /**
 604:    * @serial This string represents the local currency in an international
 605:    * context, eg, "C$" for Canadian dollars.
 606:    */
 607:   private String intlCurrencySymbol;
 608:   /**
 609:    * @serial This is the character used to represent the minus sign.
 610:    */
 611:   private char minusSign;
 612:   /**
 613:    * @serial This character is used to separate decimals when formatting
 614:    * currency values.
 615:    */
 616:   private char monetarySeparator;
 617:   /**
 618:    * @serial This string is used the represent the Java NaN value for
 619:    * "not a number".
 620:    */
 621:   private String NaN;
 622:   /**
 623:    * @serial This is the character used to separate positive and negative
 624:    * subpatterns in a format pattern.
 625:    */
 626:   private char patternSeparator;
 627:   /**
 628:    * @serial This is the percent symbols
 629:    */
 630:   private char percent;
 631:   /**
 632:    * @serial This character is used for the mille percent sign.
 633:    */
 634:   private char perMill;
 635:   /**
 636:    * @serial This value represents the type of object being de-serialized.
 637:    * 0 indicates a pre-Java 1.1.6 version, 1 indicates 1.1.6 or later.
 638:    * 0 indicates a pre-Java 1.1.6 version, 1 indicates 1.1.6 or later,
 639:    * 2 indicates 1.4 or later
 640:     */
 641:   private int serialVersionOnStream = 2;
 642:   /**
 643:    * @serial This is the character used to represent 0.
 644:    */
 645:   private char zeroDigit;
 646: 
 647:   /**
 648:    * @serial The locale of these currency symbols.
 649:    */
 650:   private Locale locale;
 651: 
 652:   /**
 653:    * The currency used for the symbols in this instance.
 654:    * This is stored temporarily for efficiency reasons,
 655:    * as well as to ensure that the correct instance
 656:    * is restored from the currency code.
 657:    *
 658:    * @serial Ignored.
 659:    */
 660:   private transient Currency currency;
 661: 
 662:   private static final long serialVersionUID = 5772796243397350300L;
 663: 
 664:   private void readObject(ObjectInputStream stream)
 665:     throws IOException, ClassNotFoundException
 666:   {
 667:     stream.defaultReadObject();
 668:     if (serialVersionOnStream < 1)
 669:       {
 670:         monetarySeparator = decimalSeparator;
 671:     exponential = 'E';
 672:       }
 673:     if (serialVersionOnStream < 2)
 674:     locale = Locale.getDefault();
 675: 
 676:     serialVersionOnStream = 2;
 677:   }
 678: 
 679:   /**
 680:    * Returns a {@link DecimalFormatSymbols} instance for the
 681:    * default locale obtained from either the runtime itself
 682:    * or one of the installed
 683:    * {@link java.text.spi.DecimalFormatSymbolsProvider} instances.
 684:    * This is equivalent to calling
 685:    * <code>getInstance(Locale.getDefault())</code>.
 686:    * 
 687:    * @return a {@link DecimalFormatSymbols} instance for the default
 688:    *         locale.
 689:    * @since 1.6
 690:    */
 691:   public static final DecimalFormatSymbols getInstance()
 692:   {
 693:     return getInstance(Locale.getDefault());
 694:   }
 695: 
 696:   /**
 697:    * Returns a {@link DecimalFormatSymbols} instance for the
 698:    * specified locale obtained from either the runtime itself
 699:    * or one of the installed
 700:    * {@link java.text.spi.DecimalFormatSymbolsProvider} instances.
 701:    * 
 702:    * @param locale the locale for which an instance should be
 703:    *               returned.
 704:    * @return a {@link DecimalFormatSymbols} instance for the specified
 705:    *         locale.
 706:    * @throws NullPointerException if <code>locale</code> is
 707:    *                              <code>null</code>.
 708:    * @since 1.6
 709:    */
 710:   public static final DecimalFormatSymbols getInstance(Locale locale)
 711:   {
 712:     try
 713:       {
 714:     if (!locale.equals(Locale.ROOT))
 715:       ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
 716:                    locale,
 717:                    ClassLoader.getSystemClassLoader());
 718:     return new DecimalFormatSymbols(locale);    
 719:       }
 720:     catch (MissingResourceException x)
 721:       {
 722:     /* This means runtime support for the locale
 723:      * is not available, so we check providers. */
 724:       }
 725:     for (DecimalFormatSymbolsProvider p :
 726:        ServiceLoader.load(DecimalFormatSymbolsProvider.class))
 727:       {
 728:     for (Locale loc : p.getAvailableLocales())
 729:       {
 730:         if (loc.equals(locale))
 731:           {
 732:         DecimalFormatSymbols syms = p.getInstance(locale);
 733:         if (syms != null)
 734:           return syms;
 735:         break;
 736:           }
 737:       }
 738:       }
 739:     return getInstance(LocaleHelper.getFallbackLocale(locale));
 740:   }
 741: 
 742: }