blob: f111b4b1c92490d59a0b27606815ec44cccc7a26 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2008, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.impl.jdkadapter;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import com.ibm.icu.impl.icuadapter.TimeZoneJDK;
import com.ibm.icu.text.DateFormatSymbols;
import com.ibm.icu.util.Calendar;
/**
* CalendarICU is an adapter class which wraps ICU4J Calendar and
* implements java.util.Calendar APIs.
*/
public class CalendarICU extends java.util.Calendar {
private static final long serialVersionUID = -8641226371713600671L;
private Calendar fIcuCal;
private CalendarICU(Calendar icuCal) {
fIcuCal = icuCal;
init();
}
public static java.util.Calendar wrap(Calendar icuCal) {
return new CalendarICU(icuCal);
}
public Calendar unwrap() {
sync();
return fIcuCal;
}
@Override
public void add(int field, int amount) {
sync();
fIcuCal.add(field, amount);
}
// Note: We do not need to override followings. These methods
// call int compareTo(Calendar anotherCalendar) and we
// override the method.
//public boolean after(Object when)
//public boolean before(Object when)
// Note: Jeez! These methods are final and we cannot override them.
// We do not want to rewrite ICU Calendar implementation classes
// as subclasses of java.util.Calendar. This adapter class
// wraps an ICU Calendar instance and the calendar calculation
// is actually done independently from java.util.Calendar
// implementation. Thus, we need to monitor the status of
// superclass fields in some methods and call ICU Calendar's
// clear if superclass clear update the status of superclass's
// calendar fields. See private void sync().
//public void clear()
//public void clear(int field)
@Override
public Object clone() {
sync();
CalendarICU other = (CalendarICU)super.clone();
other.fIcuCal = (Calendar)fIcuCal.clone();
return other;
}
public int compareTo(Calendar anotherCalendar) {
sync();
long thisMillis = getTimeInMillis();
long otherMillis = anotherCalendar.getTimeInMillis();
return thisMillis > otherMillis ? 1 : (thisMillis == otherMillis ? 0 : -1);
}
// Note: These methods are supposed to be implemented by java.util.Calendar
// subclasses. But we actually use a instance of ICU Calendar
// for all calendar calculation, we do nothing here.
@Override
protected void complete() {}
@Override
protected void computeFields() {}
@Override
protected void computeTime() {}
@Override
public boolean equals(Object obj) {
if (obj instanceof CalendarICU) {
sync();
return ((CalendarICU)obj).fIcuCal.equals(fIcuCal);
}
return false;
}
@Override
public int get(int field) {
sync();
return fIcuCal.get(field);
}
@Override
public int getActualMaximum(int field) {
return fIcuCal.getActualMaximum(field);
}
@Override
public int getActualMinimum(int field) {
return fIcuCal.getActualMinimum(field);
}
@Override
public String getDisplayName(int field, int style, Locale locale) {
if (field < 0 || field >= FIELD_COUNT || (style != SHORT && style != LONG && style != ALL_STYLES)) {
throw new IllegalArgumentException("Bad field or style.");
}
DateFormatSymbols dfs = DateFormatSymbols.getInstance(locale);
String[] array = getFieldStrings(field, style, dfs);
if (array != null) {
int fieldVal = get(field);
if (fieldVal < array.length) {
return array[fieldVal];
}
}
return null;
}
@Override
public Map<String,Integer> getDisplayNames(int field, int style, Locale locale) {
if (field < 0 || field >= FIELD_COUNT || (style != SHORT && style != LONG && style != ALL_STYLES)) {
throw new IllegalArgumentException("Bad field or style.");
}
DateFormatSymbols dfs = DateFormatSymbols.getInstance(locale);
if (style != ALL_STYLES) {
return getFieldStringsMap(field, style, dfs);
}
Map<String,Integer> result = getFieldStringsMap(field, SHORT, dfs);
if (result == null) {
return null;
}
if (field == MONTH || field == DAY_OF_WEEK) {
Map<String,Integer> longMap = getFieldStringsMap(field, LONG, dfs);
if (longMap != null) {
result.putAll(longMap);
}
}
return result;
}
@Override
public int getGreatestMinimum(int field) {
return fIcuCal.getGreatestMinimum(field);
}
@Override
public int getLeastMaximum(int field) {
return fIcuCal.getLeastMaximum(field);
}
@Override
public int getMaximum(int field) {
return fIcuCal.getMaximum(field);
}
@Override
public int getMinimalDaysInFirstWeek() {
return fIcuCal.getMinimalDaysInFirstWeek();
}
@Override
public int getMinimum(int field) {
return fIcuCal.getMinimum(field);
}
// Note: getTime() calls getTimeInMillis()
//public Date getTime()
@Override
public long getTimeInMillis() {
sync();
return fIcuCal.getTimeInMillis();
}
@Override
public TimeZone getTimeZone() {
return TimeZoneICU.wrap(fIcuCal.getTimeZone());
}
@Override
public int hashCode() {
sync();
return fIcuCal.hashCode();
}
//protected int internalGet(int field)
@Override
public boolean isLenient() {
return fIcuCal.isLenient();
}
//public boolean isSet(int field)
@Override
public void roll(int field, boolean up) {
sync();
fIcuCal.roll(field, up);
}
@Override
public void roll(int field, int amount) {
sync();
fIcuCal.roll(field, amount);
}
@Override
public void set(int field, int value) {
sync();
fIcuCal.set(field, value);
}
// Note: These set methods call set(int field, int value) for each field.
// These are final, so we cannot override them, but we override
// set(int field, int value), so the superclass implementations
// still work as we want.
//public void set(int year, int month, int date)
//public void set(int year, int month, int date, int hourOfDay, int minute)
//public void set(int year, int month, int date, int hourOfDay, int minute, int second)
@Override
public void setFirstDayOfWeek(int value) {
fIcuCal.setFirstDayOfWeek(value);
}
@Override
public void setLenient(boolean lenient) {
fIcuCal.setLenient(lenient);
}
@Override
public void setMinimalDaysInFirstWeek(int value) {
fIcuCal.setMinimalDaysInFirstWeek(value);
}
// Note: This method calls setTimeInMillis(long millis).
// This method is final, so we cannot override it, but we
// override setTimeInMillis(long millis), so the superclass
// implementation still works as we want.
//public void setTime(Date date)
@Override
public void setTimeInMillis(long millis) {
fIcuCal.setTimeInMillis(millis);
}
@Override
public void setTimeZone(TimeZone value) {
fIcuCal.setTimeZone(TimeZoneJDK.wrap(value));
}
@Override
public String toString() {
sync();
return "CalendarICU: " + fIcuCal.toString();
}
private void sync() {
// Check if clear is called for each JDK Calendar field.
// If it was, then call clear for the field in the wrapped
// ICU Calendar.
for (int i = 0; i < isSet.length; i++) {
if (!isSet[i]) {
isSet[i] = true;
try {
fIcuCal.clear(i);
} catch (ArrayIndexOutOfBoundsException e) {
// More fields in JDK calendar, which is unlikely
}
}
}
}
private void init() {
// Mark "set" for all fields, so we can detect the invocation of
// clear() later.
for (int i = 0; i < isSet.length; i++) {
isSet[i] = true;
}
}
private static String[] getFieldStrings(int field, int style, DateFormatSymbols dfs) {
String[] result = null;
switch (field) {
case AM_PM:
result = dfs.getAmPmStrings();
break;
case DAY_OF_WEEK:
result = (style == LONG) ? dfs.getWeekdays() : dfs.getShortWeekdays();
break;
case ERA:
//result = (style == LONG) ? dfs.getEraNames() : dfs.getEras();
result = dfs.getEras();
break;
case MONTH:
result = (style == LONG) ? dfs.getMonths() : dfs.getShortMonths();
break;
}
return result;
}
private static Map<String,Integer> getFieldStringsMap(int field, int style, DateFormatSymbols dfs) {
String[] strings = getFieldStrings(field, style, dfs);
if (strings == null) {
return null;
}
Map<String,Integer> res = new HashMap<String,Integer>();
for (int i = 0; i < strings.length; i++) {
if (strings[i].length() != 0) {
res.put(strings[i], Integer.valueOf(i));
}
}
return res;
}
}