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