icu4c/source/allinone/icucheck.bat -text
icu4c/source/common/common.vcxproj -text
icu4c/source/common/common.vcxproj.filters -text
+icu4c/source/common/lrucache.cpp -text
+icu4c/source/common/lrucache.h -text
+icu4c/source/common/sharedptr.h -text
icu4c/source/data/curr/pool.res -text
icu4c/source/data/in/coll/invuca.icu -text
icu4c/source/data/in/coll/ucadata.icu -text
icu4c/source/i18n/decimalformatpattern.h -text
icu4c/source/i18n/i18n.vcxproj -text
icu4c/source/i18n/i18n.vcxproj.filters -text
+icu4c/source/i18n/reldatefmt.cpp -text
+icu4c/source/i18n/unicode/reldatefmt.h -text
icu4c/source/io/io.vcxproj -text
icu4c/source/io/io.vcxproj.filters -text
icu4c/source/layout/layout.vcxproj -text
icu4c/source/test/cintltst/cintltst.vcxproj.filters -text
icu4c/source/test/intltest/intltest.vcxproj -text
icu4c/source/test/intltest/intltest.vcxproj.filters -text
+icu4c/source/test/intltest/lrucachetest.cpp -text
+icu4c/source/test/intltest/reldatefmttest.cpp -text
icu4c/source/test/iotest/iotest.vcxproj -text
icu4c/source/test/iotest/iotest.vcxproj.filters -text
icu4c/source/test/letest/cletest.vcxproj -text
serv.o servnotf.o servls.o servlk.o servlkf.o servrbf.o servslkf.o \
uidna.o usprep.o uts46.o punycode.o \
util.o util_props.o parsepos.o locbased.o cwchar.o wintz.o dtintrv.o ucnvsel.o propsvec.o \
-ulist.o uloc_tag.o icudataver.o icuplug.o listformatter.o
+ulist.o uloc_tag.o icudataver.o icuplug.o listformatter.o lrucache.o
## Header files to install
HEADERS = $(srcdir)/unicode/*.h
<ClCompile Include="locresdata.cpp" />\r
<ClCompile Include="locutil.cpp">\r
</ClCompile>\r
+ <ClCompile Include="lrucache.cpp" />\r
<ClCompile Include="resbund.cpp">\r
</ClCompile>\r
<ClCompile Include="resbund_cnv.cpp" />\r
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
</CustomBuild>\r
<ClInclude Include="locutil.h" />\r
+ <ClInclude Include="lrucache.h" />\r
<CustomBuild Include="unicode\resbund.h">\r
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode\r
</Command>\r
</Command>\r
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
</CustomBuild>\r
+ <ClInclude Include="sharedptr.h" />\r
<CustomBuild Include="unicode\ucat.h">\r
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode\r
</Command>\r
<ClCompile Include="ucol_swp.cpp">\r
<Filter>collation</Filter>\r
</ClCompile>\r
+ <ClCompile Include="lrucache.cpp">\r
+ <Filter>collections</Filter>\r
+ </ClCompile>\r
<ClCompile Include="propsvec.c">\r
<Filter>collections</Filter>\r
</ClCompile>\r
<ClInclude Include="hash.h">\r
<Filter>collections</Filter>\r
</ClInclude>\r
+ <ClInclude Include="lrucache.h">\r
+ <Filter>collections</Filter>\r
+ </ClInclude>\r
<ClInclude Include="propsvec.h">\r
<Filter>collections</Filter>\r
</ClInclude>\r
<ClInclude Include="cmemory.h">\r
<Filter>data & memory</Filter>\r
</ClInclude>\r
+ <ClInclude Include="sharedptr.h">\r
+ <Filter>data & memory</Filter>\r
+ </ClInclude>\r
<ClInclude Include="ucln.h">\r
<Filter>data & memory</Filter>\r
</ClInclude>\r
--- /dev/null
+/*
+*******************************************************************************
+* Copyright (C) 2013, International Business Machines Corporation and
+* others. All Rights Reserved.
+*******************************************************************************
+*
+* File LRUCACHE.CPP
+*******************************************************************************
+*/
+
+#include "lrucache.h"
+#include "mutex.h"
+#include "uhash.h"
+#include "cstring.h"
+
+U_NAMESPACE_BEGIN
+
+// Named CacheEntry2 to avoid conflict with CacheEntry in serv.cpp
+// Don't know how to make truly private class that the linker can't see.
+class CacheEntry2 : public UMemory {
+public:
+ CacheEntry2 *moreRecent;
+ CacheEntry2 *lessRecent;
+ char *localeId;
+ SharedPtr<UObject> cachedData;
+ UErrorCode status; // This is the error if any from creating cachedData.
+
+ CacheEntry2();
+ ~CacheEntry2();
+
+ void unlink();
+ void uninit();
+ UBool init(const char *localeId, UObject *dataToAdopt, UErrorCode err);
+private:
+ CacheEntry2(const CacheEntry2& other);
+ CacheEntry2 &operator=(const CacheEntry2& other);
+};
+
+CacheEntry2::CacheEntry2()
+ : moreRecent(NULL), lessRecent(NULL), localeId(NULL), cachedData(),
+ status(U_ZERO_ERROR) {
+}
+
+CacheEntry2::~CacheEntry2() {
+ uninit();
+}
+
+void CacheEntry2::unlink() {
+ if (moreRecent != NULL) {
+ moreRecent->lessRecent = lessRecent;
+ }
+ if (lessRecent != NULL) {
+ lessRecent->moreRecent = moreRecent;
+ }
+ moreRecent = NULL;
+ lessRecent = NULL;
+}
+
+void CacheEntry2::uninit() {
+ cachedData.clear();
+ status = U_ZERO_ERROR;
+ if (localeId != NULL) {
+ uprv_free(localeId);
+ }
+ localeId = NULL;
+}
+
+UBool CacheEntry2::init(const char *locId, UObject *dataToAdopt, UErrorCode err) {
+ uninit();
+ localeId = (char *) uprv_malloc(strlen(locId) + 1);
+ if (localeId == NULL) {
+ delete dataToAdopt;
+ return FALSE;
+ }
+ uprv_strcpy(localeId, locId);
+ if (!cachedData.adoptInstead(dataToAdopt)) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return TRUE;
+ }
+ status = err;
+ return TRUE;
+}
+
+void LRUCache::moveToMostRecent(CacheEntry2 *entry) {
+ entry->unlink();
+ entry->moreRecent = mostRecentlyUsedMarker;
+ entry->lessRecent = mostRecentlyUsedMarker->lessRecent;
+ mostRecentlyUsedMarker->lessRecent->moreRecent = entry;
+ mostRecentlyUsedMarker->lessRecent = entry;
+}
+
+UObject *LRUCache::safeCreate(const char *localeId, UErrorCode &status) {
+ UObject *result = create(localeId, status);
+
+ // Safe guard to ensure that some error is reported for missing data in
+ // case subclass forgets to set status.
+ if (result == NULL && U_SUCCESS(status)) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+
+ // Safe guard to ensure that if subclass reports an error and returns
+ // data that we don't leak memory.
+ if (result != NULL && U_FAILURE(status)) {
+ delete result;
+ return NULL;
+ }
+ return result;
+}
+
+UBool LRUCache::init(const char *localeId, CacheEntry2 *entry) {
+ UErrorCode status = U_ZERO_ERROR;
+ UObject *result = safeCreate(localeId, status);
+ return entry->init(localeId, result, status);
+}
+
+UBool LRUCache::contains(const char *localeId) const {
+ return (uhash_get(localeIdToEntries, localeId) != NULL);
+}
+
+
+void LRUCache::_get(const char *localeId, SharedPtr<UObject>& ptr, UErrorCode &status) {
+ Mutex lock(mutex);
+ CacheEntry2 *entry = (CacheEntry2 *) uhash_get(localeIdToEntries, localeId);
+ if (entry != NULL) {
+ moveToMostRecent(entry);
+ } else {
+ // Its a cache miss.
+
+ if (uhash_count(localeIdToEntries) < maxSize) {
+ entry = new CacheEntry2;
+ } else {
+ entry = leastRecentlyUsedMarker->moreRecent;
+ uhash_remove(localeIdToEntries, entry->localeId);
+ entry->unlink();
+ entry->uninit();
+ }
+
+ // entry is an uninitialized, unlinked cache entry
+ // or entry is null if memory could not be allocated.
+ if (entry != NULL) {
+ if (!init(localeId, entry)) {
+ delete entry;
+ entry = NULL;
+ }
+ }
+
+ // Entry is initialized, but unlinked or entry is null on
+ // memory allocation error.
+ if (entry != NULL) {
+ // Add to hashtable
+ uhash_put(localeIdToEntries, entry->localeId, entry, &status);
+ if (U_FAILURE(status)) {
+ delete entry;
+ entry = NULL;
+ }
+ }
+ if (entry != NULL) {
+ moveToMostRecent(entry);
+ }
+ }
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+
+ // If we get here our data is cached.
+ if (U_FAILURE(entry->status)) {
+ status = entry->status;
+ return;
+ }
+ ptr = entry->cachedData;
+}
+
+LRUCache::LRUCache(int32_t size, UMutex *mtx, UErrorCode &status) :
+ mostRecentlyUsedMarker(NULL),
+ leastRecentlyUsedMarker(NULL),
+ localeIdToEntries(NULL),
+ maxSize(size),
+ mutex(mtx) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ mostRecentlyUsedMarker = new CacheEntry2;
+ leastRecentlyUsedMarker = new CacheEntry2;
+ if (mostRecentlyUsedMarker == NULL || leastRecentlyUsedMarker == NULL) {
+ delete mostRecentlyUsedMarker;
+ delete leastRecentlyUsedMarker;
+ mostRecentlyUsedMarker = leastRecentlyUsedMarker = NULL;
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ mostRecentlyUsedMarker->moreRecent = NULL;
+ mostRecentlyUsedMarker->lessRecent = leastRecentlyUsedMarker;
+ leastRecentlyUsedMarker->moreRecent = mostRecentlyUsedMarker;
+ leastRecentlyUsedMarker->lessRecent = NULL;
+ localeIdToEntries = uhash_openSize(
+ uhash_hashChars,
+ uhash_compareChars,
+ NULL,
+ maxSize + maxSize / 5,
+ &status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+}
+
+LRUCache::~LRUCache() {
+ uhash_close(localeIdToEntries);
+ for (CacheEntry2 *i = mostRecentlyUsedMarker; i != NULL;) {
+ CacheEntry2 *next = i->lessRecent;
+ delete i;
+ i = next;
+ }
+}
+
+UObject *SimpleLRUCache::create(const char *localeId, UErrorCode &status) {
+ return createFunc(localeId, status);
+}
+
+U_NAMESPACE_END
--- /dev/null
+/*
+*******************************************************************************
+* Copyright (C) 2013, International Business Machines Corporation and
+* others. All Rights Reserved.
+*******************************************************************************
+*
+* File LRUCACHE.H
+*******************************************************************************
+*/
+
+#ifndef __LRU_CACHE_H__
+#define __LRU_CACHE_H__
+
+#include "unicode/uobject.h"
+#include "umutex.h"
+#include "sharedptr.h"
+
+struct UHashtable;
+
+U_NAMESPACE_BEGIN
+
+/**
+ * LRUCache keyed by locale ID.
+ */
+
+class CacheEntry2;
+
+class LRUCache : public UObject {
+ public:
+ template<typename T>
+ void get(const char *localeId, SharedPtr<T> &ptr, UErrorCode &status) {
+ SharedPtr<UObject> p;
+ _get(localeId, p, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ ptr = p;
+ }
+ UBool contains(const char *localeId) const;
+ virtual ~LRUCache();
+ protected:
+ virtual UObject *create(const char *localeId, UErrorCode &status)=0;
+ LRUCache(int32_t maxSize, UMutex *mutex, UErrorCode &status);
+ private:
+ LRUCache();
+ LRUCache(const LRUCache &other);
+ LRUCache &operator=(const LRUCache &other);
+ UObject *safeCreate(const char *localeId, UErrorCode &status);
+ CacheEntry2 *mostRecentlyUsedMarker;
+ CacheEntry2 *leastRecentlyUsedMarker;
+ UHashtable *localeIdToEntries;
+ int32_t maxSize;
+ UMutex *mutex;
+
+ void moveToMostRecent(CacheEntry2 *cacheEntry);
+ UBool init(const char *localeId, CacheEntry2 *cacheEntry);
+ void _get(const char *localeId, SharedPtr<UObject> &ptr, UErrorCode &status);
+};
+
+typedef UObject *(*CreateFunc)(const char *localeId, UErrorCode &status);
+
+class SimpleLRUCache : public LRUCache {
+public:
+ SimpleLRUCache(
+ int32_t maxSize,
+ UMutex *mutex,
+ CreateFunc cf,
+ UErrorCode &status) :
+ LRUCache(maxSize, mutex, status), createFunc(cf) {
+ }
+ virtual ~SimpleLRUCache() {
+ }
+protected:
+ virtual UObject *create(const char *localeId, UErrorCode &status);
+private:
+ CreateFunc createFunc;
+};
+
+U_NAMESPACE_END
+
+#endif
--- /dev/null
+/*
+*******************************************************************************
+* Copyright (C) 2013, International Business Machines Corporation and
+* others. All Rights Reserved.
+*******************************************************************************
+*
+* File SHAREDPTR.H
+*******************************************************************************
+*/
+
+#ifndef __SHARED_PTR_H__
+#define __SHARED_PTR_H__
+
+#include "unicode/uobject.h"
+#include "umutex.h"
+#include "uassert.h"
+
+U_NAMESPACE_BEGIN
+
+// Wrap u_atomic_int32_t in a UMemory so that we allocate them in the same
+// way we allocate all other ICU objects.
+struct _AtomicInt : public UMemory {
+ u_atomic_int32_t value;
+};
+
+/**
+ * SharedPtr are shared pointers that support copy-on-write sematics.
+ * SharedPtr makes the act of copying large objects cheap by deferring the
+ * cost of the copy to the first write operation after the copy.
+ *
+ * A SharedPtr<T> instance can refer to no object or an object of type T where
+ * T is a subclass of UObject. T must also have a clone() method that copies
+ * the object and returns a pointer to the copy. Copy and assignment of
+ * SharedPtr instances are cheap because they only involve copying or
+ * assigning the SharedPtr instance, not the T object which could be large.
+ * Although many SharedPtr<T> instances may refer to the same T object,
+ * clients can still assume that each SharedPtr<T> instance has its own
+ * private instance of T because each SharedPtr<T> instance offers only a
+ * const view of its T object through normal pointer operations. If a caller
+ * must change a T object through its SharedPtr<T>, it can do so by calling
+ * readWrite() on the SharedPtr instance. readWrite() ensures that the
+ * SharedPtr<T> really does have its own private T object by cloning it if
+ * it is shared by using its clone() method. SharedPtr<T> instances handle
+ * management by reference counting their T objects. T objects that are
+ * referenced by no SharedPtr<T> instances get deleted automatically.
+ */
+template<typename T>
+class SharedPtr {
+public:
+ /**
+ * Constructor. If there is a memory allocation error creating
+ * reference counter then this object will contain NULL, and adopted
+ * pointer will be freed. Note that when passing NULL or no argument to
+ * constructor, no memory allocation error can happen as NULL pointers
+ * are never reference counted.
+ */
+ explicit SharedPtr(T *adopted=NULL) : ptr(adopted), refPtr(NULL) {
+ if (ptr != NULL) {
+ refPtr = new _AtomicInt();
+ if (refPtr == NULL) {
+ delete ptr;
+ ptr = NULL;
+ } else {
+ umtx_storeRelease(refPtr->value, 1);
+ }
+ }
+ }
+
+ /**
+ * Non-templated copy costructor. Needed to keep compiler from
+ * creating its own.
+ */
+ SharedPtr(const SharedPtr<T> &other) :
+ ptr(other.ptr), refPtr(other.refPtr) {
+ if (refPtr != NULL) {
+ umtx_atomic_inc(&refPtr->value);
+ }
+ }
+
+ /**
+ * Templated copy constructor.
+ */
+ template<typename U>
+ SharedPtr(const SharedPtr<U> &other) :
+ ptr((T *) other.ptr), refPtr(other.refPtr) {
+ if (refPtr != NULL) {
+ umtx_atomic_inc(&refPtr->value);
+ }
+ }
+
+ /**
+ * Non-templated assignment operator. Needed to keep compiler
+ * from creating its own.
+ */
+ SharedPtr<T> &operator=(const SharedPtr<T> &other) {
+ if (ptr != other.ptr) {
+ SharedPtr<T> newValue(other);
+ swap(newValue);
+ }
+ return *this;
+ }
+
+ /**
+ * Templated assignment operator.
+ */
+ template<typename U>
+ SharedPtr<T> &operator=(const SharedPtr<U> &other) {
+ if (ptr != other.ptr) {
+ SharedPtr<T> newValue(other);
+ swap(newValue);
+ }
+ return *this;
+ }
+
+ /**
+ * Destructor.
+ */
+ ~SharedPtr() {
+ if (refPtr != NULL) {
+ if (umtx_atomic_dec(&refPtr->value) == 0) {
+ // Cast to UObject to avoid compiler warnings about incomplete
+ // type T.
+ delete (UObject *) ptr;
+ delete refPtr;
+ }
+ }
+ }
+
+ /**
+ * adoptInstead adopts a new pointer. On success, returns TRUE.
+ * On memory allocation error creating reference counter for adopted
+ * pointer, returns FALSE while leaving this instance unchanged.
+ */
+ bool adoptInstead(T *adopted) {
+ SharedPtr<T> newValue(adopted);
+ if (adopted != NULL && newValue.ptr == NULL) {
+ // We couldn't allocate ref counter.
+ return FALSE;
+ }
+ swap(newValue);
+ return TRUE;
+ }
+
+ /**
+ * clear makes this instance refer to no object.
+ */
+ void clear() {
+ adoptInstead(NULL);
+ }
+
+ /**
+ * count returns how many SharedPtr instances, including this one,
+ * refer to the T object. Used for testing. Clients need not use in
+ * practice.
+ */
+ int32_t count() const {
+ if (refPtr == NULL) {
+ return 0;
+ }
+ return umtx_loadAcquire(refPtr->value);
+ }
+
+ /**
+ * Swaps this instance with other. a.swap(b) is equivalent to the
+ * following though more efficient: temp = a; a = b; b = temp.
+ */
+ void swap(SharedPtr<T> &other) {
+ T *tempPtr = other.ptr;
+ _AtomicInt *tempRefPtr = other.refPtr;
+ other.ptr = ptr;
+ other.refPtr = refPtr;
+ ptr = tempPtr;
+ refPtr = tempRefPtr;
+ }
+
+ const T *operator->() const {
+ return ptr;
+ }
+
+ const T &operator*() const {
+ return *ptr;
+ }
+
+ bool operator==(const T *other) const {
+ return ptr == other;
+ }
+
+ bool operator!=(const T *other) const {
+ return ptr != other;
+ }
+
+ /**
+ * readOnly gives const access to this instance's T object. If this
+ * instance refers to no object, returns NULL.
+ */
+ const T *readOnly() const {
+ return ptr;
+ }
+
+ /**
+ * readWrite returns a writable pointer to its T object copying it first
+ * using its clone() method if it is shared.
+ * On memory allocation error or if this instance refers to no object,
+ * returns NULL leaving this instance unchanged.
+ */
+ T *readWrite() {
+ int32_t refCount = count();
+ if (refCount == 0 || refCount == 1) {
+ return ptr;
+ }
+ T *result = (T *) ptr->clone();
+ if (result == NULL) {
+ // Memory allocation error
+ return NULL;
+ }
+ if (!adoptInstead(result)) {
+ return NULL;
+ }
+ return ptr;
+ }
+private:
+ T *ptr;
+ _AtomicInt *refPtr;
+ // No heap allocation. Use only stack.
+ static void * U_EXPORT2 operator new(size_t size);
+ static void * U_EXPORT2 operator new[](size_t size);
+#if U_HAVE_PLACEMENT_NEW
+ static void * U_EXPORT2 operator new(size_t, void *ptr);
+#endif
+ template<typename U> friend class SharedPtr;
+};
+
+U_NAMESPACE_END
+
+#endif
ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o locdspnm.o \
decNumber.o decContext.o alphaindex.o tznames.o tznames_impl.o tzgnames.o \
tzfmt.o compactdecimalformat.o gender.o region.o scriptset.o identifier_info.o \
-uregion.o
+uregion.o reldatefmt.o
## Header files to install
HEADERS = $(srcdir)/unicode/*.h
<ClCompile Include="plurrule.cpp" />\r
<ClCompile Include="rbnf.cpp" />\r
<ClCompile Include="rbtz.cpp" />\r
+ <ClCompile Include="reldatefmt.cpp" />\r
<ClCompile Include="reldtfmt.cpp" />\r
<ClCompile Include="selfmt.cpp" />\r
<ClCompile Include="simpletz.cpp" />\r
</Command>\r
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">copy "%(FullPath)" ..\..\include\unicode\r
+</Command>\r
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
+ </CustomBuild>\r
+ <CustomBuild Include="unicode\reldatefmt.h">\r
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode\r
+</Command>\r
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">copy "%(FullPath)" ..\..\include\unicode\r
+</Command>\r
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">copy "%(FullPath)" ..\..\include\unicode\r
+</Command>\r
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">copy "%(FullPath)" ..\..\include\unicode\r
</Command>\r
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
</CustomBuild>\r
<ClCompile Include="rbtz.cpp">\r
<Filter>formatting</Filter>\r
</ClCompile>\r
+ <ClCompile Include="reldatefmt.cpp">\r
+ <Filter>formatting</Filter>\r
+ </ClCompile>\r
<ClCompile Include="reldtfmt.cpp">\r
<Filter>formatting</Filter>\r
</ClCompile>\r
<CustomBuild Include="unicode\rbtz.h">\r
<Filter>formatting</Filter>\r
</CustomBuild>\r
+ <ClInclude Include="unicode\reldatefmt.h">\r
+ <Filter>formatting</Filter>\r
+ </ClInclude>\r
<CustomBuild Include="unicode\selfmt.h">\r
<Filter>formatting</Filter>\r
</CustomBuild>\r
--- /dev/null
+/*
+*******************************************************************************
+* Copyright (C) 2013, International Business Machines Corporation and
+* others. All Rights Reserved.
+*******************************************************************************
+*
+* File RELDATEFMT.CPP
+*******************************************************************************
+*/
+
+#include "unicode/reldatefmt.h"
+
+#include "unicode/localpointer.h"
+#include "unicode/plurrule.h"
+#include "unicode/msgfmt.h"
+#include "unicode/decimfmt.h"
+#include "unicode/numfmt.h"
+#include "lrucache.h"
+#include "uresimp.h"
+#include "unicode/ures.h"
+#include "cstring.h"
+#include "plurrule_impl.h"
+#include "ucln_in.h"
+
+#include "sharedptr.h"
+
+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
+
+U_NAMESPACE_BEGIN
+
+// other must always be first.
+static const char * const gPluralForms[] = {
+ "other", "zero", "one", "two", "few", "many", NULL};
+
+// Must be equal to number of plural forms just above.
+#define MAX_PLURAL_FORMS 6
+
+
+class QualitativeUnits : public UObject {
+public:
+ QualitativeUnits() { }
+ UnicodeString data[UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_UNIT_COUNT];
+ virtual ~QualitativeUnits() {
+ }
+private:
+ QualitativeUnits(const QualitativeUnits &other);
+ QualitativeUnits &operator=(const QualitativeUnits& other);
+};
+
+class QuantitativeUnits : public UObject {
+public:
+ QuantitativeUnits() { }
+ UnicodeString data[UDAT_RELATIVE_UNIT_COUNT][2][MAX_PLURAL_FORMS];
+ virtual ~QuantitativeUnits() {
+ }
+private:
+ QuantitativeUnits(const QuantitativeUnits &other);
+ QuantitativeUnits &operator=(const QuantitativeUnits& other);
+};
+
+class RelativeDateTimeData : public UObject {
+public:
+ RelativeDateTimeData() {
+ }
+ SharedPtr<QualitativeUnits> qualitativeUnits;
+ SharedPtr<QuantitativeUnits> quantitativeUnits;
+ SharedPtr<MessageFormat> combinedDateAndTime;
+ SharedPtr<PluralRules> pluralRules;
+ SharedPtr<NumberFormat> numberFormat;
+ RelativeDateTimeData *clone() const {
+ return new RelativeDateTimeData(*this);
+ }
+ virtual ~RelativeDateTimeData() {
+ }
+private:
+ RelativeDateTimeData(const RelativeDateTimeData &other);
+ RelativeDateTimeData &operator=(const RelativeDateTimeData& other);
+};
+
+RelativeDateTimeData::RelativeDateTimeData(
+ const RelativeDateTimeData &other) :
+ qualitativeUnits(other.qualitativeUnits),
+ quantitativeUnits(other.quantitativeUnits),
+ combinedDateAndTime(other.combinedDateAndTime),
+ pluralRules(other.pluralRules),
+ numberFormat(other.numberFormat) {
+}
+
+static char *UnicodeString2Char(
+ const UnicodeString &source, char *buffer, int32_t bufCapacity) {
+ source.extract(0, source.length(), buffer, bufCapacity, US_INV);
+ return buffer;
+}
+
+static void 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;
+ }
+ result.setTo(TRUE, resStr, len);
+}
+
+static void getOptionalStringWithFallback(
+ const UResourceBundle *resource,
+ const char *key,
+ UnicodeString &result,
+ UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ 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;
+ }
+ if (U_FAILURE(status)) {
+ return;
+ }
+ result.setTo(TRUE, resStr, len);
+}
+
+static void 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;
+ }
+ result.setTo(TRUE, resStr, len);
+}
+
+static void 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;
+ }
+ result.setTo(TRUE, resStr, len);
+}
+
+static void addQualitativeUnit(
+ const UResourceBundle *resource,
+ UDateAbsoluteUnit absoluteUnit,
+ const UnicodeString &unitName,
+ QualitativeUnits &qualitativeUnits,
+ UErrorCode &status) {
+ getStringWithFallback(
+ resource,
+ "-1",
+ qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_LAST],
+ status);
+ getStringWithFallback(
+ resource,
+ "0",
+ qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_THIS],
+ status);
+ getStringWithFallback(
+ resource,
+ "1",
+ qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_NEXT],
+ status);
+ getOptionalStringWithFallback(
+ resource,
+ "-2",
+ qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_LAST_2],
+ status);
+ getOptionalStringWithFallback(
+ resource,
+ "2",
+ qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_NEXT_2],
+ status);
+ qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_PLAIN] = unitName;
+}
+
+static int32_t getPluralIndex(const char *pluralForm) {
+ for (int32_t i = 0; gPluralForms[i] != NULL; ++i) {
+ if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void addTimeUnit(
+ const UResourceBundle *resource,
+ UDateRelativeUnit relativeUnit,
+ int32_t pastOrFuture,
+ QuantitativeUnits &quantitativeUnits,
+ 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;
+ }
+ int32_t pluralIndex = getPluralIndex(
+ ures_getKey(pluralBundle.getAlias()));
+ if (pluralIndex != -1) {
+ getString(
+ pluralBundle.getAlias(),
+ quantitativeUnits.data[relativeUnit][pastOrFuture][pluralIndex],
+ status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ }
+ }
+}
+
+static void addTimeUnit(
+ const UResourceBundle *resource,
+ UDateRelativeUnit relativeUnit,
+ QuantitativeUnits &quantitativeUnits,
+ 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;
+ }
+ addTimeUnit(
+ futureBundle.getAlias(),
+ relativeUnit,
+ 1,
+ quantitativeUnits,
+ status);
+ LocalUResourceBundlePointer pastBundle(ures_getByKeyWithFallback(
+ topLevel.getAlias(), "past", NULL, &status));
+ if (U_FAILURE(status)) {
+ return;
+ }
+ addTimeUnit(
+ pastBundle.getAlias(),
+ relativeUnit,
+ 0,
+ quantitativeUnits,
+ status);
+}
+
+static void addTimeUnit(
+ const UResourceBundle *resource,
+ const char *path,
+ UDateRelativeUnit relativeUnit,
+ QuantitativeUnits &quantitativeUnits,
+ UErrorCode &status) {
+ LocalUResourceBundlePointer topLevel(
+ ures_getByKeyWithFallback(resource, path, NULL, &status));
+ if (U_FAILURE(status)) {
+ return;
+ }
+ addTimeUnit(topLevel.getAlias(), relativeUnit, quantitativeUnits, status);
+}
+
+static void addTimeUnit(
+ const UResourceBundle *resource,
+ const char *path,
+ UDateRelativeUnit relativeUnit,
+ UDateAbsoluteUnit absoluteUnit,
+ QuantitativeUnits &quantitativeUnits,
+ QualitativeUnits &qualitativeUnits,
+ UErrorCode &status) {
+ LocalUResourceBundlePointer topLevel(
+ ures_getByKeyWithFallback(resource, path, NULL, &status));
+ if (U_FAILURE(status)) {
+ return;
+ }
+ addTimeUnit(topLevel.getAlias(), relativeUnit, quantitativeUnits, status);
+ UnicodeString unitName;
+ getStringWithFallback(topLevel.getAlias(), "dn", unitName, status);
+ if (U_FAILURE(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;
+ }
+ addQualitativeUnit(
+ topLevel.getAlias(),
+ absoluteUnit,
+ unitName,
+ qualitativeUnits,
+ 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) {
+ getStringByIndex(topLevel.getAlias(), i, daysOfWeek[i], status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ }
+}
+
+static void addWeekDay(
+ const UResourceBundle *resource,
+ const char *path,
+ const UnicodeString *daysOfWeek,
+ UDateAbsoluteUnit absoluteUnit,
+ QualitativeUnits &qualitativeUnits,
+ UErrorCode &status) {
+ LocalUResourceBundlePointer topLevel(
+ ures_getByKeyWithFallback(resource, path, NULL, &status));
+ if (U_FAILURE(status)) {
+ return;
+ }
+ addQualitativeUnit(
+ topLevel.getAlias(),
+ absoluteUnit,
+ daysOfWeek[absoluteUnit - UDAT_ABSOLUTE_SUNDAY],
+ qualitativeUnits,
+ status);
+}
+
+static void load(
+ const UResourceBundle *resource,
+ QualitativeUnits &qualitativeUnits,
+ QuantitativeUnits &quantitativeUnits,
+ UErrorCode &status) {
+ addTimeUnit(
+ resource,
+ "fields/day",
+ UDAT_RELATIVE_DAYS,
+ UDAT_ABSOLUTE_DAY,
+ quantitativeUnits,
+ qualitativeUnits,
+ status);
+ addTimeUnit(
+ resource,
+ "fields/week",
+ UDAT_RELATIVE_WEEKS,
+ UDAT_ABSOLUTE_WEEK,
+ quantitativeUnits,
+ qualitativeUnits,
+ status);
+ addTimeUnit(
+ resource,
+ "fields/month",
+ UDAT_RELATIVE_MONTHS,
+ UDAT_ABSOLUTE_MONTH,
+ quantitativeUnits,
+ qualitativeUnits,
+ status);
+ addTimeUnit(
+ resource,
+ "fields/year",
+ UDAT_RELATIVE_YEARS,
+ UDAT_ABSOLUTE_YEAR,
+ quantitativeUnits,
+ qualitativeUnits,
+ status);
+ addTimeUnit(
+ resource,
+ "fields/second",
+ UDAT_RELATIVE_SECONDS,
+ quantitativeUnits,
+ status);
+ addTimeUnit(
+ resource,
+ "fields/minute",
+ UDAT_RELATIVE_MINUTES,
+ quantitativeUnits,
+ status);
+ addTimeUnit(
+ resource,
+ "fields/hour",
+ UDAT_RELATIVE_HOURS,
+ quantitativeUnits,
+ status);
+ getStringWithFallback(
+ resource,
+ "fields/second/relative/0",
+ qualitativeUnits.data[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,
+ qualitativeUnits,
+ status);
+ addWeekDay(
+ resource,
+ "fields/tue/relative",
+ daysOfWeek,
+ UDAT_ABSOLUTE_TUESDAY,
+ qualitativeUnits,
+ status);
+ addWeekDay(
+ resource,
+ "fields/wed/relative",
+ daysOfWeek,
+ UDAT_ABSOLUTE_WEDNESDAY,
+ qualitativeUnits,
+ status);
+ addWeekDay(
+ resource,
+ "fields/thu/relative",
+ daysOfWeek,
+ UDAT_ABSOLUTE_THURSDAY,
+ qualitativeUnits,
+ status);
+ addWeekDay(
+ resource,
+ "fields/fri/relative",
+ daysOfWeek,
+ UDAT_ABSOLUTE_FRIDAY,
+ qualitativeUnits,
+ status);
+ addWeekDay(
+ resource,
+ "fields/sat/relative",
+ daysOfWeek,
+ UDAT_ABSOLUTE_SATURDAY,
+ qualitativeUnits,
+ status);
+ addWeekDay(
+ resource,
+ "fields/sun/relative",
+ daysOfWeek,
+ UDAT_ABSOLUTE_SUNDAY,
+ qualitativeUnits,
+ status);
+}
+
+static void getDateTimePattern(
+ const UResourceBundle *resource,
+ UnicodeString &result,
+ UErrorCode &status) {
+ UnicodeString defaultCalendarName;
+ getStringWithFallback(
+ resource,
+ "calendar/default",
+ defaultCalendarName,
+ status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ char calendarNameBuffer[128];
+ char pathBuffer[256];
+ sprintf(
+ pathBuffer,
+ "calendar/%s/DateTimePatterns",
+ UnicodeString2Char(
+ defaultCalendarName,
+ calendarNameBuffer,
+ 128));
+ LocalUResourceBundlePointer topLevel(
+ ures_getByKeyWithFallback(resource, pathBuffer, NULL, &status));
+ if (U_FAILURE(status)) {
+ return;
+ }
+ int32_t size = ures_getSize(topLevel.getAlias());
+ if (size < 9) {
+ // Oops, size is to small to access the index that we want, fallback
+ // to a hard-coded value.
+ result = UnicodeString("{1} {0}");
+ return;
+ }
+ getStringByIndex(topLevel.getAlias(), 8, result, status);
+}
+
+static UObject *U_CALLCONV createData(const char *localeId, UErrorCode &status) {
+ LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ LocalPointer<RelativeDateTimeData> result(new RelativeDateTimeData());
+ LocalPointer<QualitativeUnits> qualitativeUnits(new QualitativeUnits());
+ LocalPointer<QuantitativeUnits> quantitativeUnits(new QuantitativeUnits());
+ if (qualitativeUnits.getAlias() == NULL || quantitativeUnits.getAlias() == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ load(topLevel.getAlias(), *qualitativeUnits, *quantitativeUnits, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ if (!result->qualitativeUnits.adoptInstead(qualitativeUnits.orphan())) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ if (!result->quantitativeUnits.adoptInstead(quantitativeUnits.orphan())) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+
+
+ UnicodeString dateTimePattern;
+ getDateTimePattern(topLevel.getAlias(), dateTimePattern, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ LocalPointer<MessageFormat> mf(new MessageFormat(dateTimePattern, localeId, status));
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ if (!result->combinedDateAndTime.adoptInstead(mf.orphan())) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ LocalPointer<PluralRules> pr(PluralRules::forLocale(localeId, status));
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ if (!result->pluralRules.adoptInstead(pr.orphan())) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ LocalPointer<NumberFormat> nf(
+ NumberFormat::createInstance(localeId, status));
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ if (!result->numberFormat.adoptInstead(nf.orphan())) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ 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, &gCacheMutex, &createData, status);
+ if (U_FAILURE(status)) {
+ delete gCache;
+ gCache = NULL;
+ }
+}
+
+static void getFromCache(const char *locale, SharedPtr<RelativeDateTimeData>& ptr, UErrorCode &status) {
+ umtx_initOnce(gCacheInitOnce, &cacheInit, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ gCache->get(locale, ptr, status);
+}
+
+RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) {
+ getFromCache(Locale::getDefault().getName(), ptr, status);
+}
+
+RelativeDateTimeFormatter::RelativeDateTimeFormatter(const Locale& locale, UErrorCode& status) {
+ getFromCache(locale.getName(), ptr, status);
+}
+
+
+RelativeDateTimeFormatter::RelativeDateTimeFormatter(const RelativeDateTimeFormatter& other) : ptr(other.ptr) {
+}
+
+RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(const RelativeDateTimeFormatter& other) {
+ if (this != &other) {
+ ptr = other.ptr;
+ }
+ return *this;
+}
+
+RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
+}
+
+
+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;
+ }
+ FixedDecimal dec(quantity);
+ const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(
+ ptr->numberFormat.readOnly());
+ if (decFmt != NULL) {
+ dec = decFmt->getFixedDecimal(quantity, status);
+ }
+ if (U_FAILURE(status)) {
+ return appendTo;
+ }
+ char buffer[256];
+ int32_t pluralIndex = getPluralIndex(
+ UnicodeString2Char(ptr->pluralRules->select(dec), buffer, 256));
+ if (pluralIndex == -1) {
+ pluralIndex = 0;
+ }
+ int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
+ const UnicodeString *pattern = &ptr->quantitativeUnits->data[unit][bFuture][pluralIndex];
+ if (pattern->isEmpty()) {
+ pattern = &ptr->quantitativeUnits->data[unit][bFuture][0];
+ }
+ if (pattern->isEmpty()) {
+ return appendTo;
+ }
+ UnicodeString result(*pattern);
+ UnicodeString formattedNumber;
+ result.findAndReplace(UnicodeString("{0}"), ptr->numberFormat->format(quantity, formattedNumber));
+ 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;
+ }
+ return appendTo.append(ptr->qualitativeUnits->data[unit][direction]);
+}
+
+UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
+ const UnicodeString& relativeDateString, const UnicodeString& timeString,
+ UnicodeString& appendTo, UErrorCode& status) const {
+ Formattable formattable[2];
+ formattable[0].setString(timeString);
+ formattable[1].setString(relativeDateString);
+ FieldPosition fpos(0);
+ return ptr->combinedDateAndTime->format(formattable, 2, appendTo, fpos, status);
+}
+
+void RelativeDateTimeFormatter::setNumberFormat(const NumberFormat& nf) {
+ RelativeDateTimeData *wptr = ptr.readWrite();
+ NumberFormat *newNf = (NumberFormat *) nf.clone();
+ if (newNf != NULL && wptr != NULL) {
+ wptr->numberFormat.adoptInstead(newNf);
+ }
+}
+
+U_NAMESPACE_END
+
It's usually best to have child dependencies called first. */
typedef enum ECleanupI18NType {
UCLN_I18N_START = -1,
+ UCLN_I18N_RELDATEFMT,
UCLN_I18N_IDENTIFIER_INFO,
UCLN_I18N_SPOOF,
UCLN_I18N_TRANSLITERATOR,
--- /dev/null
+/*
+********************************************************************************
+* Copyright (C) 2013, International Business Machines Corporation and
+* others.
+* All Rights Reserved.
+********************************************************************************
+*
+* File RELDATEFMT.H
+********************************************************************************
+*/
+
+#ifndef __RELDATEFMT_H
+#define __RELDATEFMT_H
+
+#include "unicode/utypes.h"
+#include "unicode/locid.h"
+#include "sharedptr.h"
+
+
+/**
+ * Represents the unit for formatting a relative date. e.g "in 5 days"
+ * or "in 3 months"
+ * @draft ICU 53
+ */
+typedef enum UDateRelativeUnit {
+
+ /**
+ * Seconds
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_RELATIVE_SECONDS,
+
+ /**
+ * Minutes
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_RELATIVE_MINUTES,
+
+ /**
+ * Hours
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_RELATIVE_HOURS,
+
+ /**
+ * Days
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_RELATIVE_DAYS,
+
+ /**
+ * Weeks
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_RELATIVE_WEEKS,
+
+ /**
+ * Months
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_RELATIVE_MONTHS,
+
+ /**
+ * Years
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_RELATIVE_YEARS,
+
+ /**
+ * Count of items in this enum.
+ * @draft ICU 53
+ */
+ UDAT_RELATIVE_UNIT_COUNT
+} UDateRelativeUnit;
+
+/**
+ * Represents an absolute unit.
+ * @draft ICU 53
+ */
+typedef enum UDateAbsoluteUnit {
+
+ // Days of week have to remain together and in order from Sunday to
+ // Saturday.
+ /**
+ * Sunday
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_SUNDAY,
+
+ /**
+ * Monday
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_MONDAY,
+
+ /**
+ * Tuesday
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_TUESDAY,
+
+ /**
+ * Wednesday
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_WEDNESDAY,
+
+ /**
+ * Thursday
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_THURSDAY,
+
+ /**
+ * Friday
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_FRIDAY,
+
+ /**
+ * Saturday
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_SATURDAY,
+
+ /**
+ * Day
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_DAY,
+
+ /**
+ * Week
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_WEEK,
+
+ /**
+ * Month
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_MONTH,
+
+ /**
+ * Year
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_ABSOLUTE_YEAR,
+
+ /**
+ * Now
+ * @draft ICU 53
+ */
+ UDAT_ABSOLUTE_NOW,
+
+ /**
+ * Count of items in this enum.
+ * @draft ICU 53
+ */
+ UDAT_ABSOLUTE_UNIT_COUNT
+} UDateAbsoluteUnit;
+
+/**
+ * Represents a direction for an absolute unit e.g "Next Tuesday"
+ * or "Last Tuesday"
+ * @draft ICU 53
+ */
+typedef enum UDateDirection {
+
+ /**
+ * Two before. Not fully supported in every locale.
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_DIRECTION_LAST_2,
+
+ /**
+ * Last
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_DIRECTION_LAST,
+
+ /**
+ * This
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_DIRECTION_THIS,
+
+ /**
+ * Next
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_DIRECTION_NEXT,
+
+ /**
+ * Two after. Not fully supported in every locale.
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_DIRECTION_NEXT_2,
+
+ /**
+ * Plain, which means the absence of a qualifier.
+ Â Â Â Â * @draft ICU 53
+ Â Â Â Â */
+ UDAT_DIRECTION_PLAIN,
+
+ /**
+ * Count of items in this enum.
+ * @draft ICU 53
+ */
+ UDAT_DIRECTION_UNIT_COUNT
+} UDateDirection;
+
+
+U_NAMESPACE_BEGIN
+
+class RelativeDateTimeData;
+class NumberFormat;
+
+/**
+ * Formats simple relative dates. There are two types of relative dates that
+ * it handles:
+ * <ul>
+ * <li>relative dates with a quantity e.g "in 5 days"</li>
+ * <li>relative dates without a quantity e.g "next Tuesday"</li>
+ * </ul>
+ * <p>
+ * This API is very basic and is intended to be a building block for more
+ * fancy APIs. The caller tells it exactly what to display in a locale
+ * independent way. While this class automatically provides the correct plural
+ * forms, the grammatical form is otherwise as neutral as possible. It is the
+ * caller's responsibility to handle cut-off logic such as deciding between
+ * displaying "in 7 days" or "in 1 week." This API supports relative dates
+ * involving one single unit. This API does not support relative dates
+ * involving compound units.
+ * e.g "in 5 days and 4 hours" nor does it support parsing.
+ * This class is NOT thread-safe.
+ * <p>
+ * Here are some examples of use:
+ * <blockquote>
+ * <pre>
+ * UErrorCode status = U_ZERO_ERROR;
+ * UnicodeString appendTo;
+ * RelativeDateTimeFormatter fmt(status);
+ * // Appends "in 1 day"
+ * fmt.format(
+ * 1, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_DAYS, appendTo, status);
+ * // Appends "in 3 days"
+ * fmt.format(
+ * 3, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_DAYS, appendTo, status);
+ * // Appends "3.2 years ago"
+ * fmt.format(
+ * 3.2, UDAT_DIRECTION_LAST, UDAT_RELATIVE_YEARS, appendTo, status);
+ * // Appends "last Sunday"
+ * fmt.format(UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_SUNDAY, appendTo, status);
+ * // Appends "this Sunday"
+ * fmt.format(UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_SUNDAY, appendTo, status);
+ * // Appends "next Sunday"
+ * fmt.format(UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_SUNDAY, appendTo, status);
+ * // Appends "Sunday"
+ * fmt.format(UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_SUNDAY, appendTo, status);
+ *
+ * // Appends "yesterday"
+ * fmt.format(UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_DAY, appendTo, status);
+ * // Appends "today"
+ * fmt.format(UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_DAY, appendTo, status);
+ * // Appends "tomorrow"
+ * fmt.format(UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_DAY, appendTo, status);
+ * // Appends "now"
+ * fmt.format(UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_NOW, appendTo, status);
+ *
+ * </pre>
+ * </blockquote>
+ * <p>
+ * In the future, we may add more forms, such as abbreviated/short forms
+ * (3 secs ago), and relative day periods ("yesterday afternoon"), etc.
+ *
+ * The RelativeDateTimeFormatter class is not intended for public subclassing.
+ *
+ * @draft ICU 53
+ */
+
+
+class U_I18N_API RelativeDateTimeFormatter : public UObject {
+public:
+
+ /**
+ * Create RelativeDateTimeFormatter with default locale.
+ * @draft ICU 53
+ */
+ RelativeDateTimeFormatter(UErrorCode& status);
+
+
+ /**
+ * Create RelativeDateTimeFormatter with given locale.
+ * @draft ICU 53
+ */
+ RelativeDateTimeFormatter(const Locale& locale, UErrorCode& status);
+
+ /**
+ * Copy constructor.
+ * @draft ICU 53
+ */
+ RelativeDateTimeFormatter(const RelativeDateTimeFormatter& other);
+
+ /**
+ * Assignment operator.
+ * @draft ICU 53
+ */
+ RelativeDateTimeFormatter& operator=(const RelativeDateTimeFormatter& other);
+
+ /**
+ * Destructor.
+ * @draft ICU 53
+ */
+ virtual ~RelativeDateTimeFormatter();
+
+ /**
+ * Formats a relative date with a quantity such as "in 5 days" or
+ * "3 months ago"
+ * @param quantity The numerical amount e.g 5. This value is formatted
+ * according to this object's NumberFormat object.
+ * @param direction NEXT means a future relative date; LAST means a past
+ * relative date. If direction is anything else, this method sets
+ * status to U_ILLEGAL_ARGUMENT_ERROR.
+ * @param unit the unit e.g day? month? year?
+ * @param appendTo The string to which the formatted result will be
+ * appended
+ * @param status ICU error code returned here.
+ * @return appendTo
+ * @draft ICU 53
+ */
+ UnicodeString& format(double quantity, UDateDirection direction, UDateRelativeUnit unit, UnicodeString& appendTo, UErrorCode& status) const;
+
+ /**
+ * Formats a relative date without a quantity.
+ * @param direction NEXT, LAST, THIS, etc.
+ * @param unit e.g SATURDAY, DAY, MONTH
+ * @param appendTo The string to which the formatted result will be
+ * appended. If the value of direction is documented as not being fully
+ * supported in all locales then this method leaves appendTo unchanged if
+ * no format string is available.
+ * @param status ICU error code returned here.
+ * @return appendTo
+ * @draft ICU 53
+ */
+ UnicodeString& format(UDateDirection direction, UDateAbsoluteUnit unit, UnicodeString& appendTo, UErrorCode& status) const;
+
+ /**
+ * Combines a relative date string and a time string in this object's
+ * locale. This is done with the same date-time separator used for the
+ * default calendar in this locale.
+ *
+ * @param relativeDateString the relative date, e.g 'yesterday'
+ * @param timeString the time e.g '3:45'
+ * @param appendTo concatenated date and time appended here
+ * @param status ICU error code returned here.
+ * @return appendTo
+ * @draft ICU 53
+ */
+ UnicodeString& combineDateAndTime(
+ const UnicodeString& relativeDateString, const UnicodeString& timeString,
+ UnicodeString& appendTo, UErrorCode& status) const;
+
+ /**
+ * Specify which NumberFormat object this object should use for
+ * formatting numbers. By default this object uses the default
+ * NumberFormat object for this object's locale.
+ * @param nf the NumberFormat object to use.
+ * @see #format(double, Direction, RelativeUnit)
+ * @draft ICU 53
+ */
+ void setNumberFormat(const NumberFormat& nf);
+private:
+ RelativeDateTimeFormatter();
+ SharedPtr<icu::RelativeDateTimeData> ptr;
+};
+
+U_NAMESPACE_END
+
+#endif
uobjtest.o idnaref.o idnaconf.o nptrans.o punyref.o testidn.o testidna.o uts46test.o \
incaltst.o calcasts.o v32test.o uvectest.o textfile.o tokiter.o utxttest.o \
windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o tzoffloc.o tzfmttst.o ssearch.o dtifmtts.o \
-tufmtts.o itspoof.o simplethread.o bidiconf.o locnmtst.o dcfmtest.o alphaindextst.o listformattertest.o genderinfotest.o compactdecimalformattest.o regiontst.o
+tufmtts.o itspoof.o simplethread.o bidiconf.o locnmtst.o dcfmtest.o alphaindextst.o listformattertest.o genderinfotest.o compactdecimalformattest.o regiontst.o \
+reldatefmttest.o lrucachetest.o
DEPS = $(OBJECTS:.o=.d)
<ClCompile Include="itrbnfp.cpp" />\r
<ClCompile Include="itrbnfrt.cpp" />\r
<ClCompile Include="locnmtst.cpp" />\r
+ <ClCompile Include="lrucachetest.cpp" />\r
<ClCompile Include="miscdtfm.cpp" />\r
<ClCompile Include="msfmrgts.cpp" />\r
<ClCompile Include="nmfmapts.cpp" />\r
<ClCompile Include="plurfmts.cpp" />\r
<ClCompile Include="plurults.cpp" />\r
<ClCompile Include="pptest.cpp" />\r
+ <ClCompile Include="reldatefmttest.cpp" />\r
<ClCompile Include="sdtfmtts.cpp" />\r
<ClCompile Include="selfmts.cpp" />\r
<ClCompile Include="tchcfmt.cpp" />\r
<ClCompile Include="ucaconf.cpp">\r
<Filter>collation</Filter>\r
</ClCompile>\r
+ <ClCompile Include="lrucachetest.cpp">\r
+ <Filter>collections</Filter>\r
+ </ClCompile>\r
<ClCompile Include="uvectest.cpp">\r
<Filter>collections</Filter>\r
</ClCompile>\r
<ClCompile Include="pptest.cpp">\r
<Filter>formatting</Filter>\r
</ClCompile>\r
+ <ClCompile Include="reldatefmttest.cpp">\r
+ <Filter>formatting</Filter>\r
+ </ClCompile>\r
<ClCompile Include="sdtfmtts.cpp">\r
<Filter>formatting</Filter>\r
</ClCompile>\r
<Filter>formatting</Filter>\r
</ClInclude>\r
</ItemGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r
extern IntlTest *createCompactDecimalFormatTest();
extern IntlTest *createGenderInfoTest();
+extern IntlTest *createRelativeDateTimeFormatterTest();
#define TESTCLASS(id, TestClass) \
case id: \
}
break;
TESTCLASS(45,RegionTest);
+ case 46:
+ name = "RelativeDateTimeFormatterTest";
+ if (exec) {
+ logln("RelativeDateTimeFormatterTest test---");
+ logln((UnicodeString)"");
+ LocalPointer<IntlTest> test(createRelativeDateTimeFormatterTest());
+ callTest(*test, par);
+ }
+ break;
default: name = ""; break; //needed to end loop
}
if (exec) {
/********************************************************************
* COPYRIGHT:
- * Copyright (c) 1997-2012, International Business Machines Corporation and
+ * Copyright (c) 1997-2013, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
static IntlTest *createLocalPointerTest();
extern IntlTest *createUCharsTrieTest();
static IntlTest *createEnumSetTest();
+extern IntlTest *createLRUCacheTest();
#define CASE(id, test) case id: \
name = #test; \
callTest(*test, par);
}
break;
+ case 20:
+ name = "LRUCacheTest";
+ if (exec) {
+ logln("TestSuite LRUCacheTest---"); logln();
+ LocalPointer<IntlTest> test(createLRUCacheTest());
+ callTest(*test, par);
+ }
+ break;
default: name = ""; break; //needed to end loop
}
}
--- /dev/null
+/*
+*******************************************************************************
+* Copyright (C) 2013, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*
+* File LRUCACHETEST.CPP
+*
+********************************************************************************
+*/
+#include "cstring.h"
+#include "intltest.h"
+#include "lrucache.h"
+#include "umutex.h"
+
+static UMutex gMutex = U_MUTEX_INITIALIZER;
+
+class CopyOnWriteForTesting : public UObject {
+public:
+ CopyOnWriteForTesting() : localeNamePtr(), formatStrPtr(), length(0) {
+ }
+ UObject *clone() const {
+ return new CopyOnWriteForTesting(*this);
+ }
+ virtual ~CopyOnWriteForTesting() {
+ }
+ SharedPtr<UnicodeString> localeNamePtr;
+ SharedPtr<UnicodeString> formatStrPtr;
+ int32_t length;
+private:
+ CopyOnWriteForTesting(const CopyOnWriteForTesting &other) :
+ localeNamePtr(other.localeNamePtr),
+ formatStrPtr(other.formatStrPtr),
+ length(other.length) {
+ }
+ CopyOnWriteForTesting &operator=(const CopyOnWriteForTesting &rhs);
+};
+
+class LRUCacheForTesting : public LRUCache {
+public:
+ LRUCacheForTesting(
+ int32_t maxSize, UMutex *mutex,
+ const UnicodeString &dfs, UErrorCode &status);
+ virtual ~LRUCacheForTesting() {
+ }
+protected:
+ virtual UObject *create(const char *localeId, UErrorCode &status);
+private:
+ SharedPtr<UnicodeString> defaultFormatStr;
+};
+
+LRUCacheForTesting::LRUCacheForTesting(
+ int32_t maxSize, UMutex *mutex,
+ const UnicodeString &dfs, UErrorCode &status) :
+ LRUCache(maxSize, mutex, status), defaultFormatStr() {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ defaultFormatStr.adoptInstead(new UnicodeString(dfs));
+}
+
+UObject *LRUCacheForTesting::create(const char *localeId, UErrorCode &status) {
+ if (uprv_strcmp(localeId, "error") == 0) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return NULL;
+ }
+ CopyOnWriteForTesting *result = new CopyOnWriteForTesting;
+ result->localeNamePtr.adoptInstead(new UnicodeString(localeId));
+ result->formatStrPtr = defaultFormatStr;
+ result->length = 5;
+ return result;
+}
+
+class LRUCacheTest : public IntlTest {
+public:
+ LRUCacheTest() {
+ }
+ void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
+private:
+ void TestSharedPointer();
+ void TestErrorCallingConstructor();
+ void TestLRUCache();
+ void TestLRUCacheError();
+ void verifySharedPointer(
+ const SharedPtr<CopyOnWriteForTesting>& ptr,
+ const UnicodeString& name,
+ const UnicodeString& format);
+ void verifyString(
+ const UnicodeString &expected, const UnicodeString &actual);
+ void verifyPtr(const void *expected, const void *actual);
+ void verifyReferences(
+ const SharedPtr<CopyOnWriteForTesting>& ptr,
+ int32_t count, int32_t nameCount, int32_t formatCount);
+};
+
+void LRUCacheTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) {
+ TESTCASE_AUTO_BEGIN;
+ TESTCASE_AUTO(TestSharedPointer);
+ TESTCASE_AUTO(TestErrorCallingConstructor);
+ TESTCASE_AUTO(TestLRUCache);
+ TESTCASE_AUTO(TestLRUCacheError);
+ TESTCASE_AUTO_END;
+}
+
+void LRUCacheTest::TestSharedPointer() {
+ UErrorCode status = U_ZERO_ERROR;
+ LRUCacheForTesting cache(3, &gMutex, "little", status);
+ SharedPtr<CopyOnWriteForTesting> ptr;
+ cache.get("boo", ptr, status);
+ verifySharedPointer(ptr, "boo", "little");
+ SharedPtr<CopyOnWriteForTesting> ptrCopy = ptr;
+ verifyPtr(ptr.readOnly(), ptrCopy.readOnly());
+ {
+ SharedPtr<CopyOnWriteForTesting> ptrCopy2(ptrCopy);
+ verifyReferences(ptr, 4, 1, 2);
+ }
+
+ // Test identity assignment
+ ptr = ptr;
+
+ verifyReferences(ptr, 3, 1, 2);
+ verifyReferences(ptrCopy, 3, 1, 2);
+ *ptrCopy.readWrite()->localeNamePtr.readWrite() = UnicodeString("hi there");
+ *ptrCopy.readWrite()->formatStrPtr.readWrite() = UnicodeString("see you");
+ verifyReferences(ptr, 2, 1, 2);
+ verifyReferences(ptrCopy, 1, 1, 1);
+ verifySharedPointer(ptr, "boo", "little");
+ verifySharedPointer(ptrCopy, "hi there", "see you");
+}
+
+void LRUCacheTest::TestErrorCallingConstructor() {
+ UErrorCode status = U_MEMORY_ALLOCATION_ERROR;
+ LRUCacheForTesting cache(3, &gMutex, "little", status);
+}
+
+void LRUCacheTest::TestLRUCache() {
+ UErrorCode status = U_ZERO_ERROR;
+ LRUCacheForTesting cache(3, &gMutex, "little", status);
+ SharedPtr<CopyOnWriteForTesting> ptr1;
+ SharedPtr<CopyOnWriteForTesting> ptr2;
+ SharedPtr<CopyOnWriteForTesting> ptr3;
+ SharedPtr<CopyOnWriteForTesting> ptr4;
+ SharedPtr<CopyOnWriteForTesting> ptr5;
+ cache.get("foo", ptr1, status);
+ cache.get("bar", ptr2, status);
+ cache.get("baz", ptr3, status);
+ verifySharedPointer(ptr1, "foo", "little");
+ verifySharedPointer(ptr2, "bar", "little");
+ verifySharedPointer(ptr3, "baz", "little");
+
+ // Cache holds a reference to returned data which explains the 2s
+ // Note the '4'. each cached data has a reference to "little" and the
+ // cache itself also has a reference to "little"
+ verifyReferences(ptr1, 2, 1, 4);
+ verifyReferences(ptr2, 2, 1, 4);
+ verifyReferences(ptr3, 2, 1, 4);
+
+ // (Most recent) "baz", "bar", "foo" (Least Recent)
+ // Cache is now full but thanks to shared pointers we can still evict.
+ cache.get("full", ptr4, status);
+ verifySharedPointer(ptr4, "full", "little");
+
+ verifyReferences(ptr4, 2, 1, 5);
+
+ // (Most Recent) "full" "baz", "bar" (Least Recent)
+ cache.get("baz", ptr5, status);
+ verifySharedPointer(ptr5, "baz", "little");
+ // ptr5, ptr3, and cache have baz data
+ verifyReferences(ptr5, 3, 1, 5);
+
+ // This should delete foo data since it got evicted from cache.
+ ptr1.clear();
+ // Reference count for little drops to 4 because foo data was deleted.
+ verifyReferences(ptr5, 3, 1, 4);
+
+ // (Most Recent) "baz" "full" "bar" (Least Recent)
+ cache.get("baz", ptr5, status);
+ verifySharedPointer(ptr5, "baz", "little");
+ verifyReferences(ptr5, 3, 1, 4);
+
+ // (Most Recent) "baz", "full", "bar" (Least Recent)
+ // ptr3, ptr5 -> "baz" ptr4 -> "full" ptr2 -> "bar"
+ if (!cache.contains("baz") || !cache.contains("full") || !cache.contains("bar") || cache.contains("foo")) {
+ errln("Unexpected keys in cache.");
+ }
+ cache.get("new1", ptr5, status);
+ verifySharedPointer(ptr5, "new1", "little");
+ verifyReferences(ptr5, 2, 1, 5);
+
+ // Since bar was evicted, clearing its pointer should delete its data.
+ // Notice that the reference count to 'little' dropped from 5 to 4.
+ ptr2.clear();
+ verifyReferences(ptr5, 2, 1, 4);
+ if (cache.contains("bar") || !cache.contains("full")) {
+ errln("Unexpected 'bar' in cache.");
+ }
+
+ // (Most Recent) "new1", "baz", "full" (Least Recent)
+ // ptr3 -> "baz" ptr4 -> "full" ptr5 -> "new1"
+ cache.get("new2", ptr5, status);
+ verifySharedPointer(ptr5, "new2", "little");
+ verifyReferences(ptr5, 2, 1, 5);
+
+ // since "full" was evicted, clearing its pointer should delete its data.
+ ptr4.clear();
+ verifyReferences(ptr5, 2, 1, 4);
+ if (cache.contains("full") || !cache.contains("baz")) {
+ errln("Unexpected 'full' in cache.");
+ }
+
+ // (Most Recent) "new2", "new1", "baz" (Least Recent)
+ // ptr3 -> "baz" ptr5 -> "new2"
+ cache.get("new3", ptr5, status);
+ verifySharedPointer(ptr5, "new3", "little");
+ verifyReferences(ptr5, 2, 1, 5);
+
+ // since "baz" was evicted, clearing its pointer should delete its data.
+ ptr3.clear();
+ verifyReferences(ptr5, 2, 1, 4);
+ if (cache.contains("baz") || !cache.contains("new3")) {
+ errln("Unexpected 'baz' in cache.");
+ }
+}
+
+void LRUCacheTest::TestLRUCacheError() {
+ UErrorCode status = U_ZERO_ERROR;
+ LRUCacheForTesting cache(3, &gMutex, "little", status);
+ SharedPtr<CopyOnWriteForTesting> ptr1;
+ cache.get("error", ptr1, status);
+ if (status != U_ILLEGAL_ARGUMENT_ERROR) {
+ errln("Expected an error.");
+ }
+}
+
+void LRUCacheTest::verifySharedPointer(
+ const SharedPtr<CopyOnWriteForTesting>& ptr,
+ const UnicodeString& name,
+ const UnicodeString& format) {
+ const UnicodeString *strPtr = ptr->localeNamePtr.readOnly();
+ verifyString(name, *strPtr);
+ strPtr = ptr->formatStrPtr.readOnly();
+ verifyString(format, *strPtr);
+}
+
+void LRUCacheTest::verifyString(const UnicodeString &expected, const UnicodeString &actual) {
+ if (expected != actual) {
+ errln(UnicodeString("Expected '") + expected + "', got '"+ actual+"'");
+ }
+}
+
+void LRUCacheTest::verifyPtr(const void *expected, const void *actual) {
+ if (expected != actual) {
+ errln("Pointer mismatch.");
+ }
+}
+
+void LRUCacheTest::verifyReferences(const SharedPtr<CopyOnWriteForTesting>& ptr, int32_t count, int32_t nameCount, int32_t formatCount) {
+ int32_t actual = ptr.count();
+ if (count != actual) {
+ errln("Main reference count wrong: Expected %d, got %d", count, actual);
+ }
+ actual = ptr->localeNamePtr.count();
+ if (nameCount != actual) {
+ errln("name reference count wrong: Expected %d, got %d", nameCount, actual);
+ }
+ actual = ptr->formatStrPtr.count();
+ if (formatCount != actual) {
+ errln("format reference count wrong: Expected %d, got %d", formatCount, actual);
+ }
+}
+
+extern IntlTest *createLRUCacheTest() {
+ return new LRUCacheTest();
+}
--- /dev/null
+/*
+*******************************************************************************
+* Copyright (C) 2013, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*
+* File RELDATEFMTTEST.CPP
+*
+*******************************************************************************
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "intltest.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/localpointer.h"
+#include "unicode/numfmt.h"
+#include "unicode/reldatefmt.h"
+
+#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
+
+static const char *DirectionStr(UDateDirection direction);
+static const char *RelativeUnitStr(UDateRelativeUnit unit);
+static const char *AbsoluteUnitStr(UDateAbsoluteUnit unit);
+
+typedef struct WithQuantityExpected {
+ double value;
+ UDateDirection direction;
+ UDateRelativeUnit unit;
+ const char *expected;
+} WithQuantityExpected;
+
+typedef struct WithoutQuantityExpected {
+ UDateDirection direction;
+ UDateAbsoluteUnit unit;
+ const char *expected;
+} WithoutQuantityExpected;
+
+static WithQuantityExpected kEnglish[] = {
+ {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_SECONDS, "in 0 seconds"},
+ {0.5, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_SECONDS, "in 0.5 seconds"},
+ {1.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_SECONDS, "in 1 second"},
+ {2.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_SECONDS, "in 2 seconds"},
+ {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MINUTES, "in 0 minutes"},
+ {0.5, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MINUTES, "in 0.5 minutes"},
+ {1.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MINUTES, "in 1 minute"},
+ {2.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MINUTES, "in 2 minutes"},
+ {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_HOURS, "in 0 hours"},
+ {0.5, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_HOURS, "in 0.5 hours"},
+ {1.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_HOURS, "in 1 hour"},
+ {2.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_HOURS, "in 2 hours"},
+ {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_DAYS, "in 0 days"},
+ {0.5, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_DAYS, "in 0.5 days"},
+ {1.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_DAYS, "in 1 day"},
+ {2.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_DAYS, "in 2 days"},
+ {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_WEEKS, "in 0 weeks"},
+ {0.5, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_WEEKS, "in 0.5 weeks"},
+ {1.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_WEEKS, "in 1 week"},
+ {2.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_WEEKS, "in 2 weeks"},
+ {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "in 0 months"},
+ {0.5, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "in 0.5 months"},
+ {1.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "in 1 month"},
+ {2.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "in 2 months"},
+ {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_YEARS, "in 0 years"},
+ {0.5, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_YEARS, "in 0.5 years"},
+ {1.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_YEARS, "in 1 year"},
+ {2.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_YEARS, "in 2 years"},
+
+ {0.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_SECONDS, "0 seconds ago"},
+ {0.5, UDAT_DIRECTION_LAST, UDAT_RELATIVE_SECONDS, "0.5 seconds ago"},
+ {1.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_SECONDS, "1 second ago"},
+ {2.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_SECONDS, "2 seconds ago"},
+ {0.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MINUTES, "0 minutes ago"},
+ {0.5, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MINUTES, "0.5 minutes ago"},
+ {1.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MINUTES, "1 minute ago"},
+ {2.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MINUTES, "2 minutes ago"},
+ {0.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_HOURS, "0 hours ago"},
+ {0.5, UDAT_DIRECTION_LAST, UDAT_RELATIVE_HOURS, "0.5 hours ago"},
+ {1.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_HOURS, "1 hour ago"},
+ {2.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_HOURS, "2 hours ago"},
+ {0.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_DAYS, "0 days ago"},
+ {0.5, UDAT_DIRECTION_LAST, UDAT_RELATIVE_DAYS, "0.5 days ago"},
+ {1.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_DAYS, "1 day ago"},
+ {2.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_DAYS, "2 days ago"},
+ {0.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_WEEKS, "0 weeks ago"},
+ {0.5, UDAT_DIRECTION_LAST, UDAT_RELATIVE_WEEKS, "0.5 weeks ago"},
+ {1.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_WEEKS, "1 week ago"},
+ {2.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_WEEKS, "2 weeks ago"},
+ {0.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MONTHS, "0 months ago"},
+ {0.5, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MONTHS, "0.5 months ago"},
+ {1.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MONTHS, "1 month ago"},
+ {2.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MONTHS, "2 months ago"},
+ {0.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_YEARS, "0 years ago"},
+ {0.5, UDAT_DIRECTION_LAST, UDAT_RELATIVE_YEARS, "0.5 years ago"},
+ {1.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_YEARS, "1 year ago"},
+ {2.0, UDAT_DIRECTION_LAST, UDAT_RELATIVE_YEARS, "2 years ago"}
+};
+
+static WithQuantityExpected kEnglishDecimal[] = {
+ {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_SECONDS, "in 0.0 seconds"},
+ {0.5, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_SECONDS, "in 0.5 seconds"},
+ {1.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_SECONDS, "in 1.0 seconds"},
+ {2.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_SECONDS, "in 2.0 seconds"}
+};
+
+static WithQuantityExpected kSerbian[] = {
+ {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "\\u0437\\u0430 0 \\u043c\\u0435\\u0441\\u0435\\u0446\\u0438"},
+ {1.2, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "\\u0437\\u0430 1,2 \\u043c\\u0435\\u0441\\u0435\\u0446\\u0430"},
+ {21.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "\\u0437\\u0430 21 \\u043c\\u0435\\u0441\\u0435\\u0446"}
+};
+
+static WithoutQuantityExpected kEnglishNoQuantity[] = {
+ {UDAT_DIRECTION_NEXT_2, UDAT_ABSOLUTE_DAY, ""},
+
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_DAY, "tomorrow"},
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_WEEK, "next week"},
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_MONTH, "next month"},
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_YEAR, "next year"},
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_MONDAY, "next Monday"},
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_TUESDAY, "next Tuesday"},
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_WEDNESDAY, "next Wednesday"},
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_THURSDAY, "next Thursday"},
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_FRIDAY, "next Friday"},
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_SATURDAY, "next Saturday"},
+ {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_SUNDAY, "next Sunday"},
+
+ {UDAT_DIRECTION_LAST_2, UDAT_ABSOLUTE_DAY, ""},
+
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_DAY, "yesterday"},
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_WEEK, "last week"},
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_MONTH, "last month"},
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_YEAR, "last year"},
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_MONDAY, "last Monday"},
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_TUESDAY, "last Tuesday"},
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_WEDNESDAY, "last Wednesday"},
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_THURSDAY, "last Thursday"},
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_FRIDAY, "last Friday"},
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_SATURDAY, "last Saturday"},
+ {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_SUNDAY, "last Sunday"},
+
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_DAY, "today"},
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_WEEK, "this week"},
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_MONTH, "this month"},
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_YEAR, "this year"},
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_MONDAY, "this Monday"},
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_TUESDAY, "this Tuesday"},
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_WEDNESDAY, "this Wednesday"},
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_THURSDAY, "this Thursday"},
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_FRIDAY, "this Friday"},
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_SATURDAY, "this Saturday"},
+ {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_SUNDAY, "this Sunday"},
+
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_DAY, "day"},
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_WEEK, "week"},
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_MONTH, "month"},
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_YEAR, "year"},
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_MONDAY, "Monday"},
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_TUESDAY, "Tuesday"},
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_WEDNESDAY, "Wednesday"},
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_THURSDAY, "Thursday"},
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_FRIDAY, "Friday"},
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_SATURDAY, "Saturday"},
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_SUNDAY, "Sunday"},
+
+ {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_NOW, "now"}
+};
+
+static WithoutQuantityExpected kSpanishNoQuantity[] = {
+ {UDAT_DIRECTION_NEXT_2, UDAT_ABSOLUTE_DAY, "pasado ma\\u00F1ana"},
+ {UDAT_DIRECTION_LAST_2, UDAT_ABSOLUTE_DAY, "antes de ayer"}
+};
+
+class RelativeDateTimeFormatterTest : public IntlTest {
+public:
+ RelativeDateTimeFormatterTest() {
+ }
+
+ void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
+private:
+ void TestEnglish();
+ void TestSerbian();
+ void TestEnglishNoQuantity();
+ void TestSpanishNoQuantity();
+ void TestFormatWithQuantityIllegalArgument();
+ void TestFormatWithoutQuantityIllegalArgument();
+ void TestSetNumberFormat();
+ void TestCombineDateAndTime();
+ void RunTest(
+ const Locale& locale,
+ const WithQuantityExpected* expectedResults,
+ int32_t expectedResultLength);
+ void RunTest(
+ const Locale& locale,
+ const WithoutQuantityExpected* expectedResults,
+ int32_t expectedResultLength);
+ void RunTest(
+ const RelativeDateTimeFormatter& fmt,
+ const WithQuantityExpected* expectedResults,
+ int32_t expectedResultLength,
+ const char *description);
+ void RunTest(
+ const RelativeDateTimeFormatter& fmt,
+ const WithoutQuantityExpected* expectedResults,
+ int32_t expectedResultLength,
+ const char *description);
+ void CheckExpectedResult(
+ const RelativeDateTimeFormatter& fmt,
+ const WithQuantityExpected& expectedResult,
+ const char* description);
+ void CheckExpectedResult(
+ const RelativeDateTimeFormatter& fmt,
+ const WithoutQuantityExpected& expectedResult,
+ const char* description);
+ void VerifyIllegalArgument(
+ const RelativeDateTimeFormatter& fmt,
+ UDateDirection direction,
+ UDateRelativeUnit unit);
+ void VerifyIllegalArgument(
+ const RelativeDateTimeFormatter& fmt,
+ UDateDirection direction,
+ UDateAbsoluteUnit unit);
+};
+
+void RelativeDateTimeFormatterTest::runIndexedTest(
+ int32_t index, UBool exec, const char *&name, char *) {
+ if (exec) {
+ logln("TestSuite RelativeDateTimeFormatterTest: ");
+ }
+ TESTCASE_AUTO_BEGIN;
+ TESTCASE_AUTO(TestEnglish);
+ TESTCASE_AUTO(TestSerbian);
+ TESTCASE_AUTO(TestEnglishNoQuantity);
+ TESTCASE_AUTO(TestSpanishNoQuantity);
+ TESTCASE_AUTO(TestFormatWithQuantityIllegalArgument);
+ TESTCASE_AUTO(TestFormatWithoutQuantityIllegalArgument);
+ TESTCASE_AUTO(TestSetNumberFormat);
+ TESTCASE_AUTO(TestCombineDateAndTime);
+ TESTCASE_AUTO_END;
+}
+
+void RelativeDateTimeFormatterTest::TestEnglish() {
+ RunTest("en", kEnglish, LENGTHOF(kEnglish));
+}
+
+void RelativeDateTimeFormatterTest::TestSerbian() {
+ RunTest("sr", kSerbian, LENGTHOF(kSerbian));
+}
+
+void RelativeDateTimeFormatterTest::TestEnglishNoQuantity() {
+ RunTest("en", kEnglishNoQuantity, LENGTHOF(kEnglishNoQuantity));
+}
+
+void RelativeDateTimeFormatterTest::TestSpanishNoQuantity() {
+ RunTest("es", kSpanishNoQuantity, LENGTHOF(kSpanishNoQuantity));
+}
+
+void RelativeDateTimeFormatterTest::TestFormatWithQuantityIllegalArgument() {
+ UErrorCode status = U_ZERO_ERROR;
+ RelativeDateTimeFormatter fmt("en", status);
+ if (U_FAILURE(status)) {
+ dataerrln("Failure creating format object - %s", u_errorName(status));
+ return;
+ }
+ VerifyIllegalArgument(fmt, UDAT_DIRECTION_PLAIN, UDAT_RELATIVE_DAYS);
+ VerifyIllegalArgument(fmt, UDAT_DIRECTION_THIS, UDAT_RELATIVE_DAYS);
+}
+
+void RelativeDateTimeFormatterTest::TestFormatWithoutQuantityIllegalArgument() {
+ UErrorCode status = U_ZERO_ERROR;
+ RelativeDateTimeFormatter fmt("en", status);
+ if (U_FAILURE(status)) {
+ dataerrln("Failure creating format object - %s", u_errorName(status));
+ return;
+ }
+ VerifyIllegalArgument(fmt, UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_NOW);
+ VerifyIllegalArgument(fmt, UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_NOW);
+ VerifyIllegalArgument(fmt, UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_NOW);
+}
+
+void RelativeDateTimeFormatterTest::TestSetNumberFormat() {
+ UErrorCode status = U_ZERO_ERROR;
+ RelativeDateTimeFormatter fmt("en", status);
+ if (U_FAILURE(status)) {
+ dataerrln("Failure creating format object - %s", u_errorName(status));
+ return;
+ }
+ LocalPointer<NumberFormat> numberFormat(NumberFormat::createInstance("en", status));
+ numberFormat->setMinimumFractionDigits(1);
+ numberFormat->setMaximumFractionDigits(1);
+ fmt.setNumberFormat(*numberFormat);
+
+ // Prove that we made a defensive copy.
+ numberFormat->setMinimumFractionDigits(3);
+ numberFormat->setMaximumFractionDigits(3);
+
+ RunTest(fmt, kEnglishDecimal, LENGTHOF(kEnglishDecimal), "en decimal digits");
+}
+
+void RelativeDateTimeFormatterTest::TestCombineDateAndTime() {
+ UErrorCode status = U_ZERO_ERROR;
+ RelativeDateTimeFormatter fmt("en", status);
+ if (U_FAILURE(status)) {
+ dataerrln("Failure creating format object - %s", u_errorName(status));
+ return;
+ }
+ UnicodeString actual;
+ fmt.combineDateAndTime(
+ UnicodeString("yesterday"),
+ UnicodeString("3:50"),
+ actual,
+ status);
+ UnicodeString expected("yesterday, 3:50");
+ if (expected != actual) {
+ errln("Expected "+expected+", got "+actual);
+ }
+}
+
+
+void RelativeDateTimeFormatterTest::RunTest(
+ const Locale& locale,
+ const WithQuantityExpected* expectedResults,
+ int32_t expectedResultLength) {
+ UErrorCode status = U_ZERO_ERROR;
+ RelativeDateTimeFormatter fmt(locale, status);
+ if (U_FAILURE(status)) {
+ dataerrln("Unable to create format object - %s", u_errorName(status));
+ return;
+ }
+ RunTest(fmt, expectedResults, expectedResultLength, locale.getName());
+}
+
+void RelativeDateTimeFormatterTest::RunTest(
+ const Locale& locale,
+ const WithoutQuantityExpected* expectedResults,
+ int32_t expectedResultLength) {
+ UErrorCode status = U_ZERO_ERROR;
+ RelativeDateTimeFormatter fmt(locale, status);
+ if (U_FAILURE(status)) {
+ dataerrln("Unable to create format object - %s", u_errorName(status));
+ return;
+ }
+ RunTest(fmt, expectedResults, expectedResultLength, locale.getName());
+}
+
+void RelativeDateTimeFormatterTest::RunTest(
+ const RelativeDateTimeFormatter& fmt,
+ const WithQuantityExpected* expectedResults,
+ int32_t expectedResultLength,
+ const char *description) {
+ for (int32_t i = 0; i < expectedResultLength; ++i) {
+ CheckExpectedResult(fmt, expectedResults[i], description);
+ }
+}
+
+void RelativeDateTimeFormatterTest::RunTest(
+ const RelativeDateTimeFormatter& fmt,
+ const WithoutQuantityExpected* expectedResults,
+ int32_t expectedResultLength,
+ const char *description) {
+ for (int32_t i = 0; i < expectedResultLength; ++i) {
+ CheckExpectedResult(fmt, expectedResults[i], description);
+ }
+}
+
+void RelativeDateTimeFormatterTest::CheckExpectedResult(
+ const RelativeDateTimeFormatter& fmt,
+ const WithQuantityExpected& expectedResult,
+ const char* description) {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString actual;
+ fmt.format(expectedResult.value, expectedResult.direction, expectedResult.unit, actual, status);
+ UnicodeString expected(expectedResult.expected, -1, US_INV);
+ expected = expected.unescape();
+ char buffer[256];
+ sprintf(
+ buffer,
+ "%s, %f, %s, %s",
+ description,
+ expectedResult.value,
+ DirectionStr(expectedResult.direction),
+ RelativeUnitStr(expectedResult.unit));
+ if (actual != expected) {
+ errln(UnicodeString("Fail: Expected: ") + expected
+ + ", Got: " + actual
+ + ", For: " + buffer);
+ }
+}
+
+void RelativeDateTimeFormatterTest::CheckExpectedResult(
+ const RelativeDateTimeFormatter& fmt,
+ const WithoutQuantityExpected& expectedResult,
+ const char* description) {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString actual;
+ fmt.format(expectedResult.direction, expectedResult.unit, actual, status);
+ UnicodeString expected(expectedResult.expected, -1, US_INV);
+ expected = expected.unescape();
+ char buffer[256];
+ sprintf(
+ buffer,
+ "%s, %s, %s",
+ description,
+ DirectionStr(expectedResult.direction),
+ AbsoluteUnitStr(expectedResult.unit));
+ if (actual != expected) {
+ errln(UnicodeString("Fail: Expected: ") + expected
+ + ", Got: " + actual
+ + ", For: " + buffer);
+ }
+}
+
+void RelativeDateTimeFormatterTest::VerifyIllegalArgument(
+ const RelativeDateTimeFormatter& fmt,
+ UDateDirection direction,
+ UDateRelativeUnit unit) {
+ UnicodeString appendTo;
+ UErrorCode status = U_ZERO_ERROR;
+ fmt.format(1.0, direction, unit, appendTo, status);
+ if (status != U_ILLEGAL_ARGUMENT_ERROR) {
+ errln("Expected U_ILLEGAL_ARGUMENT_ERROR, got %s", u_errorName(status));
+ }
+}
+
+void RelativeDateTimeFormatterTest::VerifyIllegalArgument(
+ const RelativeDateTimeFormatter& fmt,
+ UDateDirection direction,
+ UDateAbsoluteUnit unit) {
+ UnicodeString appendTo;
+ UErrorCode status = U_ZERO_ERROR;
+ fmt.format(direction, unit, appendTo, status);
+ if (status != U_ILLEGAL_ARGUMENT_ERROR) {
+ errln("Expected U_ILLEGAL_ARGUMENT_ERROR, got %s", u_errorName(status));
+ }
+}
+
+static const char *kLast2 = "Last_2";
+static const char *kLast = "Last";
+static const char *kThis = "This";
+static const char *kNext = "Next";
+static const char *kNext2 = "Next_2";
+static const char *kPlain = "Plain";
+
+static const char *kSeconds = "Seconds";
+static const char *kMinutes = "Minutes";
+static const char *kHours = "Hours";
+static const char *kDays = "Days";
+static const char *kWeeks = "Weeks";
+static const char *kMonths = "Months";
+static const char *kYears = "Years";
+
+static const char *kSunday = "Sunday";
+static const char *kMonday = "Monday";
+static const char *kTuesday = "Tuesday";
+static const char *kWednesday = "Wednesday";
+static const char *kThursday = "Thursday";
+static const char *kFriday = "Friday";
+static const char *kSaturday = "Saturday";
+static const char *kDay = "Day";
+static const char *kWeek = "Week";
+static const char *kMonth = "Month";
+static const char *kYear = "Year";
+static const char *kNow = "Now";
+
+static const char *kUndefined = "Undefined";
+
+static const char *DirectionStr(
+ UDateDirection direction) {
+ switch (direction) {
+ case UDAT_DIRECTION_LAST_2:
+ return kLast2;
+ case UDAT_DIRECTION_LAST:
+ return kLast;
+ case UDAT_DIRECTION_THIS:
+ return kThis;
+ case UDAT_DIRECTION_NEXT:
+ return kNext;
+ case UDAT_DIRECTION_NEXT_2:
+ return kNext2;
+ case UDAT_DIRECTION_PLAIN:
+ return kPlain;
+ default:
+ return kUndefined;
+ }
+ return kUndefined;
+}
+
+static const char *RelativeUnitStr(
+ UDateRelativeUnit unit) {
+ switch (unit) {
+ case UDAT_RELATIVE_SECONDS:
+ return kSeconds;
+ case UDAT_RELATIVE_MINUTES:
+ return kMinutes;
+ case UDAT_RELATIVE_HOURS:
+ return kHours;
+ case UDAT_RELATIVE_DAYS:
+ return kDays;
+ case UDAT_RELATIVE_WEEKS:
+ return kWeeks;
+ case UDAT_RELATIVE_MONTHS:
+ return kMonths;
+ case UDAT_RELATIVE_YEARS:
+ return kYears;
+ default:
+ return kUndefined;
+ }
+ return kUndefined;
+}
+
+static const char *AbsoluteUnitStr(
+ UDateAbsoluteUnit unit) {
+ switch (unit) {
+ case UDAT_ABSOLUTE_SUNDAY:
+ return kSunday;
+ case UDAT_ABSOLUTE_MONDAY:
+ return kMonday;
+ case UDAT_ABSOLUTE_TUESDAY:
+ return kTuesday;
+ case UDAT_ABSOLUTE_WEDNESDAY:
+ return kWednesday;
+ case UDAT_ABSOLUTE_THURSDAY:
+ return kThursday;
+ case UDAT_ABSOLUTE_FRIDAY:
+ return kFriday;
+ case UDAT_ABSOLUTE_SATURDAY:
+ return kSaturday;
+ case UDAT_ABSOLUTE_DAY:
+ return kDay;
+ case UDAT_ABSOLUTE_WEEK:
+ return kWeek;
+ case UDAT_ABSOLUTE_MONTH:
+ return kMonth;
+ case UDAT_ABSOLUTE_YEAR:
+ return kYear;
+ case UDAT_ABSOLUTE_NOW:
+ return kNow;
+ default:
+ return kUndefined;
+ }
+ return kUndefined;
+}
+
+extern IntlTest *createRelativeDateTimeFormatterTest() {
+ return new RelativeDateTimeFormatterTest();
+}
+
+#endif