X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/46f4442e9a5a4f3b98b7c1083586332f6a8a99a4..a01113dcd0f39d5da295ef82785beff9ed86fe38:/icuSources/i18n/reldtfmt.cpp diff --git a/icuSources/i18n/reldtfmt.cpp b/icuSources/i18n/reldtfmt.cpp index 9c467c5d..5e7ddddf 100644 --- a/icuSources/i18n/reldtfmt.cpp +++ b/icuSources/i18n/reldtfmt.cpp @@ -1,7 +1,9 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* -* Copyright (C) 2007-2009, International Business Machines Corporation and * -* others. All Rights Reserved. * +* Copyright (C) 2007-2016, International Business Machines Corporation and +* others. All Rights Reserved. ******************************************************************************* */ @@ -9,17 +11,19 @@ #if !UCONFIG_NO_FORMATTING -//#define DEBUG_RELDTFMT - -#include #include -#include "reldtfmt.h" -#include "unicode/msgfmt.h" +#include "unicode/datefmt.h" +#include "unicode/reldatefmt.h" +#include "unicode/simpleformatter.h" #include "unicode/smpdtfmt.h" - -#include "gregoimp.h" // for CalendarData +#include "unicode/udisplaycontext.h" +#include "unicode/uchar.h" +#include "unicode/brkiter.h" +#include "unicode/ucasemap.h" +#include "reldtfmt.h" #include "cmemory.h" +#include "uresimp.h" U_NAMESPACE_BEGIN @@ -33,67 +37,97 @@ struct URelativeString { const UChar* string; /** string, or NULL if not set **/ }; -static const char DT_DateTimePatternsTag[]="DateTimePatterns"; - - UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat) RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) : -DateFormat(other), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL), -fDateStyle(other.fDateStyle), fTimeStyle(other.fTimeStyle), fLocale(other.fLocale), -fDayMin(other.fDayMin), fDayMax(other.fDayMax), -fDatesLen(other.fDatesLen), fDates(NULL) + DateFormat(other), fDateTimeFormatter(NULL), fDatePattern(other.fDatePattern), + fTimePattern(other.fTimePattern), fCombinedFormat(NULL), + fDateStyle(other.fDateStyle), fLocale(other.fLocale), + fDatesLen(other.fDatesLen), fDates(NULL), + fCombinedHasDateAtStart(other.fCombinedHasDateAtStart), + fCapitalizationInfoSet(other.fCapitalizationInfoSet), + fCapitalizationOfRelativeUnitsForUIListMenu(other.fCapitalizationOfRelativeUnitsForUIListMenu), + fCapitalizationOfRelativeUnitsForStandAlone(other.fCapitalizationOfRelativeUnitsForStandAlone), + fCapitalizationBrkIter(NULL) { - if(other.fDateFormat != NULL) { - fDateFormat = (DateFormat*)other.fDateFormat->clone(); - } else { - fDateFormat = NULL; + if(other.fDateTimeFormatter != NULL) { + fDateTimeFormatter = (SimpleDateFormat*)other.fDateTimeFormatter->clone(); + } + if(other.fCombinedFormat != NULL) { + fCombinedFormat = new SimpleFormatter(*other.fCombinedFormat); } if (fDatesLen > 0) { - fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); - uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*fDatesLen); + fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*(size_t)fDatesLen); + uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*(size_t)fDatesLen); } - //fCalendar = other.fCalendar->clone(); -/* - if(other.fTimeFormat != NULL) { - fTimeFormat = (DateFormat*)other.fTimeFormat->clone(); - } else { - fTimeFormat = NULL; +#if !UCONFIG_NO_BREAK_ITERATION + if (other.fCapitalizationBrkIter != NULL) { + fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone(); } -*/ +#endif } -RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const Locale& locale, UErrorCode& status) - : DateFormat(), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL), -fDateStyle(dateStyle), fTimeStyle(timeStyle), fLocale(locale), fDatesLen(0), fDates(NULL) - { +RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, + const Locale& locale, UErrorCode& status) : + DateFormat(), fDateTimeFormatter(NULL), fDatePattern(), fTimePattern(), fCombinedFormat(NULL), + fDateStyle(dateStyle), fLocale(locale), fDatesLen(0), fDates(NULL), + fCombinedHasDateAtStart(FALSE), fCapitalizationInfoSet(FALSE), + fCapitalizationOfRelativeUnitsForUIListMenu(FALSE), fCapitalizationOfRelativeUnitsForStandAlone(FALSE), + fCapitalizationBrkIter(NULL) +{ if(U_FAILURE(status) ) { return; } - - if(fDateStyle != UDAT_NONE) { - EStyle newStyle = (EStyle)(fDateStyle & ~UDAT_RELATIVE); - // Create a DateFormat in the non-relative style requested. - fDateFormat = createDateInstance(newStyle, locale); - } - if(fTimeStyle >= UDAT_FULL && fTimeStyle <= UDAT_SHORT) { - fTimeFormat = createTimeInstance((EStyle)fTimeStyle, locale); - } else if(fTimeStyle != UDAT_NONE) { + + if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) { // don't support other time styles (e.g. relative styles), for now status = U_ILLEGAL_ARGUMENT_ERROR; return; } - + UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT)? (UDateFormatStyle)(dateStyle & ~UDAT_RELATIVE): dateStyle; + DateFormat * df; + // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern). + // We do need to get separate patterns for the date & time styles. + if (baseDateStyle != UDAT_NONE) { + df = createDateInstance((EStyle)baseDateStyle, locale); + fDateTimeFormatter=dynamic_cast(df); + if (fDateTimeFormatter == NULL) { + status = U_UNSUPPORTED_ERROR; + return; + } + fDateTimeFormatter->toPattern(fDatePattern); + if (timeStyle != UDAT_NONE) { + df = createTimeInstance((EStyle)timeStyle, locale); + SimpleDateFormat *sdf = dynamic_cast(df); + if (sdf != NULL) { + sdf->toPattern(fTimePattern); + delete sdf; + } + } + } else { + // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter + df = createTimeInstance((EStyle)timeStyle, locale); + fDateTimeFormatter=dynamic_cast(df); + if (fDateTimeFormatter == NULL) { + status = U_UNSUPPORTED_ERROR; + delete df; + return; + } + fDateTimeFormatter->toPattern(fTimePattern); + } + // Initialize the parent fCalendar, so that parse() works correctly. initializeCalendar(NULL, locale, status); loadDates(status); } RelativeDateFormat::~RelativeDateFormat() { - delete fDateFormat; - delete fTimeFormat; + delete fDateTimeFormatter; delete fCombinedFormat; uprv_free(fDates); +#if !UCONFIG_NO_BREAK_ITERATION + delete fCapitalizationBrkIter; +#endif } @@ -103,61 +137,84 @@ Format* RelativeDateFormat::clone(void) const { UBool RelativeDateFormat::operator==(const Format& other) const { if(DateFormat::operator==(other)) { + // The DateFormat::operator== check for fCapitalizationContext equality above + // is sufficient to check equality of all derived context-related data. // DateFormat::operator== guarantees following cast is safe RelativeDateFormat* that = (RelativeDateFormat*)&other; return (fDateStyle==that->fDateStyle && - fTimeStyle==that->fTimeStyle && - fLocale==that->fLocale); + fDatePattern==that->fDatePattern && + fTimePattern==that->fTimePattern && + fLocale==that->fLocale ); } return FALSE; } +static const UChar APOSTROPHE = (UChar)0x0027; + UnicodeString& RelativeDateFormat::format( Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const { UErrorCode status = U_ZERO_ERROR; - UChar emptyStr = 0; - UnicodeString dateString(&emptyStr); + UnicodeString relativeDayString; + UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); // calculate the difference, in days, between 'cal' and now. int dayDiff = dayDifference(cal, status); // look up string - int32_t len; + int32_t len = 0; const UChar *theString = getStringForDay(dayDiff, len, status); if(U_SUCCESS(status) && (theString!=NULL)) { // found a relative string - dateString.setTo(theString, len); + relativeDayString.setTo(theString, len); } - - if(fTimeFormat == NULL || fCombinedFormat == 0) { - if (dateString.length() > 0) { - appendTo.append(dateString); - } else if(fDateFormat != NULL) { - fDateFormat->format(cal,appendTo,pos); + + if ( relativeDayString.length() > 0 && !fDatePattern.isEmpty() && + (fTimePattern.isEmpty() || fCombinedFormat == NULL || fCombinedHasDateAtStart)) { +#if !UCONFIG_NO_BREAK_ITERATION + // capitalize relativeDayString according to context for relative, set formatter no context + if ( u_islower(relativeDayString.char32At(0)) && fCapitalizationBrkIter!= NULL && + ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || + (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) || + (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone) ) ) { + // titlecase first word of relativeDayString + relativeDayString.toTitle(fCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); } +#endif + fDateTimeFormatter->setContext(UDISPCTX_CAPITALIZATION_NONE, status); } else { - if (dateString.length() == 0 && fDateFormat != NULL) { - fDateFormat->format(cal,dateString,pos); + // set our context for the formatter + fDateTimeFormatter->setContext(capitalizationContext, status); + } + + if (fDatePattern.isEmpty()) { + fDateTimeFormatter->applyPattern(fTimePattern); + fDateTimeFormatter->format(cal,appendTo,pos); + } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { + if (relativeDayString.length() > 0) { + appendTo.append(relativeDayString); + } else { + fDateTimeFormatter->applyPattern(fDatePattern); + fDateTimeFormatter->format(cal,appendTo,pos); } - UnicodeString timeString(&emptyStr); - FieldPosition timepos = pos; - fTimeFormat->format(cal,timeString,timepos); - Formattable timeDateStrings[] = { timeString, dateString }; - fCombinedFormat->format(timeDateStrings, 2, appendTo, pos, status); // pos is ignored by this - int32_t offset; - if (pos.getEndIndex() > 0 && (offset = appendTo.indexOf(dateString)) >= 0) { - // pos.field was found in dateString, offset start & end based on final position of dateString - pos.setBeginIndex( pos.getBeginIndex() + offset ); - pos.setEndIndex( pos.getEndIndex() + offset ); - } else if (timepos.getEndIndex() > 0 && (offset = appendTo.indexOf(timeString)) >= 0) { - // pos.field was found in timeString, offset start & end based on final position of timeString - pos.setBeginIndex( timepos.getBeginIndex() + offset ); - pos.setEndIndex( timepos.getEndIndex() + offset ); + } else { + UnicodeString datePattern; + if (relativeDayString.length() > 0) { + // Need to quote the relativeDayString to make it a legal date pattern + relativeDayString.findAndReplace(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // double any existing APOSTROPHE + relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning... + relativeDayString.append(APOSTROPHE); // and at end + datePattern.setTo(relativeDayString); + } else { + datePattern.setTo(fDatePattern); } + UnicodeString combinedPattern; + fCombinedFormat->format(fTimePattern, datePattern, combinedPattern, status); + fDateTimeFormatter->applyPattern(combinedPattern); + fDateTimeFormatter->format(cal,appendTo,pos); } - + return appendTo; } @@ -181,40 +238,96 @@ void RelativeDateFormat::parse( const UnicodeString& text, Calendar& cal, ParsePosition& pos) const { - // Can the fDateFormat parse it? - if(fDateFormat != NULL) { - ParsePosition aPos(pos); - fDateFormat->parse(text,cal,aPos); - if((aPos.getIndex() != pos.getIndex()) && - (aPos.getErrorIndex()==-1)) { - pos=aPos; // copy the sub parse - return; // parsed subfmt OK + int32_t startIndex = pos.getIndex(); + if (fDatePattern.isEmpty()) { + // no date pattern, try parsing as time + fDateTimeFormatter->applyPattern(fTimePattern); + fDateTimeFormatter->parse(text,cal,pos); + } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { + // no time pattern or way to combine, try parsing as date + // first check whether text matches a relativeDayString + UBool matchedRelative = FALSE; + for (int n=0; n < fDatesLen && !matchedRelative; n++) { + if (fDates[n].string != NULL && + text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) { + // it matched, handle the relative day string + UErrorCode status = U_ZERO_ERROR; + matchedRelative = TRUE; + + // Set the calendar to now+offset + cal.setTime(Calendar::getNow(),status); + cal.add(UCAL_DATE,fDates[n].offset, status); + + if(U_FAILURE(status)) { + // failure in setting calendar field, set offset to beginning of rel day string + pos.setErrorIndex(startIndex); + } else { + pos.setIndex(startIndex + fDates[n].len); + } + } } - } - - // Linear search the relative strings - for(int n=0;napplyPattern(fDatePattern); + fDateTimeFormatter->parse(text,cal,pos); + } + } else { + // Here we replace any relativeDayString in text with the equivalent date + // formatted per fDatePattern, then parse text normally using the combined pattern. + UnicodeString modifiedText(text); + FieldPosition fPos; + int32_t dateStart = 0, origDateLen = 0, modDateLen = 0; + UErrorCode status = U_ZERO_ERROR; + for (int n=0; n < fDatesLen; n++) { + int32_t relativeStringOffset; + if (fDates[n].string != NULL && + (relativeStringOffset = modifiedText.indexOf(fDates[n].string, fDates[n].len, startIndex)) >= startIndex) { + // it matched, replace the relative date with a real one for parsing + UnicodeString dateString; + Calendar * tempCal = cal.clone(); + + // Set the calendar to now+offset + tempCal->setTime(Calendar::getNow(),status); + tempCal->add(UCAL_DATE,fDates[n].offset, status); + if(U_FAILURE(status)) { + pos.setErrorIndex(startIndex); + delete tempCal; + return; + } + + fDateTimeFormatter->applyPattern(fDatePattern); + fDateTimeFormatter->format(*tempCal, dateString, fPos); + dateStart = relativeStringOffset; + origDateLen = fDates[n].len; + modDateLen = dateString.length(); + modifiedText.replace(dateStart, origDateLen, dateString); + delete tempCal; + break; } - return; + } + UnicodeString combinedPattern; + fCombinedFormat->format(fTimePattern, fDatePattern, combinedPattern, status); + fDateTimeFormatter->applyPattern(combinedPattern); + fDateTimeFormatter->parse(modifiedText,cal,pos); + + // Adjust offsets + UBool noError = (pos.getErrorIndex() < 0); + int32_t offset = (noError)? pos.getIndex(): pos.getErrorIndex(); + if (offset >= dateStart + modDateLen) { + // offset at or after the end of the replaced text, + // correct by the difference between original and replacement + offset -= (modDateLen - origDateLen); + } else if (offset >= dateStart) { + // offset in the replaced text, set it to the beginning of that text + // (i.e. the beginning of the relative day string) + offset = dateStart; + } + if (noError) { + pos.setIndex(offset); + } else { + pos.setErrorIndex(offset); } } - - // parse failed } UDate @@ -238,20 +351,15 @@ const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErr if(U_FAILURE(status)) { return NULL; } - - // Is it outside the resource bundle's range? - if(day < fDayMin || day > fDayMax) { - return NULL; // don't have it. - } - - // Linear search the held strings - for(int n=0;n= 0 && n < fDatesLen) { + if (fDates[n].offset == day && fDates[n].string != NULL) { len = fDates[n].len; return fDates[n].string; } } - return NULL; // not found. } @@ -260,23 +368,12 @@ RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const { if (!U_FAILURE(status)) { result.remove(); - if (fTimeFormat == NULL || fCombinedFormat == 0) { - if (fDateFormat != NULL) { - UnicodeString datePattern; - this->toPatternDate(datePattern, status); - if (!U_FAILURE(status)) { - result.setTo(datePattern); - } - } + if (fDatePattern.isEmpty()) { + result.setTo(fTimePattern); + } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { + result.setTo(fDatePattern); } else { - UnicodeString datePattern, timePattern; - this->toPatternDate(datePattern, status); - this->toPatternTime(timePattern, status); - if (!U_FAILURE(status)) { - Formattable timeDatePatterns[] = { timePattern, datePattern }; - FieldPosition pos; - fCombinedFormat->format(timeDatePatterns, 2, result, pos, status); - } + fCombinedFormat->format(fTimePattern, fDatePattern, result, status); } } return result; @@ -287,13 +384,7 @@ RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) con { if (!U_FAILURE(status)) { result.remove(); - if ( fDateFormat ) { - if ( fDateFormat->getDynamicClassID()==SimpleDateFormat::getStaticClassID() ) { - ((SimpleDateFormat*)fDateFormat)->toPattern(result); - } else { - status = U_UNSUPPORTED_ERROR; - } - } + result.setTo(fDatePattern); } return result; } @@ -303,13 +394,7 @@ RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) con { if (!U_FAILURE(status)) { result.remove(); - if ( fTimeFormat ) { - if ( fTimeFormat->getDynamicClassID()==SimpleDateFormat::getStaticClassID() ) { - ((SimpleDateFormat*)fTimeFormat)->toPattern(result); - } else { - status = U_UNSUPPORTED_ERROR; - } - } + result.setTo(fTimePattern); } return result; } @@ -318,89 +403,161 @@ void RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status) { if (!U_FAILURE(status)) { - if ( fDateFormat && fDateFormat->getDynamicClassID()!=SimpleDateFormat::getStaticClassID() ) { - status = U_UNSUPPORTED_ERROR; - return; - } - if ( fTimeFormat && fTimeFormat->getDynamicClassID()!=SimpleDateFormat::getStaticClassID() ) { - status = U_UNSUPPORTED_ERROR; - return; - } - if ( fDateFormat ) { - ((SimpleDateFormat*)fDateFormat)->applyPattern(datePattern); + fDatePattern.setTo(datePattern); + fTimePattern.setTo(timePattern); + } +} + +const DateFormatSymbols* +RelativeDateFormat::getDateFormatSymbols() const +{ + return fDateTimeFormatter->getDateFormatSymbols(); +} + +// override the DateFormat implementation in order to +// lazily initialize relevant items +void +RelativeDateFormat::setContext(UDisplayContext value, UErrorCode& status) +{ + DateFormat::setContext(value, status); + if (U_SUCCESS(status)) { + if (!fCapitalizationInfoSet && + (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) { + initCapitalizationContextInfo(fLocale); + fCapitalizationInfoSet = TRUE; } - if ( fTimeFormat ) { - ((SimpleDateFormat*)fTimeFormat)->applyPattern(timePattern); +#if !UCONFIG_NO_BREAK_ITERATION + if ( fCapitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || + (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) || + (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone)) ) { + status = U_ZERO_ERROR; + fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status); + if (U_FAILURE(status)) { + delete fCapitalizationBrkIter; + fCapitalizationBrkIter = NULL; + } } +#endif } } -void RelativeDateFormat::loadDates(UErrorCode &status) { - CalendarData calData(fLocale, "gregorian", status); - - UErrorCode tempStatus = status; - UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, tempStatus); - if(U_SUCCESS(tempStatus) && ures_getSize(dateTimePatterns) > DateFormat::kDateTime) { - int32_t resStrLen = 0; - const UChar *resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)DateFormat::kDateTime, &resStrLen, &tempStatus); - fCombinedFormat = new MessageFormat(UnicodeString(TRUE, resStr, resStrLen), fLocale, tempStatus); +void +RelativeDateFormat::initCapitalizationContextInfo(const Locale& thelocale) +{ +#if !UCONFIG_NO_BREAK_ITERATION + const char * localeID = (thelocale != NULL)? thelocale.getBaseName(): NULL; + UErrorCode status = U_ZERO_ERROR; + LocalUResourceBundlePointer rb(ures_open(NULL, localeID, &status)); + ures_getByKeyWithFallback(rb.getAlias(), + "contextTransforms/relative", + rb.getAlias(), &status); + if (U_SUCCESS(status) && rb != NULL) { + int32_t len = 0; + const int32_t * intVector = ures_getIntVector(rb.getAlias(), + &len, &status); + if (U_SUCCESS(status) && intVector != NULL && len >= 2) { + fCapitalizationOfRelativeUnitsForUIListMenu = static_cast(intVector[0]); + fCapitalizationOfRelativeUnitsForStandAlone = static_cast(intVector[1]); + } } +#endif +} - UResourceBundle *strings = calData.getByKey3("fields", "day", "relative", status); - // set up min/max - fDayMin=-1; - fDayMax=1; +namespace { - if(U_FAILURE(status)) { - fDatesLen=0; - return; - } +/** + * Sink for getting data from fields/day/relative data. + * For loading relative day names, e.g., "yesterday", "today". + */ - fDatesLen = ures_getSize(strings); - fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); +struct RelDateFmtDataSink : public ResourceSink { + URelativeString *fDatesPtr; + int32_t fDatesLen; + + RelDateFmtDataSink(URelativeString* fDates, int32_t len) : fDatesPtr(fDates), fDatesLen(len) { + for (int32_t i = 0; i < fDatesLen; ++i) { + fDatesPtr[i].offset = 0; + fDatesPtr[i].string = NULL; + fDatesPtr[i].len = -1; + } + } - // Load in each item into the array... - int n = 0; + virtual ~RelDateFmtDataSink(); - UResourceBundle *subString = NULL; - - while(ures_hasNext(strings) && U_SUCCESS(status)) { // iterate over items - subString = ures_getNextResource(strings, subString, &status); - - if(U_FAILURE(status) || (subString==NULL)) break; - - // key = offset # - const char *key = ures_getKey(subString); - - // load the string and length - int32_t aLen; - const UChar* aString = ures_getString(subString, &aLen, &status); - - if(U_FAILURE(status) || aString == NULL) break; - - // calculate the offset + virtual void put(const char *key, ResourceValue &value, + UBool /*noFallback*/, UErrorCode &errorCode) { + ResourceTable relDayTable = value.getTable(errorCode); + int32_t n = 0; + int32_t len = 0; + for (int32_t i = 0; relDayTable.getKeyAndValue(i, key, value); ++i) { + // Find the relative offset. int32_t offset = atoi(key); - - // set min/max - if(offset < fDayMin) { - fDayMin = offset; + + // Put in the proper spot, but don't override existing data. + n = offset + UDAT_DIRECTION_THIS; // Converts to index in UDAT_R + if (n < fDatesLen && fDatesPtr[n].string == NULL) { + // Not found and n is an empty slot. + fDatesPtr[n].offset = offset; + fDatesPtr[n].string = value.getString(len, errorCode); + fDatesPtr[n].len = len; } - if(offset > fDayMax) { - fDayMax = offset; + } + } +}; + + +// Virtual destructors must be defined out of line. +RelDateFmtDataSink::~RelDateFmtDataSink() {} + +} // Namespace + + +static const UChar patItem1[] = {0x7B,0x31,0x7D}; // "{1}" +static const int32_t patItem1Len = 3; + +void RelativeDateFormat::loadDates(UErrorCode &status) { + UResourceBundle *rb = ures_open(NULL, fLocale.getBaseName(), &status); + LocalUResourceBundlePointer dateTimePatterns( + ures_getByKeyWithFallback(rb, + "calendar/gregorian/DateTimePatterns", + (UResourceBundle*)NULL, &status)); + if(U_SUCCESS(status)) { + int32_t patternsSize = ures_getSize(dateTimePatterns.getAlias()); + if (patternsSize > kDateTime) { + int32_t resStrLen = 0; + int32_t glueIndex = kDateTime; + if (patternsSize >= (kDateTimeOffset + kShort + 1)) { + int32_t offsetIncrement = (fDateStyle & ~kRelative); // Remove relative bit. + if (offsetIncrement >= (int32_t)kFull && + offsetIncrement <= (int32_t)kShortRelative) { + glueIndex = kDateTimeOffset + offsetIncrement; + } + } + + const UChar *resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), glueIndex, &resStrLen, &status); + if (U_SUCCESS(status) && resStrLen >= patItem1Len && u_strncmp(resStr,patItem1,patItem1Len)==0) { + fCombinedHasDateAtStart = TRUE; + } + fCombinedFormat = new SimpleFormatter(UnicodeString(TRUE, resStr, resStrLen), 2, 2, status); } - - // copy the string pointer - fDates[n].offset = offset; - fDates[n].string = aString; - fDates[n].len = aLen; + } + + // Data loading for relative names, e.g., "yesterday", "today", "tomorrow". + fDatesLen = UDAT_DIRECTION_COUNT; // Maximum defined by data. + fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); + + RelDateFmtDataSink sink(fDates, fDatesLen); + ures_getAllItemsWithFallback(rb, "fields/day/relative", sink, status); - n++; + ures_close(rb); + + if(U_FAILURE(status)) { + fDatesLen=0; + return; } - ures_close(subString); - - // the fDates[] array could be sorted here, for direct access. } +//---------------------------------------------------------------------- // this should to be in DateFormat, instead it was copied from SimpleDateFormat. @@ -435,5 +592,4 @@ int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) { U_NAMESPACE_END -#endif - +#endif /* !UCONFIG_NO_FORMATTING */