2 *******************************************************************************
3 * Copyright (C) 2008, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 package com.ibm.icu.impl.jdkadapter;
9 import java.text.AttributedCharacterIterator;
10 import java.text.AttributedString;
11 import java.text.CharacterIterator;
12 import java.text.DateFormatSymbols;
13 import java.text.FieldPosition;
14 import java.text.NumberFormat;
15 import java.text.ParsePosition;
16 import java.util.Calendar;
17 import java.util.Date;
18 import java.util.GregorianCalendar;
19 import java.util.HashMap;
22 import java.util.TimeZone;
24 import com.ibm.icu.impl.icuadapter.NumberFormatJDK;
25 import com.ibm.icu.impl.icuadapter.TimeZoneJDK;
26 import com.ibm.icu.text.DateFormat;
27 import com.ibm.icu.text.SimpleDateFormat;
30 * SimpleDateFormatICU is an adapter class which wraps ICU4J SimpleDateFormat and
31 * implements java.text.SimpleDateFormat APIs.
33 public class SimpleDateFormatICU extends java.text.SimpleDateFormat {
35 private static final long serialVersionUID = -2060890659010258983L;
37 private SimpleDateFormat fIcuSdf;
39 private SimpleDateFormatICU(SimpleDateFormat icuSdf) {
43 public static java.text.SimpleDateFormat wrap(SimpleDateFormat icuSdf) {
44 return new SimpleDateFormatICU(icuSdf);
47 // Methods overriding java.text.SimpleDateFormat
50 public void applyLocalizedPattern(String pattern) {
51 fIcuSdf.applyLocalizedPattern(pattern);
55 public void applyPattern(String pattern) {
56 fIcuSdf.applyPattern(pattern);
60 public Object clone() {
61 SimpleDateFormatICU other = (SimpleDateFormatICU)super.clone();
62 other.fIcuSdf = (SimpleDateFormat)this.fIcuSdf.clone();
67 public boolean equals(Object obj) {
68 if (obj instanceof SimpleDateFormatICU) {
69 return ((SimpleDateFormatICU)obj).fIcuSdf.equals(this.fIcuSdf);
75 public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
76 return fIcuSdf.format(date, toAppendTo, pos);
80 public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
81 AttributedCharacterIterator aci = fIcuSdf.formatToCharacterIterator(obj);
83 // Create a new AttributedString
84 StringBuilder sb = new StringBuilder(aci.getEndIndex() - aci.getBeginIndex());
89 if (c == CharacterIterator.DONE) {
93 AttributedString resstr = new AttributedString(sb.toString());
96 Map<AttributedCharacterIterator.Attribute,Object> attributes = null;
97 int index = aci.getBeginIndex();
100 if (aci.setIndex(index) == CharacterIterator.DONE) {
103 attributes = aci.getAttributes();
104 if (attributes != null) {
105 int end = aci.getRunLimit();
106 Map<AttributedCharacterIterator.Attribute,Object> jdkAttributes =
107 new HashMap<AttributedCharacterIterator.Attribute,Object>();
108 Set<AttributedCharacterIterator.Attribute> keys = attributes.keySet();
109 for (AttributedCharacterIterator.Attribute key : keys) {
110 AttributedCharacterIterator.Attribute jdkKey = mapAttribute(key);
111 Object jdkVal = attributes.get(key);
112 if (jdkVal instanceof AttributedCharacterIterator.Attribute) {
113 jdkVal = mapAttribute((AttributedCharacterIterator.Attribute)jdkVal);
115 jdkAttributes.put(jdkKey, jdkVal);
117 int resend = residx + (end - index);
118 resstr.addAttributes(jdkAttributes, residx, resend);
124 return resstr.getIterator();
128 public Date get2DigitYearStart() {
129 return fIcuSdf.get2DigitYearStart();
133 public DateFormatSymbols getDateFormatSymbols() {
134 return DateFormatSymbolsICU.wrap(fIcuSdf.getDateFormatSymbols());
138 public int hashCode() {
139 return fIcuSdf.hashCode();
143 public Date parse(String text, ParsePosition pos) {
144 return fIcuSdf.parse(text, pos);
148 public void set2DigitYearStart(Date startDate) {
149 fIcuSdf.set2DigitYearStart(startDate);
153 public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) {
154 com.ibm.icu.text.DateFormatSymbols icuDfs = null;
155 if (newFormatSymbols instanceof DateFormatSymbolsICU) {
156 icuDfs = ((DateFormatSymbolsICU)newFormatSymbols).unwrap();
157 } else if (fIcuSdf.getCalendar() instanceof com.ibm.icu.util.GregorianCalendar) {
158 // Java 6 uses DateFormatSymbols exclusively for Gregorian
161 String[] newJDK, curICU, newICU;
162 icuDfs = fIcuSdf.getDateFormatSymbols();
165 newJDK = newFormatSymbols.getEras();
166 curICU = icuDfs.getEras();
167 newICU = copySymbols(newJDK, curICU, true);
170 newJDK = newFormatSymbols.getMonths();
171 curICU = icuDfs.getMonths();
172 newICU = copySymbols(newJDK, curICU, false);
173 icuDfs.setMonths(newICU);
176 newJDK = newFormatSymbols.getShortMonths();
177 curICU = icuDfs.getShortMonths();
178 newICU = copySymbols(newJDK, curICU, false);
179 icuDfs.setShortMonths(newICU);
182 newJDK = newFormatSymbols.getWeekdays();
183 curICU = icuDfs.getWeekdays();
184 newICU = copySymbols(newJDK, curICU, false);
185 icuDfs.setWeekdays(newICU);
188 newJDK = newFormatSymbols.getShortWeekdays();
189 curICU = icuDfs.getShortWeekdays();
190 newICU = copySymbols(newJDK, curICU, false);
191 icuDfs.setShortWeekdays(newICU);
194 newJDK = newFormatSymbols.getAmPmStrings();
195 curICU = icuDfs.getAmPmStrings();
196 newICU = copySymbols(newJDK, curICU, false);
197 icuDfs.setAmPmStrings(newICU);
199 // For other calendars, JDK's standard DateFormatSymbols
201 throw new UnsupportedOperationException("JDK DateFormatSymbols cannot be used for the calendar type.");
203 fIcuSdf.setDateFormatSymbols(icuDfs);
207 public String toLocalizedPattern() {
208 return fIcuSdf.toLocalizedPattern();
212 public String toPattern() {
213 return fIcuSdf.toLocalizedPattern();
216 // Methods overriding java.text.DateFormat
219 public Calendar getCalendar() {
220 return CalendarICU.wrap(fIcuSdf.getCalendar());
224 public NumberFormat getNumberFormat() {
225 com.ibm.icu.text.NumberFormat nfmt = fIcuSdf.getNumberFormat();
226 if (nfmt instanceof NumberFormatJDK) {
227 return ((NumberFormatJDK)nfmt).unwrap();
229 if (nfmt instanceof com.ibm.icu.text.DecimalFormat) {
230 return DecimalFormatICU.wrap((com.ibm.icu.text.DecimalFormat)nfmt);
232 return NumberFormatICU.wrap(nfmt);
236 public TimeZone getTimeZone() {
237 return getCalendar().getTimeZone();
241 public boolean isLenient() {
242 return fIcuSdf.isLenient();
245 private static final long SAMPLE_TIME = 962409600000L; //2000-07-01T00:00:00Z
246 private static final int JAPANESE_YEAR = 12; // Japanese calendar year @ SAMPLE_TIME
247 private static final int THAI_YEAR = 2543; // Thai Buddhist calendar year @ SAMPLE_TIME
250 public void setCalendar(Calendar newCalendar) {
251 com.ibm.icu.util.Calendar icuCal = null;
252 if (newCalendar instanceof CalendarICU) {
253 icuCal = ((CalendarICU)newCalendar).unwrap();
255 // Note: There is no easy way to implement ICU Calendar with
256 // JDK Calendar implementation. For now, this code assumes
257 // the given calendar is either Gregorian, Buddhist or
258 // JapaneseImperial. Once the type is detected, this code
259 // creates an instance of ICU Calendar with the same type.
260 com.ibm.icu.util.TimeZone icuTz = TimeZoneJDK.wrap(newCalendar.getTimeZone());
261 if (newCalendar instanceof GregorianCalendar) {
262 icuCal = new com.ibm.icu.util.GregorianCalendar(icuTz);
264 newCalendar.setTimeInMillis(SAMPLE_TIME);
265 int year = newCalendar.get(Calendar.YEAR);
266 if (year == JAPANESE_YEAR) {
267 icuCal = new com.ibm.icu.util.JapaneseCalendar(icuTz);
268 } else if (year == THAI_YEAR) {
269 icuCal = new com.ibm.icu.util.BuddhistCalendar(icuTz);
271 // We cannot support the case
272 throw new UnsupportedOperationException("Unsupported calendar type by ICU Calendar adapter.");
275 // Copy the original calendar settings
276 icuCal.setFirstDayOfWeek(newCalendar.getFirstDayOfWeek());
277 icuCal.setLenient(newCalendar.isLenient());
278 icuCal.setMinimalDaysInFirstWeek(newCalendar.getMinimalDaysInFirstWeek());
280 fIcuSdf.setCalendar(icuCal);
284 public void setLenient(boolean lenient) {
285 fIcuSdf.setLenient(lenient);
289 public void setNumberFormat(NumberFormat newNumberFormat) {
290 if (newNumberFormat instanceof DecimalFormatICU) {
291 fIcuSdf.setNumberFormat(((DecimalFormatICU)newNumberFormat).unwrap());
292 } else if (newNumberFormat instanceof NumberFormatICU) {
293 fIcuSdf.setNumberFormat(((NumberFormatICU)newNumberFormat).unwrap());
295 fIcuSdf.setNumberFormat(NumberFormatJDK.wrap(newNumberFormat));
300 public void setTimeZone(TimeZone zone) {
301 fIcuSdf.setTimeZone(TimeZoneJDK.wrap(zone));
304 private String[] copySymbols(String[] newData, String[] curData, boolean alignEnd) {
305 if (newData.length >= curData.length) {
308 int startOffset = alignEnd ? curData.length - newData.length : 0;
309 System.arraycopy(newData, 0, curData, startOffset, newData.length);
313 private static AttributedCharacterIterator.Attribute mapAttribute(AttributedCharacterIterator.Attribute icuAttribute) {
314 AttributedCharacterIterator.Attribute jdkAttribute = icuAttribute;
316 if (icuAttribute == DateFormat.Field.AM_PM) {
317 jdkAttribute = java.text.DateFormat.Field.AM_PM;
318 } else if (icuAttribute == DateFormat.Field.DAY_OF_MONTH) {
319 jdkAttribute = java.text.DateFormat.Field.DAY_OF_MONTH;
320 } else if (icuAttribute == DateFormat.Field.DAY_OF_WEEK) {
321 jdkAttribute = java.text.DateFormat.Field.DAY_OF_WEEK;
322 } else if (icuAttribute == DateFormat.Field.DAY_OF_WEEK_IN_MONTH) {
323 jdkAttribute = java.text.DateFormat.Field.DAY_OF_WEEK_IN_MONTH;
324 } else if (icuAttribute == DateFormat.Field.DAY_OF_YEAR) {
325 jdkAttribute = java.text.DateFormat.Field.DAY_OF_YEAR;
326 } else if (icuAttribute == DateFormat.Field.ERA) {
327 jdkAttribute = java.text.DateFormat.Field.ERA;
328 } else if (icuAttribute == DateFormat.Field.HOUR_OF_DAY0) {
329 jdkAttribute = java.text.DateFormat.Field.HOUR_OF_DAY0;
330 } else if (icuAttribute == DateFormat.Field.HOUR_OF_DAY1) {
331 jdkAttribute = java.text.DateFormat.Field.HOUR_OF_DAY1;
332 } else if (icuAttribute == DateFormat.Field.HOUR0) {
333 jdkAttribute = java.text.DateFormat.Field.HOUR0;
334 } else if (icuAttribute == DateFormat.Field.HOUR1) {
335 jdkAttribute = java.text.DateFormat.Field.HOUR1;
336 } else if (icuAttribute == DateFormat.Field.MILLISECOND) {
337 jdkAttribute = java.text.DateFormat.Field.MILLISECOND;
338 } else if (icuAttribute == DateFormat.Field.MINUTE) {
339 jdkAttribute = java.text.DateFormat.Field.MINUTE;
340 } else if (icuAttribute == DateFormat.Field.MONTH) {
341 jdkAttribute = java.text.DateFormat.Field.MONTH;
342 } else if (icuAttribute == DateFormat.Field.SECOND) {
343 jdkAttribute = java.text.DateFormat.Field.SECOND;
344 } else if (icuAttribute == DateFormat.Field.TIME_ZONE) {
345 jdkAttribute = java.text.DateFormat.Field.TIME_ZONE;
346 } else if (icuAttribute == DateFormat.Field.WEEK_OF_MONTH) {
347 jdkAttribute = java.text.DateFormat.Field.WEEK_OF_MONTH;
348 } else if (icuAttribute == DateFormat.Field.WEEK_OF_YEAR) {
349 jdkAttribute = java.text.DateFormat.Field.WEEK_OF_YEAR;
350 } else if (icuAttribute == DateFormat.Field.YEAR) {
351 jdkAttribute = java.text.DateFormat.Field.YEAR;
353 // There are other DateFormat.Field constants defined in
354 // ICU4J DateFormat below.
359 // MILLISECONDS_IN_DAY
363 // However, the corresponding pattern characters are not used by
364 // the default factory method - getXXXInstance. So these constants
365 // are only used when user intentionally set a pattern including
366 // these ICU4J specific pattern letters. Even it happens,
367 // ICU4J's DateFormat.Field extends java.text.Format.Field, so
368 // it does not break the contract of formatToCharacterIterator.