2 *******************************************************************************
3 * Copyright (C) 2008-2011, 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;
21 import java.util.Map.Entry;
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 for (Entry<AttributedCharacterIterator.Attribute, Object> entry
109 : attributes.entrySet()) {
110 AttributedCharacterIterator.Attribute key = entry.getKey();
111 AttributedCharacterIterator.Attribute jdkKey = mapAttribute(key);
112 Object jdkVal = entry.getValue();
113 if (jdkVal instanceof AttributedCharacterIterator.Attribute) {
114 jdkVal = mapAttribute((AttributedCharacterIterator.Attribute)jdkVal);
116 jdkAttributes.put(jdkKey, jdkVal);
118 int resend = residx + (end - index);
119 resstr.addAttributes(jdkAttributes, residx, resend);
125 return resstr.getIterator();
129 public Date get2DigitYearStart() {
130 return fIcuSdf.get2DigitYearStart();
134 public DateFormatSymbols getDateFormatSymbols() {
135 return DateFormatSymbolsICU.wrap(fIcuSdf.getDateFormatSymbols());
139 public int hashCode() {
140 return fIcuSdf.hashCode();
144 public Date parse(String text, ParsePosition pos) {
145 return fIcuSdf.parse(text, pos);
149 public void set2DigitYearStart(Date startDate) {
150 fIcuSdf.set2DigitYearStart(startDate);
154 public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) {
155 com.ibm.icu.text.DateFormatSymbols icuDfs = null;
156 if (newFormatSymbols instanceof DateFormatSymbolsICU) {
157 icuDfs = ((DateFormatSymbolsICU)newFormatSymbols).unwrap();
158 } else if (fIcuSdf.getCalendar() instanceof com.ibm.icu.util.GregorianCalendar) {
159 // Java 6 uses DateFormatSymbols exclusively for Gregorian
162 String[] newJDK, curICU, newICU;
163 icuDfs = fIcuSdf.getDateFormatSymbols();
166 newJDK = newFormatSymbols.getEras();
167 curICU = icuDfs.getEras();
168 newICU = copySymbols(newJDK, curICU, true);
171 newJDK = newFormatSymbols.getMonths();
172 curICU = icuDfs.getMonths();
173 newICU = copySymbols(newJDK, curICU, false);
174 icuDfs.setMonths(newICU);
177 newJDK = newFormatSymbols.getShortMonths();
178 curICU = icuDfs.getShortMonths();
179 newICU = copySymbols(newJDK, curICU, false);
180 icuDfs.setShortMonths(newICU);
183 newJDK = newFormatSymbols.getWeekdays();
184 curICU = icuDfs.getWeekdays();
185 newICU = copySymbols(newJDK, curICU, false);
186 icuDfs.setWeekdays(newICU);
189 newJDK = newFormatSymbols.getShortWeekdays();
190 curICU = icuDfs.getShortWeekdays();
191 newICU = copySymbols(newJDK, curICU, false);
192 icuDfs.setShortWeekdays(newICU);
195 newJDK = newFormatSymbols.getAmPmStrings();
196 curICU = icuDfs.getAmPmStrings();
197 newICU = copySymbols(newJDK, curICU, false);
198 icuDfs.setAmPmStrings(newICU);
200 // For other calendars, JDK's standard DateFormatSymbols
202 throw new UnsupportedOperationException("JDK DateFormatSymbols cannot be used for the calendar type.");
204 fIcuSdf.setDateFormatSymbols(icuDfs);
208 public String toLocalizedPattern() {
209 return fIcuSdf.toLocalizedPattern();
213 public String toPattern() {
214 return fIcuSdf.toLocalizedPattern();
217 // Methods overriding java.text.DateFormat
220 public Calendar getCalendar() {
221 return CalendarICU.wrap(fIcuSdf.getCalendar());
225 public NumberFormat getNumberFormat() {
226 com.ibm.icu.text.NumberFormat nfmt = fIcuSdf.getNumberFormat();
227 if (nfmt instanceof NumberFormatJDK) {
228 return ((NumberFormatJDK)nfmt).unwrap();
230 if (nfmt instanceof com.ibm.icu.text.DecimalFormat) {
231 return DecimalFormatICU.wrap((com.ibm.icu.text.DecimalFormat)nfmt);
233 return NumberFormatICU.wrap(nfmt);
237 public TimeZone getTimeZone() {
238 return getCalendar().getTimeZone();
242 public boolean isLenient() {
243 return fIcuSdf.isLenient();
246 private static final long SAMPLE_TIME = 962409600000L; //2000-07-01T00:00:00Z
247 private static final int JAPANESE_YEAR = 12; // Japanese calendar year @ SAMPLE_TIME
248 private static final int THAI_YEAR = 2543; // Thai Buddhist calendar year @ SAMPLE_TIME
251 public void setCalendar(Calendar newCalendar) {
252 com.ibm.icu.util.Calendar icuCal = null;
253 if (newCalendar instanceof CalendarICU) {
254 icuCal = ((CalendarICU)newCalendar).unwrap();
256 // Note: There is no easy way to implement ICU Calendar with
257 // JDK Calendar implementation. For now, this code assumes
258 // the given calendar is either Gregorian, Buddhist or
259 // JapaneseImperial. Once the type is detected, this code
260 // creates an instance of ICU Calendar with the same type.
261 com.ibm.icu.util.TimeZone icuTz = TimeZoneJDK.wrap(newCalendar.getTimeZone());
262 if (newCalendar instanceof GregorianCalendar) {
263 icuCal = new com.ibm.icu.util.GregorianCalendar(icuTz);
265 newCalendar.setTimeInMillis(SAMPLE_TIME);
266 int year = newCalendar.get(Calendar.YEAR);
267 if (year == JAPANESE_YEAR) {
268 icuCal = new com.ibm.icu.util.JapaneseCalendar(icuTz);
269 } else if (year == THAI_YEAR) {
270 icuCal = new com.ibm.icu.util.BuddhistCalendar(icuTz);
272 // We cannot support the case
273 throw new UnsupportedOperationException("Unsupported calendar type by ICU Calendar adapter.");
276 // Copy the original calendar settings
277 icuCal.setFirstDayOfWeek(newCalendar.getFirstDayOfWeek());
278 icuCal.setLenient(newCalendar.isLenient());
279 icuCal.setMinimalDaysInFirstWeek(newCalendar.getMinimalDaysInFirstWeek());
281 fIcuSdf.setCalendar(icuCal);
285 public void setLenient(boolean lenient) {
286 fIcuSdf.setLenient(lenient);
290 public void setNumberFormat(NumberFormat newNumberFormat) {
291 if (newNumberFormat instanceof DecimalFormatICU) {
292 fIcuSdf.setNumberFormat(((DecimalFormatICU)newNumberFormat).unwrap());
293 } else if (newNumberFormat instanceof NumberFormatICU) {
294 fIcuSdf.setNumberFormat(((NumberFormatICU)newNumberFormat).unwrap());
296 fIcuSdf.setNumberFormat(NumberFormatJDK.wrap(newNumberFormat));
301 public void setTimeZone(TimeZone zone) {
302 fIcuSdf.setTimeZone(TimeZoneJDK.wrap(zone));
305 private String[] copySymbols(String[] newData, String[] curData, boolean alignEnd) {
306 if (newData.length >= curData.length) {
309 int startOffset = alignEnd ? curData.length - newData.length : 0;
310 System.arraycopy(newData, 0, curData, startOffset, newData.length);
314 private static AttributedCharacterIterator.Attribute mapAttribute(AttributedCharacterIterator.Attribute icuAttribute) {
315 AttributedCharacterIterator.Attribute jdkAttribute = icuAttribute;
317 if (icuAttribute == DateFormat.Field.AM_PM) {
318 jdkAttribute = java.text.DateFormat.Field.AM_PM;
319 } else if (icuAttribute == DateFormat.Field.DAY_OF_MONTH) {
320 jdkAttribute = java.text.DateFormat.Field.DAY_OF_MONTH;
321 } else if (icuAttribute == DateFormat.Field.DAY_OF_WEEK) {
322 jdkAttribute = java.text.DateFormat.Field.DAY_OF_WEEK;
323 } else if (icuAttribute == DateFormat.Field.DAY_OF_WEEK_IN_MONTH) {
324 jdkAttribute = java.text.DateFormat.Field.DAY_OF_WEEK_IN_MONTH;
325 } else if (icuAttribute == DateFormat.Field.DAY_OF_YEAR) {
326 jdkAttribute = java.text.DateFormat.Field.DAY_OF_YEAR;
327 } else if (icuAttribute == DateFormat.Field.ERA) {
328 jdkAttribute = java.text.DateFormat.Field.ERA;
329 } else if (icuAttribute == DateFormat.Field.HOUR_OF_DAY0) {
330 jdkAttribute = java.text.DateFormat.Field.HOUR_OF_DAY0;
331 } else if (icuAttribute == DateFormat.Field.HOUR_OF_DAY1) {
332 jdkAttribute = java.text.DateFormat.Field.HOUR_OF_DAY1;
333 } else if (icuAttribute == DateFormat.Field.HOUR0) {
334 jdkAttribute = java.text.DateFormat.Field.HOUR0;
335 } else if (icuAttribute == DateFormat.Field.HOUR1) {
336 jdkAttribute = java.text.DateFormat.Field.HOUR1;
337 } else if (icuAttribute == DateFormat.Field.MILLISECOND) {
338 jdkAttribute = java.text.DateFormat.Field.MILLISECOND;
339 } else if (icuAttribute == DateFormat.Field.MINUTE) {
340 jdkAttribute = java.text.DateFormat.Field.MINUTE;
341 } else if (icuAttribute == DateFormat.Field.MONTH) {
342 jdkAttribute = java.text.DateFormat.Field.MONTH;
343 } else if (icuAttribute == DateFormat.Field.SECOND) {
344 jdkAttribute = java.text.DateFormat.Field.SECOND;
345 } else if (icuAttribute == DateFormat.Field.TIME_ZONE) {
346 jdkAttribute = java.text.DateFormat.Field.TIME_ZONE;
347 } else if (icuAttribute == DateFormat.Field.WEEK_OF_MONTH) {
348 jdkAttribute = java.text.DateFormat.Field.WEEK_OF_MONTH;
349 } else if (icuAttribute == DateFormat.Field.WEEK_OF_YEAR) {
350 jdkAttribute = java.text.DateFormat.Field.WEEK_OF_YEAR;
351 } else if (icuAttribute == DateFormat.Field.YEAR) {
352 jdkAttribute = java.text.DateFormat.Field.YEAR;
354 // There are other DateFormat.Field constants defined in
355 // ICU4J DateFormat below.
360 // MILLISECONDS_IN_DAY
364 // However, the corresponding pattern characters are not used by
365 // the default factory method - getXXXInstance. So these constants
366 // are only used when user intentionally set a pattern including
367 // these ICU4J specific pattern letters. Even it happens,
368 // ICU4J's DateFormat.Field extends java.text.Format.Field, so
369 // it does not break the contract of formatToCharacterIterator.