12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others. 22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License 37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/* 47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 52d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert * Copyright (C) 2007-2016, International Business Machines Corporation and 62d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert * others. All Rights Reserved. 77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl; 107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.FieldPosition; 127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParsePosition; 132d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport java.util.ArrayList; 147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Date; 152d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport java.util.List; 167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.MissingResourceException; 177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.lang.UCharacter; 197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.BreakIterator; 207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.DateFormat; 217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.DisplayContext; 227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.MessageFormat; 237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.SimpleDateFormat; 247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Calendar; 257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeZone; 267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale; 277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.UResourceBundle; 287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/** 307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author srl 317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class RelativeDateFormat extends DateFormat { 337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author srl 367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static class URelativeString { 397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert URelativeString(int offset, String string) { 407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.offset = offset; 417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.string = string; 427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert URelativeString(String offset, String string) { 447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.offset = Integer.parseInt(offset); 457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.string = string; 467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public int offset; 487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String string; 497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // copy c'tor? 522d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param timeStyle The time style for the date and time. 557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param dateStyle The date style for the date and time. 567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param locale The locale for the date. 577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param cal The calendar to be used 587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public RelativeDateFormat(int timeStyle, int dateStyle, ULocale locale, Calendar cal) { 607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert calendar = cal; 617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fLocale = locale; 637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fTimeStyle = timeStyle; 647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateStyle = dateStyle; 657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (fDateStyle != DateFormat.NONE) { 677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int newStyle = fDateStyle & ~DateFormat.RELATIVE; 687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DateFormat df = DateFormat.getDateInstance(newStyle, locale); 697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (df instanceof SimpleDateFormat) { 707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateTimeFormat = (SimpleDateFormat)df; 717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException("Can't create SimpleDateFormat for date style"); 737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDatePattern = fDateTimeFormat.toPattern(); 757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (fTimeStyle != DateFormat.NONE) { 767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newStyle = fTimeStyle & ~DateFormat.RELATIVE; 777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert df = DateFormat.getTimeInstance(newStyle, locale); 787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (df instanceof SimpleDateFormat) { 797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fTimePattern = ((SimpleDateFormat)df).toPattern(); 807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormat 847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int newStyle = fTimeStyle & ~DateFormat.RELATIVE; 857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DateFormat df = DateFormat.getTimeInstance(newStyle, locale); 867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (df instanceof SimpleDateFormat) { 877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateTimeFormat = (SimpleDateFormat)df; 887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException("Can't create SimpleDateFormat for time style"); 907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fTimePattern = fDateTimeFormat.toPattern(); 927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert initializeCalendar(null, fLocale); 957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert loadDates(); 967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert initializeCombinedFormat(calendar, fLocale); 977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * serial version (generated) 1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static final long serialVersionUID = 1131984966440549435L; 1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* (non-Javadoc) 1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see com.ibm.icu.text.DateFormat#format(com.ibm.icu.util.Calendar, java.lang.StringBuffer, java.text.FieldPosition) 1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1072d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Override 1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public StringBuffer format(Calendar cal, StringBuffer toAppendTo, 1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert FieldPosition fieldPosition) { 1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String relativeDayString = null; 1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DisplayContext capitalizationContext = getContext(DisplayContext.Type.CAPITALIZATION); 1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (fDateStyle != DateFormat.NONE) { 1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // calculate the difference, in days, between 'cal' and now. 1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int dayDiff = dayDifference(cal); 1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // look up string 1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert relativeDayString = getStringForDay(dayDiff); 1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (fDateTimeFormat != null) { 1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (relativeDayString != null && fDatePattern != null && 1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (fTimePattern == null || fCombinedFormat == null || combinedFormatHasDateAtStart) ) { 1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // capitalize relativeDayString according to context for relative, set formatter no context 1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if ( relativeDayString.length() > 0 && UCharacter.isLowerCase(relativeDayString.codePointAt(0)) && 1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || 1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationOfRelativeUnitsForListOrMenu) || 1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationOfRelativeUnitsForStandAlone) )) { 1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (capitalizationBrkIter == null) { 1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // should only happen when deserializing, etc. 1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert capitalizationBrkIter = BreakIterator.getSentenceInstance(fLocale); 1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert relativeDayString = UCharacter.toTitleCase(fLocale, relativeDayString, capitalizationBrkIter, 1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT); 1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateTimeFormat.setContext(DisplayContext.CAPITALIZATION_NONE); 1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // set our context for the formatter 1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateTimeFormat.setContext(capitalizationContext); 1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (fDateTimeFormat != null && (fDatePattern != null || fTimePattern != null)) { 1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // The new way 1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (fDatePattern == null) { 1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // must have fTimePattern 1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateTimeFormat.applyPattern(fTimePattern); 1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateTimeFormat.format(cal, toAppendTo, fieldPosition); 1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (fTimePattern == null) { 1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // must have fDatePattern 1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (relativeDayString != null) { 1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert toAppendTo.append(relativeDayString); 1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateTimeFormat.applyPattern(fDatePattern); 1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateTimeFormat.format(cal, toAppendTo, fieldPosition); 1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String datePattern = fDatePattern; // default; 1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (relativeDayString != null) { 1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Need to quote the relativeDayString to make it a legal date pattern 1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert datePattern = "'" + relativeDayString.replace("'", "''") + "'"; 1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuffer combinedPattern = new StringBuffer(""); 1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fCombinedFormat.format(new Object[] {fTimePattern, datePattern}, combinedPattern, new FieldPosition(0)); 1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateTimeFormat.applyPattern(combinedPattern.toString()); 1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateTimeFormat.format(cal, toAppendTo, fieldPosition); 1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (fDateFormat != null) { 1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // A subset of the old way, for serialization compatibility 1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // (just do the date part) 1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (relativeDayString != null) { 1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert toAppendTo.append(relativeDayString); 1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fDateFormat.format(cal, toAppendTo, fieldPosition); 1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return toAppendTo; 1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* (non-Javadoc) 1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see com.ibm.icu.text.DateFormat#parse(java.lang.String, com.ibm.icu.util.Calendar, java.text.ParsePosition) 1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1852d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Override 1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void parse(String text, Calendar cal, ParsePosition pos) { 1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new UnsupportedOperationException("Relative Date parse is not implemented yet"); 1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* (non-Javadoc) 1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @see com.ibm.icu.text.DateFormat#setContext(com.ibm.icu.text.DisplayContext) 1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Here we override the DateFormat implementation in order to 1932d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert * lazily initialize relevant items 1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1952d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Override 1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void setContext(DisplayContext context) { 1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert super.setContext(context); 1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (!capitalizationInfoIsSet && 1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU || context==DisplayContext.CAPITALIZATION_FOR_STANDALONE)) { 2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert initCapitalizationContextInfo(fLocale); 2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert capitalizationInfoIsSet = true; 2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (capitalizationBrkIter == null && (context==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || 2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationOfRelativeUnitsForListOrMenu) || 2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert (context==DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationOfRelativeUnitsForStandAlone) )) { 2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert capitalizationBrkIter = BreakIterator.getSentenceInstance(fLocale); 2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private DateFormat fDateFormat; // keep for serialization compatibility 2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @SuppressWarnings("unused") 2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private DateFormat fTimeFormat; // now unused, keep for serialization compatibility 2132d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert private MessageFormat fCombinedFormat; // the {0} {1} format. 2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private SimpleDateFormat fDateTimeFormat = null; // the held date/time formatter 2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private String fDatePattern = null; 2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private String fTimePattern = null; 2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int fDateStyle; 2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int fTimeStyle; 2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ULocale fLocale; 2212d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 2222d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert private transient List<URelativeString> fDates = null; 2232d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private boolean combinedFormatHasDateAtStart = false; 2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private boolean capitalizationInfoIsSet = false; 2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private boolean capitalizationOfRelativeUnitsForListOrMenu = false; 2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private boolean capitalizationOfRelativeUnitsForStandAlone = false; 2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private transient BreakIterator capitalizationBrkIter = null; 2292d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Get the string at a specific offset. 2322d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert * @param day day offset ( -1, 0, 1, etc.. ). Does not require sorting by offset. 2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the string, or NULL if none at that location. 2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private String getStringForDay(int day) { 2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(fDates == null) { 2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert loadDates(); 2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2392d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert for(URelativeString dayItem : fDates) { 2402d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert if(dayItem.offset == day) { 2412d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert return dayItem.string; 2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return null; 2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 2472d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert // Sink to get "fields/day/relative". 2482d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert private final class RelDateFmtDataSink extends UResource.Sink { 2492d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 2502d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Override 2512d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert public void put(UResource.Key key, UResource.Value value, boolean noFallback) { 2522d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert if (value.getType() == ICUResourceBundle.ALIAS) { 2532d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert return; 2542d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert } 2552d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 2562d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert UResource.Table table = value.getTable(); 2572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert for (int i = 0; table.getKeyAndValue(i, key, value); ++i) { 2582d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 2592d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert int keyOffset; 2602d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert try { 2612d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert keyOffset = Integer.parseInt(key.toString()); 2622d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert } 2632d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert catch (NumberFormatException nfe) { 2642d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert // Flag the error? 2652d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert return; 2662d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert } 2672d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert // Check if already set. 2682d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert if (getStringForDay(keyOffset) == null) { 2692d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert URelativeString newDayInfo = new URelativeString(keyOffset, value.getString()); 2702d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert fDates.add(newDayInfo); 2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2752d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 2762d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert /** 2772d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert * Load the Date string array 2782d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert */ 2792d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert private synchronized void loadDates() { 2802d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, fLocale); 2812d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 2822d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert // Use sink mechanism to traverse data structure. 2832d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert fDates = new ArrayList<URelativeString>(); 2842d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert RelDateFmtDataSink sink = new RelDateFmtDataSink(); 2852d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert rb.getAllItemsWithFallback("fields/day/relative", sink); 2862d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert } 2872d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2892d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert * Set capitalizationOfRelativeUnitsForListOrMenu, capitalizationOfRelativeUnitsForStandAlone 2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private void initCapitalizationContextInfo(ULocale locale) { 2922d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale); 2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ICUResourceBundle rdb = rb.getWithFallback("contextTransforms/relative"); 2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int[] intVector = rdb.getIntVector(); 2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (intVector.length >= 2) { 2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert capitalizationOfRelativeUnitsForListOrMenu = (intVector[0] != 0); 2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert capitalizationOfRelativeUnitsForStandAlone = (intVector[1] != 0); 2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch (MissingResourceException e) { 3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // use default 3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the number of days in "until-now" 3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static int dayDifference(Calendar until) { 3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Calendar nowCal = (Calendar)until.clone(); 3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Date nowDate = new Date(System.currentTimeMillis()); 3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert nowCal.clear(); 3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert nowCal.setTime(nowDate); 3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int dayDiff = until.get(Calendar.JULIAN_DAY) - nowCal.get(Calendar.JULIAN_DAY); 3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return dayDiff; 3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3162d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * initializes fCalendar from parameters. Returns fCalendar as a convenience. 3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param zone Zone to be adopted, or NULL for TimeZone::createDefault(). 3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param locale Locale of the calendar 3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param status Error code 3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the newly constructed fCalendar 3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private Calendar initializeCalendar(TimeZone zone, ULocale locale) { 3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (calendar == null) { 3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(zone == null) { 3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert calendar = Calendar.getInstance(locale); 3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert calendar = Calendar.getInstance(zone, locale); 3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return calendar; 3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private MessageFormat initializeCombinedFormat(Calendar cal, ULocale locale) { 3362d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert String pattern; 3372d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance( 3382d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert ICUData.ICU_BASE_NAME, locale); 3392d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert String resourcePath = "calendar/" + cal.getType() + "/DateTimePatterns"; 3402d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert ICUResourceBundle patternsRb= rb.findWithFallback(resourcePath); 3412d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert if (patternsRb == null && !cal.getType().equals("gregorian")) { 3422d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert // Try again with gregorian, if not already attempted. 3432d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert patternsRb = rb.findWithFallback("calendar/gregorian/DateTimePatterns"); 3442d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert } 3452d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 3462d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert if (patternsRb == null || patternsRb.getSize() < 9) { 3472d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert // Undefined or too few elements. 3482d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert pattern = "{1} {0}"; 3492d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert } else { 3502d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert int glueIndex = 8; 3512d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert if (patternsRb.getSize() >= 13) { 3522d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert if (fDateStyle >= DateFormat.FULL && fDateStyle <= DateFormat.SHORT) { 3532d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert glueIndex += fDateStyle + 1; 3542d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert } else 3552d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert if (fDateStyle >= DateFormat.RELATIVE_FULL && 3562d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert fDateStyle <= DateFormat.RELATIVE_SHORT) { 3572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert glueIndex += fDateStyle + 1 - DateFormat.RELATIVE; 3582d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert } 3592d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert } 3602d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert int elementType = patternsRb.get(glueIndex).getType(); 3612d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert if (elementType == UResourceBundle.ARRAY) { 3622d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert pattern = patternsRb.get(glueIndex).getString(0); 3632d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert } else { 3642d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert pattern = patternsRb.getString(glueIndex); 3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert combinedFormatHasDateAtStart = pattern.startsWith("{1}"); 3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert fCombinedFormat = new MessageFormat(pattern, locale); 3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return fCombinedFormat; 3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert} 372