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.lang.ref.SoftReference;
|
||||||
import java.text.spi.DateFormatSymbolsProvider;
|
import java.text.spi.DateFormatSymbolsProvider;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Hashtable;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.spi.LocaleServiceProvider;
|
import java.util.spi.LocaleServiceProvider;
|
||||||
import sun.util.LocaleServiceProviderPool;
|
import sun.util.LocaleServiceProviderPool;
|
||||||
import sun.util.TimeZoneNameUtility;
|
import sun.util.TimeZoneNameUtility;
|
||||||
@ -321,20 +322,64 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
|||||||
* @since 1.6
|
* @since 1.6
|
||||||
*/
|
*/
|
||||||
public static final DateFormatSymbols getInstance(Locale locale) {
|
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
|
// Check whether a provider can provide an implementation that's closer
|
||||||
// to the requested locale than what the Java runtime itself can provide.
|
// to the requested locale than what the Java runtime itself can provide.
|
||||||
LocaleServiceProviderPool pool =
|
LocaleServiceProviderPool pool =
|
||||||
LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
|
LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
|
||||||
if (pool.hasProviders()) {
|
if (pool.hasProviders()) {
|
||||||
DateFormatSymbols providersInstance = pool.getLocalizedObject(
|
providersInstance = pool.getLocalizedObject(
|
||||||
DateFormatSymbolsGetter.INSTANCE, locale);
|
DateFormatSymbolsGetter.INSTANCE, locale);
|
||||||
if (providersInstance != null) {
|
}
|
||||||
return providersInstance;
|
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 dfs;
|
||||||
return new DateFormatSymbols(locale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -597,56 +642,44 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
|||||||
static final int millisPerHour = 60*60*1000;
|
static final int millisPerHour = 60*60*1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache to hold the FormatData and TimeZoneNames ResourceBundles
|
* Cache to hold DateFormatSymbols instances per Locale.
|
||||||
* of a Locale.
|
|
||||||
*/
|
*/
|
||||||
private static Hashtable cachedLocaleData = new Hashtable(3);
|
private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances
|
||||||
|
= new ConcurrentHashMap<Locale, SoftReference<DateFormatSymbols>>(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 void initializeData(Locale desiredLocale) {
|
private void initializeData(Locale desiredLocale) {
|
||||||
int i;
|
locale = desiredLocale;
|
||||||
ResourceBundle resource = cacheLookup(desiredLocale);
|
|
||||||
|
|
||||||
// FIXME: cache only ResourceBundle. Hence every time, will do
|
// Copy values of a cached instance if any.
|
||||||
// getObject(). This won't be necessary if the Resource itself
|
SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
|
||||||
// is cached.
|
DateFormatSymbols dfs;
|
||||||
eras = (String[])resource.getObject("Eras");
|
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");
|
months = resource.getStringArray("MonthNames");
|
||||||
shortMonths = resource.getStringArray("MonthAbbreviations");
|
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");
|
ampms = resource.getStringArray("AmPmMarkers");
|
||||||
localPatternChars = resource.getString("DateTimePatternChars");
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.math.RoundingMode;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Currency;
|
import java.util.Currency;
|
||||||
import java.util.Hashtable;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.ResourceBundle;
|
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.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import sun.util.resources.LocaleData;
|
import sun.util.resources.LocaleData;
|
||||||
@ -394,14 +395,14 @@ public class DecimalFormat extends NumberFormat {
|
|||||||
public DecimalFormat() {
|
public DecimalFormat() {
|
||||||
Locale def = Locale.getDefault(Locale.Category.FORMAT);
|
Locale def = Locale.getDefault(Locale.Category.FORMAT);
|
||||||
// try to get the pattern from the cache
|
// try to get the pattern from the cache
|
||||||
String pattern = (String) cachedLocaleData.get(def);
|
String pattern = cachedLocaleData.get(def);
|
||||||
if (pattern == null) { /* cache miss */
|
if (pattern == null) { /* cache miss */
|
||||||
// Get the pattern for the default locale.
|
// Get the pattern for the default locale.
|
||||||
ResourceBundle rb = LocaleData.getNumberFormatData(def);
|
ResourceBundle rb = LocaleData.getNumberFormatData(def);
|
||||||
String[] all = rb.getStringArray("NumberPatterns");
|
String[] all = rb.getStringArray("NumberPatterns");
|
||||||
pattern = all[0];
|
pattern = all[0];
|
||||||
/* update cache */
|
/* update cache */
|
||||||
cachedLocaleData.put(def, pattern);
|
cachedLocaleData.putIfAbsent(def, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always applyPattern after the symbols are set
|
// Always applyPattern after the symbols are set
|
||||||
@ -3272,5 +3273,6 @@ public class DecimalFormat extends NumberFormat {
|
|||||||
/**
|
/**
|
||||||
* Cache to hold the NumberPattern of a Locale.
|
* 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.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.Hashtable;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.MissingResourceException;
|
import java.util.MissingResourceException;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.SimpleTimeZone;
|
import java.util.SimpleTimeZone;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import sun.util.calendar.CalendarUtils;
|
import sun.util.calendar.CalendarUtils;
|
||||||
import sun.util.calendar.ZoneInfoFile;
|
import sun.util.calendar.ZoneInfoFile;
|
||||||
import sun.util.resources.LocaleData;
|
import sun.util.resources.LocaleData;
|
||||||
@ -503,14 +504,14 @@ public class SimpleDateFormat extends DateFormat {
|
|||||||
/**
|
/**
|
||||||
* Cache to hold the DateTimePatterns of a Locale.
|
* Cache to hold the DateTimePatterns of a Locale.
|
||||||
*/
|
*/
|
||||||
private static Hashtable<String,String[]> cachedLocaleData
|
private static final ConcurrentMap<String, String[]> cachedLocaleData
|
||||||
= new Hashtable<String,String[]>(3);
|
= new ConcurrentHashMap<String, String[]>(3);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache NumberFormat instances with Locale key.
|
* Cache NumberFormat instances with Locale key.
|
||||||
*/
|
*/
|
||||||
private static Hashtable<Locale,NumberFormat> cachedNumberFormatData
|
private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData
|
||||||
= new Hashtable<Locale,NumberFormat>(3);
|
= new ConcurrentHashMap<Locale, NumberFormat>(3);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Locale used to instantiate this
|
* The Locale used to instantiate this
|
||||||
@ -579,7 +580,7 @@ public class SimpleDateFormat extends DateFormat {
|
|||||||
|
|
||||||
initializeCalendar(locale);
|
initializeCalendar(locale);
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
this.formatData = DateFormatSymbols.getInstance(locale);
|
this.formatData = DateFormatSymbols.getInstanceRef(locale);
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
initialize(locale);
|
initialize(locale);
|
||||||
}
|
}
|
||||||
@ -632,9 +633,9 @@ public class SimpleDateFormat extends DateFormat {
|
|||||||
dateTimePatterns = r.getStringArray("DateTimePatterns");
|
dateTimePatterns = r.getStringArray("DateTimePatterns");
|
||||||
}
|
}
|
||||||
/* update cache */
|
/* update cache */
|
||||||
cachedLocaleData.put(key, dateTimePatterns);
|
cachedLocaleData.putIfAbsent(key, dateTimePatterns);
|
||||||
}
|
}
|
||||||
formatData = DateFormatSymbols.getInstance(loc);
|
formatData = DateFormatSymbols.getInstanceRef(loc);
|
||||||
if ((timeStyle >= 0) && (dateStyle >= 0)) {
|
if ((timeStyle >= 0) && (dateStyle >= 0)) {
|
||||||
Object[] dateTimeArgs = {dateTimePatterns[timeStyle],
|
Object[] dateTimeArgs = {dateTimePatterns[timeStyle],
|
||||||
dateTimePatterns[dateStyle + 4]};
|
dateTimePatterns[dateStyle + 4]};
|
||||||
@ -665,7 +666,7 @@ public class SimpleDateFormat extends DateFormat {
|
|||||||
numberFormat.setGroupingUsed(false);
|
numberFormat.setGroupingUsed(false);
|
||||||
|
|
||||||
/* update cache */
|
/* update cache */
|
||||||
cachedNumberFormatData.put(loc, numberFormat);
|
cachedNumberFormatData.putIfAbsent(loc, numberFormat);
|
||||||
}
|
}
|
||||||
numberFormat = (NumberFormat) numberFormat.clone();
|
numberFormat = (NumberFormat) numberFormat.clone();
|
||||||
|
|
||||||
@ -897,7 +898,7 @@ public class SimpleDateFormat extends DateFormat {
|
|||||||
* so we can call it from readObject().
|
* so we can call it from readObject().
|
||||||
*/
|
*/
|
||||||
private void initializeDefaultCentury() {
|
private void initializeDefaultCentury() {
|
||||||
calendar.setTime( new Date() );
|
calendar.setTimeInMillis(System.currentTimeMillis());
|
||||||
calendar.add( Calendar.YEAR, -80 );
|
calendar.add( Calendar.YEAR, -80 );
|
||||||
parseAmbiguousDatesAsAfter(calendar.getTime());
|
parseAmbiguousDatesAsAfter(calendar.getTime());
|
||||||
}
|
}
|
||||||
@ -921,7 +922,7 @@ public class SimpleDateFormat extends DateFormat {
|
|||||||
* @since 1.2
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public void set2DigitYearStart(Date startDate) {
|
public void set2DigitYearStart(Date startDate) {
|
||||||
parseAmbiguousDatesAsAfter(startDate);
|
parseAmbiguousDatesAsAfter(new Date(startDate.getTime()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -934,7 +935,7 @@ public class SimpleDateFormat extends DateFormat {
|
|||||||
* @since 1.2
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public Date get2DigitYearStart() {
|
public Date get2DigitYearStart() {
|
||||||
return defaultCenturyStart;
|
return (Date) defaultCenturyStart.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,6 +51,8 @@ import java.security.PrivilegedExceptionAction;
|
|||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.DateFormatSymbols;
|
import java.text.DateFormatSymbols;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import sun.util.BuddhistCalendar;
|
import sun.util.BuddhistCalendar;
|
||||||
import sun.util.calendar.ZoneInfo;
|
import sun.util.calendar.ZoneInfo;
|
||||||
import sun.util.resources.LocaleData;
|
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
|
* Cache to hold the firstDayOfWeek and minimalDaysInFirstWeek
|
||||||
* of a Locale.
|
* 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[]
|
// Special values of stamp[]
|
||||||
/**
|
/**
|
||||||
@ -1022,7 +1025,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||||||
// returns a BuddhistCalendar instance.
|
// returns a BuddhistCalendar instance.
|
||||||
if ("th".equals(aLocale.getLanguage())
|
if ("th".equals(aLocale.getLanguage())
|
||||||
&& ("TH".equals(aLocale.getCountry()))) {
|
&& ("TH".equals(aLocale.getCountry()))) {
|
||||||
cal = new BuddhistCalendar(zone, aLocale);
|
cal = new BuddhistCalendar(zone, aLocale);
|
||||||
} else {
|
} else {
|
||||||
cal = new GregorianCalendar(zone, aLocale);
|
cal = new GregorianCalendar(zone, aLocale);
|
||||||
}
|
}
|
||||||
@ -2588,7 +2591,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
|||||||
data = new int[2];
|
data = new int[2];
|
||||||
data[0] = Integer.parseInt(bundle.getString("firstDayOfWeek"));
|
data[0] = Integer.parseInt(bundle.getString("firstDayOfWeek"));
|
||||||
data[1] = Integer.parseInt(bundle.getString("minimalDaysInFirstWeek"));
|
data[1] = Integer.parseInt(bundle.getString("minimalDaysInFirstWeek"));
|
||||||
cachedLocaleData.put(desiredLocale, data);
|
cachedLocaleData.putIfAbsent(desiredLocale, data);
|
||||||
}
|
}
|
||||||
firstDayOfWeek = data[0];
|
firstDayOfWeek = data[0];
|
||||||
minimalDaysInFirstWeek = data[1];
|
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_HOUR = 60*ONE_MINUTE;
|
||||||
private static final int ONE_DAY = 24*ONE_HOUR;
|
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
|
// Proclaim serialization compatibility with JDK 1.1
|
||||||
static final long serialVersionUID = 3581463369166924961L;
|
static final long serialVersionUID = 3581463369166924961L;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user