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