| Index: source/i18n/reldatefmt.cpp
|
| diff --git a/source/i18n/reldatefmt.cpp b/source/i18n/reldatefmt.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b4bfe429dd6aca723392dc746e3f8bc50c6604a4
|
| --- /dev/null
|
| +++ b/source/i18n/reldatefmt.cpp
|
| @@ -0,0 +1,901 @@
|
| +/*
|
| +******************************************************************************
|
| +* Copyright (C) 2014, International Business Machines Corporation and
|
| +* others. All Rights Reserved.
|
| +******************************************************************************
|
| +*
|
| +* File RELDATEFMT.CPP
|
| +******************************************************************************
|
| +*/
|
| +
|
| +#include "unicode/reldatefmt.h"
|
| +
|
| +#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION
|
| +
|
| +#include "unicode/localpointer.h"
|
| +#include "quantityformatter.h"
|
| +#include "unicode/plurrule.h"
|
| +#include "unicode/msgfmt.h"
|
| +#include "unicode/decimfmt.h"
|
| +#include "unicode/numfmt.h"
|
| +#include "unicode/brkiter.h"
|
| +#include "uresimp.h"
|
| +#include "unicode/ures.h"
|
| +#include "cstring.h"
|
| +#include "ucln_in.h"
|
| +#include "mutex.h"
|
| +#include "charstr.h"
|
| +#include "uassert.h"
|
| +
|
| +#include "sharedbreakiterator.h"
|
| +#include "sharedpluralrules.h"
|
| +#include "sharednumberformat.h"
|
| +#include "unifiedcache.h"
|
| +
|
| +// Copied from uscript_props.cpp
|
| +
|
| +static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
|
| +
|
| +U_NAMESPACE_BEGIN
|
| +
|
| +// RelativeDateTimeFormatter specific data for a single locale
|
| +class RelativeDateTimeCacheData: public SharedObject {
|
| +public:
|
| + RelativeDateTimeCacheData() : combinedDateAndTime(NULL) { }
|
| + virtual ~RelativeDateTimeCacheData();
|
| +
|
| + // no numbers: e.g Next Tuesday; Yesterday; etc.
|
| + UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
|
| +
|
| + // has numbers: e.g Next Tuesday; Yesterday; etc. For second index, 0
|
| + // means past e.g 5 days ago; 1 means future e.g in 5 days.
|
| + QuantityFormatter relativeUnits[UDAT_STYLE_COUNT][UDAT_RELATIVE_UNIT_COUNT][2];
|
| +
|
| + void adoptCombinedDateAndTime(MessageFormat *mfToAdopt) {
|
| + delete combinedDateAndTime;
|
| + combinedDateAndTime = mfToAdopt;
|
| + }
|
| + const MessageFormat *getCombinedDateAndTime() const {
|
| + return combinedDateAndTime;
|
| + }
|
| +private:
|
| + MessageFormat *combinedDateAndTime;
|
| + RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
|
| + RelativeDateTimeCacheData& operator=(
|
| + const RelativeDateTimeCacheData &other);
|
| +};
|
| +
|
| +RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
|
| + delete combinedDateAndTime;
|
| +}
|
| +
|
| +static UBool getStringWithFallback(
|
| + const UResourceBundle *resource,
|
| + const char *key,
|
| + UnicodeString &result,
|
| + UErrorCode &status) {
|
| + int32_t len = 0;
|
| + const UChar *resStr = ures_getStringByKeyWithFallback(
|
| + resource, key, &len, &status);
|
| + if (U_FAILURE(status)) {
|
| + return FALSE;
|
| + }
|
| + result.setTo(TRUE, resStr, len);
|
| + return TRUE;
|
| +}
|
| +
|
| +static UBool getOptionalStringWithFallback(
|
| + const UResourceBundle *resource,
|
| + const char *key,
|
| + UnicodeString &result,
|
| + UErrorCode &status) {
|
| + if (U_FAILURE(status)) {
|
| + return FALSE;
|
| + }
|
| + int32_t len = 0;
|
| + const UChar *resStr = ures_getStringByKey(
|
| + resource, key, &len, &status);
|
| + if (status == U_MISSING_RESOURCE_ERROR) {
|
| + result.remove();
|
| + status = U_ZERO_ERROR;
|
| + return TRUE;
|
| + }
|
| + if (U_FAILURE(status)) {
|
| + return FALSE;
|
| + }
|
| + result.setTo(TRUE, resStr, len);
|
| + return TRUE;
|
| +}
|
| +
|
| +static UBool getString(
|
| + const UResourceBundle *resource,
|
| + UnicodeString &result,
|
| + UErrorCode &status) {
|
| + int32_t len = 0;
|
| + const UChar *resStr = ures_getString(resource, &len, &status);
|
| + if (U_FAILURE(status)) {
|
| + return FALSE;
|
| + }
|
| + result.setTo(TRUE, resStr, len);
|
| + return TRUE;
|
| +}
|
| +
|
| +static UBool getStringByIndex(
|
| + const UResourceBundle *resource,
|
| + int32_t idx,
|
| + UnicodeString &result,
|
| + UErrorCode &status) {
|
| + int32_t len = 0;
|
| + const UChar *resStr = ures_getStringByIndex(
|
| + resource, idx, &len, &status);
|
| + if (U_FAILURE(status)) {
|
| + return FALSE;
|
| + }
|
| + result.setTo(TRUE, resStr, len);
|
| + return TRUE;
|
| +}
|
| +
|
| +static void initAbsoluteUnit(
|
| + const UResourceBundle *resource,
|
| + const UnicodeString &unitName,
|
| + UnicodeString *absoluteUnit,
|
| + UErrorCode &status) {
|
| + getStringWithFallback(
|
| + resource,
|
| + "-1",
|
| + absoluteUnit[UDAT_DIRECTION_LAST],
|
| + status);
|
| + getStringWithFallback(
|
| + resource,
|
| + "0",
|
| + absoluteUnit[UDAT_DIRECTION_THIS],
|
| + status);
|
| + getStringWithFallback(
|
| + resource,
|
| + "1",
|
| + absoluteUnit[UDAT_DIRECTION_NEXT],
|
| + status);
|
| + getOptionalStringWithFallback(
|
| + resource,
|
| + "-2",
|
| + absoluteUnit[UDAT_DIRECTION_LAST_2],
|
| + status);
|
| + getOptionalStringWithFallback(
|
| + resource,
|
| + "2",
|
| + absoluteUnit[UDAT_DIRECTION_NEXT_2],
|
| + status);
|
| + absoluteUnit[UDAT_DIRECTION_PLAIN] = unitName;
|
| +}
|
| +
|
| +static void initQuantityFormatter(
|
| + const UResourceBundle *resource,
|
| + QuantityFormatter &formatter,
|
| + UErrorCode &status) {
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + int32_t size = ures_getSize(resource);
|
| + for (int32_t i = 0; i < size; ++i) {
|
| + LocalUResourceBundlePointer pluralBundle(
|
| + ures_getByIndex(resource, i, NULL, &status));
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + UnicodeString rawPattern;
|
| + if (!getString(pluralBundle.getAlias(), rawPattern, status)) {
|
| + return;
|
| + }
|
| + if (!formatter.add(
|
| + ures_getKey(pluralBundle.getAlias()),
|
| + rawPattern,
|
| + status)) {
|
| + return;
|
| + }
|
| + }
|
| +}
|
| +
|
| +static void initRelativeUnit(
|
| + const UResourceBundle *resource,
|
| + QuantityFormatter *relativeUnit,
|
| + UErrorCode &status) {
|
| + LocalUResourceBundlePointer topLevel(
|
| + ures_getByKeyWithFallback(
|
| + resource, "relativeTime", NULL, &status));
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + LocalUResourceBundlePointer futureBundle(ures_getByKeyWithFallback(
|
| + topLevel.getAlias(), "future", NULL, &status));
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + initQuantityFormatter(
|
| + futureBundle.getAlias(),
|
| + relativeUnit[1],
|
| + status);
|
| + LocalUResourceBundlePointer pastBundle(ures_getByKeyWithFallback(
|
| + topLevel.getAlias(), "past", NULL, &status));
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + initQuantityFormatter(
|
| + pastBundle.getAlias(),
|
| + relativeUnit[0],
|
| + status);
|
| +}
|
| +
|
| +static void initRelativeUnit(
|
| + const UResourceBundle *resource,
|
| + const char *path,
|
| + QuantityFormatter *relativeUnit,
|
| + UErrorCode &status) {
|
| + LocalUResourceBundlePointer topLevel(
|
| + ures_getByKeyWithFallback(resource, path, NULL, &status));
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + initRelativeUnit(topLevel.getAlias(), relativeUnit, status);
|
| +}
|
| +
|
| +static void addTimeUnit(
|
| + const UResourceBundle *resource,
|
| + const char *path,
|
| + QuantityFormatter *relativeUnit,
|
| + UnicodeString *absoluteUnit,
|
| + UErrorCode &status) {
|
| + LocalUResourceBundlePointer topLevel(
|
| + ures_getByKeyWithFallback(resource, path, NULL, &status));
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + initRelativeUnit(topLevel.getAlias(), relativeUnit, status);
|
| + UnicodeString unitName;
|
| + if (!getStringWithFallback(topLevel.getAlias(), "dn", unitName, status)) {
|
| + return;
|
| + }
|
| + // TODO(Travis Keep): This is a hack to get around CLDR bug 6818.
|
| + const char *localeId = ures_getLocaleByType(
|
| + topLevel.getAlias(), ULOC_ACTUAL_LOCALE, &status);
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + Locale locale(localeId);
|
| + if (uprv_strcmp("en", locale.getLanguage()) == 0) {
|
| + unitName.toLower();
|
| + }
|
| + // end hack
|
| + ures_getByKeyWithFallback(
|
| + topLevel.getAlias(), "relative", topLevel.getAlias(), &status);
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + initAbsoluteUnit(
|
| + topLevel.getAlias(),
|
| + unitName,
|
| + absoluteUnit,
|
| + status);
|
| +}
|
| +
|
| +static void readDaysOfWeek(
|
| + const UResourceBundle *resource,
|
| + const char *path,
|
| + UnicodeString *daysOfWeek,
|
| + UErrorCode &status) {
|
| + LocalUResourceBundlePointer topLevel(
|
| + ures_getByKeyWithFallback(resource, path, NULL, &status));
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + int32_t size = ures_getSize(topLevel.getAlias());
|
| + if (size != 7) {
|
| + status = U_INTERNAL_PROGRAM_ERROR;
|
| + return;
|
| + }
|
| + for (int32_t i = 0; i < size; ++i) {
|
| + if (!getStringByIndex(topLevel.getAlias(), i, daysOfWeek[i], status)) {
|
| + return;
|
| + }
|
| + }
|
| +}
|
| +
|
| +static void addWeekDay(
|
| + const UResourceBundle *resource,
|
| + const char *path,
|
| + const UnicodeString *daysOfWeek,
|
| + UDateAbsoluteUnit absoluteUnit,
|
| + UnicodeString absoluteUnits[][UDAT_DIRECTION_COUNT],
|
| + UErrorCode &status) {
|
| + LocalUResourceBundlePointer topLevel(
|
| + ures_getByKeyWithFallback(resource, path, NULL, &status));
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + initAbsoluteUnit(
|
| + topLevel.getAlias(),
|
| + daysOfWeek[absoluteUnit - UDAT_ABSOLUTE_SUNDAY],
|
| + absoluteUnits[absoluteUnit],
|
| + status);
|
| +}
|
| +
|
| +static void addTimeUnits(
|
| + const UResourceBundle *resource,
|
| + const char *path, const char *pathShort, const char *pathNarrow,
|
| + UDateRelativeUnit relativeUnit,
|
| + UDateAbsoluteUnit absoluteUnit,
|
| + RelativeDateTimeCacheData &cacheData,
|
| + UErrorCode &status) {
|
| + addTimeUnit(
|
| + resource,
|
| + path,
|
| + cacheData.relativeUnits[UDAT_STYLE_LONG][relativeUnit],
|
| + cacheData.absoluteUnits[UDAT_STYLE_LONG][absoluteUnit],
|
| + status);
|
| + addTimeUnit(
|
| + resource,
|
| + pathShort,
|
| + cacheData.relativeUnits[UDAT_STYLE_SHORT][relativeUnit],
|
| + cacheData.absoluteUnits[UDAT_STYLE_SHORT][absoluteUnit],
|
| + status);
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + addTimeUnit(
|
| + resource,
|
| + pathNarrow,
|
| + cacheData.relativeUnits[UDAT_STYLE_NARROW][relativeUnit],
|
| + cacheData.absoluteUnits[UDAT_STYLE_NARROW][absoluteUnit],
|
| + status);
|
| + if (status == U_MISSING_RESOURCE_ERROR) {
|
| + // retry addTimeUnit for UDAT_STYLE_NARROW using pathShort
|
| + status = U_ZERO_ERROR;
|
| + addTimeUnit(
|
| + resource,
|
| + pathShort,
|
| + cacheData.relativeUnits[UDAT_STYLE_NARROW][relativeUnit],
|
| + cacheData.absoluteUnits[UDAT_STYLE_NARROW][absoluteUnit],
|
| + status);
|
| + }
|
| +}
|
| +
|
| +static void initRelativeUnits(
|
| + const UResourceBundle *resource,
|
| + const char *path, const char *pathShort, const char *pathNarrow,
|
| + UDateRelativeUnit relativeUnit,
|
| + QuantityFormatter relativeUnits[][UDAT_RELATIVE_UNIT_COUNT][2],
|
| + UErrorCode &status) {
|
| + initRelativeUnit(
|
| + resource,
|
| + path,
|
| + relativeUnits[UDAT_STYLE_LONG][relativeUnit],
|
| + status);
|
| + initRelativeUnit(
|
| + resource,
|
| + pathShort,
|
| + relativeUnits[UDAT_STYLE_SHORT][relativeUnit],
|
| + status);
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + initRelativeUnit(
|
| + resource,
|
| + pathNarrow,
|
| + relativeUnits[UDAT_STYLE_NARROW][relativeUnit],
|
| + status);
|
| + if (status == U_MISSING_RESOURCE_ERROR) {
|
| + // retry initRelativeUnit for UDAT_STYLE_NARROW using pathShort
|
| + status = U_ZERO_ERROR;
|
| + initRelativeUnit(
|
| + resource,
|
| + pathShort,
|
| + relativeUnits[UDAT_STYLE_NARROW][relativeUnit],
|
| + status);
|
| + }
|
| +}
|
| +
|
| +static void addWeekDays(
|
| + const UResourceBundle *resource,
|
| + const char *path, const char *pathShort, const char *pathNarrow,
|
| + const UnicodeString daysOfWeek[][7],
|
| + UDateAbsoluteUnit absoluteUnit,
|
| + UnicodeString absoluteUnits[][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
|
| + UErrorCode &status) {
|
| + addWeekDay(
|
| + resource,
|
| + path,
|
| + daysOfWeek[UDAT_STYLE_LONG],
|
| + absoluteUnit,
|
| + absoluteUnits[UDAT_STYLE_LONG],
|
| + status);
|
| + addWeekDay(
|
| + resource,
|
| + pathShort,
|
| + daysOfWeek[UDAT_STYLE_SHORT],
|
| + absoluteUnit,
|
| + absoluteUnits[UDAT_STYLE_SHORT],
|
| + status);
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + addWeekDay(
|
| + resource,
|
| + pathNarrow,
|
| + daysOfWeek[UDAT_STYLE_NARROW],
|
| + absoluteUnit,
|
| + absoluteUnits[UDAT_STYLE_NARROW],
|
| + status);
|
| + if (status == U_MISSING_RESOURCE_ERROR) {
|
| + // retry addWeekDay for UDAT_STYLE_NARROW using pathShort
|
| + status = U_ZERO_ERROR;
|
| + addWeekDay(
|
| + resource,
|
| + pathShort,
|
| + daysOfWeek[UDAT_STYLE_NARROW],
|
| + absoluteUnit,
|
| + absoluteUnits[UDAT_STYLE_NARROW],
|
| + status);
|
| + }
|
| +}
|
| +
|
| +static UBool loadUnitData(
|
| + const UResourceBundle *resource,
|
| + RelativeDateTimeCacheData &cacheData,
|
| + UErrorCode &status) {
|
| + addTimeUnits(
|
| + resource,
|
| + "fields/day", "fields/day-short", "fields/day-narrow",
|
| + UDAT_RELATIVE_DAYS,
|
| + UDAT_ABSOLUTE_DAY,
|
| + cacheData,
|
| + status);
|
| + addTimeUnits(
|
| + resource,
|
| + "fields/week", "fields/week-short", "fields/week-narrow",
|
| + UDAT_RELATIVE_WEEKS,
|
| + UDAT_ABSOLUTE_WEEK,
|
| + cacheData,
|
| + status);
|
| + addTimeUnits(
|
| + resource,
|
| + "fields/month", "fields/month-short", "fields/month-narrow",
|
| + UDAT_RELATIVE_MONTHS,
|
| + UDAT_ABSOLUTE_MONTH,
|
| + cacheData,
|
| + status);
|
| + addTimeUnits(
|
| + resource,
|
| + "fields/year", "fields/year-short", "fields/year-narrow",
|
| + UDAT_RELATIVE_YEARS,
|
| + UDAT_ABSOLUTE_YEAR,
|
| + cacheData,
|
| + status);
|
| + initRelativeUnits(
|
| + resource,
|
| + "fields/second", "fields/second-short", "fields/second-narrow",
|
| + UDAT_RELATIVE_SECONDS,
|
| + cacheData.relativeUnits,
|
| + status);
|
| + initRelativeUnits(
|
| + resource,
|
| + "fields/minute", "fields/minute-short", "fields/minute-narrow",
|
| + UDAT_RELATIVE_MINUTES,
|
| + cacheData.relativeUnits,
|
| + status);
|
| + initRelativeUnits(
|
| + resource,
|
| + "fields/hour", "fields/hour-short", "fields/hour-narrow",
|
| + UDAT_RELATIVE_HOURS,
|
| + cacheData.relativeUnits,
|
| + status);
|
| + getStringWithFallback(
|
| + resource,
|
| + "fields/second/relative/0",
|
| + cacheData.absoluteUnits[UDAT_STYLE_LONG][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN],
|
| + status);
|
| + getStringWithFallback(
|
| + resource,
|
| + "fields/second-short/relative/0",
|
| + cacheData.absoluteUnits[UDAT_STYLE_SHORT][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN],
|
| + status);
|
| + getStringWithFallback(
|
| + resource,
|
| + "fields/second-narrow/relative/0",
|
| + cacheData.absoluteUnits[UDAT_STYLE_NARROW][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN],
|
| + status);
|
| + UnicodeString daysOfWeek[UDAT_STYLE_COUNT][7];
|
| + readDaysOfWeek(
|
| + resource,
|
| + "calendar/gregorian/dayNames/stand-alone/wide",
|
| + daysOfWeek[UDAT_STYLE_LONG],
|
| + status);
|
| + readDaysOfWeek(
|
| + resource,
|
| + "calendar/gregorian/dayNames/stand-alone/short",
|
| + daysOfWeek[UDAT_STYLE_SHORT],
|
| + status);
|
| + readDaysOfWeek(
|
| + resource,
|
| + "calendar/gregorian/dayNames/stand-alone/narrow",
|
| + daysOfWeek[UDAT_STYLE_NARROW],
|
| + status);
|
| + addWeekDays(
|
| + resource,
|
| + "fields/mon/relative",
|
| + "fields/mon-short/relative",
|
| + "fields/mon-narrow/relative",
|
| + daysOfWeek,
|
| + UDAT_ABSOLUTE_MONDAY,
|
| + cacheData.absoluteUnits,
|
| + status);
|
| + addWeekDays(
|
| + resource,
|
| + "fields/tue/relative",
|
| + "fields/tue-short/relative",
|
| + "fields/tue-narrow/relative",
|
| + daysOfWeek,
|
| + UDAT_ABSOLUTE_TUESDAY,
|
| + cacheData.absoluteUnits,
|
| + status);
|
| + addWeekDays(
|
| + resource,
|
| + "fields/wed/relative",
|
| + "fields/wed-short/relative",
|
| + "fields/wed-narrow/relative",
|
| + daysOfWeek,
|
| + UDAT_ABSOLUTE_WEDNESDAY,
|
| + cacheData.absoluteUnits,
|
| + status);
|
| + addWeekDays(
|
| + resource,
|
| + "fields/thu/relative",
|
| + "fields/thu-short/relative",
|
| + "fields/thu-narrow/relative",
|
| + daysOfWeek,
|
| + UDAT_ABSOLUTE_THURSDAY,
|
| + cacheData.absoluteUnits,
|
| + status);
|
| + addWeekDays(
|
| + resource,
|
| + "fields/fri/relative",
|
| + "fields/fri-short/relative",
|
| + "fields/fri-narrow/relative",
|
| + daysOfWeek,
|
| + UDAT_ABSOLUTE_FRIDAY,
|
| + cacheData.absoluteUnits,
|
| + status);
|
| + addWeekDays(
|
| + resource,
|
| + "fields/sat/relative",
|
| + "fields/sat-short/relative",
|
| + "fields/sat-narrow/relative",
|
| + daysOfWeek,
|
| + UDAT_ABSOLUTE_SATURDAY,
|
| + cacheData.absoluteUnits,
|
| + status);
|
| + addWeekDays(
|
| + resource,
|
| + "fields/sun/relative",
|
| + "fields/sun-short/relative",
|
| + "fields/sun-narrow/relative",
|
| + daysOfWeek,
|
| + UDAT_ABSOLUTE_SUNDAY,
|
| + cacheData.absoluteUnits,
|
| + status);
|
| + return U_SUCCESS(status);
|
| +}
|
| +
|
| +static UBool getDateTimePattern(
|
| + const UResourceBundle *resource,
|
| + UnicodeString &result,
|
| + UErrorCode &status) {
|
| + UnicodeString defaultCalendarName;
|
| + if (!getStringWithFallback(
|
| + resource,
|
| + "calendar/default",
|
| + defaultCalendarName,
|
| + status)) {
|
| + return FALSE;
|
| + }
|
| + CharString pathBuffer;
|
| + pathBuffer.append("calendar/", status)
|
| + .appendInvariantChars(defaultCalendarName, status)
|
| + .append("/DateTimePatterns", status);
|
| + LocalUResourceBundlePointer topLevel(
|
| + ures_getByKeyWithFallback(
|
| + resource, pathBuffer.data(), NULL, &status));
|
| + if (U_FAILURE(status)) {
|
| + return FALSE;
|
| + }
|
| + int32_t size = ures_getSize(topLevel.getAlias());
|
| + if (size <= 8) {
|
| + // Oops, size is to small to access the index that we want, fallback
|
| + // to a hard-coded value.
|
| + result = UNICODE_STRING_SIMPLE("{1} {0}");
|
| + return TRUE;
|
| + }
|
| + return getStringByIndex(topLevel.getAlias(), 8, result, status);
|
| +}
|
| +
|
| +template<> U_I18N_API
|
| +const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
|
| + const char *localeId = fLoc.getName();
|
| + LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
|
| + if (U_FAILURE(status)) {
|
| + return NULL;
|
| + }
|
| + LocalPointer<RelativeDateTimeCacheData> result(
|
| + new RelativeDateTimeCacheData());
|
| + if (result.isNull()) {
|
| + status = U_MEMORY_ALLOCATION_ERROR;
|
| + return NULL;
|
| + }
|
| + if (!loadUnitData(
|
| + topLevel.getAlias(),
|
| + *result,
|
| + status)) {
|
| + return NULL;
|
| + }
|
| + UnicodeString dateTimePattern;
|
| + if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
|
| + return NULL;
|
| + }
|
| + result->adoptCombinedDateAndTime(
|
| + new MessageFormat(dateTimePattern, localeId, status));
|
| + if (U_FAILURE(status)) {
|
| + return NULL;
|
| + }
|
| + result->addRef();
|
| + return result.orphan();
|
| +}
|
| +
|
| +RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
|
| + fCache(NULL),
|
| + fNumberFormat(NULL),
|
| + fPluralRules(NULL),
|
| + fStyle(UDAT_STYLE_LONG),
|
| + fContext(UDISPCTX_CAPITALIZATION_NONE),
|
| + fOptBreakIterator(NULL) {
|
| + init(NULL, NULL, status);
|
| +}
|
| +
|
| +RelativeDateTimeFormatter::RelativeDateTimeFormatter(
|
| + const Locale& locale, UErrorCode& status) :
|
| + fCache(NULL),
|
| + fNumberFormat(NULL),
|
| + fPluralRules(NULL),
|
| + fStyle(UDAT_STYLE_LONG),
|
| + fContext(UDISPCTX_CAPITALIZATION_NONE),
|
| + fOptBreakIterator(NULL),
|
| + fLocale(locale) {
|
| + init(NULL, NULL, status);
|
| +}
|
| +
|
| +RelativeDateTimeFormatter::RelativeDateTimeFormatter(
|
| + const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
|
| + fCache(NULL),
|
| + fNumberFormat(NULL),
|
| + fPluralRules(NULL),
|
| + fStyle(UDAT_STYLE_LONG),
|
| + fContext(UDISPCTX_CAPITALIZATION_NONE),
|
| + fOptBreakIterator(NULL),
|
| + fLocale(locale) {
|
| + init(nfToAdopt, NULL, status);
|
| +}
|
| +
|
| +RelativeDateTimeFormatter::RelativeDateTimeFormatter(
|
| + const Locale& locale,
|
| + NumberFormat *nfToAdopt,
|
| + UDateRelativeDateTimeFormatterStyle styl,
|
| + UDisplayContext capitalizationContext,
|
| + UErrorCode& status) :
|
| + fCache(NULL),
|
| + fNumberFormat(NULL),
|
| + fPluralRules(NULL),
|
| + fStyle(styl),
|
| + fContext(capitalizationContext),
|
| + fOptBreakIterator(NULL),
|
| + fLocale(locale) {
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) {
|
| + status = U_ILLEGAL_ARGUMENT_ERROR;
|
| + return;
|
| + }
|
| + if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
|
| + BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status);
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + init(nfToAdopt, bi, status);
|
| + } else {
|
| + init(nfToAdopt, NULL, status);
|
| + }
|
| +}
|
| +
|
| +RelativeDateTimeFormatter::RelativeDateTimeFormatter(
|
| + const RelativeDateTimeFormatter& other)
|
| + : UObject(other),
|
| + fCache(other.fCache),
|
| + fNumberFormat(other.fNumberFormat),
|
| + fPluralRules(other.fPluralRules),
|
| + fStyle(other.fStyle),
|
| + fContext(other.fContext),
|
| + fOptBreakIterator(other.fOptBreakIterator),
|
| + fLocale(other.fLocale) {
|
| + fCache->addRef();
|
| + fNumberFormat->addRef();
|
| + fPluralRules->addRef();
|
| + if (fOptBreakIterator != NULL) {
|
| + fOptBreakIterator->addRef();
|
| + }
|
| +}
|
| +
|
| +RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
|
| + const RelativeDateTimeFormatter& other) {
|
| + if (this != &other) {
|
| + SharedObject::copyPtr(other.fCache, fCache);
|
| + SharedObject::copyPtr(other.fNumberFormat, fNumberFormat);
|
| + SharedObject::copyPtr(other.fPluralRules, fPluralRules);
|
| + SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator);
|
| + fStyle = other.fStyle;
|
| + fContext = other.fContext;
|
| + fLocale = other.fLocale;
|
| + }
|
| + return *this;
|
| +}
|
| +
|
| +RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
|
| + if (fCache != NULL) {
|
| + fCache->removeRef();
|
| + }
|
| + if (fNumberFormat != NULL) {
|
| + fNumberFormat->removeRef();
|
| + }
|
| + if (fPluralRules != NULL) {
|
| + fPluralRules->removeRef();
|
| + }
|
| + if (fOptBreakIterator != NULL) {
|
| + fOptBreakIterator->removeRef();
|
| + }
|
| +}
|
| +
|
| +const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
|
| + return **fNumberFormat;
|
| +}
|
| +
|
| +UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
|
| + return fContext;
|
| +}
|
| +
|
| +UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
|
| + return fStyle;
|
| +}
|
| +
|
| +UnicodeString& RelativeDateTimeFormatter::format(
|
| + double quantity, UDateDirection direction, UDateRelativeUnit unit,
|
| + UnicodeString& appendTo, UErrorCode& status) const {
|
| + if (U_FAILURE(status)) {
|
| + return appendTo;
|
| + }
|
| + if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
|
| + status = U_ILLEGAL_ARGUMENT_ERROR;
|
| + return appendTo;
|
| + }
|
| + int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
|
| + FieldPosition pos(FieldPosition::DONT_CARE);
|
| + if (fOptBreakIterator == NULL) {
|
| + return fCache->relativeUnits[fStyle][unit][bFuture].format(
|
| + quantity,
|
| + **fNumberFormat,
|
| + **fPluralRules,
|
| + appendTo,
|
| + pos,
|
| + status);
|
| + }
|
| + UnicodeString result;
|
| + fCache->relativeUnits[fStyle][unit][bFuture].format(
|
| + quantity,
|
| + **fNumberFormat,
|
| + **fPluralRules,
|
| + result,
|
| + pos,
|
| + status);
|
| + adjustForContext(result);
|
| + return appendTo.append(result);
|
| +}
|
| +
|
| +UnicodeString& RelativeDateTimeFormatter::format(
|
| + UDateDirection direction, UDateAbsoluteUnit unit,
|
| + UnicodeString& appendTo, UErrorCode& status) const {
|
| + if (U_FAILURE(status)) {
|
| + return appendTo;
|
| + }
|
| + if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
|
| + status = U_ILLEGAL_ARGUMENT_ERROR;
|
| + return appendTo;
|
| + }
|
| + if (fOptBreakIterator == NULL) {
|
| + return appendTo.append(fCache->absoluteUnits[fStyle][unit][direction]);
|
| + }
|
| + UnicodeString result(fCache->absoluteUnits[fStyle][unit][direction]);
|
| + adjustForContext(result);
|
| + return appendTo.append(result);
|
| +}
|
| +
|
| +UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
|
| + const UnicodeString& relativeDateString, const UnicodeString& timeString,
|
| + UnicodeString& appendTo, UErrorCode& status) const {
|
| + Formattable args[2] = {timeString, relativeDateString};
|
| + FieldPosition fpos(0);
|
| + return fCache->getCombinedDateAndTime()->format(
|
| + args, 2, appendTo, fpos, status);
|
| +}
|
| +
|
| +void RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
|
| + if (fOptBreakIterator == NULL
|
| + || str.length() == 0 || !u_islower(str.char32At(0))) {
|
| + return;
|
| + }
|
| +
|
| + // Must guarantee that one thread at a time accesses the shared break
|
| + // iterator.
|
| + Mutex lock(&gBrkIterMutex);
|
| + str.toTitle(
|
| + fOptBreakIterator->get(),
|
| + fLocale,
|
| + U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
|
| +}
|
| +
|
| +void RelativeDateTimeFormatter::init(
|
| + NumberFormat *nfToAdopt,
|
| + BreakIterator *biToAdopt,
|
| + UErrorCode &status) {
|
| + LocalPointer<NumberFormat> nf(nfToAdopt);
|
| + LocalPointer<BreakIterator> bi(biToAdopt);
|
| + UnifiedCache::getByLocale(fLocale, fCache, status);
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + const SharedPluralRules *pr = PluralRules::createSharedInstance(
|
| + fLocale, UPLURAL_TYPE_CARDINAL, status);
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + SharedObject::copyPtr(pr, fPluralRules);
|
| + pr->removeRef();
|
| + if (nf.isNull()) {
|
| + const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
|
| + fLocale, UNUM_DECIMAL, status);
|
| + if (U_FAILURE(status)) {
|
| + return;
|
| + }
|
| + SharedObject::copyPtr(shared, fNumberFormat);
|
| + shared->removeRef();
|
| + } else {
|
| + SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
|
| + if (shared == NULL) {
|
| + status = U_MEMORY_ALLOCATION_ERROR;
|
| + return;
|
| + }
|
| + nf.orphan();
|
| + SharedObject::copyPtr(shared, fNumberFormat);
|
| + }
|
| + if (bi.isNull()) {
|
| + SharedObject::clearPtr(fOptBreakIterator);
|
| + } else {
|
| + SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
|
| + if (shared == NULL) {
|
| + status = U_MEMORY_ALLOCATION_ERROR;
|
| + return;
|
| + }
|
| + bi.orphan();
|
| + SharedObject::copyPtr(shared, fOptBreakIterator);
|
| + }
|
| +}
|
| +
|
| +
|
| +U_NAMESPACE_END
|
| +
|
| +#endif /* !UCONFIG_NO_FORMATTING */
|
| +
|
|
|