summaryrefslogtreecommitdiff
path: root/deps/icu-small/source/i18n/reldatefmt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'deps/icu-small/source/i18n/reldatefmt.cpp')
-rw-r--r--deps/icu-small/source/i18n/reldatefmt.cpp1190
1 files changed, 1190 insertions, 0 deletions
diff --git a/deps/icu-small/source/i18n/reldatefmt.cpp b/deps/icu-small/source/i18n/reldatefmt.cpp
new file mode 100644
index 0000000000..29f760e0e8
--- /dev/null
+++ b/deps/icu-small/source/i18n/reldatefmt.cpp
@@ -0,0 +1,1190 @@
+/*
+******************************************************************************
+* Copyright (C) 2014-2016, 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/dtfmtsym.h"
+#include "unicode/ureldatefmt.h"
+#include "unicode/udisplaycontext.h"
+#include "unicode/unum.h"
+#include "unicode/localpointer.h"
+#include "unicode/plurrule.h"
+#include "unicode/simpleformatter.h"
+#include "unicode/decimfmt.h"
+#include "unicode/numfmt.h"
+#include "unicode/brkiter.h"
+#include "unicode/simpleformatter.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 "quantityformatter.h"
+#include "resource.h"
+#include "sharedbreakiterator.h"
+#include "sharedpluralrules.h"
+#include "sharednumberformat.h"
+#include "standardplural.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) {
+ // Initialize the cache arrays
+ for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
+ for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
+ for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
+ relativeUnitsFormatters[style][relUnit][0][pl] = NULL;
+ relativeUnitsFormatters[style][relUnit][1][pl] = NULL;
+ }
+ }
+ }
+ for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) {
+ fallBackCache[i] = -1;
+ }
+ }
+ virtual ~RelativeDateTimeCacheData();
+
+ // no numbers: e.g Next Tuesday; Yesterday; etc.
+ UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
+
+ // SimpleFormatter pointers for relative unit format,
+ // e.g., Next Tuesday; Yesterday; etc. For third index, 0
+ // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
+ SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT]
+ [UDAT_RELATIVE_UNIT_COUNT][2][StandardPlural::COUNT];
+
+ const UnicodeString& getAbsoluteUnitString(int32_t fStyle,
+ UDateAbsoluteUnit unit,
+ UDateDirection direction) const;
+ const SimpleFormatter* getRelativeUnitFormatter(int32_t fStyle,
+ UDateRelativeUnit unit,
+ int32_t pastFutureIndex,
+ int32_t pluralUnit) const;
+
+ const UnicodeString emptyString;
+
+ // Mappping from source to target styles for alias fallback.
+ int32_t fallBackCache[UDAT_STYLE_COUNT];
+
+ void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) {
+ delete combinedDateAndTime;
+ combinedDateAndTime = fmtToAdopt;
+ }
+ const SimpleFormatter *getCombinedDateAndTime() const {
+ return combinedDateAndTime;
+ }
+
+private:
+ SimpleFormatter *combinedDateAndTime;
+ RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
+ RelativeDateTimeCacheData& operator=(
+ const RelativeDateTimeCacheData &other);
+};
+
+RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
+ // clear out the cache arrays
+ for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
+ for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
+ for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
+ delete relativeUnitsFormatters[style][relUnit][0][pl];
+ delete relativeUnitsFormatters[style][relUnit][1][pl];
+ }
+ }
+ }
+ delete combinedDateAndTime;
+}
+
+
+// Use fallback cache for absolute units.
+const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString(
+ int32_t fStyle, UDateAbsoluteUnit unit, UDateDirection direction) const {
+ int32_t style = fStyle;
+ do {
+ if (!absoluteUnits[style][unit][direction].isEmpty()) {
+ return absoluteUnits[style][unit][direction];
+ }
+ style = fallBackCache[style];
+ } while (style != -1);
+ return emptyString;
+}
+
+ // Use fallback cache for SimpleFormatter relativeUnits.
+ const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter(
+ int32_t fStyle,
+ UDateRelativeUnit unit,
+ int32_t pastFutureIndex,
+ int32_t pluralUnit) const {
+ int32_t style = fStyle;
+ do {
+ if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != NULL) {
+ return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
+ }
+ style = fallBackCache[style];
+ } while (style != -1);
+ return NULL; // No formatter found.
+ }
+
+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 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;
+}
+
+namespace {
+
+/**
+ * Sink for enumerating all of the measurement unit display names.
+ * Contains inner sink classes, each one corresponding to a level of the resource table.
+ *
+ * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
+ * Only store a value if it is still missing, that is, it has not been overridden.
+ *
+ * C++: Each inner sink class has a reference to the main outer sink.
+ */
+struct RelDateTimeFmtDataSink : public ResourceTableSink {
+
+ /**
+ * Sink for patterns for relative dates and times. For example,
+ * fields/relative/...
+ */
+
+ // Generic unit enum for storing Unit info.
+ typedef enum RelAbsUnit {
+ INVALID_UNIT = -1,
+ SECOND,
+ MINUTE,
+ HOUR,
+ DAY,
+ WEEK,
+ MONTH,
+ QUARTER,
+ YEAR,
+ SUNDAY,
+ MONDAY,
+ TUESDAY,
+ WEDNESDAY,
+ THURSDAY,
+ FRIDAY,
+ SATURDAY
+ } RelAbsUnit;
+
+ static int32_t relUnitFromGeneric(RelAbsUnit genUnit) {
+ // Converts the generic units to UDAT_RELATIVE version.
+ switch (genUnit) {
+ case SECOND:
+ return UDAT_RELATIVE_SECONDS;
+ case MINUTE:
+ return UDAT_RELATIVE_MINUTES;
+ case HOUR:
+ return UDAT_RELATIVE_HOURS;
+ case DAY:
+ return UDAT_RELATIVE_DAYS;
+ case WEEK:
+ return UDAT_RELATIVE_WEEKS;
+ case MONTH:
+ return UDAT_RELATIVE_MONTHS;
+ /*
+ * case QUARTER:
+ * return UDATE_RELATIVE_QUARTERS;
+ */
+ case YEAR:
+ return UDAT_RELATIVE_YEARS;
+ default:
+ return -1;
+ }
+ }
+
+ static int32_t absUnitFromGeneric(RelAbsUnit genUnit) {
+ // Converts the generic units to UDAT_RELATIVE version.
+ switch (genUnit) {
+ case DAY:
+ return UDAT_ABSOLUTE_DAY;
+ case WEEK:
+ return UDAT_ABSOLUTE_WEEK;
+ case MONTH:
+ return UDAT_ABSOLUTE_MONTH;
+ /* TODO: Add in QUARTER
+ * case QUARTER:
+ * return UDAT_ABSOLUTE_QUARTER;
+ */
+ case YEAR:
+ return UDAT_ABSOLUTE_YEAR;
+ case SUNDAY:
+ return UDAT_ABSOLUTE_SUNDAY;
+ case MONDAY:
+ return UDAT_ABSOLUTE_MONDAY;
+ case TUESDAY:
+ return UDAT_ABSOLUTE_TUESDAY;
+ case WEDNESDAY:
+ return UDAT_ABSOLUTE_WEDNESDAY;
+ case THURSDAY:
+ return UDAT_ABSOLUTE_THURSDAY;
+ case FRIDAY:
+ return UDAT_ABSOLUTE_FRIDAY;
+ case SATURDAY:
+ return UDAT_ABSOLUTE_SATURDAY;
+ default:
+ return -1;
+ }
+ }
+
+ static int32_t keyToDirection(const char* key) {
+ if (uprv_strcmp(key, "-2") == 0) {
+ return UDAT_DIRECTION_LAST_2;
+ }
+ if (uprv_strcmp(key, "-1") == 0) {
+ return UDAT_DIRECTION_LAST;
+ }
+ if (uprv_strcmp(key, "0") == 0) {
+ return UDAT_DIRECTION_THIS;
+ }
+ if (uprv_strcmp(key, "1") == 0) {
+ return UDAT_DIRECTION_NEXT;
+ }
+ if (uprv_strcmp(key, "2") == 0) {
+ return UDAT_DIRECTION_NEXT_2;
+ }
+ return -1;
+ }
+
+ // Sinks for additional levels under /fields/*/relative/ and /fields/*/relativeTime/
+
+ /**
+ * Make list of simplePatternFmtList, for past and for future.
+ * Set a SimpleFormatter for the <style, relative unit, plurality>
+ *
+ * Fill in values for the particular plural given, e.g., ONE, FEW, OTHER, etc.
+ */
+ struct RelDateTimeDetailSink : public ResourceTableSink {
+ RelDateTimeDetailSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
+ ~RelDateTimeDetailSink();
+
+ virtual void put(const char *key, const ResourceValue &value,
+ UErrorCode &errorCode) {
+ if (U_FAILURE(errorCode)) { return; }
+
+ outer.relUnitIndex = relUnitFromGeneric(outer.genericUnit);
+ if (outer.relUnitIndex < 0) {
+ return;
+ }
+
+ /* Make two lists of simplePatternFmtList, one for past and one for future.
+ * Set a SimpleFormatter pattern for the <style, relative unit, plurality>
+ *
+ * Fill in values for the particular plural given, e.g., ONE, FEW, OTHER, etc.
+ */
+ int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
+ if (pluralIndex >= 0) {
+ SimpleFormatter **patterns =
+ outer.outputData.relativeUnitsFormatters[outer.style][outer.relUnitIndex]
+ [outer.pastFutureIndex];
+ // Only set if not already established.
+ if (patterns[pluralIndex] == NULL) {
+ patterns[pluralIndex] = new SimpleFormatter(
+ value.getUnicodeString(errorCode), 0, 1, errorCode);
+ if (patterns[pluralIndex] == NULL) {
+ errorCode = U_MEMORY_ALLOCATION_ERROR;
+ }
+ }
+ }
+ }
+
+ RelDateTimeFmtDataSink &outer;
+ } relDateTimeDetailSink;
+
+ /*
+ * Handles "relativeTime" entries, e.g., under "day", "hour", "minute",
+ * "minute-short", etc.
+ */
+ struct RelativeTimeSink : public ResourceTableSink {
+ RelativeTimeSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
+ ~RelativeTimeSink();
+
+ virtual ResourceTableSink *getOrCreateTableSink(
+ const char *key, int32_t /* initialSize */, UErrorCode& errorCode) {
+ if (U_FAILURE(errorCode)) { return NULL; }
+ outer.relUnitIndex = relUnitFromGeneric(outer.genericUnit);
+ if (outer.relUnitIndex < 0) {
+ return NULL;
+ }
+
+ if (uprv_strcmp(key, "past") == 0) {
+ outer.pastFutureIndex = 0;
+ } else if (uprv_strcmp(key, "future") == 0) {
+ outer.pastFutureIndex = 1;
+ } else {
+ // Unknown key.
+ return NULL;
+ }
+ return &outer.relDateTimeDetailSink;
+ }
+
+ RelDateTimeFmtDataSink &outer;
+ } relativeTimeSink;
+
+ /*
+ * Handles "relative" entries, e.g., under "day", "day-short", "fri",
+ * "fri-narrow", "fri-short", etc.
+ */
+ struct RelativeSink : public ResourceTableSink {
+ RelativeSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
+ ~RelativeSink();
+
+ virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
+ if (U_FAILURE(errorCode)) { return; }
+ int32_t direction = keyToDirection(key);
+ if (direction < 0) {
+ return;
+ }
+
+ int32_t relUnitIndex = relUnitFromGeneric(outer.genericUnit);
+ if (relUnitIndex == UDAT_RELATIVE_SECONDS &&
+ direction == UDAT_DIRECTION_THIS &&
+ outer.outputData.absoluteUnits[outer.style][UDAT_ABSOLUTE_NOW]
+ [UDAT_DIRECTION_PLAIN].isEmpty()) {
+ // Handle "NOW"
+ outer.outputData.absoluteUnits[outer.style][UDAT_ABSOLUTE_NOW]
+ [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
+ }
+
+ int32_t absUnitIndex = absUnitFromGeneric(outer.genericUnit);
+ if (absUnitIndex < 0) {
+ return;
+ }
+ // Only reset if slot is empty.
+ if (outer.outputData.absoluteUnits[outer.style][absUnitIndex][direction].isEmpty()) {
+ outer.outputData.absoluteUnits[outer.style][absUnitIndex]
+ [direction].fastCopyFrom(value.getUnicodeString(errorCode));
+ }
+ }
+
+ RelDateTimeFmtDataSink &outer;
+ } relativeSink;
+
+ /*
+ * Handles entries under "fields", recognizing "relative" and "relativeTime" entries.
+ */
+ struct UnitSink : public ResourceTableSink {
+ UnitSink(RelDateTimeFmtDataSink &sink) : outer(sink) {}
+ ~UnitSink();
+
+ virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
+ if (U_FAILURE(errorCode)) { return; }
+ if (uprv_strcmp(key, "dn") != 0) {
+ return;
+ }
+
+ // Handle Display Name for PLAIN direction for some units.
+ int32_t absUnit = absUnitFromGeneric(outer.genericUnit);
+ if (absUnit < 0) {
+ return; // Not interesting.
+ }
+
+ // TODO(Travis Keep): This is a hack to get around CLDR bug 6818.
+ UnicodeString displayName = value.getUnicodeString(errorCode);
+ if (U_SUCCESS(errorCode)) {
+ if (uprv_strcmp("en", outer.sinkLocaleId) == 0) {
+ displayName.toLower();
+ }
+ }
+ // end hack
+
+ // Store displayname if not set.
+ if (outer.outputData.absoluteUnits[outer.style]
+ [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
+ outer.outputData.absoluteUnits[outer.style]
+ [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(displayName);
+ return;
+ }
+ }
+
+ virtual ResourceTableSink *getOrCreateTableSink(
+ const char *key, int32_t /* initialSize */, UErrorCode &errorCode) {
+ if (U_FAILURE(errorCode)) { return NULL; }
+ if (uprv_strcmp(key, "relative") == 0) {
+ return &outer.relativeSink;
+ } else if (uprv_strcmp(key, "relativeTime") == 0) {
+ return &outer.relativeTimeSink;
+ }
+ return NULL;
+ }
+
+ RelDateTimeFmtDataSink &outer;
+ } unitSink;
+
+ // For hack for locale "en".
+ // TODO(Travis Keep): This is a hack to get around CLDR bug 6818.
+ const char* sinkLocaleId;
+
+ // Values kept between levels of parsing the CLDR data.
+ int32_t pastFutureIndex; // 0 == past or 1 == future
+ UDateRelativeDateTimeFormatterStyle style; // {LONG, SHORT, NARROW}
+ RelAbsUnit genericUnit;
+ int32_t relUnitIndex;
+ int32_t absUnitIndex;
+
+ RelativeDateTimeCacheData &outputData;
+
+ // Constructor
+ RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData, const char* localeId)
+ : relDateTimeDetailSink(*this), relativeTimeSink(*this), relativeSink(*this),
+ unitSink(*this), sinkLocaleId(localeId), outputData(cacheData) {
+ // Clear cacheData.fallBackCache
+ cacheData.fallBackCache[UDAT_STYLE_LONG] = -1;
+ cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1;
+ cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1;
+ }
+
+ ~RelDateTimeFmtDataSink();
+
+ // Utility functions
+ static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) {
+ int32_t len = uprv_strlen(s);
+ if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) {
+ return UDAT_STYLE_NARROW;
+ }
+ if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) {
+ return UDAT_STYLE_SHORT;
+ }
+ return UDAT_STYLE_LONG;
+ }
+
+ static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) {
+ switch (style) {
+ case UDAT_STYLE_NARROW:
+ return 7;
+ case UDAT_STYLE_SHORT:
+ return 6;
+ default:
+ return 0;
+ }
+ }
+
+ // Utility functions
+ static UDateRelativeDateTimeFormatterStyle styleFromAliasUnicodeString(UnicodeString s) {
+ static const UChar narrow[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
+ static const UChar sshort[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
+ if (s.endsWith(narrow, 7)) {
+ return UDAT_STYLE_NARROW;
+ }
+ if (s.endsWith(sshort, 6)) {
+ return UDAT_STYLE_SHORT;
+ }
+ return UDAT_STYLE_LONG;
+ }
+
+ static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) {
+ // Quick check from string to enum.
+ switch (length) {
+ case 3:
+ if (uprv_strncmp(keyword, "day", length) == 0) {
+ return DAY;
+ } else if (uprv_strncmp(keyword, "sun", length) == 0) {
+ return SUNDAY;
+ } else if (uprv_strncmp(keyword, "mon", length) == 0) {
+ return MONDAY;
+ } else if (uprv_strncmp(keyword, "tue", length) == 0) {
+ return TUESDAY;
+ } else if (uprv_strncmp(keyword, "wed", length) == 0) {
+ return WEDNESDAY;
+ } else if (uprv_strncmp(keyword, "thu", length) == 0) {
+ return THURSDAY;
+ } else if (uprv_strncmp(keyword, "fri", length) == 0) {
+ return FRIDAY;
+ } else if (uprv_strncmp(keyword, "sat", length) == 0) {
+ return SATURDAY;
+ }
+ break;
+ case 4:
+ if (uprv_strncmp(keyword, "hour", length) == 0) {
+ return HOUR;
+ } else if (uprv_strncmp(keyword, "week", length) == 0) {
+ return WEEK;
+ } else if (uprv_strncmp(keyword, "year", length) == 0) {
+ return YEAR;
+ }
+ break;
+ case 5:
+ if (uprv_strncmp(keyword, "month", length) == 0) {
+ return MONTH;
+ }
+ break;
+ case 6:
+ if (uprv_strncmp(keyword, "minute", length) == 0) {
+ return MINUTE;
+ } else if (uprv_strncmp(keyword, "second", length) == 0) {
+ return SECOND;
+ }
+ break;
+ case 7:
+ if (uprv_strncmp(keyword, "quarter", length) == 0) {
+ return QUARTER; // TODO: Check @provisional
+ }
+ break;
+ default:
+ break;
+ }
+ return INVALID_UNIT;
+ }
+
+ // Member functions of top level sink.
+ virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
+ // Only handle aliases, storing information about alias fallback.
+
+ if (U_SUCCESS(errorCode)) {
+ if (value.getType() != URES_ALIAS) {
+ return;
+ }
+ const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
+ if (U_SUCCESS(errorCode)) {
+ UDateRelativeDateTimeFormatterStyle sourceStyle= styleFromString(key);
+ UDateRelativeDateTimeFormatterStyle targetStyle =
+ styleFromAliasUnicodeString(valueStr);
+
+ if (sourceStyle == targetStyle) {
+ errorCode = U_INVALID_FORMAT_ERROR;
+ return;
+ }
+ if (outputData.fallBackCache[sourceStyle] != -1 &&
+ outputData.fallBackCache[sourceStyle] != targetStyle) {
+ errorCode = U_INVALID_FORMAT_ERROR;
+ return;
+ }
+ outputData.fallBackCache[sourceStyle] = targetStyle;
+ }
+ }
+ return;
+ }
+
+ // Top level sink
+ virtual ResourceTableSink *getOrCreateTableSink(
+ const char *key, int32_t /* initialSize */, UErrorCode& /* errorCode */) {
+ style= styleFromString(key);
+ int32_t unitSize = uprv_strlen(key) - styleSuffixLength(style);
+ genericUnit = unitOrNegativeFromString(key, unitSize);
+ if (style < 0 || genericUnit == INVALID_UNIT) {
+ return NULL;
+ }
+ return &unitSink;
+ }
+};
+
+// Virtual destructors must be defined out of line.
+RelDateTimeFmtDataSink::RelDateTimeDetailSink::~RelDateTimeDetailSink() {}
+RelDateTimeFmtDataSink::RelativeTimeSink::~RelativeTimeSink() {}
+RelDateTimeFmtDataSink::RelativeSink::~RelativeSink() {}
+RelDateTimeFmtDataSink::UnitSink::~UnitSink() {}
+RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
+
+} // namespace
+
+DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
+ DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
+};
+
+// Get days of weeks from the DateFormatSymbols class.
+static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT]
+ [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
+ const char* localeId,
+ UErrorCode& status) {
+ Locale locale(localeId);
+ DateFormatSymbols dfSym(locale, status);
+ for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
+ DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style];
+ int32_t count;
+ const UnicodeString* weekdayNames =
+ dfSym.getWeekdays(count, DateFormatSymbols::STANDALONE, dtfmtWidth);
+ for (int32_t dayIndex = UDAT_ABSOLUTE_SUNDAY;
+ dayIndex <= UDAT_ABSOLUTE_SATURDAY; ++ dayIndex) {
+ int32_t dateSymbolIndex = (dayIndex - UDAT_ABSOLUTE_SUNDAY) + UCAL_SUNDAY;
+ absoluteUnits[style][dayIndex][UDAT_DIRECTION_PLAIN].fastCopyFrom(
+ weekdayNames[dateSymbolIndex]);
+ }
+ }
+}
+
+static UBool loadUnitData(
+ const UResourceBundle *resource,
+ RelativeDateTimeCacheData &cacheData,
+ const char* localeId,
+ UErrorCode &status) {
+ RelDateTimeFmtDataSink sink(cacheData, localeId);
+ ures_getAllTableItemsWithFallback(resource, "fields", sink, status);
+
+ // Get the weekday names from DateFormatSymbols.
+ loadWeekdayNames(cacheData.absoluteUnits, localeId, 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 too 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,
+ localeId,
+ status)) {
+ return NULL;
+ }
+ UnicodeString dateTimePattern;
+ if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
+ return NULL;
+ }
+ result->adoptCombinedDateAndTime(
+ new SimpleFormatter(dateTimePattern, 2, 2, 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);
+
+ UnicodeString result;
+ UnicodeString formattedNumber;
+
+ StandardPlural::Form pluralIndex = QuantityFormatter::selectPlural(
+ quantity, **fNumberFormat, **fPluralRules, formattedNumber, pos,
+ status);
+
+ const SimpleFormatter* formatter =
+ fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralIndex);
+ if (formatter == NULL) {
+ // TODO: WARN - look at quantity formatter's action with an error.
+ status = U_INVALID_FORMAT_ERROR;
+ return appendTo;
+ }
+ formatter->format(formattedNumber, result, status);
+ adjustForContext(result);
+ return appendTo.append(result);
+}
+
+UnicodeString& RelativeDateTimeFormatter::formatNumeric(
+ double offset, URelativeDateTimeUnit unit,
+ UnicodeString& appendTo, UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return appendTo;
+ }
+ // TODO:
+ // The full implementation of this depends on CLDR data that is not yet available,
+ // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
+ // In the meantime do a quick bring-up by calling the old format method; this
+ // leaves some holes (even for data that is currently available, such as quarter).
+ // When the new CLDR data is available, update the data storage accordingly,
+ // rewrite this to use it directly, and rewrite the old format method to call this
+ // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
+ UDateRelativeUnit relunit = UDAT_RELATIVE_UNIT_COUNT;
+ switch (unit) {
+ case UDAT_REL_UNIT_YEAR: relunit = UDAT_RELATIVE_YEARS; break;
+ case UDAT_REL_UNIT_MONTH: relunit = UDAT_RELATIVE_MONTHS; break;
+ case UDAT_REL_UNIT_WEEK: relunit = UDAT_RELATIVE_WEEKS; break;
+ case UDAT_REL_UNIT_DAY: relunit = UDAT_RELATIVE_DAYS; break;
+ case UDAT_REL_UNIT_HOUR: relunit = UDAT_RELATIVE_HOURS; break;
+ case UDAT_REL_UNIT_MINUTE: relunit = UDAT_RELATIVE_MINUTES; break;
+ case UDAT_REL_UNIT_SECOND: relunit = UDAT_RELATIVE_SECONDS; break;
+ default: // a unit that the above method does not handle
+ status = U_UNSUPPORTED_ERROR;
+ return appendTo;
+ }
+ UDateDirection direction = UDAT_DIRECTION_NEXT;
+ if (offset < 0) {
+ direction = UDAT_DIRECTION_LAST;
+ offset = -offset;
+ }
+ return format(offset, direction, relunit, appendTo, status);
+}
+
+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;
+ }
+
+ // Get string using fallback.
+ UnicodeString result;
+ result.fastCopyFrom(fCache->getAbsoluteUnitString(fStyle, unit, direction));
+ if (fOptBreakIterator != NULL) {
+ adjustForContext(result);
+ }
+ return appendTo.append(result);
+}
+
+UnicodeString& RelativeDateTimeFormatter::format(
+ double offset, URelativeDateTimeUnit unit,
+ UnicodeString& appendTo, UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return appendTo;
+ }
+ // TODO:
+ // The full implementation of this depends on CLDR data that is not yet available,
+ // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
+ // In the meantime do a quick bring-up by calling the old format method; this
+ // leaves some holes (even for data that is currently available, such as quarter).
+ // When the new CLDR data is available, update the data storage accordingly,
+ // rewrite this to use it directly, and rewrite the old format method to call this
+ // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
+ UDateDirection direction = UDAT_DIRECTION_COUNT;
+ if (offset > -2.1 && offset < 2.1) {
+ // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
+ double offsetx100 = offset * 100.0;
+ int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5);
+ switch (intoffset) {
+ case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break;
+ case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break;
+ case 0/* 0*/: direction = UDAT_DIRECTION_THIS; break;
+ case 100/* 1*/: direction = UDAT_DIRECTION_NEXT; break;
+ case 200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break;
+ default: break;
+ }
+ }
+ UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
+ switch (unit) {
+ case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break;
+ case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break;
+ case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break;
+ case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break;
+ case UDAT_REL_UNIT_SECOND:
+ if (direction == UDAT_DIRECTION_THIS) {
+ absunit = UDAT_ABSOLUTE_NOW;
+ direction = UDAT_DIRECTION_PLAIN;
+ }
+ break;
+ case UDAT_REL_UNIT_SUNDAY: absunit = UDAT_ABSOLUTE_SUNDAY; break;
+ case UDAT_REL_UNIT_MONDAY: absunit = UDAT_ABSOLUTE_MONDAY; break;
+ case UDAT_REL_UNIT_TUESDAY: absunit = UDAT_ABSOLUTE_TUESDAY; break;
+ case UDAT_REL_UNIT_WEDNESDAY: absunit = UDAT_ABSOLUTE_WEDNESDAY; break;
+ case UDAT_REL_UNIT_THURSDAY: absunit = UDAT_ABSOLUTE_THURSDAY; break;
+ case UDAT_REL_UNIT_FRIDAY: absunit = UDAT_ABSOLUTE_FRIDAY; break;
+ case UDAT_REL_UNIT_SATURDAY: absunit = UDAT_ABSOLUTE_SATURDAY; break;
+ default: break;
+ }
+ if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
+ const UnicodeString &unitFormatString =
+ fCache->getAbsoluteUnitString(fStyle, absunit, direction);
+ if (!unitFormatString.isEmpty()) {
+ if (fOptBreakIterator != NULL) {
+ UnicodeString result(unitFormatString);
+ adjustForContext(result);
+ return appendTo.append(result);
+ } else {
+ return appendTo.append(unitFormatString);
+ }
+ }
+ }
+ // otherwise fallback to formatNumeric
+ return formatNumeric(offset, unit, appendTo, status);
+}
+
+UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
+ const UnicodeString& relativeDateString, const UnicodeString& timeString,
+ UnicodeString& appendTo, UErrorCode& status) const {
+ return fCache->getCombinedDateAndTime()->format(
+ timeString, relativeDateString, appendTo, 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
+
+// Plain C API
+
+U_NAMESPACE_USE
+
+U_CAPI URelativeDateTimeFormatter* U_EXPORT2
+ureldatefmt_open( const char* locale,
+ UNumberFormat* nfToAdopt,
+ UDateRelativeDateTimeFormatterStyle width,
+ UDisplayContext capitalizationContext,
+ UErrorCode* status )
+{
+ if (U_FAILURE(*status)) {
+ return NULL;
+ }
+ LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
+ (NumberFormat*)nfToAdopt, width,
+ capitalizationContext, *status), *status);
+ if (U_FAILURE(*status)) {
+ return NULL;
+ }
+ return (URelativeDateTimeFormatter*)formatter.orphan();
+}
+
+U_CAPI void U_EXPORT2
+ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
+{
+ delete (RelativeDateTimeFormatter*)reldatefmt;
+}
+
+U_CAPI int32_t U_EXPORT2
+ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
+ double offset,
+ URelativeDateTimeUnit unit,
+ UChar* result,
+ int32_t resultCapacity,
+ UErrorCode* status)
+{
+ if (U_FAILURE(*status)) {
+ return 0;
+ }
+ if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+ UnicodeString res;
+ if (result != NULL) {
+ // NULL destination for pure preflighting: empty dummy string
+ // otherwise, alias the destination buffer (copied from udat_format)
+ res.setTo(result, 0, resultCapacity);
+ }
+ ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
+ if (U_FAILURE(*status)) {
+ return 0;
+ }
+ return res.extract(result, resultCapacity, *status);
+}
+
+U_CAPI int32_t U_EXPORT2
+ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
+ double offset,
+ URelativeDateTimeUnit unit,
+ UChar* result,
+ int32_t resultCapacity,
+ UErrorCode* status)
+{
+ if (U_FAILURE(*status)) {
+ return 0;
+ }
+ if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+ UnicodeString res;
+ if (result != NULL) {
+ // NULL destination for pure preflighting: empty dummy string
+ // otherwise, alias the destination buffer (copied from udat_format)
+ res.setTo(result, 0, resultCapacity);
+ }
+ ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
+ if (U_FAILURE(*status)) {
+ return 0;
+ }
+ return res.extract(result, resultCapacity, *status);
+}
+
+U_CAPI int32_t U_EXPORT2
+ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
+ const UChar * relativeDateString,
+ int32_t relativeDateStringLen,
+ const UChar * timeString,
+ int32_t timeStringLen,
+ UChar* result,
+ int32_t resultCapacity,
+ UErrorCode* status )
+{
+ if (U_FAILURE(*status)) {
+ return 0;
+ }
+ if (result == NULL ? resultCapacity != 0 : resultCapacity < 0 ||
+ (relativeDateString == NULL ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
+ (timeString == NULL ? timeStringLen != 0 : timeStringLen < -1)) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+ UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen);
+ UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen);
+ UnicodeString res(result, 0, resultCapacity);
+ ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status);
+ if (U_FAILURE(*status)) {
+ return 0;
+ }
+ return res.extract(result, resultCapacity, *status);
+}
+
+#endif /* !UCONFIG_NO_FORMATTING */