• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2007, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7 
8 #include "unicode/utypes.h"
9 
10 #if !UCONFIG_NO_FORMATTING
11 
12 //#define DEBUG_RELDTFMT
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 
17 #include "reldtfmt.h"
18 #include "unicode/msgfmt.h"
19 
20 #include "gregoimp.h" // for CalendarData
21 #include "cmemory.h"
22 
23 U_NAMESPACE_BEGIN
24 
25 
26 /**
27  * An array of URelativeString structs is used to store the resource data loaded out of the bundle.
28  */
29 struct URelativeString {
30     int32_t offset;         /** offset of this item, such as, the relative date **/
31     int32_t len;            /** length of the string **/
32     const UChar* string;    /** string, or NULL if not set **/
33 };
34 
35 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)36 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)
37 
38 RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) :
39 DateFormat(other), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL),
40 fDateStyle(other.fDateStyle), fTimeStyle(other.fTimeStyle), fLocale(other.fLocale),
41 fDayMin(other.fDayMin), fDayMax(other.fDayMax),
42 fDatesLen(other.fDatesLen), fDates(NULL)
43 {
44     if(other.fDateFormat != NULL) {
45         fDateFormat = (DateFormat*)other.fDateFormat->clone();
46     } else {
47         fDateFormat = NULL;
48     }
49     if (fDatesLen > 0) {
50         fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen);
51         uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*fDatesLen);
52     }
53     //fCalendar = other.fCalendar->clone();
54 /*
55     if(other.fTimeFormat != NULL) {
56         fTimeFormat = (DateFormat*)other.fTimeFormat->clone();
57     } else {
58         fTimeFormat = NULL;
59     }
60 */
61 }
62 
RelativeDateFormat(UDateFormatStyle timeStyle,UDateFormatStyle dateStyle,const Locale & locale,UErrorCode & status)63 RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const Locale& locale, UErrorCode& status)
64  : DateFormat(), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL),
65 fDateStyle(dateStyle), fTimeStyle(timeStyle), fLocale(locale), fDatesLen(0), fDates(NULL)
66  {
67     if(U_FAILURE(status) ) {
68         return;
69     }
70 
71     if(fDateStyle != UDAT_NONE) {
72         EStyle newStyle = (EStyle)(fDateStyle & ~UDAT_RELATIVE);
73         // Create a DateFormat in the non-relative style requested.
74         fDateFormat = createDateInstance(newStyle, locale);
75     }
76     if(fTimeStyle != UDAT_NONE) {
77         // don't support time style, for now
78         status = U_ILLEGAL_ARGUMENT_ERROR;
79         return;
80     }
81 
82     // Initialize the parent fCalendar, so that parse() works correctly.
83     initializeCalendar(NULL, locale, status);
84     loadDates(status);
85 }
86 
~RelativeDateFormat()87 RelativeDateFormat::~RelativeDateFormat() {
88     delete fDateFormat;
89     delete fTimeFormat;
90     delete fCombinedFormat;
91     uprv_free(fDates);
92 }
93 
94 
clone(void) const95 Format* RelativeDateFormat::clone(void) const {
96     return new RelativeDateFormat(*this);
97 }
98 
operator ==(const Format & other) const99 UBool RelativeDateFormat::operator==(const Format& other) const {
100     if(DateFormat::operator==(other)) {
101         // DateFormat::operator== guarantees following cast is safe
102         RelativeDateFormat* that = (RelativeDateFormat*)&other;
103         return (fDateStyle==that->fDateStyle   &&
104                 fTimeStyle==that->fTimeStyle   &&
105                 fLocale==that->fLocale);
106     }
107     return FALSE;
108 }
109 
format(Calendar & cal,UnicodeString & appendTo,FieldPosition & pos) const110 UnicodeString& RelativeDateFormat::format(  Calendar& cal,
111                                 UnicodeString& appendTo,
112                                 FieldPosition& pos) const {
113 
114     UErrorCode status = U_ZERO_ERROR;
115 
116     // calculate the difference, in days, between 'cal' and now.
117     int dayDiff = dayDifference(cal, status);
118 
119     // look up string
120     int32_t len;
121     const UChar *theString = getStringForDay(dayDiff, len, status);
122 
123     if(U_FAILURE(status) || (theString==NULL)) {
124         // didn't find it. Fall through to the fDateFormat
125         if(fDateFormat != NULL) {
126             return fDateFormat->format(cal,appendTo,pos);
127         } else {
128             return appendTo; // no op
129         }
130     } else {
131         // found a relative string
132         return appendTo.append(theString, len);
133     }
134 }
135 
136 
137 
138 UnicodeString&
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const139 RelativeDateFormat::format(const Formattable& obj,
140                          UnicodeString& appendTo,
141                          FieldPosition& pos,
142                          UErrorCode& status) const
143 {
144     // this is just here to get around the hiding problem
145     // (the previous format() override would hide the version of
146     // format() on DateFormat that this function correspond to, so we
147     // have to redefine it here)
148     return DateFormat::format(obj, appendTo, pos, status);
149 }
150 
151 
parse(const UnicodeString & text,Calendar & cal,ParsePosition & pos) const152 void RelativeDateFormat::parse( const UnicodeString& text,
153                     Calendar& cal,
154                     ParsePosition& pos) const {
155 
156     // Can the fDateFormat parse it?
157     if(fDateFormat != NULL) {
158         ParsePosition aPos(pos);
159         fDateFormat->parse(text,cal,aPos);
160         if((aPos.getIndex() != pos.getIndex()) &&
161             (aPos.getErrorIndex()==-1)) {
162                 pos=aPos; // copy the sub parse
163                 return; // parsed subfmt OK
164         }
165     }
166 
167     // Linear search the relative strings
168     for(int n=0;n<fDatesLen;n++) {
169         if(fDates[n].string != NULL &&
170             (0==text.compare(pos.getIndex(),
171                          fDates[n].len,
172                          fDates[n].string))) {
173             UErrorCode status = U_ZERO_ERROR;
174 
175             // Set the calendar to now+offset
176             cal.setTime(Calendar::getNow(),status);
177             cal.add(UCAL_DATE,fDates[n].offset, status);
178 
179             if(U_FAILURE(status)) {
180                 // failure in setting calendar fields
181                 pos.setErrorIndex(pos.getIndex()+fDates[n].len);
182             } else {
183                 pos.setIndex(pos.getIndex()+fDates[n].len);
184             }
185             return;
186         }
187     }
188 
189     // parse failed
190 }
191 
192 UDate
parse(const UnicodeString & text,ParsePosition & pos) const193 RelativeDateFormat::parse( const UnicodeString& text,
194                          ParsePosition& pos) const {
195     // redefined here because the other parse() function hides this function's
196     // cunterpart on DateFormat
197     return DateFormat::parse(text, pos);
198 }
199 
200 UDate
parse(const UnicodeString & text,UErrorCode & status) const201 RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const
202 {
203     // redefined here because the other parse() function hides this function's
204     // counterpart on DateFormat
205     return DateFormat::parse(text, status);
206 }
207 
208 
getStringForDay(int32_t day,int32_t & len,UErrorCode & status) const209 const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const {
210     if(U_FAILURE(status)) {
211         return NULL;
212     }
213 
214     // Is it outside the resource bundle's range?
215     if(day < fDayMin || day > fDayMax) {
216         return NULL; // don't have it.
217     }
218 
219     // Linear search the held strings
220     for(int n=0;n<fDatesLen;n++) {
221         if(fDates[n].offset == day) {
222             len = fDates[n].len;
223             return fDates[n].string;
224         }
225     }
226 
227     return NULL;  // not found.
228 }
229 
loadDates(UErrorCode & status)230 void RelativeDateFormat::loadDates(UErrorCode &status) {
231     CalendarData calData(fLocale, "gregorian", status);
232     UResourceBundle *strings = calData.getByKey3("fields", "day", "relative", status);
233     // set up min/max
234     fDayMin=-1;
235     fDayMax=1;
236 
237     if(U_FAILURE(status)) {
238         fDatesLen=0;
239         return;
240     }
241 
242     fDatesLen = ures_getSize(strings);
243     fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen);
244 
245     // Load in each item into the array...
246     int n = 0;
247 
248     UResourceBundle *subString = NULL;
249 
250     while(ures_hasNext(strings) && U_SUCCESS(status)) {  // iterate over items
251         subString = ures_getNextResource(strings, subString, &status);
252 
253         if(U_FAILURE(status) || (subString==NULL)) break;
254 
255         // key = offset #
256         const char *key = ures_getKey(subString);
257 
258         // load the string and length
259         int32_t aLen;
260         const UChar* aString = ures_getString(subString, &aLen, &status);
261 
262         if(U_FAILURE(status) || aString == NULL) break;
263 
264         // calculate the offset
265         int32_t offset = atoi(key);
266 
267         // set min/max
268         if(offset < fDayMin) {
269             fDayMin = offset;
270         }
271         if(offset > fDayMax) {
272             fDayMax = offset;
273         }
274 
275         // copy the string pointer
276         fDates[n].offset = offset;
277         fDates[n].string = aString;
278         fDates[n].len = aLen;
279 
280         n++;
281     }
282     ures_close(subString);
283 
284     // the fDates[] array could be sorted here, for direct access.
285 }
286 
287 
288 // this should to be in DateFormat, instead it was copied from SimpleDateFormat.
289 
290 Calendar*
initializeCalendar(TimeZone * adoptZone,const Locale & locale,UErrorCode & status)291 RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
292 {
293     if(!U_FAILURE(status)) {
294         fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status);
295     }
296     if (U_SUCCESS(status) && fCalendar == NULL) {
297         status = U_MEMORY_ALLOCATION_ERROR;
298     }
299     return fCalendar;
300 }
301 
dayDifference(Calendar & cal,UErrorCode & status)302 int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) {
303     if(U_FAILURE(status)) {
304         return 0;
305     }
306     // TODO: Cache the nowCal to avoid heap allocs?
307     Calendar *nowCal = cal.clone();
308     nowCal->setTime(Calendar::getNow(), status);
309     int32_t dayDiff = nowCal->fieldDifference(cal.getTime(status), Calendar::DATE, status);
310     delete nowCal;
311     return dayDiff;
312 }
313 
314 U_NAMESPACE_END
315 
316 #endif
317 
318