jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java
changeset 14765 0987999ed367
parent 13583 dc0017b1a452
child 15260 7af2d7a87806
equal deleted inserted replaced
14764:6dd2370b4cd3 14765:0987999ed367
    24  */
    24  */
    25 
    25 
    26 package sun.util.locale.provider;
    26 package sun.util.locale.provider;
    27 
    27 
    28 import java.lang.ref.SoftReference;
    28 import java.lang.ref.SoftReference;
    29 import java.util.Enumeration;
       
    30 import java.util.LinkedList;
    29 import java.util.LinkedList;
    31 import java.util.List;
    30 import java.util.List;
    32 import java.util.Locale;
    31 import java.util.Locale;
    33 import java.util.Map;
    32 import java.util.Map;
    34 import java.util.Set;
       
    35 import java.util.concurrent.ConcurrentHashMap;
    33 import java.util.concurrent.ConcurrentHashMap;
    36 import java.util.spi.TimeZoneNameProvider;
    34 import java.util.spi.TimeZoneNameProvider;
    37 import sun.util.calendar.ZoneInfo;
    35 import sun.util.calendar.ZoneInfo;
    38 import sun.util.resources.OpenListResourceBundle;
    36 import sun.util.resources.OpenListResourceBundle;
       
    37 import sun.util.resources.TimeZoneNamesBundle;
    39 
    38 
    40 /**
    39 /**
    41  * Utility class that deals with the localized time zone names
    40  * Utility class that deals with the localized time zone names
    42  *
    41  *
    43  * @author Naoto Sato
    42  * @author Naoto Sato
       
    43  * @author Masayoshi Okutsu
    44  */
    44  */
    45 public final class TimeZoneNameUtility {
    45 public final class TimeZoneNameUtility {
    46 
    46 
    47     /**
    47     /**
    48      * cache to hold time zone resource bundles. Keyed by Locale
    48      * cache to hold time zone resource bundles. Keyed by Locale
    49      */
    49      */
    50     private static ConcurrentHashMap<Locale, SoftReference<OpenListResourceBundle>> cachedBundles =
    50     private static ConcurrentHashMap<Locale, SoftReference<TimeZoneNamesBundle>> cachedBundles =
    51         new ConcurrentHashMap<>();
    51         new ConcurrentHashMap<>();
    52 
    52 
    53     /**
    53     /**
    54      * cache to hold time zone localized strings. Keyed by Locale
    54      * cache to hold time zone localized strings. Keyed by Locale
    55      */
    55      */
    71 
    71 
    72         return zones;
    72         return zones;
    73     }
    73     }
    74 
    74 
    75     private static String[][] loadZoneStrings(Locale locale) {
    75     private static String[][] loadZoneStrings(Locale locale) {
       
    76         // If the provider is a TimeZoneNameProviderImpl, call its getZoneStrings
       
    77         // in order to avoid per-ID retrieval.
       
    78         LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(TimeZoneNameProvider.class, locale);
       
    79         TimeZoneNameProvider provider = adapter.getTimeZoneNameProvider();
       
    80         if (provider instanceof TimeZoneNameProviderImpl) {
       
    81             return ((TimeZoneNameProviderImpl)provider).getZoneStrings(locale);
       
    82         }
       
    83 
       
    84         // Performs per-ID retrieval.
    76         List<String[]> zones = new LinkedList<>();
    85         List<String[]> zones = new LinkedList<>();
    77         OpenListResourceBundle rb = getBundle(locale);
    86         OpenListResourceBundle rb = getBundle(locale);
    78         Enumeration<String> keys = rb.getKeys();
    87         for (String key : rb.keySet()) {
    79         String[] names;
    88             String[] names = retrieveDisplayNamesImpl(key, locale);
    80 
       
    81         while(keys.hasMoreElements()) {
       
    82             String key = keys.nextElement();
       
    83 
       
    84             names = retrieveDisplayNames(rb, key, locale);
       
    85             if (names != null) {
    89             if (names != null) {
    86                 zones.add(names);
    90                 zones.add(names);
    87             }
    91             }
    88         }
    92         }
    89 
    93 
    93 
    97 
    94     /**
    98     /**
    95      * Retrieve display names for a time zone ID.
    99      * Retrieve display names for a time zone ID.
    96      */
   100      */
    97     public static String[] retrieveDisplayNames(String id, Locale locale) {
   101     public static String[] retrieveDisplayNames(String id, Locale locale) {
    98         OpenListResourceBundle rb = getBundle(locale);
       
    99         return retrieveDisplayNames(rb, id, locale);
       
   100     }
       
   101 
       
   102     private static String[] retrieveDisplayNames(OpenListResourceBundle rb,
       
   103                                                 String id, Locale locale) {
       
   104         if (id == null || locale == null) {
   102         if (id == null || locale == null) {
   105             throw new NullPointerException();
   103             throw new NullPointerException();
   106         }
   104         }
   107 
   105         return retrieveDisplayNamesImpl(id, locale);
       
   106     }
       
   107 
       
   108     /**
       
   109      * Retrieves a generic time zone display name for a time zone ID.
       
   110      *
       
   111      * @param id     time zone ID
       
   112      * @param style  TimeZone.LONG or TimeZone.SHORT
       
   113      * @param locale desired Locale
       
   114      * @return the requested generic time zone display name, or null if not found.
       
   115      */
       
   116     public static String retrieveGenericDisplayName(String id, int style, Locale locale) {
   108         LocaleServiceProviderPool pool =
   117         LocaleServiceProviderPool pool =
   109             LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
   118             LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
   110         return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, id);
   119         return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, "generic", style, id);
   111     }
   120     }
   112 
   121 
   113     private static OpenListResourceBundle getBundle(Locale locale) {
   122     /**
   114         OpenListResourceBundle rb;
   123      * Retrieves a standard or daylight-saving time name for the given time zone ID.
   115         SoftReference<OpenListResourceBundle> data = cachedBundles.get(locale);
   124      *
       
   125      * @param id       time zone ID
       
   126      * @param daylight true for a daylight saving time name, or false for a standard time name
       
   127      * @param style    TimeZone.LONG or TimeZone.SHORT
       
   128      * @param locale   desired Locale
       
   129      * @return the requested time zone name, or null if not found.
       
   130      */
       
   131     public static String retrieveDisplayName(String id, boolean daylight, int style, Locale locale) {
       
   132         LocaleServiceProviderPool pool =
       
   133             LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
       
   134         return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, daylight ? "dst" : "std", style, id);
       
   135     }
       
   136 
       
   137     private static String[] retrieveDisplayNamesImpl(String id, Locale locale) {
       
   138         LocaleServiceProviderPool pool =
       
   139             LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
       
   140         return pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id);
       
   141     }
       
   142 
       
   143     private static TimeZoneNamesBundle getBundle(Locale locale) {
       
   144         TimeZoneNamesBundle rb;
       
   145         SoftReference<TimeZoneNamesBundle> data = cachedBundles.get(locale);
   116 
   146 
   117         if (data == null || ((rb = data.get()) == null)) {
   147         if (data == null || ((rb = data.get()) == null)) {
   118             rb = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(locale);
   148             rb = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(locale);
   119             data = new SoftReference<>(rb);
   149             data = new SoftReference<>(rb);
   120             cachedBundles.put(locale, data);
   150             cachedBundles.put(locale, data);
   125 
   155 
   126     /**
   156     /**
   127      * Obtains a localized time zone strings from a TimeZoneNameProvider
   157      * Obtains a localized time zone strings from a TimeZoneNameProvider
   128      * implementation.
   158      * implementation.
   129      */
   159      */
   130     private static class TimeZoneNameGetter
   160     private static class TimeZoneNameArrayGetter
   131         implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider,
   161         implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider,
   132                                                                    String[]>{
   162                                                                    String[]>{
   133         private static final TimeZoneNameGetter INSTANCE =
   163         private static final TimeZoneNameArrayGetter INSTANCE =
   134             new TimeZoneNameGetter();
   164             new TimeZoneNameArrayGetter();
   135 
   165 
   136         @Override
   166         @Override
   137         public String[] getObject(TimeZoneNameProvider timeZoneNameProvider,
   167         public String[] getObject(TimeZoneNameProvider timeZoneNameProvider,
   138                                 Locale locale,
   168                                   Locale locale,
   139                                 String requestID,
   169                                   String requestID,
   140                                 Object... params) {
   170                                   Object... params) {
   141             assert params.length == 0;
   171             assert params.length == 0;
   142             String queryID = requestID;
       
   143 
   172 
   144             // First, try to get names with the request ID
   173             // First, try to get names with the request ID
   145             String[] names = buildZoneStrings(timeZoneNameProvider, locale, requestID);
   174             String[] names = buildZoneStrings(timeZoneNameProvider, locale, requestID);
   146 
   175 
   147             if (names == null) {
   176             if (names == null) {
   148                 Map<String, String> aliases = ZoneInfo.getAliasTable();
   177                 Map<String, String> aliases = ZoneInfo.getAliasTable();
   149 
   178 
   150                 if (aliases != null) {
   179                 if (aliases != null) {
   151                     // Check whether this id is an alias, if so,
   180                     // Check whether this id is an alias, if so,
   152                     // look for the standard id.
   181                     // look for the standard id.
   153                     if (aliases.containsKey(queryID)) {
   182                     String canonicalID = aliases.get(requestID);
   154                         String prevID = queryID;
   183                     if (canonicalID != null) {
   155                         while ((queryID = aliases.get(queryID)) != null) {
   184                         names = buildZoneStrings(timeZoneNameProvider, locale, canonicalID);
   156                             prevID = queryID;
   185                     }
   157                         }
       
   158                         queryID = prevID;
       
   159                     }
       
   160 
       
   161                     names = buildZoneStrings(timeZoneNameProvider, locale, queryID);
       
   162 
       
   163                     if (names == null) {
   186                     if (names == null) {
   164                         // There may be a case that a standard id has become an
   187                         // There may be a case that a standard id has become an
   165                         // alias.  so, check the aliases backward.
   188                         // alias.  so, check the aliases backward.
   166                         names = examineAliases(timeZoneNameProvider, locale,
   189                         names = examineAliases(timeZoneNameProvider, locale,
   167                                                queryID, aliases, aliases.entrySet());
   190                                    canonicalID == null ? requestID : canonicalID, aliases);
   168                     }
   191                     }
   169                 }
   192                 }
   170             }
   193             }
   171 
   194 
   172             if (names != null) {
   195             if (names != null) {
   176             return names;
   199             return names;
   177         }
   200         }
   178 
   201 
   179         private static String[] examineAliases(TimeZoneNameProvider tznp, Locale locale,
   202         private static String[] examineAliases(TimeZoneNameProvider tznp, Locale locale,
   180                                                String id,
   203                                                String id,
   181                                                Map<String, String> aliases,
   204                                                Map<String, String> aliases) {
   182                                                Set<Map.Entry<String, String>> aliasesSet) {
       
   183             if (aliases.containsValue(id)) {
   205             if (aliases.containsValue(id)) {
   184                 for (Map.Entry<String, String> entry : aliasesSet) {
   206                 for (Map.Entry<String, String> entry : aliases.entrySet()) {
   185                     if (entry.getValue().equals(id)) {
   207                     if (entry.getValue().equals(id)) {
   186                         String alias = entry.getKey();
   208                         String alias = entry.getKey();
   187                         String[] names = buildZoneStrings(tznp, locale, alias);
   209                         String[] names = buildZoneStrings(tznp, locale, alias);
   188                         if (names != null) {
   210                         if (names != null) {
   189                             return names;
   211                             return names;
   190                         } else {
       
   191                             names = examineAliases(tznp, locale, alias, aliases, aliasesSet);
       
   192                             if (names != null) {
       
   193                                 return names;
       
   194                             }
       
   195                         }
   212                         }
       
   213                         names = examineAliases(tznp, locale, alias, aliases);
       
   214                         if (names != null) {
       
   215                             return names;
       
   216                         }
   196                     }
   217                     }
   197                 }
   218                 }
   198             }
   219             }
   199 
   220 
   200             return null;
   221             return null;
   201         }
   222         }
   202 
   223 
   203         private static String[] buildZoneStrings(TimeZoneNameProvider tznp,
   224         private static String[] buildZoneStrings(TimeZoneNameProvider tznp,
   204                                     Locale locale, String id) {
   225                                                  Locale locale, String id) {
   205             String[] names = new String[5];
   226             String[] names = new String[5];
   206 
   227 
   207             for (int i = 1; i <= 4; i ++) {
   228             for (int i = 1; i <= 4; i ++) {
   208                 names[i] = tznp.getDisplayName(id, i>=3, i%2, locale);
   229                 names[i] = tznp.getDisplayName(id, i>=3, i%2, locale);
   209                 if (i >= 3 && names[i] == null) {
   230                 if (i >= 3 && names[i] == null) {
   218 
   239 
   219             return names;
   240             return names;
   220         }
   241         }
   221     }
   242     }
   222 
   243 
       
   244     private static class TimeZoneNameGetter
       
   245         implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider,
       
   246                                                                    String> {
       
   247         private static final TimeZoneNameGetter INSTANCE =
       
   248             new TimeZoneNameGetter();
       
   249 
       
   250         @Override
       
   251         public String getObject(TimeZoneNameProvider timeZoneNameProvider,
       
   252                                 Locale locale,
       
   253                                 String requestID,
       
   254                                 Object... params) {
       
   255             assert params.length == 2;
       
   256             int style = (int) params[0];
       
   257             String tzid = (String) params[1];
       
   258             String value = getName(timeZoneNameProvider, locale, requestID, style, tzid);
       
   259             if (value == null) {
       
   260                 Map<String, String> aliases = ZoneInfo.getAliasTable();
       
   261                 if (aliases != null) {
       
   262                     String canonicalID = aliases.get(tzid);
       
   263                     if (canonicalID != null) {
       
   264                         value = getName(timeZoneNameProvider, locale, requestID, style, canonicalID);
       
   265                     }
       
   266                     if (value == null) {
       
   267                         value = examineAliases(timeZoneNameProvider, locale, requestID,
       
   268                                      canonicalID != null ? canonicalID : tzid, style, aliases);
       
   269                     }
       
   270                 }
       
   271             }
       
   272 
       
   273             return value;
       
   274         }
       
   275 
       
   276         private static String examineAliases(TimeZoneNameProvider tznp, Locale locale,
       
   277                                              String requestID, String tzid, int style,
       
   278                                              Map<String, String> aliases) {
       
   279             if (aliases.containsValue(tzid)) {
       
   280                 for (Map.Entry<String, String> entry : aliases.entrySet()) {
       
   281                     if (entry.getValue().equals(tzid)) {
       
   282                         String alias = entry.getKey();
       
   283                         String name = getName(tznp, locale, requestID, style, alias);
       
   284                         if (name != null) {
       
   285                             return name;
       
   286                         }
       
   287                         name = examineAliases(tznp, locale, requestID, alias, style, aliases);
       
   288                         if (name != null) {
       
   289                             return name;
       
   290                         }
       
   291                     }
       
   292                 }
       
   293             }
       
   294             return null;
       
   295         }
       
   296 
       
   297         private static String getName(TimeZoneNameProvider timeZoneNameProvider,
       
   298                                       Locale locale, String requestID, int style, String tzid) {
       
   299             String value = null;
       
   300             switch (requestID) {
       
   301             case "std":
       
   302                 value = timeZoneNameProvider.getDisplayName(tzid, false, style, locale);
       
   303                 break;
       
   304             case "dst":
       
   305                 value = timeZoneNameProvider.getDisplayName(tzid, true, style, locale);
       
   306                 break;
       
   307             case "generic":
       
   308                 value = timeZoneNameProvider.getGenericDisplayName(tzid, style, locale);
       
   309                 break;
       
   310             }
       
   311             return value;
       
   312         }
       
   313     }
       
   314 
   223     // No instantiation
   315     // No instantiation
   224     private TimeZoneNameUtility() {
   316     private TimeZoneNameUtility() {
   225     }
   317     }
   226 }
   318 }