6991380: (cal) Calendar.cachedLocaleData should be transitioned from Hashtable to ConcurrentHashMap
6560965: [Fmt-Da] defaultCenturyStart In SimpleDateFormat should be protected 6560980: [Fmt-Da] DateFormatSymbols.cacheLookup doesn't update cache correctly Reviewed-by: naoto, peytoia
This commit is contained in:
parent
8df28ab0e3
commit
fbe778aead
@ -44,11 +44,12 @@ import java.io.Serializable;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.text.spi.DateFormatSymbolsProvider;
|
||||
import java.util.Arrays;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.spi.LocaleServiceProvider;
|
||||
import sun.util.LocaleServiceProviderPool;
|
||||
import sun.util.TimeZoneNameUtility;
|
||||
@ -321,20 +322,64 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
* @since 1.6
|
||||
*/
|
||||
public static final DateFormatSymbols getInstance(Locale locale) {
|
||||
DateFormatSymbols dfs = getProviderInstance(locale);
|
||||
if (dfs != null) {
|
||||
return dfs;
|
||||
}
|
||||
return (DateFormatSymbols) getCachedInstance(locale).clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a DateFormatSymbols provided by a provider or found in
|
||||
* the cache. Note that this method returns a cached instance,
|
||||
* not its clone. Therefore, the instance should never be given to
|
||||
* an application.
|
||||
*/
|
||||
static final DateFormatSymbols getInstanceRef(Locale locale) {
|
||||
DateFormatSymbols dfs = getProviderInstance(locale);
|
||||
if (dfs != null) {
|
||||
return dfs;
|
||||
}
|
||||
return getCachedInstance(locale);
|
||||
}
|
||||
|
||||
private static DateFormatSymbols getProviderInstance(Locale locale) {
|
||||
DateFormatSymbols providersInstance = null;
|
||||
|
||||
// Check whether a provider can provide an implementation that's closer
|
||||
// to the requested locale than what the Java runtime itself can provide.
|
||||
LocaleServiceProviderPool pool =
|
||||
LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
|
||||
if (pool.hasProviders()) {
|
||||
DateFormatSymbols providersInstance = pool.getLocalizedObject(
|
||||
DateFormatSymbolsGetter.INSTANCE, locale);
|
||||
if (providersInstance != null) {
|
||||
return providersInstance;
|
||||
providersInstance = pool.getLocalizedObject(
|
||||
DateFormatSymbolsGetter.INSTANCE, locale);
|
||||
}
|
||||
return providersInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cached DateFormatSymbols if it's found in the
|
||||
* cache. Otherwise, this method returns a newly cached instance
|
||||
* for the given locale.
|
||||
*/
|
||||
private static DateFormatSymbols getCachedInstance(Locale locale) {
|
||||
SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
|
||||
DateFormatSymbols dfs = null;
|
||||
if (ref == null || (dfs = ref.get()) == null) {
|
||||
dfs = new DateFormatSymbols(locale);
|
||||
ref = new SoftReference<DateFormatSymbols>(dfs);
|
||||
SoftReference<DateFormatSymbols> x = cachedInstances.putIfAbsent(locale, ref);
|
||||
if (x != null) {
|
||||
DateFormatSymbols y = x.get();
|
||||
if (y != null) {
|
||||
dfs = y;
|
||||
} else {
|
||||
// Replace the empty SoftReference with ref.
|
||||
cachedInstances.put(locale, ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new DateFormatSymbols(locale);
|
||||
return dfs;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -597,56 +642,44 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
static final int millisPerHour = 60*60*1000;
|
||||
|
||||
/**
|
||||
* Cache to hold the FormatData and TimeZoneNames ResourceBundles
|
||||
* of a Locale.
|
||||
* Cache to hold DateFormatSymbols instances per Locale.
|
||||
*/
|
||||
private static Hashtable cachedLocaleData = new Hashtable(3);
|
||||
|
||||
/**
|
||||
* Look up resource data for the desiredLocale in the cache; update the
|
||||
* cache if necessary.
|
||||
*/
|
||||
private static ResourceBundle cacheLookup(Locale desiredLocale) {
|
||||
ResourceBundle rb;
|
||||
SoftReference data
|
||||
= (SoftReference)cachedLocaleData.get(desiredLocale);
|
||||
if (data == null) {
|
||||
rb = LocaleData.getDateFormatData(desiredLocale);
|
||||
data = new SoftReference(rb);
|
||||
cachedLocaleData.put(desiredLocale, data);
|
||||
} else {
|
||||
if ((rb = (ResourceBundle)data.get()) == null) {
|
||||
rb = LocaleData.getDateFormatData(desiredLocale);
|
||||
data = new SoftReference(rb);
|
||||
}
|
||||
}
|
||||
return rb;
|
||||
}
|
||||
private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances
|
||||
= new ConcurrentHashMap<Locale, SoftReference<DateFormatSymbols>>(3);
|
||||
|
||||
private void initializeData(Locale desiredLocale) {
|
||||
int i;
|
||||
ResourceBundle resource = cacheLookup(desiredLocale);
|
||||
locale = desiredLocale;
|
||||
|
||||
// FIXME: cache only ResourceBundle. Hence every time, will do
|
||||
// getObject(). This won't be necessary if the Resource itself
|
||||
// is cached.
|
||||
eras = (String[])resource.getObject("Eras");
|
||||
// Copy values of a cached instance if any.
|
||||
SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
|
||||
DateFormatSymbols dfs;
|
||||
if (ref != null && (dfs = ref.get()) != null) {
|
||||
copyMembers(dfs, this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the fields from the ResourceBundle for locale.
|
||||
ResourceBundle resource = LocaleData.getDateFormatData(locale);
|
||||
|
||||
eras = resource.getStringArray("Eras");
|
||||
months = resource.getStringArray("MonthNames");
|
||||
shortMonths = resource.getStringArray("MonthAbbreviations");
|
||||
String[] lWeekdays = resource.getStringArray("DayNames");
|
||||
weekdays = new String[8];
|
||||
weekdays[0] = ""; // 1-based
|
||||
for (i=0; i<lWeekdays.length; i++)
|
||||
weekdays[i+1] = lWeekdays[i];
|
||||
String[] sWeekdays = resource.getStringArray("DayAbbreviations");
|
||||
shortWeekdays = new String[8];
|
||||
shortWeekdays[0] = ""; // 1-based
|
||||
for (i=0; i<sWeekdays.length; i++)
|
||||
shortWeekdays[i+1] = sWeekdays[i];
|
||||
ampms = resource.getStringArray("AmPmMarkers");
|
||||
localPatternChars = resource.getString("DateTimePatternChars");
|
||||
|
||||
locale = desiredLocale;
|
||||
// Day of week names are stored in a 1-based array.
|
||||
weekdays = toOneBasedArray(resource.getStringArray("DayNames"));
|
||||
shortWeekdays = toOneBasedArray(resource.getStringArray("DayAbbreviations"));
|
||||
}
|
||||
|
||||
private static String[] toOneBasedArray(String[] src) {
|
||||
int len = src.length;
|
||||
String[] dst = new String[len + 1];
|
||||
dst[0] = "";
|
||||
for (int i = 0; i < len; i++) {
|
||||
dst[i + 1] = src[i];
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -46,9 +46,10 @@ import java.math.BigInteger;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Currency;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import sun.util.resources.LocaleData;
|
||||
@ -394,14 +395,14 @@ public class DecimalFormat extends NumberFormat {
|
||||
public DecimalFormat() {
|
||||
Locale def = Locale.getDefault(Locale.Category.FORMAT);
|
||||
// try to get the pattern from the cache
|
||||
String pattern = (String) cachedLocaleData.get(def);
|
||||
String pattern = cachedLocaleData.get(def);
|
||||
if (pattern == null) { /* cache miss */
|
||||
// Get the pattern for the default locale.
|
||||
ResourceBundle rb = LocaleData.getNumberFormatData(def);
|
||||
String[] all = rb.getStringArray("NumberPatterns");
|
||||
pattern = all[0];
|
||||
/* update cache */
|
||||
cachedLocaleData.put(def, pattern);
|
||||
cachedLocaleData.putIfAbsent(def, pattern);
|
||||
}
|
||||
|
||||
// Always applyPattern after the symbols are set
|
||||
@ -3272,5 +3273,6 @@ public class DecimalFormat extends NumberFormat {
|
||||
/**
|
||||
* Cache to hold the NumberPattern of a Locale.
|
||||
*/
|
||||
private static Hashtable cachedLocaleData = new Hashtable(3);
|
||||
private static final ConcurrentMap<Locale, String> cachedLocaleData
|
||||
= new ConcurrentHashMap<Locale, String>(3);
|
||||
}
|
||||
|
@ -44,13 +44,14 @@ import java.io.ObjectInputStream;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.SimpleTimeZone;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import sun.util.calendar.CalendarUtils;
|
||||
import sun.util.calendar.ZoneInfoFile;
|
||||
import sun.util.resources.LocaleData;
|
||||
@ -503,14 +504,14 @@ public class SimpleDateFormat extends DateFormat {
|
||||
/**
|
||||
* Cache to hold the DateTimePatterns of a Locale.
|
||||
*/
|
||||
private static Hashtable<String,String[]> cachedLocaleData
|
||||
= new Hashtable<String,String[]>(3);
|
||||
private static final ConcurrentMap<String, String[]> cachedLocaleData
|
||||
= new ConcurrentHashMap<String, String[]>(3);
|
||||
|
||||
/**
|
||||
* Cache NumberFormat instances with Locale key.
|
||||
*/
|
||||
private static Hashtable<Locale,NumberFormat> cachedNumberFormatData
|
||||
= new Hashtable<Locale,NumberFormat>(3);
|
||||
private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData
|
||||
= new ConcurrentHashMap<Locale, NumberFormat>(3);
|
||||
|
||||
/**
|
||||
* The Locale used to instantiate this
|
||||
@ -579,7 +580,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
|
||||
initializeCalendar(locale);
|
||||
this.pattern = pattern;
|
||||
this.formatData = DateFormatSymbols.getInstance(locale);
|
||||
this.formatData = DateFormatSymbols.getInstanceRef(locale);
|
||||
this.locale = locale;
|
||||
initialize(locale);
|
||||
}
|
||||
@ -632,9 +633,9 @@ public class SimpleDateFormat extends DateFormat {
|
||||
dateTimePatterns = r.getStringArray("DateTimePatterns");
|
||||
}
|
||||
/* update cache */
|
||||
cachedLocaleData.put(key, dateTimePatterns);
|
||||
cachedLocaleData.putIfAbsent(key, dateTimePatterns);
|
||||
}
|
||||
formatData = DateFormatSymbols.getInstance(loc);
|
||||
formatData = DateFormatSymbols.getInstanceRef(loc);
|
||||
if ((timeStyle >= 0) && (dateStyle >= 0)) {
|
||||
Object[] dateTimeArgs = {dateTimePatterns[timeStyle],
|
||||
dateTimePatterns[dateStyle + 4]};
|
||||
@ -665,7 +666,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
numberFormat.setGroupingUsed(false);
|
||||
|
||||
/* update cache */
|
||||
cachedNumberFormatData.put(loc, numberFormat);
|
||||
cachedNumberFormatData.putIfAbsent(loc, numberFormat);
|
||||
}
|
||||
numberFormat = (NumberFormat) numberFormat.clone();
|
||||
|
||||
@ -897,7 +898,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
* so we can call it from readObject().
|
||||
*/
|
||||
private void initializeDefaultCentury() {
|
||||
calendar.setTime( new Date() );
|
||||
calendar.setTimeInMillis(System.currentTimeMillis());
|
||||
calendar.add( Calendar.YEAR, -80 );
|
||||
parseAmbiguousDatesAsAfter(calendar.getTime());
|
||||
}
|
||||
@ -921,7 +922,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
* @since 1.2
|
||||
*/
|
||||
public void set2DigitYearStart(Date startDate) {
|
||||
parseAmbiguousDatesAsAfter(startDate);
|
||||
parseAmbiguousDatesAsAfter(new Date(startDate.getTime()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -934,7 +935,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
* @since 1.2
|
||||
*/
|
||||
public Date get2DigitYearStart() {
|
||||
return defaultCenturyStart;
|
||||
return (Date) defaultCenturyStart.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,6 +51,8 @@ import java.security.PrivilegedExceptionAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.text.DateFormat;
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import sun.util.BuddhistCalendar;
|
||||
import sun.util.calendar.ZoneInfo;
|
||||
import sun.util.resources.LocaleData;
|
||||
@ -837,7 +839,8 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
||||
* Cache to hold the firstDayOfWeek and minimalDaysInFirstWeek
|
||||
* of a Locale.
|
||||
*/
|
||||
private static Hashtable<Locale, int[]> cachedLocaleData = new Hashtable<Locale, int[]>(3);
|
||||
private static final ConcurrentMap<Locale, int[]> cachedLocaleData
|
||||
= new ConcurrentHashMap<Locale, int[]>(3);
|
||||
|
||||
// Special values of stamp[]
|
||||
/**
|
||||
@ -1022,7 +1025,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
||||
// returns a BuddhistCalendar instance.
|
||||
if ("th".equals(aLocale.getLanguage())
|
||||
&& ("TH".equals(aLocale.getCountry()))) {
|
||||
cal = new BuddhistCalendar(zone, aLocale);
|
||||
cal = new BuddhistCalendar(zone, aLocale);
|
||||
} else {
|
||||
cal = new GregorianCalendar(zone, aLocale);
|
||||
}
|
||||
@ -2588,7 +2591,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
||||
data = new int[2];
|
||||
data[0] = Integer.parseInt(bundle.getString("firstDayOfWeek"));
|
||||
data[1] = Integer.parseInt(bundle.getString("minimalDaysInFirstWeek"));
|
||||
cachedLocaleData.put(desiredLocale, data);
|
||||
cachedLocaleData.putIfAbsent(desiredLocale, data);
|
||||
}
|
||||
firstDayOfWeek = data[0];
|
||||
minimalDaysInFirstWeek = data[1];
|
||||
|
@ -160,11 +160,6 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
private static final int ONE_HOUR = 60*ONE_MINUTE;
|
||||
private static final int ONE_DAY = 24*ONE_HOUR;
|
||||
|
||||
/**
|
||||
* Cache to hold the SimpleDateFormat objects for a Locale.
|
||||
*/
|
||||
private static Hashtable cachedLocaleData = new Hashtable(3);
|
||||
|
||||
// Proclaim serialization compatibility with JDK 1.1
|
||||
static final long serialVersionUID = 3581463369166924961L;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user