ICU-5402 Improve DateFormat performance

X-SVN-Rev: 20983
This commit is contained in:
Yoshito Umaoka 2007-01-26 00:22:11 +00:00
parent 0baac6e262
commit 1eb0713f37
16 changed files with 1164 additions and 765 deletions

4
.gitattributes vendored
View file

@ -136,8 +136,12 @@ icu4j/src/com/ibm/icu/dev/tool/docs/icu4j34.api.gz -text
icu4j/src/com/ibm/icu/dev/tool/docs/icu4j341.api.gz -text
icu4j/src/com/ibm/icu/dev/tool/docs/icu4j342.api.gz -text
icu4j/src/com/ibm/icu/dev/tool/docs/icu4j343.api.gz -text
icu4j/src/com/ibm/icu/impl/DateNumberFormat.java -text
icu4j/src/com/ibm/icu/impl/ICUCache.java -text
icu4j/src/com/ibm/icu/impl/SimpleCache.java -text
icu4j/src/com/ibm/icu/impl/data/icudata.jar -text
icu4j/src/com/ibm/icu/impl/data/th.brk -text
icu4j/src/com/ibm/icu/util/CalendarServiceShim.java -text
icu4j/src/com/ibm/icu/util/UResourceBundleIterator.java -text
icu4j/src/com/ibm/richtext/textapps/resources/unicode.arabic.red -text
icu4j/src/com/ibm/richtext/textapps/resources/unicode.hebrew.red -text

View file

@ -1,7 +1,7 @@
//##header
/*
*******************************************************************************
* Copyright (C) 1996-2006, International Business Machines Corporation and *
* Copyright (C) 1996-2007, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
@ -10,9 +10,10 @@
package com.ibm.icu.dev.test.serializable;
import java.util.Date;
import java.util.Locale;
import java.util.HashMap;
import java.util.Locale;
import com.ibm.icu.impl.DateNumberFormat;
import com.ibm.icu.text.ChineseDateFormat;
import com.ibm.icu.text.ChineseDateFormatSymbols;
import com.ibm.icu.text.DateFormat;
@ -1391,6 +1392,24 @@ public class FormatTests
}
//#endif
public static class DateNumberFormatHandler implements SerializableTest.Handler
{
public Object[] getTestObjects()
{
Locale locales[] = SerializableTest.getLocales();
DateNumberFormat[] dnfmts = new DateNumberFormat[locales.length];
for (int i = 0; i < locales.length; i++) {
ULocale uloc = ULocale.forLocale(locales[i]);
dnfmts[i] = new DateNumberFormat(uloc);
}
return dnfmts;
}
public boolean hasSameBehavior(Object a, Object b) {
return a.equals(b);
}
}
public static void main(String[] args)
{
// nothing needed...

View file

@ -1,7 +1,7 @@
//##header
/*
*******************************************************************************
* Copyright (C) 1996-2006, International Business Machines Corporation and *
* Copyright (C) 1996-2007, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
@ -436,6 +436,7 @@ public class SerializableTest extends TestFmwk.TestGroup
map.put("com.ibm.icu.text.SimpleDateFormat", new FormatTests.SimpleDateFormatHandler());
map.put("com.ibm.icu.text.ChineseDateFormat", new FormatTests.ChineseDateFormatHandler());
map.put("com.ibm.icu.text.ChineseDateFormatSymbols", new FormatTests.ChineseDateFormatSymbolsHandler());
map.put("com.ibm.icu.impl.DateNumberFormat", new FormatTests.DateNumberFormatHandler());
map.put("com.ibm.icu.util.Calendar", new CalendarTests.CalendarHandler());
map.put("com.ibm.icu.util.BuddhistCalendar", new CalendarTests.BuddhistCalendarHandler());

View file

@ -1,3 +1,4 @@
//##header
/*
* *****************************************************************************
* Copyright (C) 2006, International Business Machines Corporation and others.

View file

@ -0,0 +1,213 @@
//##header
/*
*******************************************************************************
* Copyright (C) 2007, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.math.BigInteger;
import java.text.FieldPosition;
import java.text.ParsePosition;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
/*
* NumberFormat implementation dedicated/optimized for DateFormat,
* used by SimpleDateFormat implementation.
*/
public final class DateNumberFormat extends NumberFormat {
private static final long serialVersionUID = -6315692826916346953L;
private char zeroDigit;
private char minusSign;
private boolean positiveOnly = false;
private transient char[] decimalBuf = new char[20]; // 20 digits is good enough to store Long.MAX_VALUE
private static SimpleCache CACHE = new SimpleCache();
private int maxIntDigits;
private int minIntDigits;
public DateNumberFormat(ULocale loc) {
initialize(loc);
}
public DateNumberFormat(char zeroDigit, char minusSign) {
this.zeroDigit = zeroDigit;
this.minusSign = minusSign;
}
private void initialize(ULocale loc) {
char[] elems = (char[])CACHE.get(loc);
if (elems == null) {
// Missed cache
ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, loc);
String[] numberElements = rb.getStringArray("NumberElements");
elems = new char[2];
elems[0] = numberElements[4].charAt(0);
elems[1] = numberElements[6].charAt(0);
CACHE.put(loc, elems);
}
zeroDigit = elems[0];
minusSign = elems[1];
}
public void setMaximumIntegerDigits(int newValue) {
maxIntDigits = newValue;
}
public int getMaximumIntegerDigits() {
return maxIntDigits;
}
public void setMinimumIntegerDigits(int newValue) {
minIntDigits = newValue;
}
public int getMinimumIntegerDigits() {
return minIntDigits;
}
/* For supporting SimpleDateFormat.parseInt */
public void setParsePositiveOnly(boolean isPositiveOnly) {
positiveOnly = isPositiveOnly;
}
public char getZeroDigit() {
return zeroDigit;
}
public StringBuffer format(double number, StringBuffer toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(double, StringBuffer, FieldPostion) is not implemented");
}
public StringBuffer format(long numberL, StringBuffer toAppendTo,
FieldPosition pos) {
if (numberL < 0) {
// negative
toAppendTo.append(minusSign);
}
// Note: NumberFormat used by DateFormat only uses int numbers.
// Remainder operation on 32bit platform using long is significantly slower
// than int. So, this method casts long number into int.
int number = (int)numberL;
int limit = decimalBuf.length < maxIntDigits ? decimalBuf.length : maxIntDigits;
int index = limit - 1;
while (true) {
decimalBuf[index] = (char)((number % 10) + zeroDigit);
number /= 10;
if (index == 0 || number == 0) {
break;
}
index--;
}
int padding = minIntDigits - (limit - index);
for (; padding > 0; padding--) {
decimalBuf[--index] = zeroDigit;
}
int length = limit - index;
toAppendTo.append(decimalBuf, index, length);
pos.setBeginIndex(0);
if (pos.getField() == NumberFormat.INTEGER_FIELD) {
pos.setEndIndex(length);
} else {
pos.setEndIndex(0);
}
return toAppendTo;
}
public StringBuffer format(BigInteger number, StringBuffer toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(BigInteger, StringBuffer, FieldPostion) is not implemented");
}
//#ifndef FOUNDATION
public StringBuffer format(java.math.BigDecimal number, StringBuffer toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(BigDecimal, StringBuffer, FieldPostion) is not implemented");
}
//#endif
public StringBuffer format(BigDecimal number,
StringBuffer toAppendTo, FieldPosition pos) {
throw new UnsupportedOperationException("StringBuffer format(BigDecimal, StringBuffer, FieldPostion) is not implemented");
}
/*
* Note: This method only parse integer numbers which can be represented by long
*/
public Number parse(String text, ParsePosition parsePosition) {
long num = 0;
boolean sawNumber = false;
boolean negative = false;
int base = parsePosition.getIndex();
int offset = 0;
for (; base + offset < text.length(); offset++) {
char ch = text.charAt(base + offset);
if (offset == 0 && ch == minusSign) {
if (positiveOnly) {
break;
}
negative = true;
} else {
int digit = ch - zeroDigit;
if (digit < 0 || 9 < digit) {
digit = UCharacter.digit(ch);
}
if (0 <= digit && digit <= 9) {
sawNumber = true;
num = num * 10 + digit;
} else {
break;
}
}
}
Number result = null;
if (sawNumber) {
num = negative ? num * (-1) : num;
result = new Long(num);
parsePosition.setIndex(base + offset);
}
return result;
}
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!super.equals(obj)) {
return false;
}
DateNumberFormat other = (DateNumberFormat)obj;
if (this.maxIntDigits == other.maxIntDigits
&& this.minIntDigits == other.minIntDigits
&& this.zeroDigit == other.zeroDigit
&& this.minusSign == other.minusSign
&& this.positiveOnly == other.positiveOnly) {
return true;
}
return false;
}
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
// re-allocate the work buffer
decimalBuf = new char[20];
}
}
//eof

View file

@ -0,0 +1,8 @@
package com.ibm.icu.impl;
public interface ICUCache {
public static final Object NULL = new Object();
public void clear();
public void put(Object key, Object value);
public Object get(Object key);
}

View file

@ -265,13 +265,16 @@ public class ICULocaleService extends ICUService {
public String currentDescriptor() {
String result = currentID();
if (result != null) {
result = "/" + result;
if (varstart != -1) {
result += primaryID.substring(varstart);
}
StringBuffer buf = new StringBuffer(result.length() + 32);
if (kind != KIND_ANY) {
result = prefix() + result;
buf.append(prefix());
}
buf.append('/');
buf.append(result);
if (varstart != -1) {
buf.append(primaryID.substring(varstart, primaryID.length()));
}
result = buf.toString();
}
return result;
}

View file

@ -0,0 +1,40 @@
package com.ibm.icu.impl;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class SimpleCache implements ICUCache {
public Object get(Object key) {
SoftReference ref = cacheRef;
if (ref != null) {
Map map = (Map)ref.get();
if (map != null) {
return map.get(key);
}
}
return null;
}
public void put(Object key, Object value) {
SoftReference ref = cacheRef;
Map map = null;
if (ref != null) {
map = (Map)ref.get();
}
if (map == null) {
map = Collections.synchronizedMap(new HashMap());
ref = new SoftReference(map);
cacheRef = ref;
}
map.put(key, value);
}
public void clear() {
cacheRef = null;
}
private SoftReference cacheRef = null;
}

View file

@ -51,8 +51,7 @@ public class ChineseDateFormat extends SimpleDateFormat {
* @stable ICU 2.0
*/
public ChineseDateFormat(String pattern, Locale locale) {
// TODO: convert to use ULocale
super(pattern, new ChineseDateFormatSymbols(locale), true);
this(pattern, ULocale.forLocale(locale));
}
/**
@ -63,9 +62,8 @@ public class ChineseDateFormat extends SimpleDateFormat {
* @provisional This API might change or be removed in a future release.
*/
public ChineseDateFormat(String pattern, ULocale locale) {
// TODO: convert CDFS to use ULocale
//super(pattern, new ChineseDateFormatSymbols(locale.toLocale()), true);
super(pattern, new ChineseDateFormatSymbols(locale), locale);
super(pattern, new ChineseDateFormatSymbols(locale),
new ChineseCalendar(TimeZone.getDefault(), locale), locale, true);
}
// NOTE: This API still exists; we just inherit it from SimpleDateFormat

View file

@ -92,4 +92,11 @@ public class ChineseDateFormatSymbols extends DateFormatSymbols {
super.initializeData(loc, calData);
isLeapMonth = calData.getStringArray("isLeapMonth");
}
void initializeData(DateFormatSymbols dfs) {
super.initializeData(dfs);
if (dfs instanceof ChineseDateFormatSymbols) {
this.isLeapMonth = ((ChineseDateFormatSymbols)dfs).isLeapMonth;
}
}
}

View file

@ -7,18 +7,6 @@
package com.ibm.icu.text;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.CalendarData;
import com.ibm.icu.impl.TextTrieMap;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.TimeZone;
import com.ibm.icu.util.UResourceBundle;
import com.ibm.icu.impl.ZoneMeta;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.impl.SoftCache;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
@ -27,6 +15,19 @@ import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import com.ibm.icu.impl.CalendarData;
import com.ibm.icu.impl.ICUCache;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.SimpleCache;
import com.ibm.icu.impl.TextTrieMap;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.impl.ZoneMeta;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.TimeZone;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
/**
* <code>DateFormatSymbols</code> is a public class for encapsulating
* localizable date-time formatting data, such as the names of the
@ -825,7 +826,6 @@ public class DateFormatSymbols implements Serializable, Cloneable {
{
try {
DateFormatSymbols other = (DateFormatSymbols)super.clone();
copyMembers(this, other);
return other;
} catch (CloneNotSupportedException e) {
///CLOVER:OFF
@ -896,6 +896,10 @@ public class DateFormatSymbols implements Serializable, Cloneable {
* Useful constant for defining timezone offsets.
*/
static final int millisPerHour = 60*60*1000;
// DateFormatSymbols cache
private static ICUCache DFSCACHE = new SimpleCache();
/**
*
* @param desiredLocale
@ -904,10 +908,54 @@ public class DateFormatSymbols implements Serializable, Cloneable {
*/
protected void initializeData(ULocale desiredLocale, String type)
{
CalendarData calData = new CalendarData(desiredLocale, type);
initializeData(desiredLocale, calData);
String key = desiredLocale.toString() + "+" + type;
DateFormatSymbols dfs = (DateFormatSymbols)DFSCACHE.get(key);
if (dfs == null) {
// Initialize data from scratch put a clone of this instance into the cache
CalendarData calData = new CalendarData(desiredLocale, type);
initializeData(desiredLocale, calData);
dfs = (DateFormatSymbols)this.clone();
DFSCACHE.put(key, dfs);
} else {
initializeData(dfs);
}
}
/*
* Initialize format symbols using another instance.
*
* TODO Clean up initialization methods for subclasses
*/
void initializeData(DateFormatSymbols dfs) {
this.eras = dfs.eras;
this.eraNames = dfs.eraNames;
this.narrowEras = dfs.narrowEras;
this.months = dfs.months;
this.shortMonths = dfs.shortMonths;
this.narrowMonths = dfs.narrowMonths;
this.standaloneMonths = dfs.standaloneMonths;
this.standaloneShortMonths = dfs.standaloneShortMonths;
this.standaloneNarrowMonths = dfs.standaloneNarrowMonths;
this.weekdays = dfs.weekdays;
this.shortWeekdays = dfs.shortWeekdays;
this.narrowWeekdays = dfs.narrowWeekdays;
this.standaloneWeekdays = dfs.standaloneWeekdays;
this.standaloneShortWeekdays = dfs.standaloneShortWeekdays;
this.standaloneNarrowWeekdays = dfs.standaloneNarrowWeekdays;
this.ampms = dfs.ampms;
this.shortQuarters = dfs.shortQuarters;
this.quarters = dfs.quarters;
this.standaloneShortQuarters = dfs.standaloneShortQuarters;
this.standaloneQuarters = dfs.standaloneQuarters;
this.zoneStrings = dfs.zoneStrings; // always null at initialization time for now
this.localPatternChars = dfs.localPatternChars;
this.actualLocale = dfs.actualLocale;
this.validLocale = dfs.validLocale;
this.requestedLocale = dfs.requestedLocale;
}
/**
*
* @param desiredLocale
@ -1242,7 +1290,7 @@ public class DateFormatSymbols implements Serializable, Cloneable {
/**
* A cache for ZoneItemInfo objects, shared by class instances.
*/
private static SoftCache zoneItemInfoCache = new SoftCache();
private static ICUCache zoneItemInfoCache = new SimpleCache();
/**
* A ZoneItemInfo instance which holds custom timezone strings
@ -1520,36 +1568,6 @@ public class DateFormatSymbols implements Serializable, Cloneable {
return aCopy;
}
/**
* Clones all the data members from the source DateFormatSymbols to
* the target DateFormatSymbols. This is only for subclasses.
* @param src the source DateFormatSymbols.
* @param dst the target DateFormatSymbols.
*/
private final void copyMembers(DateFormatSymbols src, DateFormatSymbols dst)
{
dst.eras = duplicate(src.eras);
dst.eraNames = duplicate(src.eraNames);
dst.months = duplicate(src.months);
dst.shortMonths = duplicate(src.shortMonths);
dst.narrowMonths = duplicate(src.narrowMonths);
dst.standaloneMonths = duplicate(src.standaloneMonths);
dst.standaloneShortMonths = duplicate(src.standaloneShortMonths);
dst.standaloneNarrowMonths = duplicate(src.standaloneNarrowMonths);
dst.weekdays = duplicate(src.weekdays);
dst.shortWeekdays = duplicate(src.shortWeekdays);
dst.narrowWeekdays = duplicate(src.narrowWeekdays);
dst.standaloneWeekdays = duplicate(src.standaloneWeekdays);
dst.standaloneShortWeekdays = duplicate(src.standaloneShortWeekdays);
dst.standaloneNarrowWeekdays = duplicate(src.standaloneNarrowWeekdays);
dst.ampms = duplicate(src.ampms);
if (src.zoneStrings != null) {
dst.zoneStrings = duplicate(src.zoneStrings);
}
dst.requestedLocale = new ULocale(src.requestedLocale.toString());
dst.localPatternChars = new String (src.localPatternChars);
}
/**
* Compares the equality of the two arrays of String.
* @param current this String array.

File diff suppressed because it is too large Load diff

View file

@ -5,38 +5,25 @@
package com.ibm.icu.util;
import com.ibm.icu.impl.ICULocaleService;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.ICUService.Factory;
import com.ibm.icu.impl.CalendarData;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DateFormatSymbols;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.TimeZone;
import com.ibm.icu.util.BuddhistCalendar;
import com.ibm.icu.util.ChineseCalendar;
import com.ibm.icu.util.CopticCalendar;
import com.ibm.icu.util.EthiopicCalendar;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.HebrewCalendar;
import com.ibm.icu.util.IslamicCalendar;
import com.ibm.icu.util.JapaneseCalendar;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Hashtable;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import com.ibm.icu.impl.CalendarData;
import com.ibm.icu.impl.ICUCache;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.SimpleCache;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DateFormatSymbols;
import com.ibm.icu.text.MessageFormat;
import com.ibm.icu.text.SimpleDateFormat;
/**
* <code>Calendar</code> is an abstract base class for converting between
* a <code>Date</code> object and a set of integer fields such as
@ -1575,7 +1562,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
*/
public static synchronized Calendar getInstance()
{
return getInstance(TimeZone.getDefault(), ULocale.getDefault(), null);
return getInstanceInternal(null, null);
}
/**
@ -1586,7 +1573,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
*/
public static synchronized Calendar getInstance(TimeZone zone)
{
return getInstance(zone, ULocale.getDefault(), null);
return getInstanceInternal(zone, null);
}
/**
@ -1597,7 +1584,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
*/
public static synchronized Calendar getInstance(Locale aLocale)
{
return getInstance(TimeZone.getDefault(), ULocale.forLocale(aLocale), null);
return getInstanceInternal(null, ULocale.forLocale(aLocale));
}
/**
@ -1609,7 +1596,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
*/
public static synchronized Calendar getInstance(ULocale locale)
{
return getInstance(TimeZone.getDefault(), locale, null);
return getInstanceInternal(null, locale);
}
/**
@ -1621,7 +1608,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
*/
public static synchronized Calendar getInstance(TimeZone zone,
Locale aLocale) {
return getInstance(zone, ULocale.forLocale(aLocale), null);
return getInstanceInternal(zone, ULocale.forLocale(aLocale));
}
/**
@ -1634,69 +1621,26 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
*/
public static synchronized Calendar getInstance(TimeZone zone,
ULocale locale) {
return getInstance(zone, locale, null);
return getInstanceInternal(zone, locale);
}
// ==== Factory Stuff ====
///CLOVER:OFF
/**
* Return a calendar of for the TimeZone and locale. If factoryName is
* not null, looks in the collection of CalendarFactories for a match
* and uses that factory to instantiate the calendar. Otherwise, it
* uses the default factory that has been registered for the locale.
* @prototype
*/
/* public */ static synchronized Calendar getInstance(TimeZone zone,
ULocale locale,
String factoryName)
{
CalendarFactory factory = null;
if (factoryName != null) {
factory = (CalendarFactory)getFactoryMap().get(factoryName);
/*
* All getInstance implementations call this private method to create a new
* Calendar instance.
*/
private static Calendar getInstanceInternal(TimeZone tz, ULocale locale) {
if (locale == null) {
locale = ULocale.getDefault();
}
ULocale[] actualReturn = new ULocale[1];
if (factory == null && service != null) {
factory = (CalendarFactory)service.get(locale, actualReturn);
}
if (factory == null) {
int calType = getCalendarType(locale);
switch (calType) {
case BUDDHIST:
return new BuddhistCalendar(zone, locale);
case CHINESE:
return new ChineseCalendar(zone, locale);
case COPTIC:
return new CopticCalendar(zone, locale);
case ETHIOPIC:
return new EthiopicCalendar(zone, locale);
case GREGORIAN:
return new GregorianCalendar(zone, locale);
case HEBREW:
return new HebrewCalendar(zone, locale);
case ISLAMIC:
case ISLAMIC_CIVIL: {
IslamicCalendar result = new IslamicCalendar(zone, locale);
result.setCivil(calType == ISLAMIC_CIVIL);
return result;
}
case JAPANESE:
return new JapaneseCalendar(zone, locale);
default:
throw new IllegalStateException();
}
} else {
Calendar result = factory.create(zone, locale);
// TODO: get the actual/valid locale properly
ULocale uloc = actualReturn[0];
result.setLocale(uloc, uloc);
return result;
if (tz == null) {
tz = TimeZone.getDefault();
}
Calendar cal = getShim().createInstance(locale);
cal.setTimeZone(tz);
cal.setTimeInMillis(System.currentTimeMillis());
return cal;
}
private static final int BUDDHIST = 0;
private static final int CHINESE = 1;
private static final int COPTIC = 2;
@ -1734,7 +1678,6 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
return GREGORIAN;
}
///CLOVER:ON
/**
* Gets the list of locales for which Calendars are installed.
* @return the list of locales for which Calendars are installed.
@ -1742,9 +1685,10 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
*/
public static Locale[] getAvailableLocales()
{
return service == null
? ICUResourceBundle.getAvailableLocales(ICUResourceBundle.ICU_BASE_NAME)
: service.getAvailableLocales();
if (shim == null) {
return ICUResourceBundle.getAvailableLocales(ICUResourceBundle.ICU_BASE_NAME);
}
return getShim().getAvailableLocales();
}
/**
@ -1755,75 +1699,103 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
*/
public static ULocale[] getAvailableULocales()
{
return service == null
? ICUResourceBundle.getAvailableULocales(ICUResourceBundle.ICU_BASE_NAME)
: service.getAvailableULocales();
if (shim == null) {
return ICUResourceBundle.getAvailableULocales(ICUResourceBundle.ICU_BASE_NAME);
}
return getShim().getAvailableULocales();
}
// ==== Factory Stuff ====
/**
* A CalendarFactory is used to register new calendar implementation.
* The factory should be able to create a calendar instance for the
* specified locale.
*
* @prototype
*/
/* public */ static abstract class CalendarFactory {
public boolean visible() {
return true;
}
public abstract Set getSupportedLocaleNames();
public Calendar createCalendar(ULocale loc) {
return null;
}
protected CalendarFactory() {
}
}
// shim so we can build without service code
static abstract class CalendarShim {
abstract Locale[] getAvailableLocales();
abstract ULocale[] getAvailableULocales();
abstract Object registerFactory(CalendarFactory factory);
abstract boolean unregister(Object k);
abstract Calendar createInstance(ULocale l);
}
private static CalendarShim shim;
private static CalendarShim getShim() {
if (shim == null) {
try {
Class cls = Class.forName("com.ibm.icu.util.CalendarServiceShim");
shim = (CalendarShim)cls.newInstance();
}
catch (MissingResourceException e) {
throw e;
}
catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
return shim;
}
static Calendar createInstance(ULocale locale) {
int calType = getCalendarType(locale);
TimeZone zone = TimeZone.getDefault();
switch (calType) {
case BUDDHIST:
return new BuddhistCalendar(zone, locale);
case CHINESE:
return new ChineseCalendar(zone, locale);
case COPTIC:
return new CopticCalendar(zone, locale);
case ETHIOPIC:
return new EthiopicCalendar(zone, locale);
case GREGORIAN:
return new GregorianCalendar(zone, locale);
case HEBREW:
return new HebrewCalendar(zone, locale);
case ISLAMIC:
case ISLAMIC_CIVIL: {
IslamicCalendar result = new IslamicCalendar(zone, locale);
result.setCivil(calType == ISLAMIC_CIVIL);
return result;
}
case JAPANESE:
return new JapaneseCalendar(zone, locale);
default:
throw new IllegalStateException();
}
}
///CLOVER:OFF
private static Map factoryMap;
private static Map getFactoryMap() {
if (factoryMap == null) {
Map m = new HashMap(5);
/*
addFactory(m, BuddhistCalendar.factory());
addFactory(m, ChineseCalendar.factory());
addFactory(m, GregorianCalendar.factory());
addFactory(m, HebrewCalendar.factory());
addFactory(m, IslamicCalendar.factory());
addFactory(m, JapaneseCalendar.factory());
*/
factoryMap = m;
}
return factoryMap;
}
// Never used -- why is this here? Alan 2003-05
// private static void addFactory(Map m, CalendarFactory f) {
// m.put(f.factoryName(), f);
// }
/**
* Return a set of all the registered calendar factory names.
* @prototype
*/
/* public */ static Set getCalendarFactoryNames() {
return Collections.unmodifiableSet(getFactoryMap().keySet());
}
/**
* Register a new CalendarFactory. getInstance(TimeZone, ULocale, String) will
* try to locate a registered factories matching the factoryName. Only registered
* factories will be found.
* @prototype
*/
private static void registerFactory(CalendarFactory factory) {
/* public */ static Object registerFactory(CalendarFactory factory) {
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
throw new IllegalArgumentException("factory must not be null");
}
getFactoryMap().put(factory.factoryName(), factory);
}
/**
* Convenience override of register(CalendarFactory, ULocale, boolean);
* @prototype
*/
/* public */ static Object register(CalendarFactory factory, ULocale locale) {
return register(factory, locale, true);
}
/**
* Registers a default CalendarFactory for the provided locale.
* If the factory has not already been registered with
* registerFactory, it will be.
* @prototype
*/
/* public */ static Object register(CalendarFactory factory, ULocale locale, boolean visible) {
if (factory == null) {
throw new IllegalArgumentException("calendar must not be null");
}
registerFactory(factory);
return getService().registerObject(factory, locale, visible);
return getShim().registerFactory(factory);
}
/**
@ -1832,20 +1804,17 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
* @prototype
*/
/* public */ static boolean unregister(Object registryKey) {
return service == null
? false
: service.unregisterFactory((Factory)registryKey);
if (registryKey == null) {
throw new IllegalArgumentException("registryKey must not be null");
}
if (shim == null) {
return false;
}
return shim.unregister(registryKey);
}
private static ICULocaleService service = null;
private static ICULocaleService getService() {
synchronized (Calendar.class) {
if (service == null) {
service = new ICULocaleService("Calendar");
}
}
return service;
}
///CLOVER:ON
// ==== End of factory Stuff ====
@ -3047,51 +3016,96 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable {
* @provisional This API might change or be removed in a future release.
*/
protected DateFormat handleGetDateFormat(String pattern, ULocale locale) {
DateFormatSymbols symbols = new DateFormatSymbols(this, locale);
return new SimpleDateFormat(pattern, symbols, locale);
FormatConfiguration fmtConfig = new FormatConfiguration();
fmtConfig.pattern = pattern;
fmtConfig.formatData = new DateFormatSymbols(this, locale);
fmtConfig.loc = locale;
fmtConfig.cal = this;
return SimpleDateFormat.getInstance(fmtConfig);
}
static private DateFormat formatHelper(Calendar cal, ULocale loc,
int dateStyle, int timeStyle)
{
// See if there are any custom resources for this calendar
// If not, just use the default DateFormat
DateFormat result = null;
// date format pattern cache
private static final ICUCache PATTERN_CACHE = new SimpleCache();
// final fallback patterns
private static final String[] DEFAULT_PATTERNS = {
"HH:mm:ss z",
"HH:mm:ss z",
"HH:mm:ss",
"HH:mm",
"EEEE, yyyy MMMM dd",
"yyyy MMMM d",
"yyyy MMM d",
"yy/MM/dd",
"{1} {0}"
};
static private DateFormat formatHelper(Calendar cal, ULocale loc, int dateStyle, int timeStyle) {
// First, try to get a pattern from PATTERN_CACHE
String key = loc.toString() + cal.getType();
String[] patterns = (String[])PATTERN_CACHE.get(key);
if (patterns == null) {
// Cache missed. Get one from bundle
try {
CalendarData calData = new CalendarData(loc, cal.getType());
String[] patterns = calData.get("DateTimePatterns").getStringArray();
String pattern = null;
if ((timeStyle >= 0) && (dateStyle >= 0)) {
Object[] dateTimeArgs = { patterns[timeStyle],
patterns[dateStyle + 4] };
pattern = MessageFormat.format(patterns[8], dateTimeArgs);
}
else if (timeStyle >= 0) {
pattern = patterns[timeStyle];
}
else if (dateStyle >= 0) {
pattern = patterns[dateStyle + 4];
}
else {
throw new IllegalArgumentException("No date or time style specified");
}
result = cal.handleGetDateFormat(pattern, loc);
patterns = calData.get("DateTimePatterns").getStringArray();
} catch (MissingResourceException e) {
// !!! need dateformat subclass appropriate to calendar type here!
// No custom patterns
// !!! note: possible circularity here, if getDateTimeInstance calls us because of
// loc specifying a calendar.
result = DateFormat.getDateTimeInstance(dateStyle, timeStyle, loc);
DateFormatSymbols symbols = new DateFormatSymbols(cal, loc);
((SimpleDateFormat) result).setDateFormatSymbols(symbols); // aliu
patterns = DEFAULT_PATTERNS;
}
PATTERN_CACHE.put(key, patterns);
}
// Resolve a pattern for the date/time style
String pattern = null;
if ((timeStyle >= 0) && (dateStyle >= 0)) {
pattern = MessageFormat.format(patterns[8],
new Object[] {patterns[timeStyle], patterns[dateStyle + 4]});
} else if (timeStyle >= 0) {
pattern = patterns[timeStyle];
} else if (dateStyle >= 0) {
pattern = patterns[dateStyle + 4];
} else {
throw new IllegalArgumentException("No date or time style specified");
}
DateFormat result = cal.handleGetDateFormat(pattern, loc);
result.setCalendar(cal);
return result;
}
/**
* An instance of FormatConfiguration represents calendar specific
* date format configuration and used for calling the ICU private
* SimpleDateFormat factory method.
*
* @internal
* @deprecated This API is ICU internal only.
*/
public static class FormatConfiguration {
private String pattern;
private DateFormatSymbols formatData;
private Calendar cal;
private ULocale loc;
// Only Calendar can instantiate
private FormatConfiguration() {
}
public String getPatternString() {
return pattern;
}
public Calendar getCalendar() {
return cal;
}
public ULocale getLocale() {
return loc;
}
public DateFormatSymbols getDateFormatSymbols() {
return formatData;
}
}
//-------------------------------------------------------------------------
// Protected utility methods for use by subclasses. These are very handy
// for implementing add, roll, and computeFields.

View file

@ -1,18 +0,0 @@
/**
*******************************************************************************
* Copyright (C) 2002-2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
import com.ibm.icu.util.TimeZone;
import java.util.Locale;
/**
* @prototype
*/
interface CalendarFactory {
public Calendar create(TimeZone tz, ULocale loc);
public String factoryName();
}

View file

@ -0,0 +1,107 @@
/*
* Copyright (C) 2007, International Business Machines
* Corporation and others. All Rights Reserved.
*/
package com.ibm.icu.util;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Set;
import com.ibm.icu.impl.ICULocaleService;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.ICUService;
import com.ibm.icu.impl.ICULocaleService.LocaleKey;
import com.ibm.icu.impl.ICULocaleService.LocaleKeyFactory;
import com.ibm.icu.impl.ICUService.Factory;
import com.ibm.icu.impl.ICUService.Key;
import com.ibm.icu.util.Calendar.CalendarFactory;
class CalendarServiceShim extends Calendar.CalendarShim {
Locale[] getAvailableLocales() {
if (service.isDefault()) {
return ICUResourceBundle.getAvailableLocales(ICUResourceBundle.ICU_BASE_NAME);
}
return service.getAvailableLocales();
}
ULocale[] getAvailableULocales() {
if (service.isDefault()) {
return ICUResourceBundle.getAvailableULocales(ICUResourceBundle.ICU_BASE_NAME);
}
return service.getAvailableULocales();
}
private static final class CalFactory extends LocaleKeyFactory {
private CalendarFactory delegate;
CalFactory(CalendarFactory delegate) {
super(delegate.visible() ? VISIBLE : INVISIBLE);
this.delegate = delegate;
}
public Object create(Key key, ICUService service) {
if (handlesKey(key)) {
LocaleKey lkey = (LocaleKey)key;
ULocale loc = lkey.canonicalLocale();
Object result = delegate.createCalendar(loc);
if (result == null) {
result = service.getKey(key, null, this);
}
return result;
}
return null;
}
protected Set getSupportedIDs() {
return delegate.getSupportedLocaleNames();
}
}
Calendar createInstance(ULocale desiredLocale) {
ULocale[] actualLoc = new ULocale[1];
if (desiredLocale.equals(ULocale.ROOT)) {
desiredLocale = ULocale.ROOT;
}
Calendar cal = (Calendar)service.get(desiredLocale, actualLoc);
if (cal == null) {
throw new MissingResourceException("Unable to construct Calendar", "", "");
}
cal = (Calendar)cal.clone();
/* !!! TODO !!! actualLoc returned by service is not properly set.
* When this Calendar object is being created, cal.setLocale is called
* and proper actual locale is set at that time. Revisit this later.
* -yoshito
*/
/*
ULocale uloc = actualLoc[0];
cal.setLocale(uloc, uloc); // service make no distinction between actual and valid
*/
return cal;
}
Object registerFactory(CalendarFactory factory) {
return service.registerFactory(new CalFactory(factory));
}
boolean unregister(Object k) {
return service.unregisterFactory((Factory)k);
}
private static class CalService extends ICULocaleService {
CalService() {
super("Calendar");
class RBCalendarFactory extends ICUResourceBundleFactory {
protected Object handleCreate(ULocale loc, int kind, ICUService sercice) {
return Calendar.createInstance(loc);
}
}
this.registerFactory(new RBCalendarFactory());
markDefault();
}
}
private static ICULocaleService service = new CalService();
}

View file

@ -8,6 +8,7 @@
package com.ibm.icu.util;
import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@ -16,12 +17,11 @@ import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.TreeMap;
import java.lang.ref.SoftReference;
import com.ibm.icu.impl.LocaleUtility;
import com.ibm.icu.impl.SimpleCache;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.LocaleUtility;
import com.ibm.icu.lang.UCharacter;
/**
* A class analogous to {@link java.util.Locale} that provides additional
* support for ICU protocol. In ICU 3.0 this class is enhanced to support
@ -232,30 +232,7 @@ public final class ULocale implements Serializable {
*/
public static final ULocale ROOT = new ULocale("root", EMPTY_LOCALE);
private static final HashMap CACHE = new HashMap(20);
static {
CACHE.put(EMPTY_LOCALE, ROOT);
CACHE.put(Locale.ENGLISH, ENGLISH);
CACHE.put(Locale.FRENCH, FRENCH);
CACHE.put(Locale.GERMAN, GERMAN);
CACHE.put(Locale.ITALIAN, ITALIAN);
CACHE.put(Locale.JAPANESE, JAPANESE);
CACHE.put(Locale.KOREAN, KOREAN);
CACHE.put(Locale.CHINESE, CHINESE);
CACHE.put(Locale.SIMPLIFIED_CHINESE, SIMPLIFIED_CHINESE);
CACHE.put(Locale.TRADITIONAL_CHINESE, TRADITIONAL_CHINESE);
CACHE.put(Locale.FRANCE, FRANCE);
CACHE.put(Locale.GERMANY, GERMANY);
CACHE.put(Locale.ITALY, ITALY);
CACHE.put(Locale.JAPAN, JAPAN);
CACHE.put(Locale.KOREA, KOREA);
CACHE.put(Locale.CHINA, CHINA);
CACHE.put(Locale.TAIWAN, TAIWAN);
CACHE.put(Locale.UK, UK);
CACHE.put(Locale.US, US);
CACHE.put(Locale.CANADA, CANADA);
CACHE.put(Locale.CANADA_FRENCH, CANADA_FRENCH);
}
private static final SimpleCache CACHE = new SimpleCache();
/**
* Cache the locale.
@ -749,14 +726,19 @@ public final class ULocale implements Serializable {
if (loc == null) {
return null;
}
if (loc.toString().length() == 0) {
return ROOT;
}
ULocale result = (ULocale)CACHE.get(loc);
if (result == null && defaultULocale != null && loc == defaultULocale.locale) {
if (result == null) {
if (defaultULocale != null && loc == defaultULocale.locale) {
result = defaultULocale;
} else {
result = new ULocale(loc.toString(), loc);
String locStr = loc.toString();
if (locStr.length() == 0) {
result = ROOT;
} else {
result = new ULocale(locStr, loc);
}
}
CACHE.put(loc, result);
}
return result;
}
@ -863,10 +845,12 @@ public final class ULocale implements Serializable {
return locale;
}
private static SoftReference nameCacheRef = new SoftReference(Collections.synchronizedMap(new HashMap()));
/**
* Keep our own default ULocale.
*/
private static ULocale defaultULocale;
private static Locale defaultLocale = Locale.getDefault();
private static ULocale defaultULocale = new ULocale(defaultLocale);
/**
* Returns the current default ULocale.
@ -874,8 +858,9 @@ public final class ULocale implements Serializable {
*/
public static ULocale getDefault() {
synchronized (ULocale.class) {
Locale defaultLocale = Locale.getDefault();
if (defaultULocale == null || defaultULocale.toLocale() != defaultLocale) {
Locale currentDefault = Locale.getDefault();
if (defaultLocale != currentDefault) {
defaultLocale = currentDefault;
defaultULocale = new ULocale(defaultLocale);
}
return defaultULocale;
@ -1143,7 +1128,6 @@ public final class ULocale implements Serializable {
}
return name;
}
private static SoftReference nameCacheRef = new SoftReference(Collections.synchronizedMap(new HashMap()));
/**
* Returns a string representation of this object.