8076287: Performance degradation observed with TimeZone Benchmark
Reviewed-by: okutsu
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java Thu Apr 16 14:48:11 2015 +0200
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java Thu Apr 16 08:25:19 2015 -0700
@@ -47,6 +47,7 @@
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -250,17 +251,17 @@
return (String) localeName;
}
- String[] getTimeZoneNames(String key, int size) {
+ String[] getTimeZoneNames(String key) {
String[] names = null;
- String cacheKey = TIME_ZONE_NAMES + size + '.' + key;
+ String cacheKey = TIME_ZONE_NAMES + '.' + key;
removeEmptyReferences();
ResourceReference data = cache.get(cacheKey);
- if (data == null || ((names = (String[]) data.get()) == null)) {
+ if (Objects.isNull(data) || Objects.isNull((names = (String[]) data.get()))) {
TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
if (tznb.containsKey(key)) {
- names = tznb.getStringArray(key, size);
+ names = tznb.getStringArray(key);
cache.put(cacheKey,
new ResourceReference(cacheKey, (Object) names, referenceQueue));
}
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java Thu Apr 16 14:48:11 2015 +0200
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java Thu Apr 16 08:25:19 2015 -0700
@@ -26,6 +26,7 @@
package sun.util.locale.provider;
import java.util.Locale;
+import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.spi.TimeZoneNameProvider;
@@ -95,8 +96,9 @@
*/
@Override
public String getDisplayName(String id, boolean daylight, int style, Locale locale) {
- String[] names = getDisplayNameArray(id, 5, locale);
- if (names != null) {
+ String[] names = getDisplayNameArray(id, locale);
+ if (Objects.nonNull(names)) {
+ assert names.length >= 7;
int index = daylight ? 3 : 1;
if (style == TimeZone.SHORT) {
index++;
@@ -108,18 +110,18 @@
@Override
public String getGenericDisplayName(String id, int style, Locale locale) {
- String[] names = getDisplayNameArray(id, 7, locale);
- if (names != null && names.length >= 7) {
+ String[] names = getDisplayNameArray(id, locale);
+ if (Objects.nonNull(names)) {
+ assert names.length >= 7;
return names[(style == TimeZone.LONG) ? 5 : 6];
}
return null;
}
- private String[] getDisplayNameArray(String id, int n, Locale locale) {
- if (id == null || locale == null) {
- throw new NullPointerException();
- }
- return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getTimeZoneNames(id, n);
+ private String[] getDisplayNameArray(String id, Locale locale) {
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(locale);
+ return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getTimeZoneNames(id);
}
/**
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java Thu Apr 16 14:48:11 2015 +0200
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java Thu Apr 16 08:25:19 2015 -0700
@@ -30,6 +30,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.spi.TimeZoneNameProvider;
@@ -100,9 +101,9 @@
* Retrieve display names for a time zone ID.
*/
public static String[] retrieveDisplayNames(String id, Locale locale) {
- if (id == null || locale == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(locale);
+
return retrieveDisplayNamesImpl(id, locale);
}
@@ -115,9 +116,12 @@
* @return the requested generic time zone display name, or null if not found.
*/
public static String retrieveGenericDisplayName(String id, int style, Locale locale) {
- LocaleServiceProviderPool pool =
- LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
- return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, "generic", style, id);
+ String[] names = retrieveDisplayNamesImpl(id, locale);
+ if (Objects.nonNull(names)) {
+ return names[6 - style];
+ } else {
+ return null;
+ }
}
/**
@@ -130,140 +134,53 @@
* @return the requested time zone name, or null if not found.
*/
public static String retrieveDisplayName(String id, boolean daylight, int style, Locale locale) {
- LocaleServiceProviderPool pool =
- LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
- return pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale, daylight ? "dst" : "std", style, id);
+ String[] names = retrieveDisplayNamesImpl(id, locale);
+ if (Objects.nonNull(names)) {
+ return names[(daylight ? 4 : 2) - style];
+ } else {
+ return null;
+ }
}
private static String[] retrieveDisplayNamesImpl(String id, Locale locale) {
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
+ String[] names;
+ Map<Locale, String[]> perLocale = null;
SoftReference<Map<Locale, String[]>> ref = cachedDisplayNames.get(id);
- if (ref != null) {
- Map<Locale, String[]> perLocale = ref.get();
- if (perLocale != null) {
- String[] names = perLocale.get(locale);
- if (names != null) {
+ if (Objects.nonNull(ref)) {
+ perLocale = ref.get();
+ if (Objects.nonNull(perLocale)) {
+ names = perLocale.get(locale);
+ if (Objects.nonNull(names)) {
return names;
}
- names = pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id);
- if (names != null) {
- perLocale.put(locale, names);
- }
- return names;
}
}
- String[] names = pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id);
- if (names != null) {
- Map<Locale, String[]> perLocale = new ConcurrentHashMap<>();
- perLocale.put(locale, names);
- ref = new SoftReference<>(perLocale);
- cachedDisplayNames.put(id, ref);
+ // build names array
+ names = new String[7];
+ names[0] = id;
+ for (int i = 1; i <= 6; i ++) {
+ names[i] = pool.getLocalizedObject(TimeZoneNameGetter.INSTANCE, locale,
+ i<5 ? (i<3 ? "std" : "dst") : "generic", i%2, id);
}
+
+ if (Objects.isNull(perLocale)) {
+ perLocale = new ConcurrentHashMap<>();
+ }
+ perLocale.put(locale, names);
+ ref = new SoftReference<>(perLocale);
+ cachedDisplayNames.put(id, ref);
return names;
}
+
/**
* Obtains a localized time zone strings from a TimeZoneNameProvider
* implementation.
*/
- private static class TimeZoneNameArrayGetter
- implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider,
- String[]>{
- private static final TimeZoneNameArrayGetter INSTANCE =
- new TimeZoneNameArrayGetter();
-
- @Override
- public String[] getObject(TimeZoneNameProvider timeZoneNameProvider,
- Locale locale,
- String requestID,
- Object... params) {
- assert params.length == 0;
-
- // First, try to get names with the request ID
- String[] names = buildZoneStrings(timeZoneNameProvider, locale, requestID);
-
- if (names == null) {
- Map<String, String> aliases = ZoneInfo.getAliasTable();
-
- if (aliases != null) {
- // Check whether this id is an alias, if so,
- // look for the standard id.
- String canonicalID = aliases.get(requestID);
- if (canonicalID != null) {
- names = buildZoneStrings(timeZoneNameProvider, locale, canonicalID);
- }
- if (names == null) {
- // There may be a case that a standard id has become an
- // alias. so, check the aliases backward.
- names = examineAliases(timeZoneNameProvider, locale,
- canonicalID == null ? requestID : canonicalID, aliases);
- }
- }
- }
-
- if (names != null) {
- names[0] = requestID;
- }
-
- return names;
- }
-
- private static String[] examineAliases(TimeZoneNameProvider tznp, Locale locale,
- String id,
- Map<String, String> aliases) {
- if (aliases.containsValue(id)) {
- for (Map.Entry<String, String> entry : aliases.entrySet()) {
- if (entry.getValue().equals(id)) {
- String alias = entry.getKey();
- String[] names = buildZoneStrings(tznp, locale, alias);
- if (names != null) {
- return names;
- }
- names = examineAliases(tznp, locale, alias, aliases);
- if (names != null) {
- return names;
- }
- }
- }
- }
-
- return null;
- }
-
- private static String[] buildZoneStrings(TimeZoneNameProvider tznp,
- Locale locale, String id) {
- String[] names = new String[5];
-
- for (int i = 1; i <= 4; i ++) {
- names[i] = tznp.getDisplayName(id, i>=3, i%2, locale);
-
- if (names[i] == null) {
- switch (i) {
- case 1:
- // this id seems not localized by this provider
- return null;
- case 2:
- case 4:
- // If the display name for SHORT is not supplied,
- // copy the LONG name.
- names[i] = names[i-1];
- break;
- case 3:
- // If the display name for DST is not supplied,
- // copy the "standard" name.
- names[3] = names[1];
- break;
- }
- }
- }
-
- return names;
- }
- }
-
private static class TimeZoneNameGetter
implements LocaleServiceProviderPool.LocalizedObjectGetter<TimeZoneNameProvider,
String> {
@@ -299,18 +216,16 @@
private static String examineAliases(TimeZoneNameProvider tznp, Locale locale,
String requestID, String tzid, int style,
Map<String, String> aliases) {
- if (aliases.containsValue(tzid)) {
- for (Map.Entry<String, String> entry : aliases.entrySet()) {
- if (entry.getValue().equals(tzid)) {
- String alias = entry.getKey();
- String name = getName(tznp, locale, requestID, style, alias);
- if (name != null) {
- return name;
- }
- name = examineAliases(tznp, locale, requestID, alias, style, aliases);
- if (name != null) {
- return name;
- }
+ for (Map.Entry<String, String> entry : aliases.entrySet()) {
+ if (entry.getValue().equals(tzid)) {
+ String alias = entry.getKey();
+ String name = getName(tznp, locale, requestID, style, alias);
+ if (name != null) {
+ return name;
+ }
+ name = examineAliases(tznp, locale, requestID, alias, style, aliases);
+ if (name != null) {
+ return name;
}
}
}
--- a/jdk/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java Thu Apr 16 14:48:11 2015 +0200
+++ b/jdk/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java Thu Apr 16 08:25:19 2015 -0700
@@ -44,6 +44,7 @@
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.MissingResourceException;
+import java.util.Objects;
import java.util.Set;
/**
@@ -61,26 +62,6 @@
public abstract class TimeZoneNamesBundle extends OpenListResourceBundle {
/**
- * Returns a String array containing time zone names. The String array has
- * at most size elements.
- *
- * @param key the time zone ID for which names are obtained
- * @param size the requested size of array for names
- * @return a String array containing names
- */
- public String[] getStringArray(String key, int size) {
- String[] names = handleGetObject(key, size);
- if ((names == null || names.length != size) && parent != null) {
- names = ((TimeZoneNamesBundle)parent).getStringArray(key, size);
- }
- if (names == null) {
- throw new MissingResourceException("no time zone names", getClass().getName(), key);
- }
- return names;
-
- }
-
- /**
* Maps time zone IDs to locale-specific names.
* The value returned is an array of five strings:
* <ul>
@@ -89,6 +70,8 @@
* <li>The short name of the time zone in standard time (localized).
* <li>The long name of the time zone in daylight savings time (localized).
* <li>The short name of the time zone in daylight savings time (localized).
+ * <li>The long name of the time zone in generic form (localized).
+ * <li>The short name of the time zone in generic form (localized).
* </ul>
* The localized names come from the subclasses's
* <code>getContents</code> implementations, while the time zone
@@ -96,16 +79,12 @@
*/
@Override
public Object handleGetObject(String key) {
- return handleGetObject(key, 5);
- }
-
- private String[] handleGetObject(String key, int n) {
String[] contents = (String[]) super.handleGetObject(key);
- if (contents == null) {
+ if (Objects.isNull(contents)) {
return null;
}
- int clen = Math.min(n - 1, contents.length);
- String[] tmpobj = new String[clen+1];
+ int clen = contents.length;
+ String[] tmpobj = new String[7];
tmpobj[0] = key;
System.arraycopy(contents, 0, tmpobj, 1, clen);
return tmpobj;
--- a/jdk/src/java.base/share/classes/sun/util/resources/en/TimeZoneNames_en_IE.java Thu Apr 16 14:48:11 2015 +0200
+++ b/jdk/src/java.base/share/classes/sun/util/resources/en/TimeZoneNames_en_IE.java Thu Apr 16 08:25:19 2015 -0700
@@ -47,7 +47,8 @@
protected final Object[][] getContents() {
return new Object[][] {
{"Europe/London", new String[] {"Greenwich Mean Time", "GMT",
- "Irish Summer Time", "IST" /*Dublin*/}},
+ "Irish Summer Time", "IST", /*Dublin*/
+ "Irish Time", "IT" /*Dublin*/}},
};
}
}
--- a/jdk/test/java/util/PluggableLocale/TimeZoneNameProviderTest.java Thu Apr 16 14:48:11 2015 +0200
+++ b/jdk/test/java/util/PluggableLocale/TimeZoneNameProviderTest.java Thu Apr 16 08:25:19 2015 -0700
@@ -25,6 +25,7 @@
*/
import java.text.*;
+import java.time.format.TextStyle;
import java.util.*;
import sun.util.locale.provider.*;
import sun.util.resources.*;
@@ -42,6 +43,7 @@
test2();
test3();
aliasTest();
+ genericFallbackTest();
}
void test1() {
@@ -169,9 +171,9 @@
for (int style : new int[] { TimeZone.LONG, TimeZone.SHORT }) {
String osakaStd = tz.getDisplayName(false, style, OSAKA);
if (osakaStd != null) {
- // No API for getting generic time zone names
- String generic = TimeZoneNameUtility.retrieveGenericDisplayName(tzname,
- style, GENERIC);
+ String generic = tz.toZoneId().getDisplayName(
+ style == TimeZone.LONG ? TextStyle.FULL : TextStyle.SHORT,
+ GENERIC);
String expected = "Generic " + osakaStd;
if (!expected.equals(generic)) {
throw new RuntimeException("Wrong generic name: got=\"" + generic
@@ -230,4 +232,20 @@
throw new RuntimeException("Provider's localized name is not available for an alias ID: "+JAPAN+". result: "+japan+" expected: "+JST_IN_OSAKA);
}
}
+
+ /*
+ * Tests whether generic names can be retrieved through fallback.
+ * The test assumes the provider impl for OSAKA locale does NOT
+ * provide generic names.
+ */
+ final String PT = "PT"; // SHORT generic name for "America/Los_Angeles"
+ void genericFallbackTest() {
+ String generic =
+ TimeZone.getTimeZone(LATIME)
+ .toZoneId()
+ .getDisplayName(TextStyle.SHORT, OSAKA);
+ if (!PT.equals(generic)) {
+ throw new RuntimeException("Generic name fallback failed. got: "+generic);
+ }
+ }
}