1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2016 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 /* 5 ******************************************************************************* 6 * Copyright (C) 2007-2016, International Business Machines Corporation and 7 * others. All Rights Reserved. 8 ******************************************************************************* 9 */ 10 package ohos.global.icu.impl; 11 12 import java.text.FieldPosition; 13 import java.text.ParsePosition; 14 import java.util.ArrayList; 15 import java.util.Date; 16 import java.util.List; 17 import java.util.MissingResourceException; 18 19 import ohos.global.icu.lang.UCharacter; 20 import ohos.global.icu.text.BreakIterator; 21 import ohos.global.icu.text.DateFormat; 22 import ohos.global.icu.text.DisplayContext; 23 import ohos.global.icu.text.MessageFormat; 24 import ohos.global.icu.text.SimpleDateFormat; 25 import ohos.global.icu.util.Calendar; 26 import ohos.global.icu.util.TimeZone; 27 import ohos.global.icu.util.ULocale; 28 import ohos.global.icu.util.UResourceBundle; 29 30 /** 31 * @author srl 32 * @hide exposed on OHOS 33 */ 34 public class RelativeDateFormat extends DateFormat { 35 36 /** 37 * @author srl 38 * @hide exposed on OHOS 39 * 40 */ 41 public static class URelativeString { URelativeString(int offset, String string)42 URelativeString(int offset, String string) { 43 this.offset = offset; 44 this.string = string; 45 } URelativeString(String offset, String string)46 URelativeString(String offset, String string) { 47 this.offset = Integer.parseInt(offset); 48 this.string = string; 49 } 50 public int offset; 51 public String string; 52 } 53 54 // copy c'tor? 55 56 /** 57 * @param timeStyle The time style for the date and time. 58 * @param dateStyle The date style for the date and time. 59 * @param locale The locale for the date. 60 * @param cal The calendar to be used 61 */ RelativeDateFormat(int timeStyle, int dateStyle, ULocale locale, Calendar cal)62 public RelativeDateFormat(int timeStyle, int dateStyle, ULocale locale, Calendar cal) { 63 calendar = cal; 64 65 fLocale = locale; 66 fTimeStyle = timeStyle; 67 fDateStyle = dateStyle; 68 69 if (fDateStyle != DateFormat.NONE) { 70 int newStyle = fDateStyle & ~DateFormat.RELATIVE; 71 DateFormat df = DateFormat.getDateInstance(newStyle, locale); 72 if (df instanceof SimpleDateFormat) { 73 fDateTimeFormat = (SimpleDateFormat)df; 74 } else { 75 throw new IllegalArgumentException("Can't create SimpleDateFormat for date style"); 76 } 77 fDatePattern = fDateTimeFormat.toPattern(); 78 if (fTimeStyle != DateFormat.NONE) { 79 newStyle = fTimeStyle & ~DateFormat.RELATIVE; 80 df = DateFormat.getTimeInstance(newStyle, locale); 81 if (df instanceof SimpleDateFormat) { 82 fTimePattern = ((SimpleDateFormat)df).toPattern(); 83 } 84 } 85 } else { 86 // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormat 87 int newStyle = fTimeStyle & ~DateFormat.RELATIVE; 88 DateFormat df = DateFormat.getTimeInstance(newStyle, locale); 89 if (df instanceof SimpleDateFormat) { 90 fDateTimeFormat = (SimpleDateFormat)df; 91 } else { 92 throw new IllegalArgumentException("Can't create SimpleDateFormat for time style"); 93 } 94 fTimePattern = fDateTimeFormat.toPattern(); 95 } 96 97 initializeCalendar(null, fLocale); 98 loadDates(); 99 initializeCombinedFormat(calendar, fLocale); 100 } 101 102 /** 103 * serial version (generated) 104 */ 105 private static final long serialVersionUID = 1131984966440549435L; 106 107 /* (non-Javadoc) 108 * @see ohos.global.icu.text.DateFormat#format(ohos.global.icu.util.Calendar, java.lang.StringBuffer, java.text.FieldPosition) 109 */ 110 @Override format(Calendar cal, StringBuffer toAppendTo, FieldPosition fieldPosition)111 public StringBuffer format(Calendar cal, StringBuffer toAppendTo, 112 FieldPosition fieldPosition) { 113 114 String relativeDayString = null; 115 DisplayContext capitalizationContext = getContext(DisplayContext.Type.CAPITALIZATION); 116 117 if (fDateStyle != DateFormat.NONE) { 118 // calculate the difference, in days, between 'cal' and now. 119 int dayDiff = dayDifference(cal); 120 121 // look up string 122 relativeDayString = getStringForDay(dayDiff); 123 } 124 125 if (fDateTimeFormat != null) { 126 if (relativeDayString != null && fDatePattern != null && 127 (fTimePattern == null || fCombinedFormat == null || combinedFormatHasDateAtStart) ) { 128 // capitalize relativeDayString according to context for relative, set formatter no context 129 if ( relativeDayString.length() > 0 && UCharacter.isLowerCase(relativeDayString.codePointAt(0)) && 130 (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || 131 (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationOfRelativeUnitsForListOrMenu) || 132 (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationOfRelativeUnitsForStandAlone) )) { 133 if (capitalizationBrkIter == null) { 134 // should only happen when deserializing, etc. 135 capitalizationBrkIter = BreakIterator.getSentenceInstance(fLocale); 136 } 137 relativeDayString = UCharacter.toTitleCase(fLocale, relativeDayString, capitalizationBrkIter, 138 UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT); 139 } 140 fDateTimeFormat.setContext(DisplayContext.CAPITALIZATION_NONE); 141 } else { 142 // set our context for the formatter 143 fDateTimeFormat.setContext(capitalizationContext); 144 } 145 } 146 147 if (fDateTimeFormat != null && (fDatePattern != null || fTimePattern != null)) { 148 // The new way 149 if (fDatePattern == null) { 150 // must have fTimePattern 151 fDateTimeFormat.applyPattern(fTimePattern); 152 fDateTimeFormat.format(cal, toAppendTo, fieldPosition); 153 } else if (fTimePattern == null) { 154 // must have fDatePattern 155 if (relativeDayString != null) { 156 toAppendTo.append(relativeDayString); 157 } else { 158 fDateTimeFormat.applyPattern(fDatePattern); 159 fDateTimeFormat.format(cal, toAppendTo, fieldPosition); 160 } 161 } else { 162 String datePattern = fDatePattern; // default; 163 if (relativeDayString != null) { 164 // Need to quote the relativeDayString to make it a legal date pattern 165 datePattern = "'" + relativeDayString.replace("'", "''") + "'"; 166 } 167 StringBuffer combinedPattern = new StringBuffer(""); 168 fCombinedFormat.format(new Object[] {fTimePattern, datePattern}, combinedPattern, new FieldPosition(0)); 169 fDateTimeFormat.applyPattern(combinedPattern.toString()); 170 fDateTimeFormat.format(cal, toAppendTo, fieldPosition); 171 } 172 } else if (fDateFormat != null) { 173 // A subset of the old way, for serialization compatibility 174 // (just do the date part) 175 if (relativeDayString != null) { 176 toAppendTo.append(relativeDayString); 177 } else { 178 fDateFormat.format(cal, toAppendTo, fieldPosition); 179 } 180 } 181 182 return toAppendTo; 183 } 184 185 /* (non-Javadoc) 186 * @see ohos.global.icu.text.DateFormat#parse(java.lang.String, ohos.global.icu.util.Calendar, java.text.ParsePosition) 187 */ 188 @Override parse(String text, Calendar cal, ParsePosition pos)189 public void parse(String text, Calendar cal, ParsePosition pos) { 190 throw new UnsupportedOperationException("Relative Date parse is not implemented yet"); 191 } 192 193 /* (non-Javadoc) 194 * @see ohos.global.icu.text.DateFormat#setContext(ohos.global.icu.text.DisplayContext) 195 * Here we override the DateFormat implementation in order to 196 * lazily initialize relevant items 197 */ 198 @Override setContext(DisplayContext context)199 public void setContext(DisplayContext context) { 200 super.setContext(context); 201 if (!capitalizationInfoIsSet && 202 (context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU || context==DisplayContext.CAPITALIZATION_FOR_STANDALONE)) { 203 initCapitalizationContextInfo(fLocale); 204 capitalizationInfoIsSet = true; 205 } 206 if (capitalizationBrkIter == null && (context==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || 207 (context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationOfRelativeUnitsForListOrMenu) || 208 (context==DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationOfRelativeUnitsForStandAlone) )) { 209 capitalizationBrkIter = BreakIterator.getSentenceInstance(fLocale); 210 } 211 } 212 213 private DateFormat fDateFormat; // keep for serialization compatibility 214 @SuppressWarnings("unused") 215 private DateFormat fTimeFormat; // now unused, keep for serialization compatibility 216 private MessageFormat fCombinedFormat; // the {0} {1} format. 217 private SimpleDateFormat fDateTimeFormat = null; // the held date/time formatter 218 private String fDatePattern = null; 219 private String fTimePattern = null; 220 221 int fDateStyle; 222 int fTimeStyle; 223 ULocale fLocale; 224 225 private transient List<URelativeString> fDates = null; 226 227 private boolean combinedFormatHasDateAtStart = false; 228 private boolean capitalizationInfoIsSet = false; 229 private boolean capitalizationOfRelativeUnitsForListOrMenu = false; 230 private boolean capitalizationOfRelativeUnitsForStandAlone = false; 231 private transient BreakIterator capitalizationBrkIter = null; 232 233 /** 234 * Get the string at a specific offset. 235 * @param day day offset ( -1, 0, 1, etc.. ). Does not require sorting by offset. 236 * @return the string, or NULL if none at that location. 237 */ getStringForDay(int day)238 private String getStringForDay(int day) { 239 if(fDates == null) { 240 loadDates(); 241 } 242 for(URelativeString dayItem : fDates) { 243 if(dayItem.offset == day) { 244 return dayItem.string; 245 } 246 } 247 return null; 248 } 249 250 // Sink to get "fields/day/relative". 251 private final class RelDateFmtDataSink extends UResource.Sink { 252 253 @Override put(UResource.Key key, UResource.Value value, boolean noFallback)254 public void put(UResource.Key key, UResource.Value value, boolean noFallback) { 255 if (value.getType() == ICUResourceBundle.ALIAS) { 256 return; 257 } 258 259 UResource.Table table = value.getTable(); 260 for (int i = 0; table.getKeyAndValue(i, key, value); ++i) { 261 262 int keyOffset; 263 try { 264 keyOffset = Integer.parseInt(key.toString()); 265 } 266 catch (NumberFormatException nfe) { 267 // Flag the error? 268 return; 269 } 270 // Check if already set. 271 if (getStringForDay(keyOffset) == null) { 272 URelativeString newDayInfo = new URelativeString(keyOffset, value.getString()); 273 fDates.add(newDayInfo); 274 } 275 } 276 } 277 } 278 279 /** 280 * Load the Date string array 281 */ loadDates()282 private synchronized void loadDates() { 283 ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, fLocale); 284 285 // Use sink mechanism to traverse data structure. 286 fDates = new ArrayList<URelativeString>(); 287 RelDateFmtDataSink sink = new RelDateFmtDataSink(); 288 rb.getAllItemsWithFallback("fields/day/relative", sink); 289 } 290 291 /** 292 * Set capitalizationOfRelativeUnitsForListOrMenu, capitalizationOfRelativeUnitsForStandAlone 293 */ initCapitalizationContextInfo(ULocale locale)294 private void initCapitalizationContextInfo(ULocale locale) { 295 ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale); 296 try { 297 ICUResourceBundle rdb = rb.getWithFallback("contextTransforms/relative"); 298 int[] intVector = rdb.getIntVector(); 299 if (intVector.length >= 2) { 300 capitalizationOfRelativeUnitsForListOrMenu = (intVector[0] != 0); 301 capitalizationOfRelativeUnitsForStandAlone = (intVector[1] != 0); 302 } 303 } catch (MissingResourceException e) { 304 // use default 305 } 306 } 307 308 /** 309 * @return the number of days in "until-now" 310 */ dayDifference(Calendar until)311 private static int dayDifference(Calendar until) { 312 Calendar nowCal = (Calendar)until.clone(); 313 Date nowDate = new Date(System.currentTimeMillis()); 314 nowCal.clear(); 315 nowCal.setTime(nowDate); 316 int dayDiff = until.get(Calendar.JULIAN_DAY) - nowCal.get(Calendar.JULIAN_DAY); 317 return dayDiff; 318 } 319 320 /** 321 * initializes fCalendar from parameters. Returns fCalendar as a convenience. 322 * @param zone Zone to be adopted, or NULL for TimeZone::createDefault(). 323 * @param locale Locale of the calendar 324 * @param status Error code 325 * @return the newly constructed fCalendar 326 */ initializeCalendar(TimeZone zone, ULocale locale)327 private Calendar initializeCalendar(TimeZone zone, ULocale locale) { 328 if (calendar == null) { 329 if(zone == null) { 330 calendar = Calendar.getInstance(locale); 331 } else { 332 calendar = Calendar.getInstance(zone, locale); 333 } 334 } 335 return calendar; 336 } 337 initializeCombinedFormat(Calendar cal, ULocale locale)338 private MessageFormat initializeCombinedFormat(Calendar cal, ULocale locale) { 339 String pattern; 340 ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance( 341 ICUData.ICU_BASE_NAME, locale); 342 String resourcePath = "calendar/" + cal.getType() + "/DateTimePatterns"; 343 ICUResourceBundle patternsRb= rb.findWithFallback(resourcePath); 344 if (patternsRb == null && !cal.getType().equals("gregorian")) { 345 // Try again with gregorian, if not already attempted. 346 patternsRb = rb.findWithFallback("calendar/gregorian/DateTimePatterns"); 347 } 348 349 if (patternsRb == null || patternsRb.getSize() < 9) { 350 // Undefined or too few elements. 351 pattern = "{1} {0}"; 352 } else { 353 int glueIndex = 8; 354 if (patternsRb.getSize() >= 13) { 355 if (fDateStyle >= DateFormat.FULL && fDateStyle <= DateFormat.SHORT) { 356 glueIndex += fDateStyle + 1; 357 } else 358 if (fDateStyle >= DateFormat.RELATIVE_FULL && 359 fDateStyle <= DateFormat.RELATIVE_SHORT) { 360 glueIndex += fDateStyle + 1 - DateFormat.RELATIVE; 361 } 362 } 363 int elementType = patternsRb.get(glueIndex).getType(); 364 if (elementType == UResourceBundle.ARRAY) { 365 pattern = patternsRb.get(glueIndex).getString(0); 366 } else { 367 pattern = patternsRb.getString(glueIndex); 368 } 369 } 370 combinedFormatHasDateAtStart = pattern.startsWith("{1}"); 371 fCombinedFormat = new MessageFormat(pattern, locale); 372 return fCombinedFormat; 373 } 374 } 375