Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Unified Diff: icu46/source/i18n/zstrfmt.cpp

Issue 5516007: Check in the pristine copy of ICU 4.6... (Closed) Base URL: svn://chrome-svn/chrome/trunk/deps/third_party/
Patch Set: Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « icu46/source/i18n/zstrfmt.h ('k') | icu46/source/i18n/ztrans.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: icu46/source/i18n/zstrfmt.cpp
===================================================================
--- icu46/source/i18n/zstrfmt.cpp (revision 0)
+++ icu46/source/i18n/zstrfmt.cpp (revision 0)
@@ -0,0 +1,2228 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007-2010, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "zstrfmt.h"
+
+#include "unicode/ustring.h"
+#include "unicode/putil.h"
+#include "unicode/msgfmt.h"
+#include "unicode/basictz.h"
+#include "unicode/simpletz.h"
+#include "unicode/rbtz.h"
+#include "unicode/vtzone.h"
+
+#include "uvector.h"
+#include "cstring.h"
+#include "cmemory.h"
+#include "uresimp.h"
+#include "zonemeta.h"
+#include "olsontz.h"
+#include "umutex.h"
+#include "ucln_in.h"
+#include "uassert.h"
+#include "ureslocs.h"
+
+/**
+ * global ZoneStringFormatCache stuffs
+ */
+static UMTX gZSFCacheLock = NULL;
+static U_NAMESPACE_QUALIFIER ZSFCache *gZoneStringFormatCache = NULL;
+
+U_CDECL_BEGIN
+/**
+ * ZoneStringFormatCache cleanup callback func
+ */
+static UBool U_CALLCONV zoneStringFormat_cleanup(void)
+{
+ umtx_destroy(&gZSFCacheLock);
+ if (gZoneStringFormatCache != NULL) {
+ delete gZoneStringFormatCache;
+ gZoneStringFormatCache = NULL;
+ }
+ gZoneStringFormatCache = NULL;
+ return TRUE;
+}
+
+/**
+ * Deleter for ZoneStringInfo
+ */
+static void U_CALLCONV
+deleteZoneStringInfo(void *obj) {
+ delete (U_NAMESPACE_QUALIFIER ZoneStringInfo*)obj;
+}
+
+/**
+ * Deleter for ZoneStrings
+ */
+static void U_CALLCONV
+deleteZoneStrings(void *obj) {
+ delete (U_NAMESPACE_QUALIFIER ZoneStrings*)obj;
+}
+U_CDECL_END
+
+U_NAMESPACE_BEGIN
+
+#define ZID_KEY_MAX 128
+
+static const char gCountriesTag[] = "Countries";
+static const char gZoneStringsTag[] = "zoneStrings";
+static const char gShortGenericTag[] = "sg";
+static const char gShortStandardTag[] = "ss";
+static const char gShortDaylightTag[] = "sd";
+static const char gLongGenericTag[] = "lg";
+static const char gLongStandardTag[] = "ls";
+static const char gLongDaylightTag[] = "ld";
+static const char gExemplarCityTag[] = "ec";
+static const char gCommonlyUsedTag[] = "cu";
+static const char gFallbackFormatTag[] = "fallbackFormat";
+static const char gRegionFormatTag[] = "regionFormat";
+
+#define MZID_PREFIX_LEN 5
+static const char gMetazoneIdPrefix[] = "meta:";
+
+#define MAX_METAZONES_PER_ZONE 10
+
+static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
+static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
+static const UChar gCommonlyUsedTrue[] = {0x31, 0x00}; // "1"
+
+static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY;
+
+static int32_t
+getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type) {
+ int32_t typeIdx = 0;
+ switch (type) {
+ case LOCATION:
+ typeIdx = ZSIDX_LOCATION;
+ break;
+ case GENERIC_LONG:
+ typeIdx = ZSIDX_LONG_GENERIC;
+ break;
+ case GENERIC_SHORT:
+ typeIdx = ZSIDX_SHORT_GENERIC;
+ break;
+ case STANDARD_LONG:
+ typeIdx = ZSIDX_LONG_STANDARD;
+ break;
+ case STANDARD_SHORT:
+ typeIdx = ZSIDX_SHORT_STANDARD;
+ break;
+ case DAYLIGHT_LONG:
+ typeIdx = ZSIDX_LONG_DAYLIGHT;
+ break;
+ case DAYLIGHT_SHORT:
+ typeIdx = ZSIDX_SHORT_DAYLIGHT;
+ break;
+ }
+ return typeIdx;
+}
+
+static int32_t
+getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx) {
+ int32_t type = 0;
+ switch (typeIdx) {
+ case ZSIDX_LOCATION:
+ type = LOCATION;
+ break;
+ case ZSIDX_LONG_GENERIC:
+ type = GENERIC_LONG;
+ break;
+ case ZSIDX_SHORT_GENERIC:
+ type = GENERIC_SHORT;
+ break;
+ case ZSIDX_LONG_STANDARD:
+ type = STANDARD_LONG;
+ break;
+ case ZSIDX_SHORT_STANDARD:
+ type = STANDARD_SHORT;
+ break;
+ case ZSIDX_LONG_DAYLIGHT:
+ type = DAYLIGHT_LONG;
+ break;
+ case ZSIDX_COUNT:
+ case ZSIDX_SHORT_DAYLIGHT:
+ type = DAYLIGHT_SHORT;
+ break;
+ default:
+ break;
+ }
+ return type;
+}
+
+#define DEFAULT_CHARACTERNODE_CAPACITY 1
+
+// ----------------------------------------------------------------------------
+void CharacterNode::clear() {
+ uprv_memset(this, 0, sizeof(*this));
+}
+
+void CharacterNode::deleteValues() {
+ if (fValues == NULL) {
+ // Do nothing.
+ } else if (!fHasValuesVector) {
+ deleteZoneStringInfo(fValues);
+ } else {
+ delete (UVector *)fValues;
+ }
+}
+
+void
+CharacterNode::addValue(void *value, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ deleteZoneStringInfo(value);
+ return;
+ }
+ if (fValues == NULL) {
+ fValues = value;
+ } else {
+ // At least one value already.
+ if (!fHasValuesVector) {
+ // There is only one value so far, and not in a vector yet.
+ // Create a vector and add the old value.
+ UVector *values = new UVector(deleteZoneStringInfo, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status);
+ if (U_FAILURE(status)) {
+ deleteZoneStringInfo(value);
+ return;
+ }
+ values->addElement(fValues, status);
+ fValues = values;
+ fHasValuesVector = TRUE;
+ }
+ // Add the new value.
+ ((UVector *)fValues)->addElement(value, status);
+ }
+}
+
+//----------------------------------------------------------------------------
+// Virtual destructor to avoid warning
+TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
+}
+
+// ----------------------------------------------------------------------------
+TextTrieMap::TextTrieMap(UBool ignoreCase)
+: fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0),
+ fLazyContents(NULL), fIsEmpty(TRUE) {
+}
+
+TextTrieMap::~TextTrieMap() {
+ int32_t index;
+ for (index = 0; index < fNodesCount; ++index) {
+ fNodes[index].deleteValues();
+ }
+ uprv_free(fNodes);
+ if (fLazyContents != NULL) {
+ for (int32_t i=0; i<fLazyContents->size(); i+=2) {
+ ZoneStringInfo *zsinf = (ZoneStringInfo *)fLazyContents->elementAt(i+1);
+ delete zsinf;
+ }
+ delete fLazyContents;
+ }
+}
+
+int32_t TextTrieMap::isEmpty() const {
+ // Use a separate field for fIsEmpty because it will remain unchanged once the
+ // Trie is built, while fNodes and fLazyContents change with the lazy init
+ // of the nodes structure. Trying to test the changing fields has
+ // thread safety complications.
+ return fIsEmpty;
+}
+
+
+// We defer actually building the TextTrieMap node structure until the first time a
+// search is performed. put() simply saves the parameters in case we do
+// eventually need to build it.
+//
+void
+TextTrieMap::put(const UnicodeString &key, void *value, ZSFStringPool &sp, UErrorCode &status) {
+ fIsEmpty = FALSE;
+ if (fLazyContents == NULL) {
+ fLazyContents = new UVector(status);
+ if (fLazyContents == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ }
+ if (U_FAILURE(status)) {
+ return;
+ }
+ UChar *s = const_cast<UChar *>(sp.get(key, status));
+ fLazyContents->addElement(s, status);
+ fLazyContents->addElement(value, status);
+}
+
+
+void
+TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) {
+ if (fNodes == NULL) {
+ fNodesCapacity = 512;
+ fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
+ fNodes[0].clear(); // Init root node.
+ fNodesCount = 1;
+ }
+
+ UnicodeString foldedKey;
+ const UChar *keyBuffer;
+ int32_t keyLength;
+ if (fIgnoreCase) {
+ // Ok to use fastCopyFrom() because we discard the copy when we return.
+ foldedKey.fastCopyFrom(key).foldCase();
+ keyBuffer = foldedKey.getBuffer();
+ keyLength = foldedKey.length();
+ } else {
+ keyBuffer = key.getBuffer();
+ keyLength = key.length();
+ }
+
+ CharacterNode *node = fNodes;
+ int32_t index;
+ for (index = 0; index < keyLength; ++index) {
+ node = addChildNode(node, keyBuffer[index], status);
+ }
+ node->addValue(value, status);
+}
+
+UBool
+TextTrieMap::growNodes() {
+ if (fNodesCapacity == 0xffff) {
+ return FALSE; // We use 16-bit node indexes.
+ }
+ int32_t newCapacity = fNodesCapacity + 1000;
+ if (newCapacity > 0xffff) {
+ newCapacity = 0xffff;
+ }
+ CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode));
+ if (newNodes == NULL) {
+ return FALSE;
+ }
+ uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode));
+ uprv_free(fNodes);
+ fNodes = newNodes;
+ fNodesCapacity = newCapacity;
+ return TRUE;
+}
+
+CharacterNode*
+TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ // Linear search of the sorted list of children.
+ uint16_t prevIndex = 0;
+ uint16_t nodeIndex = parent->fFirstChild;
+ while (nodeIndex > 0) {
+ CharacterNode *current = fNodes + nodeIndex;
+ UChar childCharacter = current->fCharacter;
+ if (childCharacter == c) {
+ return current;
+ } else if (childCharacter > c) {
+ break;
+ }
+ prevIndex = nodeIndex;
+ nodeIndex = current->fNextSibling;
+ }
+
+ // Ensure capacity. Grow fNodes[] if needed.
+ if (fNodesCount == fNodesCapacity) {
+ int32_t parentIndex = (int32_t)(parent - fNodes);
+ if (!growNodes()) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ parent = fNodes + parentIndex;
+ }
+
+ // Insert a new child node with c in sorted order.
+ CharacterNode *node = fNodes + fNodesCount;
+ node->clear();
+ node->fCharacter = c;
+ node->fNextSibling = nodeIndex;
+ if (prevIndex == 0) {
+ parent->fFirstChild = (uint16_t)fNodesCount;
+ } else {
+ fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount;
+ }
+ ++fNodesCount;
+ return node;
+}
+
+CharacterNode*
+TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
+ // Linear search of the sorted list of children.
+ uint16_t nodeIndex = parent->fFirstChild;
+ while (nodeIndex > 0) {
+ CharacterNode *current = fNodes + nodeIndex;
+ UChar childCharacter = current->fCharacter;
+ if (childCharacter == c) {
+ return current;
+ } else if (childCharacter > c) {
+ break;
+ }
+ nodeIndex = current->fNextSibling;
+ }
+ return NULL;
+}
+
+// Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
+static UMTX TextTrieMutex;
+
+// buildTrie() - The Trie node structure is needed. Create it from the data that was
+// saved at the time the ZoneStringFormatter was created. The Trie is only
+// needed for parsing operations, which are less common than formatting,
+// and the Trie is big, which is why its creation is deferred until first use.
+void TextTrieMap::buildTrie(UErrorCode &status) {
+ umtx_lock(&TextTrieMutex);
+ if (fLazyContents != NULL) {
+ for (int32_t i=0; i<fLazyContents->size(); i+=2) {
+ const UChar *key = (UChar *)fLazyContents->elementAt(i);
+ void *val = fLazyContents->elementAt(i+1);
+ UnicodeString keyString(TRUE, key, -1); // Aliasing UnicodeString constructor.
+ putImpl(keyString, val, status);
+ }
+ delete fLazyContents;
+ fLazyContents = NULL;
+ }
+ umtx_unlock(&TextTrieMutex);
+}
+
+
+void
+TextTrieMap::search(const UnicodeString &text, int32_t start,
+ TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
+ UBool trieNeedsInitialization = FALSE;
+ UMTX_CHECK(&TextTrieMutex, fLazyContents != NULL, trieNeedsInitialization);
+ if (trieNeedsInitialization) {
+ TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
+ nonConstThis->buildTrie(status);
+ }
+ if (fNodes == NULL) {
+ return;
+ }
+ search(fNodes, text, start, start, handler, status);
+}
+
+void
+TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
+ int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ if (node->hasValues()) {
+ if (!handler->handleMatch(index - start, node, status)) {
+ return;
+ }
+ if (U_FAILURE(status)) {
+ return;
+ }
+ }
+ UChar32 c = text.char32At(index);
+ if (fIgnoreCase) {
+ // size of character may grow after fold operation
+ UnicodeString tmp(c);
+ tmp.foldCase();
+ int32_t tmpidx = 0;
+ while (tmpidx < tmp.length()) {
+ c = tmp.char32At(tmpidx);
+ node = getChildNode(node, c);
+ if (node == NULL) {
+ break;
+ }
+ tmpidx = tmp.moveIndex32(tmpidx, 1);
+ }
+ } else {
+ node = getChildNode(node, c);
+ }
+ if (node != NULL) {
+ search(node, text, start, index+1, handler, status);
+ }
+}
+
+// ----------------------------------------------------------------------------
+ZoneStringInfo::ZoneStringInfo(const UnicodeString &id, const UnicodeString &str,
+ TimeZoneTranslationType type, ZSFStringPool &sp, UErrorCode &status)
+: fType(type) {
+ fId = sp.get(id, status);
+ fStr = sp.get(str, status);
+}
+
+ZoneStringInfo::~ZoneStringInfo() {
+}
+
+
+// ----------------------------------------------------------------------------
+ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode &status)
+: fResults(status)
+{
+ clear();
+}
+
+ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() {
+ clear();
+}
+
+UBool
+ZoneStringSearchResultHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return FALSE;
+ }
+ if (node->hasValues()) {
+ int32_t valuesCount = node->countValues();
+ for (int32_t i = 0; i < valuesCount; i++) {
+ ZoneStringInfo *zsinfo = (ZoneStringInfo*)node->getValue(i);
+ if (zsinfo == NULL) {
+ break;
+ }
+ // Update the results
+ UBool foundType = FALSE;
+ for (int32_t j = 0; j < fResults.size(); j++) {
+ ZoneStringInfo *tmp = (ZoneStringInfo*)fResults.elementAt(j);
+ if (zsinfo->fType == tmp->fType) {
+ int32_t lenidx = getTimeZoneTranslationTypeIndex(tmp->fType);
+ if (matchLength > fMatchLen[lenidx]) {
+ // Same type, longer match
+ fResults.setElementAt(zsinfo, j);
+ fMatchLen[lenidx] = matchLength;
+ }
+ foundType = TRUE;
+ break;
+ }
+ }
+ if (!foundType) {
+ // not found in the current list
+ fResults.addElement(zsinfo, status);
+ fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)] = matchLength;
+ }
+ }
+ }
+ return TRUE;
+}
+
+int32_t
+ZoneStringSearchResultHandler::countMatches(void) {
+ return fResults.size();
+}
+
+const ZoneStringInfo*
+ZoneStringSearchResultHandler::getMatch(int32_t index, int32_t &matchLength) {
+ ZoneStringInfo *zsinfo = NULL;
+ if (index < fResults.size()) {
+ zsinfo = (ZoneStringInfo*)fResults.elementAt(index);
+ matchLength = fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)];
+ }
+ return zsinfo;
+}
+
+void
+ZoneStringSearchResultHandler::clear(void) {
+ fResults.removeAllElements();
+ for (int32_t i = 0; i < (int32_t)(sizeof(fMatchLen)/sizeof(fMatchLen[0])); i++) {
+ fMatchLen[i] = 0;
+ }
+}
+
+// Mutex for protecting the lazy load of a zone ID (or a full load) to ZoneStringFormat structures.
+static UMTX ZoneStringFormatMutex;
+
+
+// ----------------------------------------------------------------------------
+ZoneStringFormat::ZoneStringFormat(const UnicodeString* const* strings,
+ int32_t rowCount, int32_t columnCount, UErrorCode &status)
+: fLocale(""),
+ fTzidToStrings(NULL),
+ fMzidToStrings(NULL),
+ fZoneStringsTrie(TRUE),
+ fStringPool(status),
+ fZoneStringsArray(NULL),
+ fMetazoneItem(NULL),
+ fZoneItem(NULL),
+ fIsFullyLoaded(FALSE)
+{
+ if (U_FAILURE(status)) {
+ return;
+ }
+ fLocale.setToBogus();
+ if (strings == NULL || columnCount <= 0 || rowCount <= 0) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+ fTzidToStrings = uhash_open(uhash_hashUChars, // key hash function
+ uhash_compareUChars, // key comparison function
+ NULL, // Value comparison function
+ &status);
+ fMzidToStrings = uhash_open(uhash_hashUChars,
+ uhash_compareUChars,
+ NULL,
+ &status);
+
+ uhash_setValueDeleter(fTzidToStrings, deleteZoneStrings);
+ uhash_setValueDeleter(fMzidToStrings, deleteZoneStrings);
+
+ for (int32_t row = 0; row < rowCount; row++) {
+ if (strings[row][0].isEmpty()) {
+ continue;
+ }
+ UnicodeString *names = new UnicodeString[ZSIDX_COUNT];
+ for (int32_t col = 1; col < columnCount; col++) {
+ if (!strings[row][col].isEmpty()) {
+ int32_t typeIdx = -1;
+ switch (col) {
+ case 1:
+ typeIdx = ZSIDX_LONG_STANDARD;
+ break;
+ case 2:
+ typeIdx = ZSIDX_SHORT_STANDARD;
+ break;
+ case 3:
+ typeIdx = ZSIDX_LONG_DAYLIGHT;
+ break;
+ case 4:
+ typeIdx = ZSIDX_SHORT_DAYLIGHT;
+ break;
+ case 5:
+ typeIdx = ZSIDX_LOCATION;
+ break;
+ case 6:
+ typeIdx = ZSIDX_LONG_GENERIC;
+ break;
+ case 7:
+ typeIdx = ZSIDX_SHORT_GENERIC;
+ break;
+ }
+ if (typeIdx != -1) {
+ names[typeIdx].setTo(strings[row][col]);
+
+ // Put the name into the trie
+ int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeIdx);
+ ZoneStringInfo *zsinf = new ZoneStringInfo(strings[row][0],
+ strings[row][col],
+ (TimeZoneTranslationType)type,
+ fStringPool,
+ status);
+ fZoneStringsTrie.put(strings[row][col], zsinf, fStringPool, status);
+ if (U_FAILURE(status)) {
+ delete zsinf;
+ goto error_cleanup;
+ }
+ }
+ }
+ }
+ // Note: ZoneStrings constructor adopts and delete the names array.
+ ZoneStrings *zstrings = new ZoneStrings(names, ZSIDX_COUNT, TRUE, NULL, 0, 0,
+ fStringPool, status);
+ UChar *utzid = const_cast<UChar *>(fStringPool.get(strings[row][0], status));
+ uhash_put(fTzidToStrings, utzid, zstrings, &status);
+ if (U_FAILURE(status)) {
+ delete zstrings;
+ goto error_cleanup;
+ }
+ }
+ fStringPool.freeze();
+ fIsFullyLoaded = TRUE;
+ return;
+
+error_cleanup:
+ return;
+}
+
+ZoneStringFormat::ZoneStringFormat(const Locale &locale, UErrorCode &status)
+: fLocale(locale),
+ fTzidToStrings(NULL),
+ fMzidToStrings(NULL),
+ fZoneStringsTrie(TRUE),
+ fStringPool(status),
+ fZoneStringsArray(NULL),
+ fMetazoneItem(NULL),
+ fZoneItem(NULL),
+ fIsFullyLoaded(FALSE)
+{
+ if (U_FAILURE(status)) {
+ return;
+ }
+ fTzidToStrings = uhash_open(uhash_hashUChars, // key hash function
+ uhash_compareUChars, // key comparison function
+ NULL, // Value comparison function
+ &status);
+ fMzidToStrings = uhash_open(uhash_hashUChars, // key hash function
+ uhash_compareUChars, // key comparison function
+ NULL, // Value comparison function
+ &status);
+ uhash_setValueDeleter(fTzidToStrings, deleteZoneStrings);
+ uhash_setValueDeleter(fMzidToStrings, deleteZoneStrings);
+}
+
+// Load only a single zone
+void
+ZoneStringFormat::loadZone(const UnicodeString &utzid, UErrorCode &status)
+{
+ if (fIsFullyLoaded) {
+ return;
+ }
+
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ umtx_lock(&ZoneStringFormatMutex);
+
+ if (fZoneStringsArray == NULL) {
+ fZoneStringsArray = ures_open(U_ICUDATA_ZONE, fLocale.getName(), &status);
+ fZoneStringsArray = ures_getByKeyWithFallback(fZoneStringsArray, gZoneStringsTag, fZoneStringsArray, &status);
+ if (U_FAILURE(status)) {
+ // If no locale bundles are available, zoneStrings will be null.
+ // We still want to go through the rest of zone strings initialization,
+ // because generic location format is generated from tzid for the case.
+ // The rest of code should work even zoneStrings is null.
+ status = U_ZERO_ERROR;
+ ures_close(fZoneStringsArray);
+ fZoneStringsArray = NULL;
+ }
+ }
+
+ // Skip non-canonical IDs
+ UnicodeString canonicalID;
+ TimeZone::getCanonicalID(utzid, canonicalID, status);
+ if (U_FAILURE(status)) {
+ // Ignore unknown ID - we should not get here, but just in case.
+ // status = U_ZERO_ERROR;
+ umtx_unlock(&ZoneStringFormatMutex);
+ return;
+ }
+
+ if (U_SUCCESS(status)) {
+ if (uhash_count(fTzidToStrings) > 0) {
+ ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer());
+ if (zstrings != NULL) {
+ umtx_unlock(&ZoneStringFormatMutex);
+ return; // We already about this one
+ }
+ }
+ }
+
+ addSingleZone(canonicalID, status);
+
+ umtx_unlock(&ZoneStringFormatMutex);
+}
+
+// Load only a single zone
+void
+ZoneStringFormat::addSingleZone(UnicodeString &utzid, UErrorCode &status)
+{
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ if (uhash_count(fTzidToStrings) > 0) {
+ ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, utzid.getTerminatedBuffer());
+ if (zstrings != NULL) {
+ return; // We already about this one
+ }
+ }
+
+ MessageFormat *fallbackFmt = NULL;
+ MessageFormat *regionFmt = NULL;
+
+ fallbackFmt = getFallbackFormat(fLocale, status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ regionFmt = getRegionFormat(fLocale, status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+
+ {
+ char zidkey[ZID_KEY_MAX+1];
+ char tzid[ZID_KEY_MAX+1];
+ utzid.extract(0, utzid.length(), zidkey, ZID_KEY_MAX, US_INV);
+ utzid.extract(0, utzid.length(), tzid, ZID_KEY_MAX, US_INV);
+
+ const UChar *zstrarray[ZSIDX_COUNT];
+ const UChar *mzstrarray[ZSIDX_COUNT];
+ UnicodeString mzPartialLoc[MAX_METAZONES_PER_ZONE][4];
+
+ // Replace '/' with ':'
+ char *pCity = NULL;
+ char *p = zidkey;
+ while (*p) {
+ if (*p == '/') {
+ *p = ':';
+ pCity = p + 1;
+ }
+ p++;
+ }
+
+ if (fZoneStringsArray != NULL) {
+ fZoneItem = ures_getByKeyWithFallback(fZoneStringsArray, zidkey, fZoneItem, &status);
+ if (U_FAILURE(status)) {
+ // If failed to open the zone item, create only location string
+ ures_close(fZoneItem);
+ fZoneItem = NULL;
+ status = U_ZERO_ERROR;
+ }
+ }
+
+ UnicodeString region;
+ getRegion(region);
+
+ zstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(fZoneItem, gLongStandardTag);
+ zstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(fZoneItem, gShortStandardTag);
+ zstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(fZoneItem, gLongDaylightTag);
+ zstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(fZoneItem, gShortDaylightTag);
+ zstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(fZoneItem, gLongGenericTag);
+ zstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(fZoneItem, gShortGenericTag);
+
+ // Compose location format string
+ UnicodeString location;
+ UnicodeString country;
+ UnicodeString city;
+ UnicodeString countryCode;
+ ZoneMeta::getCanonicalCountry(utzid, countryCode);
+ if (!countryCode.isEmpty()) {
+ const UChar* tmpCity = getZoneStringFromBundle(fZoneItem, gExemplarCityTag);
+ if (tmpCity != NULL) {
+ city.setTo(TRUE, tmpCity, -1);
+ } else {
+ city.setTo(UnicodeString(pCity, -1, US_INV));
+ // Replace '_' with ' '
+ for (int32_t i = 0; i < city.length(); i++) {
+ if (city.charAt(i) == (UChar)0x5F /*'_'*/) {
+ city.setCharAt(i, (UChar)0x20 /*' '*/);
+ }
+ }
+ }
+ getLocalizedCountry(countryCode, fLocale, country);
+ UnicodeString singleCountry;
+ ZoneMeta::getSingleCountry(utzid, singleCountry);
+ FieldPosition fpos;
+ if (singleCountry.isEmpty()) {
+ Formattable params [] = {
+ Formattable(city),
+ Formattable(country)
+ };
+ fallbackFmt->format(params, 2, location, fpos, status);
+ } else {
+ // If the zone is only one zone in the country, do not add city
+ Formattable params [] = {
+ Formattable(country)
+ };
+ regionFmt->format(params, 1, location, fpos, status);
+ }
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer();
+ } else {
+ if (uprv_strlen(tzid) > 4 && uprv_strncmp(tzid, "Etc/", 4) == 0) {
+ // "Etc/xxx" is not associated with a specific location, so localized
+ // GMT format is always used as generic location format.
+ zstrarray[ZSIDX_LOCATION] = NULL;
+ } else {
+ // When a new time zone ID, which is actually associated with a specific
+ // location, is added in tzdata, but the current CLDR data does not have
+ // the information yet, ICU creates a generic location string based on
+ // the ID. This implementation supports canonical time zone round trip
+ // with format pattern "VVVV". See #6602 for the details.
+ UnicodeString loc(utzid);
+ int32_t slashIdx = loc.lastIndexOf((UChar)0x2f);
+ if (slashIdx == -1) {
+ // A time zone ID without slash in the tz database is not
+ // associated with a specific location. For instances,
+ // MET, CET, EET and WET fall into this category.
+ // In this case, we still use GMT format as fallback.
+ zstrarray[ZSIDX_LOCATION] = NULL;
+ } else {
+ FieldPosition fpos;
+ Formattable params[] = {
+ Formattable(loc)
+ };
+ regionFmt->format(params, 1, location, fpos, status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer();
+ }
+ }
+ }
+
+ UBool commonlyUsed = isCommonlyUsed(fZoneItem);
+
+ // Resolve metazones used by this zone
+ int32_t mzPartialLocIdx = 0;
+ const UVector *metazoneMappings = ZoneMeta::getMetazoneMappings(utzid);
+ if (metazoneMappings != NULL) {
+ for (int32_t i = 0; i < metazoneMappings->size(); i++) {
+ const OlsonToMetaMappingEntry *mzmap =
+ (const OlsonToMetaMappingEntry*)metazoneMappings->elementAt(i);
+ UnicodeString mzid(mzmap->mzid);
+ const ZoneStrings *mzStrings =
+ (const ZoneStrings*)uhash_get(fMzidToStrings, mzid.getTerminatedBuffer());
+ if (mzStrings == NULL) {
+ // If the metazone strings are not yet processed, do it now.
+ char mzidkey[ZID_KEY_MAX];
+ uprv_strcpy(mzidkey, gMetazoneIdPrefix);
+ u_UCharsToChars(mzmap->mzid, mzidkey + MZID_PREFIX_LEN, u_strlen(mzmap->mzid) + 1);
+ fMetazoneItem = ures_getByKeyWithFallback(fZoneStringsArray, mzidkey, fMetazoneItem, &status);
+ if (U_FAILURE(status)) {
+ // No resources available for this metazone
+ // Resource bundle will be cleaned up after end of the loop.
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ UBool mzCommonlyUsed = isCommonlyUsed(fMetazoneItem);
+ mzstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(fMetazoneItem, gLongStandardTag);
+ mzstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(fMetazoneItem, gShortStandardTag);
+ mzstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(fMetazoneItem, gLongDaylightTag);
+ mzstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(fMetazoneItem, gShortDaylightTag);
+ mzstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(fMetazoneItem, gLongGenericTag);
+ mzstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(fMetazoneItem, gShortGenericTag);
+ mzstrarray[ZSIDX_LOCATION] = NULL;
+
+ int32_t lastNonNullIdx = ZSIDX_COUNT - 1;
+ while (lastNonNullIdx >= 0) {
+ if (mzstrarray[lastNonNullIdx] != NULL) {
+ break;
+ }
+ lastNonNullIdx--;
+ }
+ UnicodeString *strings_mz = NULL;
+ ZoneStrings *tmp_mzStrings = NULL;
+ if (lastNonNullIdx >= 0) {
+ // Create UnicodeString array and put strings to the zone string trie
+ strings_mz = new UnicodeString[lastNonNullIdx + 1];
+
+ UnicodeString preferredIdForLocale;
+ ZoneMeta::getZoneIdByMetazone(mzid, region, preferredIdForLocale);
+
+ for (int32_t typeidx = 0; typeidx <= lastNonNullIdx; typeidx++) {
+ if (mzstrarray[typeidx] != NULL) {
+ strings_mz[typeidx].setTo(TRUE, mzstrarray[typeidx], -1);
+
+ // Add a metazone string to the zone string trie
+ int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeidx);
+ ZoneStringInfo *zsinfo = new ZoneStringInfo(
+ preferredIdForLocale,
+ strings_mz[typeidx],
+ (TimeZoneTranslationType)type,
+ fStringPool,
+ status);
+ fZoneStringsTrie.put(strings_mz[typeidx], zsinfo, fStringPool, status);
+ if (U_FAILURE(status)) {
+ delete []strings_mz;
+ goto error_cleanup;
+ }
+ }
+ }
+ // Note: ZoneStrings constructor adopts and deletes the strings_mz array.
+ tmp_mzStrings = new ZoneStrings(strings_mz, lastNonNullIdx + 1,
+ mzCommonlyUsed, NULL, 0, 0, fStringPool, status);
+ } else {
+ // Create ZoneStrings with empty contents
+ tmp_mzStrings = new ZoneStrings(NULL, 0, FALSE, NULL, 0, 0, fStringPool, status);
+ }
+
+ UChar *umzid = const_cast<UChar *>(fStringPool.get(mzid, status));
+ uhash_put(fMzidToStrings, umzid, tmp_mzStrings, &status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ mzStrings = tmp_mzStrings;
+ }
+
+ // Compose generic partial location format
+ UnicodeString lg;
+ UnicodeString sg;
+
+ mzStrings->getString(ZSIDX_LONG_GENERIC, lg);
+ mzStrings->getString(ZSIDX_SHORT_GENERIC, sg);
+
+ if (!lg.isEmpty() || !sg.isEmpty()) {
+ UBool addMzPartialLocationNames = TRUE;
+ for (int32_t j = 0; j < mzPartialLocIdx; j++) {
+ if (mzPartialLoc[j][0] == mzid) {
+ // already processed
+ addMzPartialLocationNames = FALSE;
+ break;
+ }
+ }
+ if (addMzPartialLocationNames) {
+ UnicodeString *locationPart = NULL;
+ // Check if the zone is the preferred zone for the territory associated with the zone
+ UnicodeString preferredID;
+ ZoneMeta::getZoneIdByMetazone(mzid, countryCode, preferredID);
+ if (utzid == preferredID) {
+ // Use country for the location
+ locationPart = &country;
+ } else {
+ // Use city for the location
+ locationPart = &city;
+ }
+ // Reset the partial location string array
+ mzPartialLoc[mzPartialLocIdx][0].setTo(mzid);
+ mzPartialLoc[mzPartialLocIdx][1].remove();
+ mzPartialLoc[mzPartialLocIdx][2].remove();
+ mzPartialLoc[mzPartialLocIdx][3].remove();
+
+ if (locationPart->length() != 0) {
+ FieldPosition fpos;
+ if (!lg.isEmpty()) {
+ Formattable params [] = {
+ Formattable(*locationPart),
+ Formattable(lg)
+ };
+ fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][1], fpos, status);
+ }
+ if (!sg.isEmpty()) {
+ Formattable params [] = {
+ Formattable(*locationPart),
+ Formattable(sg)
+ };
+ fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][2], fpos, status);
+ if (mzStrings->isShortFormatCommonlyUsed()) {
+ mzPartialLoc[mzPartialLocIdx][3].setTo(TRUE, gCommonlyUsedTrue, -1);
+ }
+ }
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ }
+ mzPartialLocIdx++;
+ }
+ }
+ }
+ }
+ // Collected names for a zone
+
+ // Create UnicodeString array for localized zone strings
+ int32_t lastIdx = ZSIDX_COUNT - 1;
+ while (lastIdx >= 0) {
+ if (zstrarray[lastIdx] != NULL) {
+ break;
+ }
+ lastIdx--;
+ }
+ UnicodeString *strings = NULL;
+ int32_t stringsCount = lastIdx + 1;
+
+ if (stringsCount > 0) {
+ strings = new UnicodeString[stringsCount];
+ for (int32_t i = 0; i < stringsCount; i++) {
+ if (zstrarray[i] != NULL) {
+ strings[i].setTo(zstrarray[i], -1);
+
+ // Add names to the trie
+ int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)i);
+ ZoneStringInfo *zsinfo = new ZoneStringInfo(utzid,
+ strings[i],
+ (TimeZoneTranslationType)type,
+ fStringPool,
+ status);
+ fZoneStringsTrie.put(strings[i], zsinfo, fStringPool, status);
+ if (U_FAILURE(status)) {
+ delete zsinfo;
+ delete[] strings;
+ goto error_cleanup;
+ }
+ }
+ }
+ }
+
+ // Create UnicodeString array for generic partial location strings
+ UnicodeString **genericPartialLocationNames = NULL;
+ int32_t genericPartialRowCount = mzPartialLocIdx;
+ int32_t genericPartialColCount = 4;
+
+ if (genericPartialRowCount != 0) {
+ genericPartialLocationNames =
+ (UnicodeString**)uprv_malloc(genericPartialRowCount * sizeof(UnicodeString*));
+ if (genericPartialLocationNames == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ delete[] strings;
+ goto error_cleanup;
+ }
+ for (int32_t i = 0; i < genericPartialRowCount; i++) {
+ genericPartialLocationNames[i] = new UnicodeString[genericPartialColCount];
+ for (int32_t j = 0; j < genericPartialColCount; j++) {
+ genericPartialLocationNames[i][j].setTo(mzPartialLoc[i][j]);
+ // Add names to the trie
+ if ((j == 1 || j == 2) &&!genericPartialLocationNames[i][j].isEmpty()) {
+ ZoneStringInfo *zsinfo;
+ TimeZoneTranslationType type = (j == 1) ? GENERIC_LONG : GENERIC_SHORT;
+ zsinfo = new ZoneStringInfo(utzid, genericPartialLocationNames[i][j], type,
+ fStringPool, status);
+ fZoneStringsTrie.put(genericPartialLocationNames[i][j], zsinfo, fStringPool, status);
+ if (U_FAILURE(status)) {
+ delete[] genericPartialLocationNames[i];
+ uprv_free(genericPartialLocationNames);
+ delete[] strings;
+ goto error_cleanup;
+ }
+ }
+ }
+ }
+ }
+
+ // Finally, create ZoneStrings instance and put it into the tzidToStinrgs map
+ ZoneStrings *zstrings = new ZoneStrings(strings, stringsCount, commonlyUsed,
+ genericPartialLocationNames, genericPartialRowCount,
+ genericPartialColCount, fStringPool, status);
+
+ UChar *uutzid = const_cast<UChar *>(fStringPool.get(utzid, status));
+ uhash_put(fTzidToStrings, uutzid, zstrings, &status);
+ if (U_FAILURE(status)) {
+ delete zstrings;
+ goto error_cleanup;
+ }
+ }
+
+error_cleanup:
+ if (fallbackFmt != NULL) {
+ delete fallbackFmt;
+ }
+ if (regionFmt != NULL) {
+ delete regionFmt;
+ }
+ // fStringPool.freeze();
+}
+
+void
+ZoneStringFormat::loadFull(UErrorCode &status)
+{
+ if (U_FAILURE(status)) {
+ return;
+ }
+ if (fIsFullyLoaded) {
+ return;
+ }
+
+ umtx_lock(&ZoneStringFormatMutex);
+
+ if (fZoneStringsArray == NULL) {
+ fZoneStringsArray = ures_open(U_ICUDATA_ZONE, fLocale.getName(), &status);
+ fZoneStringsArray = ures_getByKeyWithFallback(fZoneStringsArray, gZoneStringsTag, fZoneStringsArray, &status);
+ if (U_FAILURE(status)) {
+ // If no locale bundles are available, zoneStrings will be null.
+ // We still want to go through the rest of zone strings initialization,
+ // because generic location format is generated from tzid for the case.
+ // The rest of code should work even zoneStrings is null.
+ status = U_ZERO_ERROR;
+ ures_close(fZoneStringsArray);
+ fZoneStringsArray = NULL;
+ }
+ }
+
+ StringEnumeration *tzids = NULL;
+
+ tzids = TimeZone::createEnumeration();
+ const char *tzid;
+ while ((tzid = tzids->next(NULL, status))) {
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ // Skip non-canonical IDs
+ UnicodeString utzid(tzid, -1, US_INV);
+ UnicodeString canonicalID;
+ TimeZone::getCanonicalID(utzid, canonicalID, status);
+ if (U_FAILURE(status)) {
+ // Ignore unknown ID - we should not get here, but just in case.
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ if (U_SUCCESS(status)) {
+ if (uhash_count(fTzidToStrings) > 0) {
+ ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer());
+ if (zstrings != NULL) {
+ continue; // We already about this one
+ }
+ }
+ }
+
+ addSingleZone(canonicalID, status);
+
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ }
+
+ fIsFullyLoaded = TRUE;
+
+error_cleanup:
+ if (tzids != NULL) {
+ delete tzids;
+ }
+ fStringPool.freeze();
+
+ umtx_unlock(&ZoneStringFormatMutex);
+}
+
+
+ZoneStringFormat::~ZoneStringFormat() {
+ uhash_close(fTzidToStrings);
+ uhash_close(fMzidToStrings);
+ ures_close(fZoneItem);
+ ures_close(fMetazoneItem);
+ ures_close(fZoneStringsArray);
+}
+
+SafeZoneStringFormatPtr*
+ZoneStringFormat::getZoneStringFormat(const Locale& locale, UErrorCode &status) {
+ umtx_lock(&gZSFCacheLock);
+ if (gZoneStringFormatCache == NULL) {
+ gZoneStringFormatCache = new ZSFCache(10 /* capacity */);
+ ucln_i18n_registerCleanup(UCLN_I18N_ZSFORMAT, zoneStringFormat_cleanup);
+ }
+ umtx_unlock(&gZSFCacheLock);
+
+ return gZoneStringFormatCache->get(locale, status);
+}
+
+
+UnicodeString**
+ZoneStringFormat::createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this);
+ nonConstThis->loadFull(status);
+
+ UnicodeString **result = NULL;
+ rowCount = 0;
+ colCount = 0;
+
+ // Collect canonical time zone IDs
+ UVector canonicalIDs(uhash_deleteUnicodeString, uhash_compareUnicodeString, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ StringEnumeration *tzids = TimeZone::createEnumeration();
+ const UChar *tzid;
+ while ((tzid = tzids->unext(NULL, status))) {
+ if (U_FAILURE(status)) {
+ delete tzids;
+ return NULL;
+ }
+ UnicodeString utzid(tzid);
+ UnicodeString canonicalID;
+ TimeZone::getCanonicalID(UnicodeString(tzid), canonicalID, status);
+ if (U_FAILURE(status)) {
+ // Ignore unknown ID - we should not get here, but just in case.
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ if (utzid == canonicalID) {
+ canonicalIDs.addElement(new UnicodeString(utzid), status);
+ if (U_FAILURE(status)) {
+ delete tzids;
+ return NULL;
+ }
+ }
+ }
+ delete tzids;
+
+ // Allocate array
+ result = (UnicodeString**)uprv_malloc(canonicalIDs.size() * sizeof(UnicodeString*));
+ if (result == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ for (int32_t i = 0; i < canonicalIDs.size(); i++) {
+ result[i] = new UnicodeString[8];
+ UnicodeString *id = (UnicodeString*)canonicalIDs.elementAt(i);
+ result[i][0].setTo(*id);
+ getLongStandard(*id, date, result[i][1]);
+ getShortStandard(*id, date, FALSE, result[i][2]);
+ getLongDaylight(*id, date, result[i][3]);
+ getShortDaylight(*id, date, FALSE, result[i][4]);
+ getGenericLocation(*id, result[i][5]);
+ getLongGenericNonLocation(*id, date, result[i][6]);
+ getShortGenericNonLocation(*id, date, FALSE, result[i][7]);
+ }
+
+ rowCount = canonicalIDs.size();
+ colCount = 8;
+ return result;
+}
+
+UnicodeString&
+ZoneStringFormat::getSpecificLongString(const Calendar &cal, UnicodeString &result,
+ UErrorCode &status) const {
+ result.remove();
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ UnicodeString tzid;
+ cal.getTimeZone().getID(tzid);
+ UDate date = cal.getTime(status);
+ if (cal.get(UCAL_DST_OFFSET, status) == 0) {
+ return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /*not used*/, result);
+ } else {
+ return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /*not used*/, result);
+ }
+}
+
+UnicodeString&
+ZoneStringFormat::getSpecificShortString(const Calendar &cal, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const {
+ result.remove();
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ UnicodeString tzid;
+ cal.getTimeZone().getID(tzid);
+ UDate date = cal.getTime(status);
+ if (cal.get(UCAL_DST_OFFSET, status) == 0) {
+ return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result);
+ } else {
+ return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result);
+ }
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericLongString(const Calendar &cal, UnicodeString &result,
+ UErrorCode &status) const {
+ return getGenericString(cal, FALSE /*long*/, FALSE /* not used */, result, status);
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericShortString(const Calendar &cal, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const {
+ return getGenericString(cal, TRUE /*short*/, commonlyUsedOnly, result, status);
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericLocationString(const Calendar &cal, UnicodeString &result,
+ UErrorCode &status) const {
+ UnicodeString tzid;
+ cal.getTimeZone().getID(tzid);
+ UDate date = cal.getTime(status);
+ return getString(tzid, ZSIDX_LOCATION, date, FALSE /*not used*/, result);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findSpecificLong(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, STANDARD_LONG | DAYLIGHT_LONG, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findSpecificShort(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, STANDARD_SHORT | DAYLIGHT_SHORT, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findGenericLong(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, GENERIC_LONG | STANDARD_LONG | LOCATION, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findGenericShort(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, GENERIC_SHORT | STANDARD_SHORT | LOCATION, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findGenericLocation(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, LOCATION, matchLength, status);
+}
+
+UnicodeString&
+ZoneStringFormat::getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date,
+ UBool commonlyUsedOnly, UnicodeString& result) const {
+ UErrorCode status = U_ZERO_ERROR;
+ result.remove();
+ if (!fIsFullyLoaded) {
+ // Lazy loading
+ ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this);
+ nonConstThis->loadZone(tzid, status);
+ }
+
+ // ICU's own array does not have entries for aliases
+ UnicodeString canonicalID;
+ TimeZone::getCanonicalID(tzid, canonicalID, status);
+ if (U_FAILURE(status)) {
+ // Unknown ID, but users might have their own data.
+ canonicalID.setTo(tzid);
+ }
+
+ if (uhash_count(fTzidToStrings) > 0) {
+ ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer());
+ if (zstrings != NULL) {
+ switch (typeIdx) {
+ case ZSIDX_LONG_STANDARD:
+ case ZSIDX_LONG_DAYLIGHT:
+ case ZSIDX_LONG_GENERIC:
+ case ZSIDX_LOCATION:
+ zstrings->getString(typeIdx, result);
+ break;
+ case ZSIDX_SHORT_STANDARD:
+ case ZSIDX_SHORT_DAYLIGHT:
+ case ZSIDX_COUNT: //added to avoid warning
+ case ZSIDX_SHORT_GENERIC:
+ if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
+ zstrings->getString(typeIdx, result);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (result.isEmpty() && uhash_count(fMzidToStrings) > 0 && typeIdx != ZSIDX_LOCATION) {
+ // Try metazone
+ UnicodeString mzid;
+ ZoneMeta::getMetazoneID(canonicalID, date, mzid);
+ if (!mzid.isEmpty()) {
+ ZoneStrings *mzstrings = (ZoneStrings*)uhash_get(fMzidToStrings, mzid.getTerminatedBuffer());
+ if (mzstrings != NULL) {
+ switch (typeIdx) {
+ case ZSIDX_LONG_STANDARD:
+ case ZSIDX_LONG_DAYLIGHT:
+ case ZSIDX_LONG_GENERIC:
+ case ZSIDX_LOCATION:
+ mzstrings->getString(typeIdx, result);
+ break;
+ case ZSIDX_SHORT_STANDARD:
+ case ZSIDX_SHORT_DAYLIGHT:
+ case ZSIDX_COUNT: //added to avoid warning
+ case ZSIDX_SHORT_GENERIC:
+ if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
+ mzstrings->getString(typeIdx, result);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const {
+ result.remove();
+ UDate time = cal.getTime(status);
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ const TimeZone &tz = cal.getTimeZone();
+ UnicodeString tzid;
+ tz.getID(tzid);
+
+ if (!fIsFullyLoaded) {
+ // Lazy loading
+ ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this);
+ nonConstThis->loadZone(tzid, status);
+ }
+
+ // ICU's own array does not have entries for aliases
+ UnicodeString canonicalID;
+ TimeZone::getCanonicalID(tzid, canonicalID, status);
+ if (U_FAILURE(status)) {
+ // Unknown ID, but users might have their own data.
+ status = U_ZERO_ERROR;
+ canonicalID.setTo(tzid);
+ }
+
+ ZoneStrings *zstrings = NULL;
+ if (uhash_count(fTzidToStrings) > 0) {
+ zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer());
+ if (zstrings != NULL) {
+ if (isShort) {
+ if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
+ zstrings->getString(ZSIDX_SHORT_GENERIC, result);
+ }
+ } else {
+ zstrings->getString(ZSIDX_LONG_GENERIC, result);
+ }
+ }
+ }
+ if (result.isEmpty() && uhash_count(fMzidToStrings) > 0) {
+ // try metazone
+ int32_t raw, sav;
+ UnicodeString mzid;
+ ZoneMeta::getMetazoneID(canonicalID, time, mzid);
+ if (!mzid.isEmpty()) {
+ UBool useStandard = FALSE;
+ sav = cal.get(UCAL_DST_OFFSET, status);
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ if (sav == 0) {
+ useStandard = TRUE;
+ // Check if the zone actually uses daylight saving time around the time
+ TimeZone *tmptz = tz.clone();
+ BasicTimeZone *btz = NULL;
+ if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL
+ || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL
+ || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL
+ || dynamic_cast<VTimeZone *>(tmptz) != NULL) {
+ btz = (BasicTimeZone*)tmptz;
+ }
+
+ if (btz != NULL) {
+ TimeZoneTransition before;
+ UBool beforTrs = btz->getPreviousTransition(time, TRUE, before);
+ if (beforTrs
+ && (time - before.getTime() < kDstCheckRange)
+ && before.getFrom()->getDSTSavings() != 0) {
+ useStandard = FALSE;
+ } else {
+ TimeZoneTransition after;
+ UBool afterTrs = btz->getNextTransition(time, FALSE, after);
+ if (afterTrs
+ && (after.getTime() - time < kDstCheckRange)
+ && after.getTo()->getDSTSavings() != 0) {
+ useStandard = FALSE;
+ }
+ }
+ } else {
+ // If not BasicTimeZone... only if the instance is not an ICU's implementation.
+ // We may get a wrong answer in edge case, but it should practically work OK.
+ tmptz->getOffset(time - kDstCheckRange, FALSE, raw, sav, status);
+ if (sav != 0) {
+ useStandard = FALSE;
+ } else {
+ tmptz->getOffset(time + kDstCheckRange, FALSE, raw, sav, status);
+ if (sav != 0){
+ useStandard = FALSE;
+ }
+ }
+ if (U_FAILURE(status)) {
+ delete tmptz;
+ result.remove();
+ return result;
+ }
+ }
+ delete tmptz;
+ }
+ if (useStandard) {
+ getString(canonicalID, (isShort ? ZSIDX_SHORT_STANDARD : ZSIDX_LONG_STANDARD),
+ time, commonlyUsedOnly, result);
+
+ // Note:
+ // In CLDR 1.5.1, a same localization is used for both generic and standard
+ // for some metazones in some locales. This is actually data bugs and should
+ // be resolved in later versions of CLDR. For now, we check if the standard
+ // name is different from its generic name below.
+ if (!result.isEmpty()) {
+ UnicodeString genericNonLocation;
+ getString(canonicalID, (isShort ? ZSIDX_SHORT_GENERIC : ZSIDX_LONG_GENERIC),
+ time, commonlyUsedOnly, genericNonLocation);
+ if (!genericNonLocation.isEmpty() && result == genericNonLocation) {
+ result.remove();
+ }
+ }
+ }
+ if (result.isEmpty()) {
+ ZoneStrings *mzstrings = (ZoneStrings*)uhash_get(fMzidToStrings, mzid.getTerminatedBuffer());
+ if (mzstrings != NULL) {
+ if (isShort) {
+ if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
+ mzstrings->getString(ZSIDX_SHORT_GENERIC, result);
+ }
+ } else {
+ mzstrings->getString(ZSIDX_LONG_GENERIC, result);
+ }
+ }
+ if (!result.isEmpty()) {
+ // Check if the offsets at the given time matches the preferred zone's offsets
+ UnicodeString preferredId;
+ UnicodeString region;
+ ZoneMeta::getZoneIdByMetazone(mzid, getRegion(region), preferredId);
+ if (canonicalID != preferredId) {
+ // Check if the offsets at the given time are identical with the preferred zone
+ raw = cal.get(UCAL_ZONE_OFFSET, status);
+ if (U_FAILURE(status)) {
+ result.remove();
+ return result;
+ }
+ TimeZone *preferredZone = TimeZone::createTimeZone(preferredId);
+ int32_t prfRaw, prfSav;
+ // Check offset in preferred time zone with wall time.
+ // With getOffset(time, false, preferredOffsets),
+ // you may get incorrect results because of time overlap at DST->STD
+ // transition.
+ preferredZone->getOffset(time + raw + sav, TRUE, prfRaw, prfSav, status);
+ delete preferredZone;
+
+ if (U_FAILURE(status)) {
+ result.remove();
+ return result;
+ }
+ if ((raw != prfRaw || sav != prfSav) && zstrings != NULL) {
+ // Use generic partial location string as fallback
+ zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (result.isEmpty()) {
+ // Use location format as the final fallback
+ getString(canonicalID, ZSIDX_LOCATION, time, FALSE /*not used*/, result);
+ }
+
+ return result;
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort,
+ UDate date, UBool commonlyUsedOnly, UnicodeString &result) const {
+ UErrorCode status = U_ZERO_ERROR;
+ result.remove();
+ if (!fIsFullyLoaded) {
+ // Lazy loading
+ ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this);
+ nonConstThis->loadZone(tzid, status);
+ }
+
+ if (uhash_count(fTzidToStrings) <= 0) {
+ return result;
+ }
+
+ UnicodeString canonicalID;
+ TimeZone::getCanonicalID(tzid, canonicalID, status);
+ if (U_FAILURE(status)) {
+ // Unknown ID, so no corresponding meta data.
+ return result;
+ }
+
+ UnicodeString mzid;
+ ZoneMeta::getMetazoneID(canonicalID, date, mzid);
+
+ if (!mzid.isEmpty()) {
+ ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer());
+ if (zstrings != NULL) {
+ zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
+ }
+ }
+ return result;
+}
+
+// This method does lazy zone string loading
+const ZoneStringInfo*
+ZoneStringFormat::find(const UnicodeString &text, int32_t start, int32_t types,
+ int32_t &matchLength, UErrorCode &status) const {
+
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ const ZoneStringInfo * result = subFind(text, start, types, matchLength, status);
+ if (fIsFullyLoaded) {
+ return result;
+ }
+ // When zone string data is partially loaded,
+ // this method return the result only when
+ // the input text is fully consumed.
+ if (result != NULL) {
+ UnicodeString tmpString;
+ matchLength = (result->getString(tmpString)).length();
+ if (text.length() - start == matchLength) {
+ return result;
+ }
+ }
+
+ // Now load all zone strings
+ ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this);
+ nonConstThis->loadFull(status);
+
+ return subFind(text, start, types, matchLength, status);
+}
+
+
+/*
+ * Find a prefix matching time zone for the given zone string types.
+ * @param text The text contains a time zone string
+ * @param start The start index within the text
+ * @param types The bit mask representing a set of requested types
+ * @return If any zone string matched for the requested types, returns a
+ * ZoneStringInfo for the longest match. If no matches are found for
+ * the requested types, returns a ZoneStringInfo for the longest match
+ * for any other types. If nothing matches at all, returns null.
+ */
+const ZoneStringInfo*
+ZoneStringFormat::subFind(const UnicodeString &text, int32_t start, int32_t types,
+ int32_t &matchLength, UErrorCode &status) const {
+ matchLength = 0;
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ if (fZoneStringsTrie.isEmpty()) {
+ return NULL;
+ }
+
+ const ZoneStringInfo *result = NULL;
+ const ZoneStringInfo *fallback = NULL;
+ int32_t fallbackMatchLen = 0;
+
+ ZoneStringSearchResultHandler handler(status);
+ fZoneStringsTrie.search(text, start, (TextTrieMapSearchResultHandler*)&handler, status);
+ if (U_SUCCESS(status)) {
+ int32_t numMatches = handler.countMatches();
+ for (int32_t i = 0; i < numMatches; i++) {
+ int32_t tmpMatchLen = 0; // init. output only param to silence gcc
+ const ZoneStringInfo *tmp = handler.getMatch(i, tmpMatchLen);
+ if ((types & tmp->fType) != 0) {
+ if (result == NULL || matchLength < tmpMatchLen) {
+ result = tmp;
+ matchLength = tmpMatchLen;
+ } else if (matchLength == tmpMatchLen) {
+ // Tie breaker - there are some examples that a
+ // long standard name is identical with a location
+ // name - for example, "Uruguay Time". In this case,
+ // we interpret it as generic, not specific.
+ if (tmp->isGeneric() && !result->isGeneric()) {
+ result = tmp;
+ }
+ }
+ } else if (result == NULL) {
+ if (fallback == NULL || fallbackMatchLen < tmpMatchLen) {
+ fallback = tmp;
+ fallbackMatchLen = tmpMatchLen;
+ } else if (fallbackMatchLen == tmpMatchLen) {
+ if (tmp->isGeneric() && !fallback->isGeneric()) {
+ fallback = tmp;
+ }
+ }
+ }
+ }
+ if (result == NULL && fallback != NULL) {
+ result = fallback;
+ matchLength = fallbackMatchLen;
+ }
+ }
+ return result;
+}
+
+
+UnicodeString&
+ZoneStringFormat::getRegion(UnicodeString &region) const {
+ const char* country = fLocale.getCountry();
+ // TODO: Utilize addLikelySubtag in Locale to resolve default region
+ // when the implementation is ready.
+ region.setTo(UnicodeString(country, -1, US_INV));
+ return region;
+}
+
+MessageFormat*
+ZoneStringFormat::getFallbackFormat(const Locale &locale, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ UnicodeString pattern(TRUE, gDefFallbackPattern, -1);
+ UResourceBundle *zoneStringsArray = ures_open(U_ICUDATA_ZONE, locale.getName(), &status);
+ zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
+ int32_t len;
+ const UChar *flbkfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gFallbackFormatTag, &len, &status);
+ if (U_SUCCESS(status)) {
+ pattern.setTo(flbkfmt);
+ } else {
+ status = U_ZERO_ERROR;
+ }
+ ures_close(zoneStringsArray);
+
+ MessageFormat *fallbackFmt = new MessageFormat(pattern, status);
+ return fallbackFmt;
+}
+
+MessageFormat*
+ZoneStringFormat::getRegionFormat(const Locale& locale, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ UnicodeString pattern(TRUE, gDefRegionPattern, -1);
+ UResourceBundle *zoneStringsArray = ures_open(U_ICUDATA_ZONE, locale.getName(), &status);
+ zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
+ int32_t len;
+ const UChar *regionfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gRegionFormatTag, &len, &status);
+ if (U_SUCCESS(status)) {
+ pattern.setTo(regionfmt);
+ } else {
+ status = U_ZERO_ERROR;
+ }
+ ures_close(zoneStringsArray);
+
+ MessageFormat *regionFmt = new MessageFormat(pattern, status);
+ return regionFmt;
+}
+
+const UChar*
+ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key) {
+ const UChar *str = NULL;
+ if (zoneitem != NULL) {
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t len;
+ str = ures_getStringByKeyWithFallback(zoneitem, key, &len, &status);
+ str = fStringPool.adopt(str, status);
+ if (U_FAILURE(status)) {
+ str = NULL;
+ }
+ }
+ return str;
+}
+
+UBool
+ZoneStringFormat::isCommonlyUsed(const UResourceBundle *zoneitem) {
+ if (zoneitem == NULL) {
+ return TRUE;
+ }
+
+ UBool commonlyUsed = FALSE;
+ UErrorCode status = U_ZERO_ERROR;
+ UResourceBundle *cuRes = ures_getByKey(zoneitem, gCommonlyUsedTag, NULL, &status);
+ int32_t cuValue = ures_getInt(cuRes, &status);
+ if (U_SUCCESS(status)) {
+ if (cuValue == 1) {
+ commonlyUsed = TRUE;
+ }
+ }
+ ures_close(cuRes);
+ return commonlyUsed;
+}
+
+UnicodeString&
+ZoneStringFormat::getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale, UnicodeString &displayCountry) {
+ // We do not want to use display country names only from the target language bundle
+ // Note: we should do this in better way.
+ displayCountry.remove();
+ int32_t ccLen = countryCode.length();
+ if (ccLen > 0 && ccLen < ULOC_COUNTRY_CAPACITY) {
+ UErrorCode status = U_ZERO_ERROR;
+ UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
+ if (U_SUCCESS(status)) {
+ const char *bundleLocStr = ures_getLocale(localeBundle, &status);
+ if (U_SUCCESS(status) && uprv_strlen(bundleLocStr) > 0) {
+ Locale bundleLoc(bundleLocStr);
+ if (uprv_strcmp(bundleLocStr, "root") != 0 &&
+ uprv_strcmp(bundleLoc.getLanguage(), locale.getLanguage()) == 0) {
+ // Create a fake locale strings
+ char tmpLocStr[ULOC_COUNTRY_CAPACITY + 3];
+ uprv_strcpy(tmpLocStr, "xx_");
+ u_UCharsToChars(countryCode.getBuffer(), &tmpLocStr[3], ccLen);
+ tmpLocStr[3 + ccLen] = 0;
+
+ Locale tmpLoc(tmpLocStr);
+ tmpLoc.getDisplayCountry(locale, displayCountry);
+ }
+ }
+ }
+ ures_close(localeBundle);
+ }
+ if (displayCountry.isEmpty()) {
+ // Use the country code as the fallback
+ displayCountry.setTo(countryCode);
+ }
+ return displayCountry;
+}
+
+// ----------------------------------------------------------------------------
+/*
+ * ZoneStrings constructor adopts (and promptly copies and deletes)
+ * the input UnicodeString arrays.
+ */
+ZoneStrings::ZoneStrings(UnicodeString *strings,
+ int32_t stringsCount,
+ UBool commonlyUsed,
+ UnicodeString **genericPartialLocationStrings,
+ int32_t genericRowCount,
+ int32_t genericColCount,
+ ZSFStringPool &sp,
+ UErrorCode &status)
+: fStrings(NULL),
+ fStringsCount(stringsCount),
+ fIsCommonlyUsed(commonlyUsed),
+ fGenericPartialLocationStrings(NULL),
+ fGenericPartialLocationRowCount(genericRowCount),
+ fGenericPartialLocationColCount(genericColCount)
+{
+ if (U_FAILURE(status)) {
+ return;
+ }
+ int32_t i, j;
+ if (strings != NULL) {
+ fStrings = (const UChar **)uprv_malloc(sizeof(const UChar **) * stringsCount);
+ if (fStrings == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ for (i=0; i<fStringsCount; i++) {
+ fStrings[i] = sp.get(strings[i], status);
+ }
+ delete[] strings;
+ }
+ if (genericPartialLocationStrings != NULL) {
+ fGenericPartialLocationStrings =
+ (const UChar ***)uprv_malloc(sizeof(const UChar ***) * genericRowCount);
+ if (fGenericPartialLocationStrings == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ for (i=0; i < fGenericPartialLocationRowCount; i++) {
+ fGenericPartialLocationStrings[i] =
+ (const UChar **)uprv_malloc(sizeof(const UChar **) * genericColCount);
+ if (fGenericPartialLocationStrings[i] == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ continue; // Continue so that fGenericPartialLocationStrings will not contain uninitialized junk,
+ } // which would crash the destructor.
+ for (j=0; j<genericColCount; j++) {
+ fGenericPartialLocationStrings[i][j] =
+ sp.get(genericPartialLocationStrings[i][j], status);
+ }
+ delete[] genericPartialLocationStrings[i];
+ }
+ uprv_free(genericPartialLocationStrings);
+ }
+}
+
+ZoneStrings::~ZoneStrings() {
+ uprv_free(fStrings);
+ if (fGenericPartialLocationStrings != NULL) {
+ for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
+ uprv_free(fGenericPartialLocationStrings[i]);
+ }
+ uprv_free(fGenericPartialLocationStrings);
+ }
+}
+
+
+UnicodeString&
+ZoneStrings::getString(int32_t typeIdx, UnicodeString &result) const {
+ if (typeIdx >= 0 && typeIdx < fStringsCount) {
+ result.setTo(fStrings[typeIdx], -1);
+ } else {
+ result.remove();
+ }
+ return result;
+}
+
+UnicodeString&
+ZoneStrings::getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort,
+ UBool commonlyUsedOnly, UnicodeString &result) const {
+ UBool isSet = FALSE;
+ if (fGenericPartialLocationColCount >= 2) {
+ for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
+ if (mzid.compare(fGenericPartialLocationStrings[i][0], -1) == 0) {
+ if (isShort) {
+ if (fGenericPartialLocationColCount >= 3) {
+ if (!commonlyUsedOnly ||
+ fGenericPartialLocationColCount == 3 ||
+ fGenericPartialLocationStrings[i][3][0] != 0) {
+ result.setTo(fGenericPartialLocationStrings[i][2], -1);
+ isSet = TRUE;
+ }
+ }
+ } else {
+ result.setTo(fGenericPartialLocationStrings[i][1], -1);
+ isSet = TRUE;
+ }
+ break;
+ }
+ }
+ }
+ if (!isSet) {
+ result.remove();
+ }
+ return result;
+}
+
+// --------------------------------------------------------------
+SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry)
+: fCacheEntry(cacheEntry) {
+}
+
+SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() {
+ fCacheEntry->delRef();
+}
+
+const ZoneStringFormat*
+SafeZoneStringFormatPtr::get() const {
+ return fCacheEntry->getZoneStringFormat();
+}
+
+ZSFCacheEntry::ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next)
+: fLocale(locale), fZoneStringFormat(zsf),
+ fNext(next), fRefCount(1)
+{
+}
+
+ZSFCacheEntry::~ZSFCacheEntry () {
+ delete fZoneStringFormat;
+}
+
+const ZoneStringFormat*
+ZSFCacheEntry::getZoneStringFormat(void) {
+ return (const ZoneStringFormat*)fZoneStringFormat;
+}
+
+void
+ZSFCacheEntry::delRef(void) {
+ umtx_lock(&gZSFCacheLock);
+ --fRefCount;
+ umtx_unlock(&gZSFCacheLock);
+}
+
+ZSFCache::ZSFCache(int32_t capacity)
+: fCapacity(capacity), fFirst(NULL) {
+}
+
+ZSFCache::~ZSFCache() {
+ ZSFCacheEntry *entry = fFirst;
+ while (entry) {
+ ZSFCacheEntry *next = entry->fNext;
+ delete entry;
+ entry = next;
+ }
+}
+
+SafeZoneStringFormatPtr*
+ZSFCache::get(const Locale &locale, UErrorCode &status) {
+ SafeZoneStringFormatPtr *result = NULL;
+
+ // Search the cache entry list
+ ZSFCacheEntry *entry = NULL;
+ ZSFCacheEntry *next, *prev;
+
+ umtx_lock(&gZSFCacheLock);
+ entry = fFirst;
+ prev = NULL;
+ while (entry) {
+ next = entry->fNext;
+ if (entry->fLocale == locale) {
+ // Add reference count
+ entry->fRefCount++;
+
+ // move the entry to the top
+ if (entry != fFirst) {
+ prev->fNext = next;
+ entry->fNext = fFirst;
+ fFirst = entry;
+ }
+ break;
+ }
+ prev = entry;
+ entry = next;
+ }
+ umtx_unlock(&gZSFCacheLock);
+
+ // Create a new ZoneStringFormat
+ if (entry == NULL) {
+ ZoneStringFormat *zsf = new ZoneStringFormat(locale, status);
+ if (U_FAILURE(status)) {
+ delete zsf;
+ return NULL;
+ }
+ if (zsf == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+
+ // Now add the new entry
+ umtx_lock(&gZSFCacheLock);
+ // Make sure no other threads already created the one for the same locale
+ entry = fFirst;
+ prev = NULL;
+ while (entry) {
+ next = entry->fNext;
+ if (entry->fLocale == locale) {
+ // Add reference count
+ entry->fRefCount++;
+
+ // move the entry to the top
+ if (entry != fFirst) {
+ prev->fNext = next;
+ entry->fNext = fFirst;
+ fFirst = entry;
+ }
+ break;
+ }
+ prev = entry;
+ entry = next;
+ }
+ if (entry == NULL) {
+ // Add the new one to the top
+ next = fFirst;
+ entry = new ZSFCacheEntry(locale, zsf, next);
+ fFirst = entry;
+ } else {
+ delete zsf;
+ }
+ umtx_unlock(&gZSFCacheLock);
+ }
+
+ result = new SafeZoneStringFormatPtr(entry);
+
+ // Now, delete unused cache entries beyond the capacity
+ umtx_lock(&gZSFCacheLock);
+ entry = fFirst;
+ prev = NULL;
+ int32_t idx = 1;
+ while (entry) {
+ next = entry->fNext;
+ if (idx >= fCapacity && entry->fRefCount == 0) {
+ if (entry == fFirst) {
+ fFirst = next;
+ } else {
+ prev->fNext = next;
+ }
+ delete entry;
+ } else {
+ prev = entry;
+ }
+ entry = next;
+ idx++;
+ }
+ umtx_unlock(&gZSFCacheLock);
+
+ return result;
+}
+
+
+/*
+ * Zone String Formatter String Pool Implementation
+ *
+ * String pool for (UChar *) strings. Avoids having repeated copies of the same string.
+ */
+
+static const int32_t POOL_CHUNK_SIZE = 2000;
+struct ZSFStringPoolChunk: public UMemory {
+ ZSFStringPoolChunk *fNext; // Ptr to next pool chunk
+ int32_t fLimit; // Index to start of unused area at end of fStrings
+ UChar fStrings[POOL_CHUNK_SIZE]; // Strings array
+ ZSFStringPoolChunk();
+};
+
+ZSFStringPoolChunk::ZSFStringPoolChunk() {
+ fNext = NULL;
+ fLimit = 0;
+}
+
+ZSFStringPool::ZSFStringPool(UErrorCode &status) {
+ fChunks = NULL;
+ fHash = NULL;
+ if (U_FAILURE(status)) {
+ return;
+ }
+ fChunks = new ZSFStringPoolChunk;
+ if (fChunks == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+
+ fHash = uhash_open(uhash_hashUChars /* keyHash */,
+ uhash_compareUChars /* keyComp */,
+ uhash_compareUChars /* valueComp */,
+ &status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+}
+
+
+ZSFStringPool::~ZSFStringPool() {
+ if (fHash != NULL) {
+ uhash_close(fHash);
+ fHash = NULL;
+ }
+
+ while (fChunks != NULL) {
+ ZSFStringPoolChunk *nextChunk = fChunks->fNext;
+ delete fChunks;
+ fChunks = nextChunk;
+ }
+}
+
+static const UChar EmptyString = 0;
+
+const UChar *ZSFStringPool::get(const UChar *s, UErrorCode &status) {
+ const UChar *pooledString;
+ if (U_FAILURE(status)) {
+ return &EmptyString;
+ }
+
+ pooledString = static_cast<UChar *>(uhash_get(fHash, s));
+ if (pooledString != NULL) {
+ return pooledString;
+ }
+
+ int32_t length = u_strlen(s);
+ int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit;
+ if (remainingLength <= length) {
+ U_ASSERT(length < POOL_CHUNK_SIZE);
+ if (length >= POOL_CHUNK_SIZE) {
+ status = U_INTERNAL_PROGRAM_ERROR;
+ return &EmptyString;
+ }
+ ZSFStringPoolChunk *oldChunk = fChunks;
+ fChunks = new ZSFStringPoolChunk;
+ if (fChunks == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return &EmptyString;
+ }
+ fChunks->fNext = oldChunk;
+ }
+
+ UChar *destString = &fChunks->fStrings[fChunks->fLimit];
+ u_strcpy(destString, s);
+ fChunks->fLimit += (length + 1);
+ uhash_put(fHash, destString, destString, &status);
+ return destString;
+}
+
+
+//
+// ZSFStringPool::adopt() Put a string into the hash, but do not copy the string data
+// into the pool's storage. Used for strings from resource bundles,
+// which will perisist for the life of the zone string formatter, and
+// therefore can be used directly without copying.
+const UChar *ZSFStringPool::adopt(const UChar * s, UErrorCode &status) {
+ const UChar *pooledString;
+ if (U_FAILURE(status)) {
+ return &EmptyString;
+ }
+ if (s != NULL) {
+ pooledString = static_cast<UChar *>(uhash_get(fHash, s));
+ if (pooledString == NULL) {
+ UChar *ncs = const_cast<UChar *>(s);
+ uhash_put(fHash, ncs, ncs, &status);
+ }
+ }
+ return s;
+}
+
+
+const UChar *ZSFStringPool::get(const UnicodeString &s, UErrorCode &status) {
+ UnicodeString &nonConstStr = const_cast<UnicodeString &>(s);
+ return this->get(nonConstStr.getTerminatedBuffer(), status);
+}
+
+/*
+ * freeze(). Close the hash table that maps to the pooled strings.
+ * After freezing, the pool can not be searched or added to,
+ * but all existing references to pooled strings remain valid.
+ *
+ * The main purpose is to recover the storage used for the hash.
+ */
+void ZSFStringPool::freeze() {
+ uhash_close(fHash);
+ fHash = NULL;
+}
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
Property changes on: icu46/source/i18n/zstrfmt.cpp
___________________________________________________________________
Added: svn:eol-style
+ LF
« no previous file with comments | « icu46/source/i18n/zstrfmt.h ('k') | icu46/source/i18n/ztrans.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698