1 /* 2 ******************************************************************************* 3 * Copyright (C) 2008, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 package com.ibm.icu.impl.jdkadapter; 8 9 import java.util.HashMap; 10 import java.util.Locale; 11 import java.util.Map; 12 import java.util.TimeZone; 13 14 import com.ibm.icu.impl.icuadapter.TimeZoneJDK; 15 import com.ibm.icu.text.DateFormatSymbols; 16 import com.ibm.icu.util.Calendar; 17 18 /** 19 * CalendarICU is an adapter class which wraps ICU4J Calendar and 20 * implements java.util.Calendar APIs. 21 */ 22 public class CalendarICU extends java.util.Calendar { 23 24 private static final long serialVersionUID = -8641226371713600671L; 25 26 private Calendar fIcuCal; 27 28 private CalendarICU(Calendar icuCal) { 29 fIcuCal = icuCal; 30 init(); 31 } 32 33 public static java.util.Calendar wrap(Calendar icuCal) { 34 return new CalendarICU(icuCal); 35 } 36 37 public Calendar unwrap() { 38 sync(); 39 return fIcuCal; 40 } 41 42 @Override 43 public void add(int field, int amount) { 44 sync(); 45 fIcuCal.add(field, amount); 46 } 47 48 // Note: We do not need to override followings. These methods 49 // call int compareTo(Calendar anotherCalendar) and we 50 // override the method. 51 //public boolean after(Object when) 52 //public boolean before(Object when) 53 54 // Note: Jeez! These methods are final and we cannot override them. 55 // We do not want to rewrite ICU Calendar implementation classes 56 // as subclasses of java.util.Calendar. This adapter class 57 // wraps an ICU Calendar instance and the calendar calculation 58 // is actually done independently from java.util.Calendar 59 // implementation. Thus, we need to monitor the status of 60 // superclass fields in some methods and call ICU Calendar's 61 // clear if superclass clear update the status of superclass's 62 // calendar fields. See private void sync(). 63 //public void clear() 64 //public void clear(int field) 65 66 @Override 67 public Object clone() { 68 sync(); 69 CalendarICU other = (CalendarICU)super.clone(); 70 other.fIcuCal = (Calendar)fIcuCal.clone(); 71 return other; 72 } 73 74 public int compareTo(Calendar anotherCalendar) { 75 sync(); 76 long thisMillis = getTimeInMillis(); 77 long otherMillis = anotherCalendar.getTimeInMillis(); 78 return thisMillis > otherMillis ? 1 : (thisMillis == otherMillis ? 0 : -1); 79 } 80 81 // Note: These methods are supposed to be implemented by java.util.Calendar 82 // subclasses. But we actually use a instance of ICU Calendar 83 // for all calendar calculation, we do nothing here. 84 @Override 85 protected void complete() {} 86 @Override 87 protected void computeFields() {} 88 @Override 89 protected void computeTime() {} 90 91 @Override 92 public boolean equals(Object obj) { 93 if (obj instanceof CalendarICU) { 94 sync(); 95 return ((CalendarICU)obj).fIcuCal.equals(fIcuCal); 96 } 97 return false; 98 } 99 100 @Override 101 public int get(int field) { 102 sync(); 103 return fIcuCal.get(field); 104 } 105 106 @Override 107 public int getActualMaximum(int field) { 108 return fIcuCal.getActualMaximum(field); 109 } 110 111 @Override 112 public int getActualMinimum(int field) { 113 return fIcuCal.getActualMinimum(field); 114 } 115 116 @Override 117 public String getDisplayName(int field, int style, Locale locale) { 118 if (field < 0 || field >= FIELD_COUNT || (style != SHORT && style != LONG && style != ALL_STYLES)) { 119 throw new IllegalArgumentException("Bad field or style."); 120 } 121 DateFormatSymbols dfs = DateFormatSymbols.getInstance(locale); 122 String[] array = getFieldStrings(field, style, dfs); 123 if (array != null) { 124 int fieldVal = get(field); 125 if (fieldVal < array.length) { 126 return array[fieldVal]; 127 } 128 } 129 return null; 130 } 131 132 @Override 133 public Map<String,Integer> getDisplayNames(int field, int style, Locale locale) { 134 if (field < 0 || field >= FIELD_COUNT || (style != SHORT && style != LONG && style != ALL_STYLES)) { 135 throw new IllegalArgumentException("Bad field or style."); 136 } 137 DateFormatSymbols dfs = DateFormatSymbols.getInstance(locale); 138 if (style != ALL_STYLES) { 139 return getFieldStringsMap(field, style, dfs); 140 } 141 142 Map<String,Integer> result = getFieldStringsMap(field, SHORT, dfs); 143 if (result == null) { 144 return null; 145 } 146 if (field == MONTH || field == DAY_OF_WEEK) { 147 Map<String,Integer> longMap = getFieldStringsMap(field, LONG, dfs); 148 if (longMap != null) { 149 result.putAll(longMap); 150 } 151 } 152 return result; 153 } 154 155 @Override 156 public int getGreatestMinimum(int field) { 157 return fIcuCal.getGreatestMinimum(field); 158 } 159 160 @Override 161 public int getLeastMaximum(int field) { 162 return fIcuCal.getLeastMaximum(field); 163 } 164 165 @Override 166 public int getMaximum(int field) { 167 return fIcuCal.getMaximum(field); 168 } 169 170 @Override 171 public int getMinimalDaysInFirstWeek() { 172 return fIcuCal.getMinimalDaysInFirstWeek(); 173 } 174 175 @Override 176 public int getMinimum(int field) { 177 return fIcuCal.getMinimum(field); 178 } 179 180 // Note: getTime() calls getTimeInMillis() 181 //public Date getTime() 182 183 @Override 184 public long getTimeInMillis() { 185 sync(); 186 return fIcuCal.getTimeInMillis(); 187 } 188 189 @Override 190 public TimeZone getTimeZone() { 191 return TimeZoneICU.wrap(fIcuCal.getTimeZone()); 192 } 193 194 @Override 195 public int hashCode() { 196 sync(); 197 return fIcuCal.hashCode(); 198 } 199 200 //protected int internalGet(int field) 201 202 @Override 203 public boolean isLenient() { 204 return fIcuCal.isLenient(); 205 } 206 207 //public boolean isSet(int field) 208 209 @Override 210 public void roll(int field, boolean up) { 211 sync(); 212 fIcuCal.roll(field, up); 213 } 214 215 @Override 216 public void roll(int field, int amount) { 217 sync(); 218 fIcuCal.roll(field, amount); 219 } 220 221 @Override 222 public void set(int field, int value) { 223 sync(); 224 fIcuCal.set(field, value); 225 } 226 227 // Note: These set methods call set(int field, int value) for each field. 228 // These are final, so we cannot override them, but we override 229 // set(int field, int value), so the superclass implementations 230 // still work as we want. 231 //public void set(int year, int month, int date) 232 //public void set(int year, int month, int date, int hourOfDay, int minute) 233 //public void set(int year, int month, int date, int hourOfDay, int minute, int second) 234 235 @Override 236 public void setFirstDayOfWeek(int value) { 237 fIcuCal.setFirstDayOfWeek(value); 238 } 239 240 @Override 241 public void setLenient(boolean lenient) { 242 fIcuCal.setLenient(lenient); 243 } 244 245 @Override 246 public void setMinimalDaysInFirstWeek(int value) { 247 fIcuCal.setMinimalDaysInFirstWeek(value); 248 } 249 250 // Note: This method calls setTimeInMillis(long millis). 251 // This method is final, so we cannot override it, but we 252 // override setTimeInMillis(long millis), so the superclass 253 // implementation still works as we want. 254 //public void setTime(Date date) 255 256 @Override 257 public void setTimeInMillis(long millis) { 258 fIcuCal.setTimeInMillis(millis); 259 } 260 261 @Override 262 public void setTimeZone(TimeZone value) { 263 fIcuCal.setTimeZone(TimeZoneJDK.wrap(value)); 264 } 265 266 @Override 267 public String toString() { 268 sync(); 269 return "CalendarICU: " + fIcuCal.toString(); 270 } 271 272 private void sync() { 273 // Check if clear is called for each JDK Calendar field. 274 // If it was, then call clear for the field in the wrapped 275 // ICU Calendar. 276 for (int i = 0; i < isSet.length; i++) { 277 if (!isSet[i]) { 278 isSet[i] = true; 279 try { 280 fIcuCal.clear(i); 281 } catch (ArrayIndexOutOfBoundsException e) { 282 // More fields in JDK calendar, which is unlikely 283 } 284 } 285 } 286 } 287 288 private void init() { 289 // Mark "set" for all fields, so we can detect the invocation of 290 // clear() later. 291 for (int i = 0; i < isSet.length; i++) { 292 isSet[i] = true; 293 } 294 } 295 296 private static String[] getFieldStrings(int field, int style, DateFormatSymbols dfs) { 297 String[] result = null; 298 switch (field) { 299 case AM_PM: 300 result = dfs.getAmPmStrings(); 301 break; 302 case DAY_OF_WEEK: 303 result = (style == LONG) ? dfs.getWeekdays() : dfs.getShortWeekdays(); 304 break; 305 case ERA: 306 //result = (style == LONG) ? dfs.getEraNames() : dfs.getEras(); 307 result = dfs.getEras(); 308 break; 309 case MONTH: 310 result = (style == LONG) ? dfs.getMonths() : dfs.getShortMonths(); 311 break; 312 } 313 return result; 314 } 315 316 private static Map<String,Integer> getFieldStringsMap(int field, int style, DateFormatSymbols dfs) { 317 String[] strings = getFieldStrings(field, style, dfs); 318 if (strings == null) { 319 return null; 320 } 321 Map<String,Integer> res = new HashMap<String,Integer>(); 322 for (int i = 0; i < strings.length; i++) { 323 if (strings[i].length() != 0) { 324 res.put(strings[i], Integer.valueOf(i)); 325 } 326 } 327 return res; 328 } 329 } 330