• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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