Print this page
rev 5696 : 6336885: RFE: Locale Data Deployment Enhancements
4609153: Provide locale data for Indic locales
5104387: Support for gl_ES locale (galician language)
6337471: desktop/system locale preferences support
7056139: (cal) SPI support for locale-dependent Calendar parameters
7058206: Provide CalendarData SPI for week params and display field value names
7073852: Support multiple scripts for digits and decimal symbols per locale
7079560: [Fmt-Da] Context dependent month names support in SimpleDateFormat
7171324: getAvailableLocales() of locale sensitive services should return the actual availability of locales
7151414: (cal) Support calendar type identification
7168528: LocaleServiceProvider needs to be aware of Locale extensions
7171372: (cal) locale's default Calendar should be created if unknown calendar is specified
Summary: JEP 127: Improve Locale Data Packaging and Adopt Unicode CLDR Data (part 1 w/o packaging changes. by Naoto Sato and Masayoshi Okutsu)

Split Close
Expand all
Collapse all
          --- old/src/share/classes/java/text/DecimalFormatSymbols.java
          +++ new/src/share/classes/java/text/DecimalFormatSymbols.java
   1    1  /*
   2      - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
        2 + * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
   3    3   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4    4   *
   5    5   * This code is free software; you can redistribute it and/or modify it
   6    6   * under the terms of the GNU General Public License version 2 only, as
   7    7   * published by the Free Software Foundation.  Oracle designates this
   8    8   * particular file as subject to the "Classpath" exception as provided
   9    9   * by Oracle in the LICENSE file that accompanied this code.
  10   10   *
  11   11   * This code is distributed in the hope that it will be useful, but WITHOUT
  12   12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
↓ open down ↓ 24 lines elided ↑ open up ↑
  37   37   */
  38   38  
  39   39  package java.text;
  40   40  
  41   41  import java.io.IOException;
  42   42  import java.io.ObjectInputStream;
  43   43  import java.io.Serializable;
  44   44  import java.text.spi.DecimalFormatSymbolsProvider;
  45   45  import java.util.Currency;
  46   46  import java.util.Locale;
       47 +import java.util.MissingResourceException;
  47   48  import java.util.ResourceBundle;
  48   49  import java.util.concurrent.ConcurrentHashMap;
       50 +import java.util.concurrent.ConcurrentMap;
       51 +import sun.util.locale.provider.LocaleProviderAdapter;
       52 +import sun.util.locale.provider.LocaleServiceProviderPool;
  49   53  
  50      -import sun.util.LocaleServiceProviderPool;
  51      -import sun.util.resources.LocaleData;
  52      -
  53   54  /**
  54   55   * This class represents the set of symbols (such as the decimal separator,
  55   56   * the grouping separator, and so on) needed by <code>DecimalFormat</code>
  56   57   * to format numbers. <code>DecimalFormat</code> creates for itself an instance of
  57   58   * <code>DecimalFormatSymbols</code> from its locale data.  If you need to change any
  58   59   * of these symbols, you can get the <code>DecimalFormatSymbols</code> object from
  59   60   * your <code>DecimalFormat</code> and modify it.
  60   61   *
  61   62   * @see          java.util.Locale
  62   63   * @see          DecimalFormat
↓ open down ↓ 17 lines elided ↑ open up ↑
  80   81      }
  81   82  
  82   83      /**
  83   84       * Create a DecimalFormatSymbols object for the given locale.
  84   85       * This constructor can only construct instances for the locales
  85   86       * supported by the Java runtime environment, not for those
  86   87       * supported by installed
  87   88       * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
  88   89       * implementations. For full locale coverage, use the
  89   90       * {@link #getInstance(Locale) getInstance} method.
       91 +     * If the specified locale contains the {@link java.util.Locale#UNICODE_LOCALE_EXTENSION}
       92 +     * for the numbering system, the instance is initialized with the specified numbering
       93 +     * system if the JRE implementation supports it. For example,
       94 +     * <pre>
       95 +     * NumberFormat.getNumberInstance(Locale.forLanguageTag("th-TH-u-nu-thai"))
       96 +     * </pre>
       97 +     * This may return a {@code NumberFormat} instance with the Thai numbering system,
       98 +     * instead of the Latin numbering system.
  90   99       *
  91  100       * @exception NullPointerException if <code>locale</code> is null
  92  101       */
  93  102      public DecimalFormatSymbols( Locale locale ) {
  94  103          initialize( locale );
  95  104      }
  96  105  
  97  106      /**
  98  107       * Returns an array of all locales for which the
  99  108       * <code>getInstance</code> methods of this class can return
↓ open down ↓ 28 lines elided ↑ open up ↑
 128  137          return getInstance(Locale.getDefault(Locale.Category.FORMAT));
 129  138      }
 130  139  
 131  140      /**
 132  141       * Gets the <code>DecimalFormatSymbols</code> instance for the specified
 133  142       * locale.  This method provides access to <code>DecimalFormatSymbols</code>
 134  143       * instances for locales supported by the Java runtime itself as well
 135  144       * as for those supported by installed
 136  145       * {@link java.text.spi.DecimalFormatSymbolsProvider
 137  146       * DecimalFormatSymbolsProvider} implementations.
      147 +     * If the specified locale contains the {@link java.util.Locale#UNICODE_LOCALE_EXTENSION}
      148 +     * for the numbering system, the instance is initialized with the specified numbering
      149 +     * system if the JRE implementation supports it. For example,
      150 +     * <pre>
      151 +     * NumberFormat.getNumberInstance(Locale.forLanguageTag("th-TH-u-nu-thai"))
      152 +     * </pre>
      153 +     * This may return a {@code NumberFormat} instance with the Thai numbering system,
      154 +     * instead of the Latin numbering system.
 138  155       * @param locale the desired locale.
 139  156       * @return a <code>DecimalFormatSymbols</code> instance.
 140  157       * @exception NullPointerException if <code>locale</code> is null
 141  158       * @since 1.6
 142  159       */
 143  160      public static final DecimalFormatSymbols getInstance(Locale locale) {
 144      -
 145      -        // Check whether a provider can provide an implementation that's closer
 146      -        // to the requested locale than what the Java runtime itself can provide.
 147      -        LocaleServiceProviderPool pool =
 148      -            LocaleServiceProviderPool.getPool(DecimalFormatSymbolsProvider.class);
 149      -        if (pool.hasProviders()) {
 150      -            DecimalFormatSymbols providersInstance = pool.getLocalizedObject(
 151      -                                DecimalFormatSymbolsGetter.INSTANCE, locale);
 152      -            if (providersInstance != null) {
 153      -                return providersInstance;
 154      -            }
      161 +        LocaleProviderAdapter adapter;
      162 +        adapter = LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale);
      163 +        DecimalFormatSymbolsProvider provider = adapter.getDecimalFormatSymbolsProvider();
      164 +        DecimalFormatSymbols dfsyms = provider.getInstance(locale);
      165 +        if (dfsyms == null) {
      166 +            provider = LocaleProviderAdapter.forJRE().getDecimalFormatSymbolsProvider();
      167 +            dfsyms = provider.getInstance(locale);
 155  168          }
 156      -
 157      -        return new DecimalFormatSymbols(locale);
      169 +        return dfsyms;
 158  170      }
 159  171  
 160  172      /**
 161  173       * Gets the character used for zero. Different for Arabic, etc.
 162  174       */
 163  175      public char getZeroDigit() {
 164  176          return zeroDigit;
 165  177      }
 166  178  
 167  179      /**
↓ open down ↓ 299 lines elided ↑ open up ↑
 467  479       }
 468  480  
 469  481  
 470  482      //------------------------------------------------------------
 471  483      // END     Package Private methods ... to be made public later
 472  484      //------------------------------------------------------------
 473  485  
 474  486      /**
 475  487       * Standard override.
 476  488       */
      489 +    @Override
 477  490      public Object clone() {
 478  491          try {
 479  492              return (DecimalFormatSymbols)super.clone();
 480  493              // other fields are bit-copied
 481  494          } catch (CloneNotSupportedException e) {
 482  495              throw new InternalError(e);
 483  496          }
 484  497      }
 485  498  
 486  499      /**
 487  500       * Override equals.
 488  501       */
      502 +    @Override
 489  503      public boolean equals(Object obj) {
 490  504          if (obj == null) return false;
 491  505          if (this == obj) return true;
 492  506          if (getClass() != obj.getClass()) return false;
 493  507          DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
 494  508          return (zeroDigit == other.zeroDigit &&
 495  509          groupingSeparator == other.groupingSeparator &&
 496  510          decimalSeparator == other.decimalSeparator &&
 497  511          percent == other.percent &&
 498  512          perMill == other.perMill &&
↓ open down ↓ 6 lines elided ↑ open up ↑
 505  519          intlCurrencySymbol.equals(other.intlCurrencySymbol) &&
 506  520          currency == other.currency &&
 507  521          monetarySeparator == other.monetarySeparator &&
 508  522          exponentialSeparator.equals(other.exponentialSeparator) &&
 509  523          locale.equals(other.locale));
 510  524      }
 511  525  
 512  526      /**
 513  527       * Override hashCode.
 514  528       */
      529 +    @Override
 515  530      public int hashCode() {
 516  531              int result = zeroDigit;
 517  532              result = result * 37 + groupingSeparator;
 518  533              result = result * 37 + decimalSeparator;
 519  534              return result;
 520  535      }
 521  536  
 522  537      /**
 523  538       * Initializes the symbols from the FormatData resource bundle.
 524  539       */
 525  540      private void initialize( Locale locale ) {
 526  541          this.locale = locale;
 527  542  
 528  543          // get resource bundle data - try the cache first
 529  544          boolean needCacheUpdate = false;
 530  545          Object[] data = cachedLocaleData.get(locale);
 531  546          if (data == null) {  /* cache miss */
 532      -            // When numbering system is thai (Locale's extension contains u-nu-thai),
 533      -            // we read the data from th_TH_TH.
 534      -            Locale lookupLocale = locale;
 535      -            String numberType = locale.getUnicodeLocaleType("nu");
 536      -            if (numberType != null && numberType.equals("thai")) {
 537      -                lookupLocale = new Locale("th", "TH", "TH");
      547 +            LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale);
      548 +            // Avoid potential recursions
      549 +            switch (adapter.getAdapterType()) {
      550 +            case HOST:
      551 +            case SPI:
      552 +                adapter = LocaleProviderAdapter.getResourceBundleBased();
      553 +                break;
 538  554              }
      555 +            ResourceBundle rb = adapter.getLocaleData().getNumberFormatData(locale);
 539  556              data = new Object[3];
 540      -            ResourceBundle rb = LocaleData.getNumberFormatData(lookupLocale);
 541      -            data[0] = rb.getStringArray("NumberElements");
      557 +            String numberType = locale.getUnicodeLocaleType("nu");
      558 +            StringBuilder numElemKey =
      559 +                new StringBuilder(numberType != null ?
      560 +                                  numberType : rb.getString("DefaultNumberingSystem"));
      561 +            if (numElemKey.length() != 0) {
      562 +                numElemKey.append(".");
      563 +            }
      564 +            numElemKey.append("NumberElements");
      565 +            try {
      566 +                data[0] = rb.getStringArray(numElemKey.toString());
      567 +            } catch (MissingResourceException mre) {
      568 +                // numberType must be bogus. Use the last resort numbering system.
      569 +                data[0] = rb.getStringArray("NumberElements");
      570 +            }
 542  571              needCacheUpdate = true;
 543  572          }
 544  573  
 545  574          String[] numberElements = (String[]) data[0];
 546  575  
 547  576          decimalSeparator = numberElements[0].charAt(0);
 548  577          groupingSeparator = numberElements[1].charAt(0);
 549  578          patternSeparator = numberElements[2].charAt(0);
 550  579          percent = numberElements[3].charAt(0);
 551  580          zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
↓ open down ↓ 2 lines elided ↑ open up ↑
 554  583          exponential = numberElements[7].charAt(0);
 555  584          exponentialSeparator = numberElements[7]; //string representation new since 1.6
 556  585          perMill = numberElements[8].charAt(0);
 557  586          infinity  = numberElements[9];
 558  587          NaN = numberElements[10];
 559  588  
 560  589          // Try to obtain the currency used in the locale's country.
 561  590          // Check for empty country string separately because it's a valid
 562  591          // country ID for Locale (and used for the C locale), but not a valid
 563  592          // ISO 3166 country code, and exceptions are expensive.
 564      -        if (!"".equals(locale.getCountry())) {
      593 +        if (locale.getCountry().length() > 0) {
 565  594              try {
 566  595                  currency = Currency.getInstance(locale);
 567  596              } catch (IllegalArgumentException e) {
 568  597                  // use default values below for compatibility
 569  598              }
 570  599          }
 571  600          if (currency != null) {
 572  601              intlCurrencySymbol = currency.getCurrencyCode();
 573  602              if (data[1] != null && data[1] == intlCurrencySymbol) {
 574  603                  currencySymbol = (String) data[2];
↓ open down ↓ 231 lines elided ↑ open up ↑
 806  835       *
 807  836       * @serial
 808  837       * @since JDK 1.1.6
 809  838       */
 810  839      private int serialVersionOnStream = currentSerialVersion;
 811  840  
 812  841      /**
 813  842       * cache to hold the NumberElements and the Currency
 814  843       * of a Locale.
 815  844       */
 816      -    private static final ConcurrentHashMap<Locale, Object[]> cachedLocaleData = new ConcurrentHashMap<Locale, Object[]>(3);
 817      -
 818      -    /**
 819      -     * Obtains a DecimalFormatSymbols instance from a DecimalFormatSymbolsProvider
 820      -     * implementation.
 821      -     */
 822      -    private static class DecimalFormatSymbolsGetter
 823      -        implements LocaleServiceProviderPool.LocalizedObjectGetter<DecimalFormatSymbolsProvider,
 824      -                                                                   DecimalFormatSymbols> {
 825      -        private static final DecimalFormatSymbolsGetter INSTANCE =
 826      -            new DecimalFormatSymbolsGetter();
 827      -
 828      -        public DecimalFormatSymbols getObject(
 829      -                                DecimalFormatSymbolsProvider decimalFormatSymbolsProvider,
 830      -                                Locale locale,
 831      -                                String key,
 832      -                                Object... params) {
 833      -            assert params.length == 0;
 834      -            return decimalFormatSymbolsProvider.getInstance(locale);
 835      -        }
 836      -    }
      845 +    private static final ConcurrentMap<Locale, Object[]> cachedLocaleData
      846 +            = new ConcurrentHashMap<>(3);
 837  847  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX