+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
******************************************************************************
-* Copyright (C) 2014, International Business Machines Corporation and
-* others. All Rights Reserved.
+* Copyright (C) 2014-2016, International Business Machines Corporation and
+* others. All Rights Reserved.
******************************************************************************
-*
-* File RELDATEFMT.CPP
+*
+* File reldatefmt.cpp
******************************************************************************
*/
#include "unicode/reldatefmt.h"
-#if !UCONFIG_NO_FORMATTING
+#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION
+#include <cmath>
+#include "unicode/dtfmtsym.h"
+#include "unicode/ucasemap.h"
+#include "unicode/ureldatefmt.h"
+#include "unicode/udisplaycontext.h"
+#include "unicode/unum.h"
#include "unicode/localpointer.h"
-#include "quantityformatter.h"
#include "unicode/plurrule.h"
-#include "unicode/msgfmt.h"
+#include "unicode/simpleformatter.h"
#include "unicode/decimfmt.h"
#include "unicode/numfmt.h"
-#include "lrucache.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 "sharedptr.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
-#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
-static icu::LRUCache *gCache = NULL;
-static UMutex gCacheMutex = U_MUTEX_INITIALIZER;
-static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER;
-
-U_CDECL_BEGIN
-static UBool U_CALLCONV reldatefmt_cleanup() {
- gCacheInitOnce.reset();
- if (gCache) {
- delete gCache;
- gCache = NULL;
- }
- return TRUE;
-}
-U_CDECL_END
+static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
U_NAMESPACE_BEGIN
// RelativeDateTimeFormatter specific data for a single locale
class RelativeDateTimeCacheData: public SharedObject {
public:
- RelativeDateTimeCacheData() : combinedDateAndTime(NULL) { }
+ 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_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
+ 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;
- // 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_RELATIVE_UNIT_COUNT][2];
+ const UnicodeString emptyString;
- void adoptCombinedDateAndTime(MessageFormat *mfToAdopt) {
+ // Mappping from source to target styles for alias fallback.
+ int32_t fallBackCache[UDAT_STYLE_COUNT];
+
+ void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) {
delete combinedDateAndTime;
- combinedDateAndTime = mfToAdopt;
+ combinedDateAndTime = fmtToAdopt;
}
- const MessageFormat *getCombinedDateAndTime() const {
+ const SimpleFormatter *getCombinedDateAndTime() const {
return combinedDateAndTime;
}
+
private:
- MessageFormat *combinedDateAndTime;
+ 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;
}
-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;
+
+// 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;
}
-static UBool getOptionalStringWithFallback(
- const UResourceBundle *resource,
+ // 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) {
- if (U_FAILURE(status)) {
- return FALSE;
- }
int32_t len = 0;
- const UChar *resStr = ures_getStringByKey(
+ const UChar *resStr = ures_getStringByKeyWithFallback(
resource, key, &len, &status);
- if (status == U_MISSING_RESOURCE_ERROR) {
- result.remove();
- status = U_ZERO_ERROR;
- return TRUE;
- }
if (U_FAILURE(status)) {
return FALSE;
}
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,
+ const UResourceBundle *resource,
int32_t idx,
UnicodeString &result,
UErrorCode &status) {
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;
-}
+namespace {
-static void initQuantityFormatter(
- const UResourceBundle *resource,
- QuantityFormatter &formatter,
- UErrorCode &status) {
- if (U_FAILURE(status)) {
- return;
+/**
+ * Sink for enumerating all of the measurement unit display names.
+ *
+ * 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.
+ */
+struct RelDateTimeFmtDataSink : public ResourceSink {
+
+ /**
+ * 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;
+ }
}
- 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;
+
+ 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;
}
- UnicodeString rawPattern;
- if (!getString(pluralBundle.getAlias(), rawPattern, status)) {
- return;
+ }
+
+ static int32_t keyToDirection(const char* key) {
+ if (uprv_strcmp(key, "-2") == 0) {
+ return UDAT_DIRECTION_LAST_2;
}
- if (!formatter.add(
- ures_getKey(pluralBundle.getAlias()),
- rawPattern,
- status)) {
- return;
+ 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;
}
-}
-static void initRelativeUnit(
- const UResourceBundle *resource,
- QuantityFormatter *relativeUnit,
- UErrorCode &status) {
- LocalUResourceBundlePointer topLevel(
- ures_getByKeyWithFallback(
- resource, "relativeTime", NULL, &status));
- if (U_FAILURE(status)) {
- return;
+ // Values kept between levels of parsing the CLDR data.
+ int32_t pastFutureIndex; // 0 == past or 1 == future
+ UDateRelativeDateTimeFormatterStyle style; // {LONG, SHORT, NARROW}
+ RelAbsUnit genericUnit;
+
+ RelativeDateTimeCacheData &outputData;
+
+ // Constructor
+ RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData)
+ : outputData(cacheData) {
+ // Clear cacheData.fallBackCache
+ cacheData.fallBackCache[UDAT_STYLE_LONG] = -1;
+ cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1;
+ cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1;
}
- LocalUResourceBundlePointer futureBundle(ures_getByKeyWithFallback(
- topLevel.getAlias(), "future", NULL, &status));
- if (U_FAILURE(status)) {
- return;
+
+ ~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;
}
- initQuantityFormatter(
- futureBundle.getAlias(),
- relativeUnit[1],
- status);
- LocalUResourceBundlePointer pastBundle(ures_getByKeyWithFallback(
- topLevel.getAlias(), "past", NULL, &status));
- if (U_FAILURE(status)) {
- return;
+
+ static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) {
+ switch (style) {
+ case UDAT_STYLE_NARROW:
+ return 7;
+ case UDAT_STYLE_SHORT:
+ return 6;
+ default:
+ return 0;
+ }
}
- 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;
+ // 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;
}
- 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;
+ 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;
}
- initRelativeUnit(topLevel.getAlias(), relativeUnit, status);
- UnicodeString unitName;
- if (!getStringWithFallback(topLevel.getAlias(), "dn", unitName, status)) {
- return;
+
+ void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) {
+ // Handle Display Name for PLAIN direction for some units.
+ if (U_FAILURE(errorCode)) { return; }
+
+ int32_t absUnit = absUnitFromGeneric(genericUnit);
+ if (absUnit < 0) {
+ return; // Not interesting.
+ }
+
+ // Store displayname if not set.
+ if (outputData.absoluteUnits[style]
+ [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
+ outputData.absoluteUnits[style]
+ [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
+ 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;
+
+ void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) {
+ ResourceTable unitTypesTable = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
+ if (value.getType() == URES_STRING) {
+ int32_t direction = keyToDirection(key);
+ if (direction < 0) {
+ continue;
+ }
+
+ int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
+ if (relUnitIndex == UDAT_RELATIVE_SECONDS && uprv_strcmp(key, "0") == 0 &&
+ outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) {
+ // Handle "NOW"
+ outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW]
+ [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
+ }
+
+ int32_t absUnitIndex = absUnitFromGeneric(genericUnit);
+ if (absUnitIndex < 0) {
+ continue;
+ }
+ // Only reset if slot is empty.
+ if (outputData.absoluteUnits[style][absUnitIndex][direction].isEmpty()) {
+ outputData.absoluteUnits[style][absUnitIndex]
+ [direction].fastCopyFrom(value.getUnicodeString(errorCode));
+ }
+ }
+ }
}
- Locale locale(localeId);
- if (uprv_strcmp("en", locale.getLanguage()) == 0) {
- unitName.toLower();
+
+ void consumeTimeDetail(int32_t relUnitIndex,
+ const char *key, ResourceValue &value, UErrorCode &errorCode) {
+ ResourceTable unitTypesTable = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
+ if (value.getType() == URES_STRING) {
+ int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
+ if (pluralIndex >= 0) {
+ SimpleFormatter **patterns =
+ outputData.relativeUnitsFormatters[style][relUnitIndex]
+ [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;
+ }
+ }
+ }
+ }
+ }
}
- // end hack
- ures_getByKeyWithFallback(
- topLevel.getAlias(), "relative", topLevel.getAlias(), &status);
- if (U_FAILURE(status)) {
- return;
+
+ void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) {
+ ResourceTable relativeTimeTable = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
+ if (relUnitIndex < 0) {
+ return;
+ }
+ for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) {
+ if (uprv_strcmp(key, "past") == 0) {
+ pastFutureIndex = 0;
+ } else if (uprv_strcmp(key, "future") == 0) {
+ pastFutureIndex = 1;
+ } else {
+ // Unknown key.
+ continue;
+ }
+ consumeTimeDetail(relUnitIndex, key, value, errorCode);
+ }
}
- 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;
+ void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
+
+ UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key);
+ const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ 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;
}
- int32_t size = ures_getSize(topLevel.getAlias());
- if (size != 7) {
- status = U_INTERNAL_PROGRAM_ERROR;
- return;
+
+ void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) {
+ ResourceTable unitTypesTable = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
+ // Handle display name.
+ if (uprv_strcmp(key, "dn") == 0 && value.getType() == URES_STRING) {
+ handlePlainDirection(value, errorCode);
+ }
+ if (value.getType() == URES_TABLE) {
+ if (uprv_strcmp(key, "relative") == 0) {
+ consumeTableRelative(key, value, errorCode);
+ } else if (uprv_strcmp(key, "relativeTime") == 0) {
+ consumeTableRelativeTime(key, value, errorCode);
+ }
+ }
+ }
}
- for (int32_t i = 0; i < size; ++i) {
- if (!getStringByIndex(topLevel.getAlias(), i, daysOfWeek[i], status)) {
- return;
+
+ virtual void put(const char *key, ResourceValue &value,
+ UBool /*noFallback*/, UErrorCode &errorCode) {
+ // Main entry point to sink
+ ResourceTable table = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ for (int32_t i = 0; table.getKeyAndValue(i, key, value); ++i) {
+ if (value.getType() == URES_ALIAS) {
+ consumeAlias(key, value, errorCode);
+ } else {
+ style = styleFromString(key);
+ int32_t unitSize = uprv_strlen(key) - styleSuffixLength(style);
+ genericUnit = unitOrNegativeFromString(key, unitSize);
+ if (style >= 0 && genericUnit != INVALID_UNIT) {
+ consumeTimeUnit(key, value, errorCode);
+ }
+ }
}
}
-}
-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;
+};
+
+// Virtual destructors must be defined out of line.
+RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
+} // namespace
+
+static const 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]);
+ }
}
- initAbsoluteUnit(
- topLevel.getAlias(),
- daysOfWeek[absoluteUnit - UDAT_ABSOLUTE_SUNDAY],
- absoluteUnits[absoluteUnit],
- status);
}
static UBool loadUnitData(
const UResourceBundle *resource,
RelativeDateTimeCacheData &cacheData,
+ const char* localeId,
UErrorCode &status) {
- addTimeUnit(
- resource,
- "fields/day",
- cacheData.relativeUnits[UDAT_RELATIVE_DAYS],
- cacheData.absoluteUnits[UDAT_ABSOLUTE_DAY],
- status);
- addTimeUnit(
- resource,
- "fields/week",
- cacheData.relativeUnits[UDAT_RELATIVE_WEEKS],
- cacheData.absoluteUnits[UDAT_ABSOLUTE_WEEK],
- status);
- addTimeUnit(
- resource,
- "fields/month",
- cacheData.relativeUnits[UDAT_RELATIVE_MONTHS],
- cacheData.absoluteUnits[UDAT_ABSOLUTE_MONTH],
- status);
- addTimeUnit(
- resource,
- "fields/year",
- cacheData.relativeUnits[UDAT_RELATIVE_YEARS],
- cacheData.absoluteUnits[UDAT_ABSOLUTE_YEAR],
- status);
- initRelativeUnit(
- resource,
- "fields/second",
- cacheData.relativeUnits[UDAT_RELATIVE_SECONDS],
- status);
- initRelativeUnit(
- resource,
- "fields/minute",
- cacheData.relativeUnits[UDAT_RELATIVE_MINUTES],
- status);
- initRelativeUnit(
- resource,
- "fields/hour",
- cacheData.relativeUnits[UDAT_RELATIVE_HOURS],
- status);
- getStringWithFallback(
- resource,
- "fields/second/relative/0",
- cacheData.absoluteUnits[UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN],
- status);
- UnicodeString daysOfWeek[7];
- readDaysOfWeek(
- resource,
- "calendar/gregorian/dayNames/stand-alone/wide",
- daysOfWeek,
- status);
- addWeekDay(
- resource,
- "fields/mon/relative",
- daysOfWeek,
- UDAT_ABSOLUTE_MONDAY,
- cacheData.absoluteUnits,
- status);
- addWeekDay(
- resource,
- "fields/tue/relative",
- daysOfWeek,
- UDAT_ABSOLUTE_TUESDAY,
- cacheData.absoluteUnits,
- status);
- addWeekDay(
- resource,
- "fields/wed/relative",
- daysOfWeek,
- UDAT_ABSOLUTE_WEDNESDAY,
- cacheData.absoluteUnits,
- status);
- addWeekDay(
- resource,
- "fields/thu/relative",
- daysOfWeek,
- UDAT_ABSOLUTE_THURSDAY,
- cacheData.absoluteUnits,
- status);
- addWeekDay(
- resource,
- "fields/fri/relative",
- daysOfWeek,
- UDAT_ABSOLUTE_FRIDAY,
- cacheData.absoluteUnits,
- status);
- addWeekDay(
- resource,
- "fields/sat/relative",
- daysOfWeek,
- UDAT_ABSOLUTE_SATURDAY,
- cacheData.absoluteUnits,
- status);
- addWeekDay(
- resource,
- "fields/sun/relative",
- daysOfWeek,
- UDAT_ABSOLUTE_SUNDAY,
- cacheData.absoluteUnits,
- status);
+
+ RelDateTimeFmtDataSink sink(cacheData);
+
+ ures_getAllItemsWithFallback(resource, "fields", sink, status);
+
+ // Get the weekday names from DateFormatSymbols.
+ loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
return U_SUCCESS(status);
}
}
int32_t size = ures_getSize(topLevel.getAlias());
if (size <= 8) {
- // Oops, size is to small to access the index that we want, fallback
+ // 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);
}
-// Creates RelativeDateTimeFormatter specific data for a given locale
-static SharedObject *U_CALLCONV createData(
- const char *localeId, UErrorCode &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;
if (!loadUnitData(
topLevel.getAlias(),
*result,
+ localeId,
status)) {
return NULL;
}
return NULL;
}
result->adoptCombinedDateAndTime(
- new MessageFormat(dateTimePattern, localeId, status));
+ new SimpleFormatter(dateTimePattern, 2, 2, status));
if (U_FAILURE(status)) {
return NULL;
}
+ result->addRef();
return result.orphan();
}
-static void U_CALLCONV cacheInit(UErrorCode &status) {
- U_ASSERT(gCache == NULL);
- ucln_i18n_registerCleanup(UCLN_I18N_RELDATEFMT, reldatefmt_cleanup);
- gCache = new SimpleLRUCache(100, &createData, status);
- if (U_FAILURE(status)) {
- delete gCache;
- gCache = NULL;
- }
-}
-
-static UBool getFromCache(
- const char *locale,
- const RelativeDateTimeCacheData *&ptr,
- UErrorCode &status) {
- umtx_initOnce(gCacheInitOnce, &cacheInit, status);
- if (U_FAILURE(status)) {
- return FALSE;
- }
- Mutex lock(&gCacheMutex);
- gCache->get(locale, ptr, status);
- return U_SUCCESS(status);
+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(UErrorCode& status)
- : cache(NULL), numberFormat(NULL), pluralRules(NULL) {
- init(Locale::getDefault(), 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, UErrorCode& status)
- : cache(NULL), numberFormat(NULL), pluralRules(NULL) {
- init(locale, NULL, status);
+ 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, UErrorCode& status)
- : cache(NULL), numberFormat(NULL), pluralRules(NULL) {
- init(locale, nfToAdopt, status);
+ 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)
- : cache(other.cache),
- numberFormat(other.numberFormat),
- pluralRules(other.pluralRules) {
- cache->addRef();
- numberFormat->addRef();
- pluralRules->addRef();
+ : 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.cache, cache);
- SharedObject::copyPtr(other.numberFormat, numberFormat);
- SharedObject::copyPtr(other.pluralRules, pluralRules);
+ 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 (cache != NULL) {
- cache->removeRef();
+ if (fCache != NULL) {
+ fCache->removeRef();
}
- if (numberFormat != NULL) {
- numberFormat->removeRef();
+ if (fNumberFormat != NULL) {
+ fNumberFormat->removeRef();
}
- if (pluralRules != NULL) {
- pluralRules->removeRef();
+ if (fPluralRules != NULL) {
+ fPluralRules->removeRef();
+ }
+ if (fOptBreakIterator != NULL) {
+ fOptBreakIterator->removeRef();
}
}
const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
- return **numberFormat;
+ return **fNumberFormat;
+}
+
+UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
+ return fContext;
+}
+
+UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
+ return fStyle;
}
UnicodeString& RelativeDateTimeFormatter::format(
}
int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
FieldPosition pos(FieldPosition::DONT_CARE);
- return cache->relativeUnits[unit][bFuture].format(
- quantity,
- **numberFormat,
- **pluralRules,
- appendTo,
- pos,
- status);
+
+ 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 (std::signbit(offset)) { // needed to handle -0.0
+ direction = UDAT_DIRECTION_LAST;
+ offset = -offset;
+ }
+ return format(offset, direction, relunit, appendTo, status);
}
UnicodeString& RelativeDateTimeFormatter::format(
status = U_ILLEGAL_ARGUMENT_ERROR;
return appendTo;
}
- return appendTo.append(cache->absoluteUnits[unit][direction]);
+
+ // 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 {
- Formattable args[2] = {timeString, relativeDateString};
- FieldPosition fpos(0);
- return cache->getCombinedDateAndTime()->format(
- args, 2, appendTo, fpos, status);
+ 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(
- const Locale &locale, NumberFormat *nfToAdopt, UErrorCode &status) {
+ NumberFormat *nfToAdopt,
+ BreakIterator *biToAdopt,
+ UErrorCode &status) {
LocalPointer<NumberFormat> nf(nfToAdopt);
- if (!getFromCache(locale.getName(), cache, status)) {
+ LocalPointer<BreakIterator> bi(biToAdopt);
+ UnifiedCache::getByLocale(fLocale, fCache, status);
+ if (U_FAILURE(status)) {
return;
}
- SharedObject::copyPtr(
- PluralRules::createSharedInstance(
- locale, UPLURAL_TYPE_CARDINAL, status),
- pluralRules);
+ const SharedPluralRules *pr = PluralRules::createSharedInstance(
+ fLocale, UPLURAL_TYPE_CARDINAL, status);
if (U_FAILURE(status)) {
return;
}
- pluralRules->removeRef();
+ SharedObject::copyPtr(pr, fPluralRules);
+ pr->removeRef();
if (nf.isNull()) {
- SharedObject::copyPtr(
- NumberFormat::createSharedInstance(
- locale, UNUM_DECIMAL, status),
- numberFormat);
+ const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
+ fLocale, UNUM_DECIMAL, status);
if (U_FAILURE(status)) {
return;
}
- numberFormat->removeRef();
+ SharedObject::copyPtr(shared, fNumberFormat);
+ shared->removeRef();
} else {
SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
if (shared == NULL) {
return;
}
nf.orphan();
- SharedObject::copyPtr(shared, numberFormat);
+ 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 */
+// 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 */