Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2007-2010, International Business Machines Corporation and    *
      4 * others. All Rights Reserved.                                                *
      5 *******************************************************************************
      6 */
      7 
      8 #include "unicode/utypes.h"
      9 
     10 #if !UCONFIG_NO_FORMATTING
     11 
     12 #include "zstrfmt.h"
     13 
     14 #include "unicode/ustring.h"
     15 #include "unicode/putil.h"
     16 #include "unicode/msgfmt.h"
     17 #include "unicode/basictz.h"
     18 #include "unicode/simpletz.h"
     19 #include "unicode/rbtz.h"
     20 #include "unicode/vtzone.h"
     21 
     22 #include "uvector.h"
     23 #include "cstring.h"
     24 #include "cmemory.h"
     25 #include "uresimp.h"
     26 #include "zonemeta.h"
     27 #include "olsontz.h"
     28 #include "umutex.h"
     29 #include "ucln_in.h"
     30 #include "uassert.h"
     31 #include "ureslocs.h"
     32 
     33 /**
     34  * global ZoneStringFormatCache stuffs
     35  */
     36 static UMTX gZSFCacheLock = NULL;
     37 static U_NAMESPACE_QUALIFIER ZSFCache *gZoneStringFormatCache = NULL;
     38 
     39 U_CDECL_BEGIN
     40 /**
     41  * ZoneStringFormatCache cleanup callback func
     42  */
     43 static UBool U_CALLCONV zoneStringFormat_cleanup(void)
     44 {
     45     umtx_destroy(&gZSFCacheLock);
     46     if (gZoneStringFormatCache != NULL) {
     47         delete gZoneStringFormatCache;
     48         gZoneStringFormatCache = NULL;
     49     }
     50     gZoneStringFormatCache = NULL;
     51     return TRUE;
     52 }
     53 
     54 /**
     55  * Deleter for ZoneStringInfo
     56  */
     57 static void U_CALLCONV
     58 deleteZoneStringInfo(void *obj) {
     59     delete (U_NAMESPACE_QUALIFIER ZoneStringInfo*)obj;
     60 }
     61 
     62 /**
     63  * Deleter for ZoneStrings
     64  */
     65 static void U_CALLCONV
     66 deleteZoneStrings(void *obj) {
     67     delete (U_NAMESPACE_QUALIFIER ZoneStrings*)obj;
     68 }
     69 U_CDECL_END
     70 
     71 U_NAMESPACE_BEGIN
     72 
     73 #define ZID_KEY_MAX 128
     74 
     75 static const char gCountriesTag[]       = "Countries";
     76 static const char gZoneStringsTag[]     = "zoneStrings";
     77 static const char gShortGenericTag[]    = "sg";
     78 static const char gShortStandardTag[]   = "ss";
     79 static const char gShortDaylightTag[]   = "sd";
     80 static const char gLongGenericTag[]     = "lg";
     81 static const char gLongStandardTag[]    = "ls";
     82 static const char gLongDaylightTag[]    = "ld";
     83 static const char gExemplarCityTag[]    = "ec";
     84 static const char gCommonlyUsedTag[]    = "cu";
     85 static const char gFallbackFormatTag[]  = "fallbackFormat";
     86 static const char gRegionFormatTag[]    = "regionFormat";
     87 
     88 #define MZID_PREFIX_LEN 5
     89 static const char gMetazoneIdPrefix[]   = "meta:";
     90 
     91 #define MAX_METAZONES_PER_ZONE 10
     92 
     93 static const UChar gDefFallbackPattern[]    = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
     94 static const UChar gDefRegionPattern[]      = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
     95 static const UChar gCommonlyUsedTrue[]      = {0x31, 0x00}; // "1"
     96 
     97 static const double kDstCheckRange      = (double)184*U_MILLIS_PER_DAY;
     98 
     99 static int32_t
    100 getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type) {
    101     int32_t typeIdx = 0;
    102     switch (type) {
    103         case LOCATION:
    104             typeIdx = ZSIDX_LOCATION;
    105             break;
    106         case GENERIC_LONG:
    107             typeIdx = ZSIDX_LONG_GENERIC;
    108             break;
    109         case GENERIC_SHORT:
    110             typeIdx = ZSIDX_SHORT_GENERIC;
    111             break;
    112         case STANDARD_LONG:
    113             typeIdx = ZSIDX_LONG_STANDARD;
    114             break;
    115         case STANDARD_SHORT:
    116             typeIdx = ZSIDX_SHORT_STANDARD;
    117             break;
    118         case DAYLIGHT_LONG:
    119             typeIdx = ZSIDX_LONG_DAYLIGHT;
    120             break;
    121         case DAYLIGHT_SHORT:
    122             typeIdx = ZSIDX_SHORT_DAYLIGHT;
    123             break;
    124     }
    125     return typeIdx;
    126 }
    127 
    128 static int32_t
    129 getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx) {
    130     int32_t type = 0;
    131     switch (typeIdx) {
    132         case ZSIDX_LOCATION:
    133             type = LOCATION;
    134             break;
    135         case ZSIDX_LONG_GENERIC:
    136             type = GENERIC_LONG;
    137             break;
    138         case ZSIDX_SHORT_GENERIC:
    139             type = GENERIC_SHORT;
    140             break;
    141         case ZSIDX_LONG_STANDARD:
    142             type = STANDARD_LONG;
    143             break;
    144         case ZSIDX_SHORT_STANDARD:
    145             type = STANDARD_SHORT;
    146             break;
    147         case ZSIDX_LONG_DAYLIGHT:
    148             type = DAYLIGHT_LONG;
    149             break;
    150         case ZSIDX_COUNT:
    151         case ZSIDX_SHORT_DAYLIGHT:
    152             type = DAYLIGHT_SHORT;
    153             break;
    154         default:
    155             break;
    156     }
    157     return type;
    158 }
    159 
    160 #define DEFAULT_CHARACTERNODE_CAPACITY 1
    161 
    162 // ----------------------------------------------------------------------------
    163 void CharacterNode::clear() {
    164     uprv_memset(this, 0, sizeof(*this));
    165 }
    166 
    167 void CharacterNode::deleteValues() {
    168     if (fValues == NULL) {
    169         // Do nothing.
    170     } else if (!fHasValuesVector) {
    171         deleteZoneStringInfo(fValues);
    172     } else {
    173         delete (UVector *)fValues;
    174     }
    175 }
    176 
    177 void
    178 CharacterNode::addValue(void *value, UErrorCode &status) {
    179     if (U_FAILURE(status)) {
    180         deleteZoneStringInfo(value);
    181         return;
    182     }
    183     if (fValues == NULL) {
    184         fValues = value;
    185     } else {
    186         // At least one value already.
    187         if (!fHasValuesVector) {
    188             // There is only one value so far, and not in a vector yet.
    189             // Create a vector and add the old value.
    190             UVector *values = new UVector(deleteZoneStringInfo, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status);
    191             if (U_FAILURE(status)) {
    192                 deleteZoneStringInfo(value);
    193                 return;
    194             }
    195             values->addElement(fValues, status);
    196             fValues = values;
    197             fHasValuesVector = TRUE;
    198         }
    199         // Add the new value.
    200         ((UVector *)fValues)->addElement(value, status);
    201     }
    202 }
    203 
    204 //----------------------------------------------------------------------------
    205 // Virtual destructor to avoid warning
    206 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
    207 }
    208 
    209 // ----------------------------------------------------------------------------
    210 TextTrieMap::TextTrieMap(UBool ignoreCase)
    211 : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0),
    212   fLazyContents(NULL), fIsEmpty(TRUE) {
    213 }
    214 
    215 TextTrieMap::~TextTrieMap() {
    216     int32_t index;
    217     for (index = 0; index < fNodesCount; ++index) {
    218         fNodes[index].deleteValues();
    219     }
    220     uprv_free(fNodes);
    221     if (fLazyContents != NULL) {
    222         for (int32_t i=0; i<fLazyContents->size(); i+=2) {
    223             ZoneStringInfo *zsinf = (ZoneStringInfo *)fLazyContents->elementAt(i+1);
    224             delete zsinf;
    225         }
    226         delete fLazyContents;
    227     }
    228 }
    229 
    230 int32_t TextTrieMap::isEmpty() const {
    231     // Use a separate field for fIsEmpty because it will remain unchanged once the
    232     //   Trie is built, while fNodes and fLazyContents change with the lazy init
    233     //   of the nodes structure.  Trying to test the changing fields has
    234     //   thread safety complications.
    235     return fIsEmpty;
    236 }
    237 
    238 
    239 //  We defer actually building the TextTrieMap node structure until the first time a
    240 //     search is performed.  put() simply saves the parameters in case we do
    241 //     eventually need to build it.
    242 //
    243 void
    244 TextTrieMap::put(const UnicodeString &key, void *value, ZSFStringPool &sp, UErrorCode &status) {
    245     fIsEmpty = FALSE;
    246     if (fLazyContents == NULL) {
    247         fLazyContents = new UVector(status);
    248         if (fLazyContents == NULL) {
    249             status = U_MEMORY_ALLOCATION_ERROR;
    250         }
    251     }
    252     if (U_FAILURE(status)) {
    253         return;
    254     }
    255     UChar *s = const_cast<UChar *>(sp.get(key, status));
    256     fLazyContents->addElement(s, status);
    257     fLazyContents->addElement(value, status);
    258 }
    259 
    260 
    261 void
    262 TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) {
    263     if (fNodes == NULL) {
    264         fNodesCapacity = 512;
    265         fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
    266         fNodes[0].clear();  // Init root node.
    267         fNodesCount = 1;
    268     }
    269 
    270     UnicodeString foldedKey;
    271     const UChar *keyBuffer;
    272     int32_t keyLength;
    273     if (fIgnoreCase) {
    274         // Ok to use fastCopyFrom() because we discard the copy when we return.
    275         foldedKey.fastCopyFrom(key).foldCase();
    276         keyBuffer = foldedKey.getBuffer();
    277         keyLength = foldedKey.length();
    278     } else {
    279         keyBuffer = key.getBuffer();
    280         keyLength = key.length();
    281     }
    282 
    283     CharacterNode *node = fNodes;
    284     int32_t index;
    285     for (index = 0; index < keyLength; ++index) {
    286         node = addChildNode(node, keyBuffer[index], status);
    287     }
    288     node->addValue(value, status);
    289 }
    290 
    291 UBool
    292 TextTrieMap::growNodes() {
    293     if (fNodesCapacity == 0xffff) {
    294         return FALSE;  // We use 16-bit node indexes.
    295     }
    296     int32_t newCapacity = fNodesCapacity + 1000;
    297     if (newCapacity > 0xffff) {
    298         newCapacity = 0xffff;
    299     }
    300     CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode));
    301     if (newNodes == NULL) {
    302         return FALSE;
    303     }
    304     uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode));
    305     uprv_free(fNodes);
    306     fNodes = newNodes;
    307     fNodesCapacity = newCapacity;
    308     return TRUE;
    309 }
    310 
    311 CharacterNode*
    312 TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) {
    313     if (U_FAILURE(status)) {
    314         return NULL;
    315     }
    316     // Linear search of the sorted list of children.
    317     uint16_t prevIndex = 0;
    318     uint16_t nodeIndex = parent->fFirstChild;
    319     while (nodeIndex > 0) {
    320         CharacterNode *current = fNodes + nodeIndex;
    321         UChar childCharacter = current->fCharacter;
    322         if (childCharacter == c) {
    323             return current;
    324         } else if (childCharacter > c) {
    325             break;
    326         }
    327         prevIndex = nodeIndex;
    328         nodeIndex = current->fNextSibling;
    329     }
    330 
    331     // Ensure capacity. Grow fNodes[] if needed.
    332     if (fNodesCount == fNodesCapacity) {
    333         int32_t parentIndex = (int32_t)(parent - fNodes);
    334         if (!growNodes()) {
    335             status = U_MEMORY_ALLOCATION_ERROR;
    336             return NULL;
    337         }
    338         parent = fNodes + parentIndex;
    339     }
    340 
    341     // Insert a new child node with c in sorted order.
    342     CharacterNode *node = fNodes + fNodesCount;
    343     node->clear();
    344     node->fCharacter = c;
    345     node->fNextSibling = nodeIndex;
    346     if (prevIndex == 0) {
    347         parent->fFirstChild = (uint16_t)fNodesCount;
    348     } else {
    349         fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount;
    350     }
    351     ++fNodesCount;
    352     return node;
    353 }
    354 
    355 CharacterNode*
    356 TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
    357     // Linear search of the sorted list of children.
    358     uint16_t nodeIndex = parent->fFirstChild;
    359     while (nodeIndex > 0) {
    360         CharacterNode *current = fNodes + nodeIndex;
    361         UChar childCharacter = current->fCharacter;
    362         if (childCharacter == c) {
    363             return current;
    364         } else if (childCharacter > c) {
    365             break;
    366         }
    367         nodeIndex = current->fNextSibling;
    368     }
    369     return NULL;
    370 }
    371 
    372 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
    373 static UMTX TextTrieMutex;
    374 
    375 // buildTrie() - The Trie node structure is needed.  Create it from the data that was
    376 //               saved at the time the ZoneStringFormatter was created.  The Trie is only
    377 //               needed for parsing operations, which are less common than formatting,
    378 //               and the Trie is big, which is why its creation is deferred until first use.
    379 void TextTrieMap::buildTrie(UErrorCode &status) {
    380     umtx_lock(&TextTrieMutex);
    381     if (fLazyContents != NULL) {
    382         for (int32_t i=0; i<fLazyContents->size(); i+=2) {
    383             const UChar *key = (UChar *)fLazyContents->elementAt(i);
    384             void  *val = fLazyContents->elementAt(i+1);
    385             UnicodeString keyString(TRUE, key, -1);  // Aliasing UnicodeString constructor.
    386             putImpl(keyString, val, status);
    387         }
    388         delete fLazyContents;
    389         fLazyContents = NULL;
    390     }
    391     umtx_unlock(&TextTrieMutex);
    392 }
    393 
    394 
    395 void
    396 TextTrieMap::search(const UnicodeString &text, int32_t start,
    397                   TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
    398     UBool trieNeedsInitialization = FALSE;
    399     UMTX_CHECK(&TextTrieMutex, fLazyContents != NULL, trieNeedsInitialization);
    400     if (trieNeedsInitialization) {
    401         TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
    402         nonConstThis->buildTrie(status);
    403     }
    404     if (fNodes == NULL) {
    405         return;
    406     }
    407     search(fNodes, text, start, start, handler, status);
    408 }
    409 
    410 void
    411 TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
    412                   int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
    413     if (U_FAILURE(status)) {
    414         return;
    415     }
    416     if (node->hasValues()) {
    417         if (!handler->handleMatch(index - start, node, status)) {
    418             return;
    419         }
    420         if (U_FAILURE(status)) {
    421             return;
    422         }
    423     }
    424     UChar32 c = text.char32At(index);
    425     if (fIgnoreCase) {
    426         // size of character may grow after fold operation
    427         UnicodeString tmp(c);
    428         tmp.foldCase();
    429         int32_t tmpidx = 0;
    430         while (tmpidx < tmp.length()) {
    431             c = tmp.char32At(tmpidx);
    432             node = getChildNode(node, c);
    433             if (node == NULL) {
    434                 break;
    435             }
    436             tmpidx = tmp.moveIndex32(tmpidx, 1);
    437         }
    438     } else {
    439         node = getChildNode(node, c);
    440     }
    441     if (node != NULL) {
    442         search(node, text, start, index+1, handler, status);
    443     }
    444 }
    445 
    446 // ----------------------------------------------------------------------------
    447 ZoneStringInfo::ZoneStringInfo(const UnicodeString &id, const UnicodeString &str,
    448                                TimeZoneTranslationType type, ZSFStringPool &sp, UErrorCode &status)
    449 : fType(type) {
    450     fId = sp.get(id, status);
    451     fStr = sp.get(str, status);
    452 }
    453 
    454 ZoneStringInfo::~ZoneStringInfo() {
    455 }
    456 
    457 
    458 // ----------------------------------------------------------------------------
    459 ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode &status)
    460 : fResults(status)
    461 {
    462     clear();
    463 }
    464 
    465 ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() {
    466     clear();
    467 }
    468 
    469 UBool
    470 ZoneStringSearchResultHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
    471     if (U_FAILURE(status)) {
    472         return FALSE;
    473     }
    474     if (node->hasValues()) {
    475         int32_t valuesCount = node->countValues();
    476         for (int32_t i = 0; i < valuesCount; i++) {
    477             ZoneStringInfo *zsinfo = (ZoneStringInfo*)node->getValue(i);
    478             if (zsinfo == NULL) {
    479                 break;
    480             }
    481             // Update the results
    482             UBool foundType = FALSE;
    483             for (int32_t j = 0; j < fResults.size(); j++) {
    484                 ZoneStringInfo *tmp = (ZoneStringInfo*)fResults.elementAt(j);
    485                 if (zsinfo->fType == tmp->fType) {
    486                     int32_t lenidx = getTimeZoneTranslationTypeIndex(tmp->fType);
    487                     if (matchLength > fMatchLen[lenidx]) {
    488                         // Same type, longer match
    489                         fResults.setElementAt(zsinfo, j);
    490                         fMatchLen[lenidx] = matchLength;
    491                     }
    492                     foundType = TRUE;
    493                     break;
    494                 }
    495             }
    496             if (!foundType) {
    497                 // not found in the current list
    498                 fResults.addElement(zsinfo, status);
    499                 fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)] = matchLength;
    500             }
    501         }
    502     }
    503     return TRUE;
    504 }
    505 
    506 int32_t
    507 ZoneStringSearchResultHandler::countMatches(void) {
    508     return fResults.size();
    509 }
    510 
    511 const ZoneStringInfo*
    512 ZoneStringSearchResultHandler::getMatch(int32_t index, int32_t &matchLength) {
    513     ZoneStringInfo *zsinfo = NULL;
    514     if (index < fResults.size()) {
    515         zsinfo = (ZoneStringInfo*)fResults.elementAt(index);
    516         matchLength = fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)];
    517     }
    518     return zsinfo;
    519 }
    520 
    521 void
    522 ZoneStringSearchResultHandler::clear(void) {
    523     fResults.removeAllElements();
    524     for (int32_t i = 0; i < (int32_t)(sizeof(fMatchLen)/sizeof(fMatchLen[0])); i++) {
    525         fMatchLen[i] = 0;
    526     }
    527 }
    528 
    529 // Mutex for protecting the lazy load of a zone ID (or a full load) to ZoneStringFormat structures.
    530 static UMTX ZoneStringFormatMutex;
    531 
    532 
    533 // ----------------------------------------------------------------------------
    534 ZoneStringFormat::ZoneStringFormat(const UnicodeString* const* strings,
    535                                    int32_t rowCount, int32_t columnCount, UErrorCode &status)
    536 : fLocale(""),
    537   fTzidToStrings(NULL),
    538   fMzidToStrings(NULL),
    539   fZoneStringsTrie(TRUE),
    540   fStringPool(status),
    541   fZoneStringsArray(NULL),
    542   fMetazoneItem(NULL),
    543   fZoneItem(NULL),
    544   fIsFullyLoaded(FALSE)
    545 {
    546     if (U_FAILURE(status)) {
    547         return;
    548     }
    549     fLocale.setToBogus();
    550     if (strings == NULL || columnCount <= 0 || rowCount <= 0) {
    551         status = U_ILLEGAL_ARGUMENT_ERROR;
    552         return;
    553     }
    554     fTzidToStrings = uhash_open(uhash_hashUChars,     // key hash function
    555                                 uhash_compareUChars,  // key comparison function
    556                                 NULL,                 // Value comparison function
    557                                 &status);
    558     fMzidToStrings = uhash_open(uhash_hashUChars,
    559                                 uhash_compareUChars,
    560                                 NULL,
    561                                 &status);
    562 
    563     uhash_setValueDeleter(fTzidToStrings, deleteZoneStrings);
    564     uhash_setValueDeleter(fMzidToStrings, deleteZoneStrings);
    565 
    566     for (int32_t row = 0; row < rowCount; row++) {
    567         if (strings[row][0].isEmpty()) {
    568             continue;
    569         }
    570         UnicodeString *names = new UnicodeString[ZSIDX_COUNT];
    571         for (int32_t col = 1; col < columnCount; col++) {
    572             if (!strings[row][col].isEmpty()) {
    573                 int32_t typeIdx = -1;
    574                 switch (col) {
    575                     case 1:
    576                         typeIdx = ZSIDX_LONG_STANDARD;
    577                         break;
    578                     case 2:
    579                         typeIdx = ZSIDX_SHORT_STANDARD;
    580                         break;
    581                     case 3:
    582                         typeIdx = ZSIDX_LONG_DAYLIGHT;
    583                         break;
    584                     case 4:
    585                         typeIdx = ZSIDX_SHORT_DAYLIGHT;
    586                         break;
    587                     case 5:
    588                         typeIdx = ZSIDX_LOCATION;
    589                         break;
    590                     case 6:
    591                         typeIdx = ZSIDX_LONG_GENERIC;
    592                         break;
    593                     case 7:
    594                         typeIdx = ZSIDX_SHORT_GENERIC;
    595                         break;
    596                 }
    597                 if (typeIdx != -1) {
    598                     names[typeIdx].setTo(strings[row][col]);
    599 
    600                     // Put the name into the trie
    601                     int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeIdx);
    602                     ZoneStringInfo *zsinf = new ZoneStringInfo(strings[row][0],
    603                                                                strings[row][col],
    604                                                                (TimeZoneTranslationType)type,
    605                                                                fStringPool,
    606                                                                status);
    607                     fZoneStringsTrie.put(strings[row][col], zsinf, fStringPool, status);
    608                     if (U_FAILURE(status)) {
    609                         delete zsinf;
    610                         goto error_cleanup;
    611                     }
    612                 }
    613             }
    614         }
    615         // Note:  ZoneStrings constructor adopts and delete the names array.
    616         ZoneStrings *zstrings = new ZoneStrings(names, ZSIDX_COUNT, TRUE, NULL, 0, 0,
    617                                                 fStringPool, status);
    618         UChar *utzid = const_cast<UChar *>(fStringPool.get(strings[row][0], status));
    619         uhash_put(fTzidToStrings, utzid, zstrings, &status);
    620         if (U_FAILURE(status)) {
    621             delete zstrings;
    622             goto error_cleanup;
    623         }
    624     }
    625     fStringPool.freeze();
    626 	fIsFullyLoaded = TRUE;
    627     return;
    628 
    629 error_cleanup:
    630     return;
    631 }
    632 
    633 ZoneStringFormat::ZoneStringFormat(const Locale &locale, UErrorCode &status)
    634 : fLocale(locale),
    635   fTzidToStrings(NULL),
    636   fMzidToStrings(NULL),
    637   fZoneStringsTrie(TRUE),
    638   fStringPool(status),
    639   fZoneStringsArray(NULL),
    640   fMetazoneItem(NULL),
    641   fZoneItem(NULL),
    642   fIsFullyLoaded(FALSE)
    643 {
    644     if (U_FAILURE(status)) {
    645         return;
    646     }
    647     fTzidToStrings = uhash_open(uhash_hashUChars,     // key hash function
    648                                 uhash_compareUChars,  // key comparison function
    649                                 NULL,                 // Value comparison function
    650                                 &status);
    651     fMzidToStrings = uhash_open(uhash_hashUChars,     // key hash function
    652                                 uhash_compareUChars,  // key comparison function
    653                                 NULL,                 // Value comparison function
    654                                 &status);
    655     uhash_setValueDeleter(fTzidToStrings, deleteZoneStrings);
    656     uhash_setValueDeleter(fMzidToStrings, deleteZoneStrings);
    657 }
    658 
    659 // Load only a single zone
    660 void
    661 ZoneStringFormat::loadZone(const UnicodeString &utzid, UErrorCode &status)
    662 {
    663 	if (fIsFullyLoaded) {
    664 		return;
    665 	}
    666 
    667 	if (U_FAILURE(status)) {
    668 		return;
    669 	}
    670 
    671 	umtx_lock(&ZoneStringFormatMutex);
    672 
    673 	if (fZoneStringsArray == NULL) {
    674 		fZoneStringsArray = ures_open(U_ICUDATA_ZONE, fLocale.getName(), &status);
    675 		fZoneStringsArray = ures_getByKeyWithFallback(fZoneStringsArray, gZoneStringsTag, fZoneStringsArray, &status);
    676 		if (U_FAILURE(status)) {
    677 			// If no locale bundles are available, zoneStrings will be null.
    678 			// We still want to go through the rest of zone strings initialization,
    679 			// because generic location format is generated from tzid for the case.
    680 			// The rest of code should work even zoneStrings is null.
    681 			status = U_ZERO_ERROR;
    682 			ures_close(fZoneStringsArray);
    683 			fZoneStringsArray = NULL;
    684 		}
    685     }
    686 
    687 	// Skip non-canonical IDs
    688 	UnicodeString canonicalID;
    689 	TimeZone::getCanonicalID(utzid, canonicalID, status);
    690 	if (U_FAILURE(status)) {
    691 		// Ignore unknown ID - we should not get here, but just in case.
    692 		//	status = U_ZERO_ERROR;
    693 		umtx_unlock(&ZoneStringFormatMutex);
    694 		return;
    695 	}
    696 
    697     if (U_SUCCESS(status)) {
    698 		if (uhash_count(fTzidToStrings) > 0) {
    699 			ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer());
    700 			if (zstrings != NULL) {
    701 				umtx_unlock(&ZoneStringFormatMutex);
    702 				return;	//	We already about this one
    703 			}
    704 		}
    705 	}
    706 
    707 	addSingleZone(canonicalID, status);
    708 
    709 	umtx_unlock(&ZoneStringFormatMutex);
    710 }
    711 
    712 // Load only a single zone
    713 void
    714 ZoneStringFormat::addSingleZone(UnicodeString &utzid, UErrorCode &status)
    715 {
    716 	if (U_FAILURE(status)) {
    717 		return;
    718 	}
    719 
    720 	if (uhash_count(fTzidToStrings) > 0) {
    721 		ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, utzid.getTerminatedBuffer());
    722 		if (zstrings != NULL) {
    723 			return;	//	We already about this one
    724 		}
    725 	}
    726 
    727     MessageFormat *fallbackFmt = NULL;
    728     MessageFormat *regionFmt = NULL;
    729 
    730     fallbackFmt = getFallbackFormat(fLocale, status);
    731     if (U_FAILURE(status)) {
    732         goto error_cleanup;
    733     }
    734     regionFmt = getRegionFormat(fLocale, status);
    735     if (U_FAILURE(status)) {
    736         goto error_cleanup;
    737     }
    738 
    739 
    740 	{
    741 		char zidkey[ZID_KEY_MAX+1];
    742 		char tzid[ZID_KEY_MAX+1];
    743 		utzid.extract(0, utzid.length(), zidkey, ZID_KEY_MAX, US_INV);
    744 		utzid.extract(0, utzid.length(), tzid, ZID_KEY_MAX, US_INV);
    745 
    746 		const UChar *zstrarray[ZSIDX_COUNT];
    747 		const UChar *mzstrarray[ZSIDX_COUNT];
    748 		UnicodeString mzPartialLoc[MAX_METAZONES_PER_ZONE][4];
    749 
    750 		// Replace '/' with ':'
    751 		char *pCity = NULL;
    752 		char *p = zidkey;
    753 		while (*p) {
    754 			if (*p == '/') {
    755 				*p = ':';
    756 				pCity = p + 1;
    757 			}
    758 			p++;
    759 		}
    760 
    761 		if (fZoneStringsArray != NULL) {
    762 			fZoneItem = ures_getByKeyWithFallback(fZoneStringsArray, zidkey, fZoneItem, &status);
    763 			if (U_FAILURE(status)) {
    764 				// If failed to open the zone item, create only location string
    765 				ures_close(fZoneItem);
    766 				fZoneItem = NULL;
    767 				status = U_ZERO_ERROR;
    768 			}
    769 		}
    770 
    771 		UnicodeString region;
    772 		getRegion(region);
    773 
    774 		zstrarray[ZSIDX_LONG_STANDARD]  = getZoneStringFromBundle(fZoneItem, gLongStandardTag);
    775 		zstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(fZoneItem, gShortStandardTag);
    776 		zstrarray[ZSIDX_LONG_DAYLIGHT]  = getZoneStringFromBundle(fZoneItem, gLongDaylightTag);
    777 		zstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(fZoneItem, gShortDaylightTag);
    778 		zstrarray[ZSIDX_LONG_GENERIC]   = getZoneStringFromBundle(fZoneItem, gLongGenericTag);
    779 		zstrarray[ZSIDX_SHORT_GENERIC]  = getZoneStringFromBundle(fZoneItem, gShortGenericTag);
    780 
    781 		// Compose location format string
    782 		UnicodeString location;
    783 		UnicodeString country;
    784 		UnicodeString city;
    785 		UnicodeString countryCode;
    786 		ZoneMeta::getCanonicalCountry(utzid, countryCode);
    787 		if (!countryCode.isEmpty()) {
    788 			const UChar* tmpCity = getZoneStringFromBundle(fZoneItem, gExemplarCityTag);
    789 			if (tmpCity != NULL) {
    790 				city.setTo(TRUE, tmpCity, -1);
    791 			} else {
    792 				city.setTo(UnicodeString(pCity, -1, US_INV));
    793 				// Replace '_' with ' '
    794 				for (int32_t i = 0; i < city.length(); i++) {
    795 					if (city.charAt(i) == (UChar)0x5F /*'_'*/) {
    796 						city.setCharAt(i, (UChar)0x20 /*' '*/);
    797 					}
    798 				}
    799 			}
    800 			getLocalizedCountry(countryCode, fLocale, country);
    801 			UnicodeString singleCountry;
    802 			ZoneMeta::getSingleCountry(utzid, singleCountry);
    803 			FieldPosition fpos;
    804 			if (singleCountry.isEmpty()) {
    805 				Formattable params [] = {
    806 					Formattable(city),
    807 					Formattable(country)
    808 				};
    809 				fallbackFmt->format(params, 2, location, fpos, status);
    810 			} else {
    811 				// If the zone is only one zone in the country, do not add city
    812 				Formattable params [] = {
    813 					Formattable(country)
    814 				};
    815 				regionFmt->format(params, 1, location, fpos, status);
    816 			}
    817 			if (U_FAILURE(status)) {
    818 				goto error_cleanup;
    819 			}
    820 
    821 			zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer();
    822 		} else {
    823 			if (uprv_strlen(tzid) > 4 && uprv_strncmp(tzid, "Etc/", 4) == 0) {
    824 				// "Etc/xxx" is not associated with a specific location, so localized
    825 				// GMT format is always used as generic location format.
    826 				zstrarray[ZSIDX_LOCATION] = NULL;
    827 			} else {
    828 				// When a new time zone ID, which is actually associated with a specific
    829 				// location, is added in tzdata, but the current CLDR data does not have
    830 				// the information yet, ICU creates a generic location string based on
    831 				// the ID.  This implementation supports canonical time zone round trip
    832 				// with format pattern "VVVV".  See #6602 for the details.
    833 				UnicodeString loc(utzid);
    834 				int32_t slashIdx = loc.lastIndexOf((UChar)0x2f);
    835 				if (slashIdx == -1) {
    836 					// A time zone ID without slash in the tz database is not
    837 					// associated with a specific location.  For instances,
    838 					// MET, CET, EET and WET fall into this category.
    839 					// In this case, we still use GMT format as fallback.
    840 					zstrarray[ZSIDX_LOCATION] = NULL;
    841 				} else {
    842 					FieldPosition fpos;
    843 					Formattable params[] = {
    844 						Formattable(loc)
    845 					};
    846 					regionFmt->format(params, 1, location, fpos, status);
    847 					if (U_FAILURE(status)) {
    848 						goto error_cleanup;
    849 					}
    850 					zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer();
    851 				}
    852 			}
    853 		}
    854 
    855 		UBool commonlyUsed = isCommonlyUsed(fZoneItem);
    856 
    857 		// Resolve metazones used by this zone
    858 		int32_t mzPartialLocIdx = 0;
    859 		const UVector *metazoneMappings = ZoneMeta::getMetazoneMappings(utzid);
    860 		if (metazoneMappings != NULL) {
    861 			for (int32_t i = 0; i < metazoneMappings->size(); i++) {
    862 				const OlsonToMetaMappingEntry *mzmap =
    863 						(const OlsonToMetaMappingEntry*)metazoneMappings->elementAt(i);
    864 				UnicodeString mzid(mzmap->mzid);
    865 				const ZoneStrings *mzStrings =
    866 					(const ZoneStrings*)uhash_get(fMzidToStrings, mzid.getTerminatedBuffer());
    867 				if (mzStrings == NULL) {
    868 					// If the metazone strings are not yet processed, do it now.
    869 					char mzidkey[ZID_KEY_MAX];
    870 					uprv_strcpy(mzidkey, gMetazoneIdPrefix);
    871 					u_UCharsToChars(mzmap->mzid, mzidkey + MZID_PREFIX_LEN, u_strlen(mzmap->mzid) + 1);
    872 					fMetazoneItem = ures_getByKeyWithFallback(fZoneStringsArray, mzidkey, fMetazoneItem, &status);
    873 					if (U_FAILURE(status)) {
    874 						// No resources available for this metazone
    875 						// Resource bundle will be cleaned up after end of the loop.
    876 						status = U_ZERO_ERROR;
    877 						continue;
    878 					}
    879 					UBool mzCommonlyUsed = isCommonlyUsed(fMetazoneItem);
    880 					mzstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(fMetazoneItem, gLongStandardTag);
    881 					mzstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(fMetazoneItem, gShortStandardTag);
    882 					mzstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(fMetazoneItem, gLongDaylightTag);
    883 					mzstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(fMetazoneItem, gShortDaylightTag);
    884 					mzstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(fMetazoneItem, gLongGenericTag);
    885 					mzstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(fMetazoneItem, gShortGenericTag);
    886 					mzstrarray[ZSIDX_LOCATION] = NULL;
    887 
    888 					int32_t lastNonNullIdx = ZSIDX_COUNT - 1;
    889 					while (lastNonNullIdx >= 0) {
    890 						if (mzstrarray[lastNonNullIdx] != NULL) {
    891 							break;
    892 						}
    893 						lastNonNullIdx--;
    894 					}
    895 					UnicodeString *strings_mz = NULL;
    896 					ZoneStrings *tmp_mzStrings = NULL;
    897 					if (lastNonNullIdx >= 0) {
    898 						// Create UnicodeString array and put strings to the zone string trie
    899 						strings_mz = new UnicodeString[lastNonNullIdx + 1];
    900 
    901 						UnicodeString preferredIdForLocale;
    902 						ZoneMeta::getZoneIdByMetazone(mzid, region, preferredIdForLocale);
    903 
    904 						for (int32_t typeidx = 0; typeidx <= lastNonNullIdx; typeidx++) {
    905 							if (mzstrarray[typeidx] != NULL) {
    906 								strings_mz[typeidx].setTo(TRUE, mzstrarray[typeidx], -1);
    907 
    908 								// Add a metazone string to the zone string trie
    909 								int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeidx);
    910 								ZoneStringInfo *zsinfo = new ZoneStringInfo(
    911 																preferredIdForLocale,
    912 																strings_mz[typeidx],
    913 																(TimeZoneTranslationType)type,
    914 																fStringPool,
    915 																status);
    916 								fZoneStringsTrie.put(strings_mz[typeidx], zsinfo, fStringPool, status);
    917 								if (U_FAILURE(status)) {
    918 									delete []strings_mz;
    919 									goto error_cleanup;
    920 								}
    921 							}
    922 						}
    923 						// Note: ZoneStrings constructor adopts and deletes the strings_mz array.
    924 						tmp_mzStrings = new ZoneStrings(strings_mz, lastNonNullIdx + 1,
    925 														mzCommonlyUsed, NULL, 0, 0, fStringPool, status);
    926 					} else {
    927 						// Create ZoneStrings with empty contents
    928 						tmp_mzStrings = new ZoneStrings(NULL, 0, FALSE, NULL, 0, 0, fStringPool, status);
    929 					}
    930 
    931 					UChar *umzid = const_cast<UChar *>(fStringPool.get(mzid, status));
    932 					uhash_put(fMzidToStrings, umzid, tmp_mzStrings, &status);
    933 					if (U_FAILURE(status)) {
    934 						goto error_cleanup;
    935 					}
    936 
    937 					mzStrings = tmp_mzStrings;
    938 				}
    939 
    940 				// Compose generic partial location format
    941 				UnicodeString lg;
    942 				UnicodeString sg;
    943 
    944 				mzStrings->getString(ZSIDX_LONG_GENERIC, lg);
    945 				mzStrings->getString(ZSIDX_SHORT_GENERIC, sg);
    946 
    947 				if (!lg.isEmpty() || !sg.isEmpty()) {
    948 					UBool addMzPartialLocationNames = TRUE;
    949 					for (int32_t j = 0; j < mzPartialLocIdx; j++) {
    950 						if (mzPartialLoc[j][0] == mzid) {
    951 							// already processed
    952 							addMzPartialLocationNames = FALSE;
    953 							break;
    954 						}
    955 					}
    956 					if (addMzPartialLocationNames) {
    957 						UnicodeString *locationPart = NULL;
    958 						// Check if the zone is the preferred zone for the territory associated with the zone
    959 						UnicodeString preferredID;
    960 						ZoneMeta::getZoneIdByMetazone(mzid, countryCode, preferredID);
    961 						if (utzid == preferredID) {
    962 							// Use country for the location
    963 							locationPart = &country;
    964 						} else {
    965 							// Use city for the location
    966 							locationPart = &city;
    967 						}
    968 						// Reset the partial location string array
    969 						mzPartialLoc[mzPartialLocIdx][0].setTo(mzid);
    970 						mzPartialLoc[mzPartialLocIdx][1].remove();
    971 						mzPartialLoc[mzPartialLocIdx][2].remove();
    972 						mzPartialLoc[mzPartialLocIdx][3].remove();
    973 
    974 						if (locationPart->length() != 0) {
    975 							FieldPosition fpos;
    976 							if (!lg.isEmpty()) {
    977 								Formattable params [] = {
    978 									Formattable(*locationPart),
    979 									Formattable(lg)
    980 								};
    981 								fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][1], fpos, status);
    982 							}
    983 							if (!sg.isEmpty()) {
    984 								Formattable params [] = {
    985 									Formattable(*locationPart),
    986 									Formattable(sg)
    987 								};
    988 								fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][2], fpos, status);
    989 								if (mzStrings->isShortFormatCommonlyUsed()) {
    990 									mzPartialLoc[mzPartialLocIdx][3].setTo(TRUE, gCommonlyUsedTrue, -1);
    991 								}
    992 							}
    993 							if (U_FAILURE(status)) {
    994 								goto error_cleanup;
    995 							}
    996 						}
    997 						mzPartialLocIdx++;
    998 					}
    999 				}
   1000 			}
   1001 		}
   1002 		// Collected names for a zone
   1003 
   1004 		// Create UnicodeString array for localized zone strings
   1005 		int32_t lastIdx = ZSIDX_COUNT - 1;
   1006 		while (lastIdx >= 0) {
   1007 			if (zstrarray[lastIdx] != NULL) {
   1008 				break;
   1009 			}
   1010 			lastIdx--;
   1011 		}
   1012 		UnicodeString *strings = NULL;
   1013 		int32_t stringsCount = lastIdx + 1;
   1014 
   1015 		if (stringsCount > 0) {
   1016 			strings = new UnicodeString[stringsCount];
   1017 			for (int32_t i = 0; i < stringsCount; i++) {
   1018 				if (zstrarray[i] != NULL) {
   1019 					strings[i].setTo(zstrarray[i], -1);
   1020 
   1021 					// Add names to the trie
   1022 					int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)i);
   1023 					ZoneStringInfo *zsinfo = new ZoneStringInfo(utzid,
   1024 																strings[i],
   1025 																(TimeZoneTranslationType)type,
   1026 																fStringPool,
   1027 																status);
   1028 					fZoneStringsTrie.put(strings[i], zsinfo, fStringPool, status);
   1029 					if (U_FAILURE(status)) {
   1030 						delete zsinfo;
   1031 						delete[] strings;
   1032 						goto error_cleanup;
   1033 					}
   1034 				}
   1035 			}
   1036 		}
   1037 
   1038 		// Create UnicodeString array for generic partial location strings
   1039 		UnicodeString **genericPartialLocationNames = NULL;
   1040 		int32_t genericPartialRowCount = mzPartialLocIdx;
   1041 		int32_t genericPartialColCount = 4;
   1042 
   1043 		if (genericPartialRowCount != 0) {
   1044 			genericPartialLocationNames =
   1045 					 (UnicodeString**)uprv_malloc(genericPartialRowCount * sizeof(UnicodeString*));
   1046 			if (genericPartialLocationNames == NULL) {
   1047 				status = U_MEMORY_ALLOCATION_ERROR;
   1048 				delete[] strings;
   1049 				goto error_cleanup;
   1050 			}
   1051 			for (int32_t i = 0; i < genericPartialRowCount; i++) {
   1052 				genericPartialLocationNames[i] = new UnicodeString[genericPartialColCount];
   1053 				for (int32_t j = 0; j < genericPartialColCount; j++) {
   1054 					genericPartialLocationNames[i][j].setTo(mzPartialLoc[i][j]);
   1055 					// Add names to the trie
   1056 					if ((j == 1 || j == 2) &&!genericPartialLocationNames[i][j].isEmpty()) {
   1057 						ZoneStringInfo *zsinfo;
   1058 						TimeZoneTranslationType type = (j == 1) ? GENERIC_LONG : GENERIC_SHORT;
   1059 						zsinfo = new ZoneStringInfo(utzid, genericPartialLocationNames[i][j], type,
   1060 													fStringPool, status);
   1061 						fZoneStringsTrie.put(genericPartialLocationNames[i][j], zsinfo, fStringPool, status);
   1062 						if (U_FAILURE(status)) {
   1063 							delete[] genericPartialLocationNames[i];
   1064 							uprv_free(genericPartialLocationNames);
   1065 							delete[] strings;
   1066 							goto error_cleanup;
   1067 						}
   1068 					}
   1069 				}
   1070 			}
   1071 		}
   1072 
   1073 		// Finally, create ZoneStrings instance and put it into the tzidToStinrgs map
   1074 		ZoneStrings *zstrings = new ZoneStrings(strings, stringsCount, commonlyUsed,
   1075 												genericPartialLocationNames, genericPartialRowCount,
   1076 												genericPartialColCount, fStringPool, status);
   1077 
   1078 		UChar *uutzid = const_cast<UChar *>(fStringPool.get(utzid, status));
   1079 		uhash_put(fTzidToStrings, uutzid, zstrings, &status);
   1080 		if (U_FAILURE(status)) {
   1081 			delete zstrings;
   1082 			goto error_cleanup;
   1083 		}
   1084 	}
   1085 
   1086 error_cleanup:
   1087     if (fallbackFmt != NULL) {
   1088         delete fallbackFmt;
   1089     }
   1090     if (regionFmt != NULL) {
   1091         delete regionFmt;
   1092     }
   1093     //	fStringPool.freeze();
   1094 }
   1095 
   1096 void
   1097 ZoneStringFormat::loadFull(UErrorCode &status)
   1098 {
   1099     if (U_FAILURE(status)) {
   1100         return;
   1101     }
   1102 	if (fIsFullyLoaded) {
   1103 		return;
   1104 	}
   1105 
   1106 	umtx_lock(&ZoneStringFormatMutex);
   1107 
   1108 	if (fZoneStringsArray == NULL) {
   1109 		fZoneStringsArray = ures_open(U_ICUDATA_ZONE, fLocale.getName(), &status);
   1110 		fZoneStringsArray = ures_getByKeyWithFallback(fZoneStringsArray, gZoneStringsTag, fZoneStringsArray, &status);
   1111 		if (U_FAILURE(status)) {
   1112 			// If no locale bundles are available, zoneStrings will be null.
   1113 			// We still want to go through the rest of zone strings initialization,
   1114 			// because generic location format is generated from tzid for the case.
   1115 			// The rest of code should work even zoneStrings is null.
   1116 			status = U_ZERO_ERROR;
   1117 			ures_close(fZoneStringsArray);
   1118 			fZoneStringsArray = NULL;
   1119 		}
   1120     }
   1121 
   1122     StringEnumeration *tzids = NULL;
   1123 
   1124     tzids = TimeZone::createEnumeration();
   1125     const char *tzid;
   1126     while ((tzid = tzids->next(NULL, status))) {
   1127         if (U_FAILURE(status)) {
   1128             goto error_cleanup;
   1129         }
   1130         // Skip non-canonical IDs
   1131         UnicodeString utzid(tzid, -1, US_INV);
   1132         UnicodeString canonicalID;
   1133         TimeZone::getCanonicalID(utzid, canonicalID, status);
   1134         if (U_FAILURE(status)) {
   1135             // Ignore unknown ID - we should not get here, but just in case.
   1136             status = U_ZERO_ERROR;
   1137             continue;
   1138         }
   1139 
   1140 		if (U_SUCCESS(status)) {
   1141 			if (uhash_count(fTzidToStrings) > 0) {
   1142 				ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer());
   1143 				if (zstrings != NULL) {
   1144 					continue;	//	We already about this one
   1145 				}
   1146 			}
   1147 		}
   1148 
   1149 		addSingleZone(canonicalID, status);
   1150 
   1151         if (U_FAILURE(status)) {
   1152 			goto error_cleanup;
   1153 		}
   1154     }
   1155 
   1156 	fIsFullyLoaded = TRUE;
   1157 
   1158 error_cleanup:
   1159     if (tzids != NULL) {
   1160         delete tzids;
   1161     }
   1162     fStringPool.freeze();
   1163 
   1164 	umtx_unlock(&ZoneStringFormatMutex);
   1165 }
   1166 
   1167 
   1168 ZoneStringFormat::~ZoneStringFormat() {
   1169     uhash_close(fTzidToStrings);
   1170     uhash_close(fMzidToStrings);
   1171     ures_close(fZoneItem);
   1172     ures_close(fMetazoneItem);
   1173     ures_close(fZoneStringsArray);
   1174 }
   1175 
   1176 SafeZoneStringFormatPtr*
   1177 ZoneStringFormat::getZoneStringFormat(const Locale& locale, UErrorCode &status) {
   1178     umtx_lock(&gZSFCacheLock);
   1179     if (gZoneStringFormatCache == NULL) {
   1180         gZoneStringFormatCache = new ZSFCache(10 /* capacity */);
   1181         ucln_i18n_registerCleanup(UCLN_I18N_ZSFORMAT, zoneStringFormat_cleanup);
   1182     }
   1183     umtx_unlock(&gZSFCacheLock);
   1184 
   1185     return gZoneStringFormatCache->get(locale, status);
   1186 }
   1187 
   1188 
   1189 UnicodeString**
   1190 ZoneStringFormat::createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const {
   1191     if (U_FAILURE(status)) {
   1192         return NULL;
   1193     }
   1194 	ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this);
   1195 	nonConstThis->loadFull(status);
   1196 
   1197     UnicodeString **result = NULL;
   1198     rowCount = 0;
   1199     colCount = 0;
   1200 
   1201     // Collect canonical time zone IDs
   1202     UVector canonicalIDs(uhash_deleteUnicodeString, uhash_compareUnicodeString, status);
   1203     if (U_FAILURE(status)) {
   1204         return NULL;
   1205     }
   1206     StringEnumeration *tzids = TimeZone::createEnumeration();
   1207     const UChar *tzid;
   1208     while ((tzid = tzids->unext(NULL, status))) {
   1209         if (U_FAILURE(status)) {
   1210             delete tzids;
   1211             return NULL;
   1212         }
   1213         UnicodeString utzid(tzid);
   1214         UnicodeString canonicalID;
   1215         TimeZone::getCanonicalID(UnicodeString(tzid), canonicalID, status);
   1216         if (U_FAILURE(status)) {
   1217             // Ignore unknown ID - we should not get here, but just in case.
   1218             status = U_ZERO_ERROR;
   1219             continue;
   1220         }
   1221         if (utzid == canonicalID) {
   1222             canonicalIDs.addElement(new UnicodeString(utzid), status);
   1223             if (U_FAILURE(status)) {
   1224                 delete tzids;
   1225                 return NULL;
   1226             }
   1227         }
   1228     }
   1229     delete tzids;
   1230 
   1231     // Allocate array
   1232     result = (UnicodeString**)uprv_malloc(canonicalIDs.size() * sizeof(UnicodeString*));
   1233     if (result == NULL) {
   1234         status = U_MEMORY_ALLOCATION_ERROR;
   1235         return NULL;
   1236     }
   1237     for (int32_t i = 0; i < canonicalIDs.size(); i++) {
   1238         result[i] = new UnicodeString[8];
   1239         UnicodeString *id = (UnicodeString*)canonicalIDs.elementAt(i);
   1240         result[i][0].setTo(*id);
   1241         getLongStandard(*id, date, result[i][1]);
   1242         getShortStandard(*id, date, FALSE, result[i][2]);
   1243         getLongDaylight(*id, date, result[i][3]);
   1244         getShortDaylight(*id, date, FALSE, result[i][4]);
   1245         getGenericLocation(*id, result[i][5]);
   1246         getLongGenericNonLocation(*id, date, result[i][6]);
   1247         getShortGenericNonLocation(*id, date, FALSE, result[i][7]);
   1248     }
   1249 
   1250     rowCount = canonicalIDs.size();
   1251     colCount = 8;
   1252     return result;
   1253 }
   1254 
   1255 UnicodeString&
   1256 ZoneStringFormat::getSpecificLongString(const Calendar &cal, UnicodeString &result,
   1257                                         UErrorCode &status) const {
   1258     result.remove();
   1259     if (U_FAILURE(status)) {
   1260         return result;
   1261     }
   1262     UnicodeString tzid;
   1263     cal.getTimeZone().getID(tzid);
   1264     UDate date = cal.getTime(status);
   1265     if (cal.get(UCAL_DST_OFFSET, status) == 0) {
   1266         return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /*not used*/, result);
   1267     } else {
   1268         return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /*not used*/, result);
   1269     }
   1270 }
   1271 
   1272 UnicodeString&
   1273 ZoneStringFormat::getSpecificShortString(const Calendar &cal, UBool commonlyUsedOnly,
   1274                                          UnicodeString &result, UErrorCode &status) const {
   1275     result.remove();
   1276     if (U_FAILURE(status)) {
   1277         return result;
   1278     }
   1279     UnicodeString tzid;
   1280     cal.getTimeZone().getID(tzid);
   1281     UDate date = cal.getTime(status);
   1282     if (cal.get(UCAL_DST_OFFSET, status) == 0) {
   1283         return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result);
   1284     } else {
   1285         return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result);
   1286     }
   1287 }
   1288 
   1289 UnicodeString&
   1290 ZoneStringFormat::getGenericLongString(const Calendar &cal, UnicodeString &result,
   1291                                        UErrorCode &status) const {
   1292     return getGenericString(cal, FALSE /*long*/, FALSE /* not used */, result, status);
   1293 }
   1294 
   1295 UnicodeString&
   1296 ZoneStringFormat::getGenericShortString(const Calendar &cal, UBool commonlyUsedOnly,
   1297                                         UnicodeString &result, UErrorCode &status) const {
   1298     return getGenericString(cal, TRUE /*short*/, commonlyUsedOnly, result, status);
   1299 }
   1300 
   1301 UnicodeString&
   1302 ZoneStringFormat::getGenericLocationString(const Calendar &cal, UnicodeString &result,
   1303                                            UErrorCode &status) const {
   1304     UnicodeString tzid;
   1305     cal.getTimeZone().getID(tzid);
   1306     UDate date = cal.getTime(status);
   1307     return getString(tzid, ZSIDX_LOCATION, date, FALSE /*not used*/, result);
   1308 }
   1309 
   1310 const ZoneStringInfo*
   1311 ZoneStringFormat::findSpecificLong(const UnicodeString &text, int32_t start,
   1312                                    int32_t &matchLength, UErrorCode &status) const {
   1313     return find(text, start, STANDARD_LONG | DAYLIGHT_LONG, matchLength, status);
   1314 }
   1315 
   1316 const ZoneStringInfo*
   1317 ZoneStringFormat::findSpecificShort(const UnicodeString &text, int32_t start,
   1318                                     int32_t &matchLength, UErrorCode &status) const {
   1319     return find(text, start, STANDARD_SHORT | DAYLIGHT_SHORT, matchLength, status);
   1320 }
   1321 
   1322 const ZoneStringInfo*
   1323 ZoneStringFormat::findGenericLong(const UnicodeString &text, int32_t start,
   1324                                   int32_t &matchLength, UErrorCode &status) const {
   1325     return find(text, start, GENERIC_LONG | STANDARD_LONG | LOCATION, matchLength, status);
   1326 }
   1327 
   1328 const ZoneStringInfo*
   1329 ZoneStringFormat::findGenericShort(const UnicodeString &text, int32_t start,
   1330                                    int32_t &matchLength, UErrorCode &status) const {
   1331     return find(text, start, GENERIC_SHORT | STANDARD_SHORT | LOCATION, matchLength, status);
   1332 }
   1333 
   1334 const ZoneStringInfo*
   1335 ZoneStringFormat::findGenericLocation(const UnicodeString &text, int32_t start,
   1336                                       int32_t &matchLength, UErrorCode &status) const {
   1337     return find(text, start, LOCATION, matchLength, status);
   1338 }
   1339 
   1340 UnicodeString&
   1341 ZoneStringFormat::getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date,
   1342                             UBool commonlyUsedOnly, UnicodeString& result) const {
   1343     UErrorCode status = U_ZERO_ERROR;
   1344     result.remove();
   1345 	if (!fIsFullyLoaded) {
   1346 		// Lazy loading
   1347 		ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this);
   1348 		nonConstThis->loadZone(tzid, status);
   1349 	}
   1350 
   1351     // ICU's own array does not have entries for aliases
   1352     UnicodeString canonicalID;
   1353     TimeZone::getCanonicalID(tzid, canonicalID, status);
   1354     if (U_FAILURE(status)) {
   1355         // Unknown ID, but users might have their own data.
   1356         canonicalID.setTo(tzid);
   1357     }
   1358 
   1359     if (uhash_count(fTzidToStrings) > 0) {
   1360         ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer());
   1361         if (zstrings != NULL) {
   1362             switch (typeIdx) {
   1363                 case ZSIDX_LONG_STANDARD:
   1364                 case ZSIDX_LONG_DAYLIGHT:
   1365                 case ZSIDX_LONG_GENERIC:
   1366                 case ZSIDX_LOCATION:
   1367                     zstrings->getString(typeIdx, result);
   1368                     break;
   1369                 case ZSIDX_SHORT_STANDARD:
   1370                 case ZSIDX_SHORT_DAYLIGHT:
   1371                 case ZSIDX_COUNT: //added to avoid warning
   1372                 case ZSIDX_SHORT_GENERIC:
   1373                     if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
   1374                         zstrings->getString(typeIdx, result);
   1375                     }
   1376                     break;
   1377                 default:
   1378                     break;
   1379             }
   1380         }
   1381     }
   1382     if (result.isEmpty() && uhash_count(fMzidToStrings) > 0 && typeIdx != ZSIDX_LOCATION) {
   1383         // Try metazone
   1384         UnicodeString mzid;
   1385         ZoneMeta::getMetazoneID(canonicalID, date, mzid);
   1386         if (!mzid.isEmpty()) {
   1387             ZoneStrings *mzstrings = (ZoneStrings*)uhash_get(fMzidToStrings, mzid.getTerminatedBuffer());
   1388             if (mzstrings != NULL) {
   1389                 switch (typeIdx) {
   1390                     case ZSIDX_LONG_STANDARD:
   1391                     case ZSIDX_LONG_DAYLIGHT:
   1392                     case ZSIDX_LONG_GENERIC:
   1393                     case ZSIDX_LOCATION:
   1394                         mzstrings->getString(typeIdx, result);
   1395                         break;
   1396                     case ZSIDX_SHORT_STANDARD:
   1397                     case ZSIDX_SHORT_DAYLIGHT:
   1398                     case ZSIDX_COUNT: //added to avoid warning
   1399                     case ZSIDX_SHORT_GENERIC:
   1400                         if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
   1401                             mzstrings->getString(typeIdx, result);
   1402                         }
   1403                         break;
   1404                     default:
   1405                         break;
   1406                 }
   1407             }
   1408         }
   1409     }
   1410     return result;
   1411 }
   1412 
   1413 UnicodeString&
   1414 ZoneStringFormat::getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly,
   1415                                    UnicodeString &result, UErrorCode &status) const {
   1416     result.remove();
   1417     UDate time = cal.getTime(status);
   1418     if (U_FAILURE(status)) {
   1419         return result;
   1420     }
   1421     const TimeZone &tz = cal.getTimeZone();
   1422     UnicodeString tzid;
   1423     tz.getID(tzid);
   1424 
   1425 	if (!fIsFullyLoaded) {
   1426 		// Lazy loading
   1427 		ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this);
   1428 		nonConstThis->loadZone(tzid, status);
   1429 	}
   1430 
   1431     // ICU's own array does not have entries for aliases
   1432     UnicodeString canonicalID;
   1433     TimeZone::getCanonicalID(tzid, canonicalID, status);
   1434     if (U_FAILURE(status)) {
   1435         // Unknown ID, but users might have their own data.
   1436         status = U_ZERO_ERROR;
   1437         canonicalID.setTo(tzid);
   1438     }
   1439 
   1440     ZoneStrings *zstrings = NULL;
   1441     if (uhash_count(fTzidToStrings) > 0) {
   1442         zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer());
   1443         if (zstrings != NULL) {
   1444             if (isShort) {
   1445                 if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
   1446                     zstrings->getString(ZSIDX_SHORT_GENERIC, result);
   1447                 }
   1448             } else {
   1449                 zstrings->getString(ZSIDX_LONG_GENERIC, result);
   1450             }
   1451         }
   1452     }
   1453     if (result.isEmpty() && uhash_count(fMzidToStrings) > 0) {
   1454         // try metazone
   1455         int32_t raw, sav;
   1456         UnicodeString mzid;
   1457         ZoneMeta::getMetazoneID(canonicalID, time, mzid);
   1458         if (!mzid.isEmpty()) {
   1459             UBool useStandard = FALSE;
   1460             sav = cal.get(UCAL_DST_OFFSET, status);
   1461             if (U_FAILURE(status)) {
   1462                 return result;
   1463             }
   1464             if (sav == 0) {
   1465                 useStandard = TRUE;
   1466                 // Check if the zone actually uses daylight saving time around the time
   1467                 TimeZone *tmptz = tz.clone();
   1468                 BasicTimeZone *btz = NULL;
   1469                 if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL
   1470                     || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL
   1471                     || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL
   1472                     || dynamic_cast<VTimeZone *>(tmptz) != NULL) {
   1473                     btz = (BasicTimeZone*)tmptz;
   1474                 }
   1475 
   1476                 if (btz != NULL) {
   1477                     TimeZoneTransition before;
   1478                     UBool beforTrs = btz->getPreviousTransition(time, TRUE, before);
   1479                     if (beforTrs
   1480                             && (time - before.getTime() < kDstCheckRange)
   1481                             && before.getFrom()->getDSTSavings() != 0) {
   1482                         useStandard = FALSE;
   1483                     } else {
   1484                         TimeZoneTransition after;
   1485                         UBool afterTrs = btz->getNextTransition(time, FALSE, after);
   1486                         if (afterTrs
   1487                                 && (after.getTime() - time < kDstCheckRange)
   1488                                 && after.getTo()->getDSTSavings() != 0) {
   1489                             useStandard = FALSE;
   1490                         }
   1491                     }
   1492                 } else {
   1493                     // If not BasicTimeZone... only if the instance is not an ICU's implementation.
   1494                     // We may get a wrong answer in edge case, but it should practically work OK.
   1495                     tmptz->getOffset(time - kDstCheckRange, FALSE, raw, sav, status);
   1496                     if (sav != 0) {
   1497                         useStandard = FALSE;
   1498                     } else {
   1499                         tmptz->getOffset(time + kDstCheckRange, FALSE, raw, sav, status);
   1500                         if (sav != 0){
   1501                             useStandard = FALSE;
   1502                         }
   1503                     }
   1504                     if (U_FAILURE(status)) {
   1505                         delete tmptz;
   1506                         result.remove();
   1507                         return result;
   1508                     }
   1509                 }
   1510                 delete tmptz;
   1511             }
   1512             if (useStandard) {
   1513                 getString(canonicalID, (isShort ? ZSIDX_SHORT_STANDARD : ZSIDX_LONG_STANDARD),
   1514                     time, commonlyUsedOnly, result);
   1515 
   1516                 // Note:
   1517                 // In CLDR 1.5.1, a same localization is used for both generic and standard
   1518                 // for some metazones in some locales.  This is actually data bugs and should
   1519                 // be resolved in later versions of CLDR.  For now, we check if the standard
   1520                 // name is different from its generic name below.
   1521                 if (!result.isEmpty()) {
   1522                     UnicodeString genericNonLocation;
   1523                     getString(canonicalID, (isShort ? ZSIDX_SHORT_GENERIC : ZSIDX_LONG_GENERIC),
   1524                         time, commonlyUsedOnly, genericNonLocation);
   1525                     if (!genericNonLocation.isEmpty() && result == genericNonLocation) {
   1526                         result.remove();
   1527                     }
   1528                 }
   1529             }
   1530             if (result.isEmpty()) {
   1531                 ZoneStrings *mzstrings = (ZoneStrings*)uhash_get(fMzidToStrings, mzid.getTerminatedBuffer());
   1532                 if (mzstrings != NULL) {
   1533                     if (isShort) {
   1534                         if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
   1535                             mzstrings->getString(ZSIDX_SHORT_GENERIC, result);
   1536                         }
   1537                     } else {
   1538                         mzstrings->getString(ZSIDX_LONG_GENERIC, result);
   1539                     }
   1540                 }
   1541                 if (!result.isEmpty()) {
   1542                     // Check if the offsets at the given time matches the preferred zone's offsets
   1543                     UnicodeString preferredId;
   1544                     UnicodeString region;
   1545                     ZoneMeta::getZoneIdByMetazone(mzid, getRegion(region), preferredId);
   1546                     if (canonicalID != preferredId) {
   1547                         // Check if the offsets at the given time are identical with the preferred zone
   1548                         raw = cal.get(UCAL_ZONE_OFFSET, status);
   1549                         if (U_FAILURE(status)) {
   1550                             result.remove();
   1551                             return result;
   1552                         }
   1553                         TimeZone *preferredZone = TimeZone::createTimeZone(preferredId);
   1554                         int32_t prfRaw, prfSav;
   1555                         // Check offset in preferred time zone with wall time.
   1556                         // With getOffset(time, false, preferredOffsets),
   1557                         // you may get incorrect results because of time overlap at DST->STD
   1558                         // transition.
   1559                         preferredZone->getOffset(time + raw + sav, TRUE, prfRaw, prfSav, status);
   1560                         delete preferredZone;
   1561 
   1562                         if (U_FAILURE(status)) {
   1563                             result.remove();
   1564                             return result;
   1565                         }
   1566                         if ((raw != prfRaw || sav != prfSav) && zstrings != NULL) {
   1567                             // Use generic partial location string as fallback
   1568                             zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
   1569                         }
   1570                     }
   1571                 }
   1572             }
   1573         }
   1574     }
   1575     if (result.isEmpty()) {
   1576         // Use location format as the final fallback
   1577         getString(canonicalID, ZSIDX_LOCATION, time, FALSE /*not used*/, result);
   1578     }
   1579 
   1580     return result;
   1581 }
   1582 
   1583 UnicodeString&
   1584 ZoneStringFormat::getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort,
   1585                                                   UDate date, UBool commonlyUsedOnly, UnicodeString &result) const {
   1586     UErrorCode status = U_ZERO_ERROR;
   1587     result.remove();
   1588 	if (!fIsFullyLoaded) {
   1589 		// Lazy loading
   1590 		ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this);
   1591 		nonConstThis->loadZone(tzid, status);
   1592 	}
   1593 
   1594     if (uhash_count(fTzidToStrings) <= 0) {
   1595         return result;
   1596     }
   1597 
   1598     UnicodeString canonicalID;
   1599     TimeZone::getCanonicalID(tzid, canonicalID, status);
   1600     if (U_FAILURE(status)) {
   1601         // Unknown ID, so no corresponding meta data.
   1602         return result;
   1603     }
   1604 
   1605     UnicodeString mzid;
   1606     ZoneMeta::getMetazoneID(canonicalID, date, mzid);
   1607 
   1608     if (!mzid.isEmpty()) {
   1609         ZoneStrings *zstrings = (ZoneStrings*)uhash_get(fTzidToStrings, canonicalID.getTerminatedBuffer());
   1610         if (zstrings != NULL) {
   1611             zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
   1612         }
   1613     }
   1614     return result;
   1615 }
   1616 
   1617 // This method does lazy zone string loading
   1618 const ZoneStringInfo*
   1619 ZoneStringFormat::find(const UnicodeString &text, int32_t start, int32_t types,
   1620                        int32_t &matchLength, UErrorCode &status) const {
   1621 
   1622     if (U_FAILURE(status)) {
   1623         return NULL;
   1624     }
   1625 
   1626 	const ZoneStringInfo *	result = subFind(text, start, types, matchLength, status);
   1627 	if (fIsFullyLoaded) {
   1628 		return result;
   1629 	}
   1630 	// When zone string data is partially loaded,
   1631 	// this method return the result only when
   1632 	// the input text is fully consumed.
   1633 	if (result != NULL) {
   1634 		UnicodeString tmpString;
   1635 		matchLength = (result->getString(tmpString)).length();
   1636 		if (text.length() - start == matchLength) {
   1637 			return result;
   1638 		}
   1639 	}
   1640 
   1641 	// Now load all zone strings
   1642 	ZoneStringFormat *nonConstThis = const_cast<ZoneStringFormat *>(this);
   1643 	nonConstThis->loadFull(status);
   1644 
   1645 	return subFind(text, start, types, matchLength, status);
   1646 }
   1647 
   1648 
   1649 /*
   1650  * Find a prefix matching time zone for the given zone string types.
   1651  * @param text The text contains a time zone string
   1652  * @param start The start index within the text
   1653  * @param types The bit mask representing a set of requested types
   1654  * @return If any zone string matched for the requested types, returns a
   1655  * ZoneStringInfo for the longest match.  If no matches are found for
   1656  * the requested types, returns a ZoneStringInfo for the longest match
   1657  * for any other types.  If nothing matches at all, returns null.
   1658  */
   1659 const ZoneStringInfo*
   1660 ZoneStringFormat::subFind(const UnicodeString &text, int32_t start, int32_t types,
   1661                        int32_t &matchLength, UErrorCode &status) const {
   1662     matchLength = 0;
   1663     if (U_FAILURE(status)) {
   1664         return NULL;
   1665     }
   1666     if (fZoneStringsTrie.isEmpty()) {
   1667         return NULL;
   1668     }
   1669 
   1670     const ZoneStringInfo *result = NULL;
   1671     const ZoneStringInfo *fallback = NULL;
   1672     int32_t fallbackMatchLen = 0;
   1673 
   1674     ZoneStringSearchResultHandler handler(status);
   1675     fZoneStringsTrie.search(text, start, (TextTrieMapSearchResultHandler*)&handler, status);
   1676     if (U_SUCCESS(status)) {
   1677         int32_t numMatches = handler.countMatches();
   1678         for (int32_t i = 0; i < numMatches; i++) {
   1679             int32_t tmpMatchLen = 0; // init. output only param to silence gcc
   1680             const ZoneStringInfo *tmp = handler.getMatch(i, tmpMatchLen);
   1681             if ((types & tmp->fType) != 0) {
   1682                 if (result == NULL || matchLength < tmpMatchLen) {
   1683                     result = tmp;
   1684                     matchLength = tmpMatchLen;
   1685                 } else if (matchLength == tmpMatchLen) {
   1686                     // Tie breaker - there are some examples that a
   1687                     // long standard name is identical with a location
   1688                     // name - for example, "Uruguay Time".  In this case,
   1689                     // we interpret it as generic, not specific.
   1690                     if (tmp->isGeneric() && !result->isGeneric()) {
   1691                         result = tmp;
   1692                     }
   1693                 }
   1694             } else if (result == NULL) {
   1695                 if (fallback == NULL || fallbackMatchLen < tmpMatchLen) {
   1696                     fallback = tmp;
   1697                     fallbackMatchLen = tmpMatchLen;
   1698                 } else if (fallbackMatchLen == tmpMatchLen) {
   1699                     if (tmp->isGeneric() && !fallback->isGeneric()) {
   1700                         fallback = tmp;
   1701                     }
   1702                 }
   1703             }
   1704         }
   1705         if (result == NULL && fallback != NULL) {
   1706             result = fallback;
   1707             matchLength = fallbackMatchLen;
   1708         }
   1709     }
   1710     return result;
   1711 }
   1712 
   1713 
   1714 UnicodeString&
   1715 ZoneStringFormat::getRegion(UnicodeString &region) const {
   1716     const char* country = fLocale.getCountry();
   1717     // TODO: Utilize addLikelySubtag in Locale to resolve default region
   1718     // when the implementation is ready.
   1719     region.setTo(UnicodeString(country, -1, US_INV));
   1720     return region;
   1721 }
   1722 
   1723 MessageFormat*
   1724 ZoneStringFormat::getFallbackFormat(const Locale &locale, UErrorCode &status) {
   1725     if (U_FAILURE(status)) {
   1726         return NULL;
   1727     }
   1728     UnicodeString pattern(TRUE, gDefFallbackPattern, -1);
   1729     UResourceBundle *zoneStringsArray = ures_open(U_ICUDATA_ZONE, locale.getName(), &status);
   1730     zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
   1731     int32_t len;
   1732     const UChar *flbkfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gFallbackFormatTag, &len, &status);
   1733     if (U_SUCCESS(status)) {
   1734         pattern.setTo(flbkfmt);
   1735     } else {
   1736         status = U_ZERO_ERROR;
   1737     }
   1738     ures_close(zoneStringsArray);
   1739 
   1740     MessageFormat *fallbackFmt = new MessageFormat(pattern, status);
   1741     return fallbackFmt;
   1742 }
   1743 
   1744 MessageFormat*
   1745 ZoneStringFormat::getRegionFormat(const Locale& locale, UErrorCode &status) {
   1746     if (U_FAILURE(status)) {
   1747         return NULL;
   1748     }
   1749     UnicodeString pattern(TRUE, gDefRegionPattern, -1);
   1750     UResourceBundle *zoneStringsArray = ures_open(U_ICUDATA_ZONE, locale.getName(), &status);
   1751     zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
   1752     int32_t len;
   1753     const UChar *regionfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gRegionFormatTag, &len, &status);
   1754     if (U_SUCCESS(status)) {
   1755         pattern.setTo(regionfmt);
   1756     } else {
   1757         status = U_ZERO_ERROR;
   1758     }
   1759     ures_close(zoneStringsArray);
   1760 
   1761     MessageFormat *regionFmt = new MessageFormat(pattern, status);
   1762     return regionFmt;
   1763 }
   1764 
   1765 const UChar*
   1766 ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key) {
   1767     const UChar *str = NULL;
   1768     if (zoneitem != NULL) {
   1769         UErrorCode status = U_ZERO_ERROR;
   1770         int32_t len;
   1771         str = ures_getStringByKeyWithFallback(zoneitem, key, &len, &status);
   1772         str = fStringPool.adopt(str, status);
   1773         if (U_FAILURE(status)) {
   1774             str = NULL;
   1775         }
   1776     }
   1777     return str;
   1778 }
   1779 
   1780 UBool
   1781 ZoneStringFormat::isCommonlyUsed(const UResourceBundle *zoneitem) {
   1782     if (zoneitem == NULL) {
   1783         return TRUE;
   1784     }
   1785 
   1786     UBool commonlyUsed = FALSE;
   1787     UErrorCode status = U_ZERO_ERROR;
   1788     UResourceBundle *cuRes = ures_getByKey(zoneitem, gCommonlyUsedTag, NULL, &status);
   1789     int32_t cuValue = ures_getInt(cuRes, &status);
   1790     if (U_SUCCESS(status)) {
   1791         if (cuValue == 1) {
   1792             commonlyUsed = TRUE;
   1793         }
   1794     }
   1795     ures_close(cuRes);
   1796     return commonlyUsed;
   1797 }
   1798 
   1799 UnicodeString&
   1800 ZoneStringFormat::getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale, UnicodeString &displayCountry) {
   1801     // We do not want to use display country names only from the target language bundle
   1802     // Note: we should do this in better way.
   1803     displayCountry.remove();
   1804     int32_t ccLen = countryCode.length();
   1805     if (ccLen > 0 && ccLen < ULOC_COUNTRY_CAPACITY) {
   1806         UErrorCode status = U_ZERO_ERROR;
   1807         UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
   1808         if (U_SUCCESS(status)) {
   1809             const char *bundleLocStr = ures_getLocale(localeBundle, &status);
   1810             if (U_SUCCESS(status) && uprv_strlen(bundleLocStr) > 0) {
   1811                 Locale bundleLoc(bundleLocStr);
   1812                 if (uprv_strcmp(bundleLocStr, "root") != 0 &&
   1813                     uprv_strcmp(bundleLoc.getLanguage(), locale.getLanguage()) == 0) {
   1814                     // Create a fake locale strings
   1815                     char tmpLocStr[ULOC_COUNTRY_CAPACITY + 3];
   1816                     uprv_strcpy(tmpLocStr, "xx_");
   1817                     u_UCharsToChars(countryCode.getBuffer(), &tmpLocStr[3], ccLen);
   1818                     tmpLocStr[3 + ccLen] = 0;
   1819 
   1820                     Locale tmpLoc(tmpLocStr);
   1821                     tmpLoc.getDisplayCountry(locale, displayCountry);
   1822                 }
   1823             }
   1824         }
   1825         ures_close(localeBundle);
   1826     }
   1827     if (displayCountry.isEmpty()) {
   1828         // Use the country code as the fallback
   1829         displayCountry.setTo(countryCode);
   1830     }
   1831     return displayCountry;
   1832 }
   1833 
   1834 // ----------------------------------------------------------------------------
   1835 /*
   1836  * ZoneStrings constructor adopts (and promptly copies and deletes)
   1837  *    the input UnicodeString arrays.
   1838  */
   1839 ZoneStrings::ZoneStrings(UnicodeString *strings,
   1840                          int32_t stringsCount,
   1841                          UBool commonlyUsed,
   1842                          UnicodeString **genericPartialLocationStrings,
   1843                          int32_t genericRowCount,
   1844                          int32_t genericColCount,
   1845                          ZSFStringPool &sp,
   1846                          UErrorCode &status)
   1847 :   fStrings(NULL),
   1848     fStringsCount(stringsCount),
   1849     fIsCommonlyUsed(commonlyUsed),
   1850     fGenericPartialLocationStrings(NULL),
   1851     fGenericPartialLocationRowCount(genericRowCount),
   1852     fGenericPartialLocationColCount(genericColCount)
   1853 {
   1854     if (U_FAILURE(status)) {
   1855         return;
   1856     }
   1857     int32_t i, j;
   1858     if (strings != NULL) {
   1859         fStrings = (const UChar **)uprv_malloc(sizeof(const UChar **) * stringsCount);
   1860         if (fStrings == NULL) {
   1861             status = U_MEMORY_ALLOCATION_ERROR;
   1862             return;
   1863         }
   1864         for (i=0; i<fStringsCount; i++) {
   1865             fStrings[i] = sp.get(strings[i], status);
   1866         }
   1867         delete[] strings;
   1868     }
   1869     if (genericPartialLocationStrings != NULL) {
   1870         fGenericPartialLocationStrings =
   1871             (const UChar ***)uprv_malloc(sizeof(const UChar ***) * genericRowCount);
   1872         if (fGenericPartialLocationStrings == NULL) {
   1873             status = U_MEMORY_ALLOCATION_ERROR;
   1874             return;
   1875         }
   1876         for (i=0; i < fGenericPartialLocationRowCount; i++) {
   1877             fGenericPartialLocationStrings[i] =
   1878                 (const UChar **)uprv_malloc(sizeof(const UChar **) * genericColCount);
   1879             if (fGenericPartialLocationStrings[i] == NULL) {
   1880                 status = U_MEMORY_ALLOCATION_ERROR;
   1881                 continue;   // Continue so that fGenericPartialLocationStrings will not contain uninitialized junk,
   1882             }               //   which would crash the destructor.
   1883             for (j=0; j<genericColCount; j++) {
   1884                 fGenericPartialLocationStrings[i][j] =
   1885                         sp.get(genericPartialLocationStrings[i][j], status);
   1886             }
   1887             delete[] genericPartialLocationStrings[i];
   1888         }
   1889         uprv_free(genericPartialLocationStrings);
   1890     }
   1891 }
   1892 
   1893 ZoneStrings::~ZoneStrings() {
   1894     uprv_free(fStrings);
   1895     if (fGenericPartialLocationStrings != NULL) {
   1896         for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
   1897             uprv_free(fGenericPartialLocationStrings[i]);
   1898         }
   1899         uprv_free(fGenericPartialLocationStrings);
   1900     }
   1901 }
   1902 
   1903 
   1904 UnicodeString&
   1905 ZoneStrings::getString(int32_t typeIdx, UnicodeString &result) const {
   1906     if (typeIdx >= 0 && typeIdx < fStringsCount) {
   1907         result.setTo(fStrings[typeIdx], -1);
   1908     } else {
   1909         result.remove();
   1910     }
   1911     return result;
   1912 }
   1913 
   1914 UnicodeString&
   1915 ZoneStrings::getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort,
   1916                                         UBool commonlyUsedOnly, UnicodeString &result) const {
   1917     UBool isSet = FALSE;
   1918     if (fGenericPartialLocationColCount >= 2) {
   1919         for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
   1920             if (mzid.compare(fGenericPartialLocationStrings[i][0], -1) == 0) {
   1921                 if (isShort) {
   1922                     if (fGenericPartialLocationColCount >= 3) {
   1923                         if (!commonlyUsedOnly ||
   1924                             fGenericPartialLocationColCount == 3 ||
   1925                             fGenericPartialLocationStrings[i][3][0] != 0) {
   1926                                 result.setTo(fGenericPartialLocationStrings[i][2], -1);
   1927                             isSet = TRUE;
   1928                         }
   1929                     }
   1930                 } else {
   1931                     result.setTo(fGenericPartialLocationStrings[i][1], -1);
   1932                     isSet = TRUE;
   1933                 }
   1934                 break;
   1935             }
   1936         }
   1937     }
   1938     if (!isSet) {
   1939         result.remove();
   1940     }
   1941     return result;
   1942 }
   1943 
   1944 // --------------------------------------------------------------
   1945 SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry)
   1946 : fCacheEntry(cacheEntry) {
   1947 }
   1948 
   1949 SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() {
   1950     fCacheEntry->delRef();
   1951 }
   1952 
   1953 const ZoneStringFormat*
   1954 SafeZoneStringFormatPtr::get() const {
   1955     return fCacheEntry->getZoneStringFormat();
   1956 }
   1957 
   1958 ZSFCacheEntry::ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next)
   1959 : fLocale(locale), fZoneStringFormat(zsf),
   1960  fNext(next), fRefCount(1)
   1961 {
   1962 }
   1963 
   1964 ZSFCacheEntry::~ZSFCacheEntry () {
   1965     delete fZoneStringFormat;
   1966 }
   1967 
   1968 const ZoneStringFormat*
   1969 ZSFCacheEntry::getZoneStringFormat(void) {
   1970     return (const ZoneStringFormat*)fZoneStringFormat;
   1971 }
   1972 
   1973 void
   1974 ZSFCacheEntry::delRef(void) {
   1975     umtx_lock(&gZSFCacheLock);
   1976     --fRefCount;
   1977     umtx_unlock(&gZSFCacheLock);
   1978 }
   1979 
   1980 ZSFCache::ZSFCache(int32_t capacity)
   1981 : fCapacity(capacity), fFirst(NULL) {
   1982 }
   1983 
   1984 ZSFCache::~ZSFCache() {
   1985     ZSFCacheEntry *entry = fFirst;
   1986     while (entry) {
   1987         ZSFCacheEntry *next = entry->fNext;
   1988         delete entry;
   1989         entry = next;
   1990     }
   1991 }
   1992 
   1993 SafeZoneStringFormatPtr*
   1994 ZSFCache::get(const Locale &locale, UErrorCode &status) {
   1995     SafeZoneStringFormatPtr *result = NULL;
   1996 
   1997     // Search the cache entry list
   1998     ZSFCacheEntry *entry = NULL;
   1999     ZSFCacheEntry *next, *prev;
   2000 
   2001     umtx_lock(&gZSFCacheLock);
   2002     entry = fFirst;
   2003     prev = NULL;
   2004     while (entry) {
   2005         next = entry->fNext;
   2006         if (entry->fLocale == locale) {
   2007             // Add reference count
   2008             entry->fRefCount++;
   2009 
   2010             // move the entry to the top
   2011             if (entry != fFirst) {
   2012                 prev->fNext = next;
   2013                 entry->fNext = fFirst;
   2014                 fFirst = entry;
   2015             }
   2016             break;
   2017         }
   2018         prev = entry;
   2019         entry = next;
   2020     }
   2021     umtx_unlock(&gZSFCacheLock);
   2022 
   2023     // Create a new ZoneStringFormat
   2024     if (entry == NULL) {
   2025         ZoneStringFormat *zsf = new ZoneStringFormat(locale, status);
   2026         if (U_FAILURE(status)) {
   2027             delete zsf;
   2028             return NULL;
   2029         }
   2030         if (zsf == NULL) {
   2031             status = U_MEMORY_ALLOCATION_ERROR;
   2032             return NULL;
   2033         }
   2034 
   2035         // Now add the new entry
   2036         umtx_lock(&gZSFCacheLock);
   2037         // Make sure no other threads already created the one for the same locale
   2038         entry = fFirst;
   2039         prev = NULL;
   2040         while (entry) {
   2041             next = entry->fNext;
   2042             if (entry->fLocale == locale) {
   2043                 // Add reference count
   2044                 entry->fRefCount++;
   2045 
   2046                 // move the entry to the top
   2047                 if (entry != fFirst) {
   2048                     prev->fNext = next;
   2049                     entry->fNext = fFirst;
   2050                     fFirst = entry;
   2051                 }
   2052                 break;
   2053             }
   2054             prev = entry;
   2055             entry = next;
   2056         }
   2057         if (entry == NULL) {
   2058             // Add the new one to the top
   2059             next = fFirst;
   2060             entry = new ZSFCacheEntry(locale, zsf, next);
   2061             fFirst = entry;
   2062         } else {
   2063             delete zsf;
   2064         }
   2065         umtx_unlock(&gZSFCacheLock);
   2066     }
   2067 
   2068     result = new SafeZoneStringFormatPtr(entry);
   2069 
   2070     // Now, delete unused cache entries beyond the capacity
   2071     umtx_lock(&gZSFCacheLock);
   2072     entry = fFirst;
   2073     prev = NULL;
   2074     int32_t idx = 1;
   2075     while (entry) {
   2076         next = entry->fNext;
   2077         if (idx >= fCapacity && entry->fRefCount == 0) {
   2078             if (entry == fFirst) {
   2079                 fFirst = next;
   2080             } else {
   2081                 prev->fNext = next;
   2082             }
   2083             delete entry;
   2084         } else {
   2085             prev = entry;
   2086         }
   2087         entry = next;
   2088         idx++;
   2089     }
   2090     umtx_unlock(&gZSFCacheLock);
   2091 
   2092     return result;
   2093 }
   2094 
   2095 
   2096 /*
   2097  * Zone String Formatter String Pool Implementation
   2098  *
   2099  *    String pool for (UChar *) strings.  Avoids having repeated copies of the same string.
   2100  */
   2101 
   2102 static const int32_t POOL_CHUNK_SIZE = 2000;
   2103 struct ZSFStringPoolChunk: public UMemory {
   2104     ZSFStringPoolChunk    *fNext;                       // Ptr to next pool chunk
   2105     int32_t               fLimit;                       // Index to start of unused area at end of fStrings
   2106     UChar                 fStrings[POOL_CHUNK_SIZE];    //  Strings array
   2107     ZSFStringPoolChunk();
   2108 };
   2109 
   2110 ZSFStringPoolChunk::ZSFStringPoolChunk() {
   2111     fNext = NULL;
   2112     fLimit = 0;
   2113 }
   2114 
   2115 ZSFStringPool::ZSFStringPool(UErrorCode &status) {
   2116     fChunks = NULL;
   2117     fHash   = NULL;
   2118     if (U_FAILURE(status)) {
   2119         return;
   2120     }
   2121     fChunks = new ZSFStringPoolChunk;
   2122     if (fChunks == NULL) {
   2123         status = U_MEMORY_ALLOCATION_ERROR;
   2124         return;
   2125     }
   2126 
   2127     fHash   = uhash_open(uhash_hashUChars      /* keyHash */,
   2128                          uhash_compareUChars   /* keyComp */,
   2129                          uhash_compareUChars   /* valueComp */,
   2130                          &status);
   2131     if (U_FAILURE(status)) {
   2132         return;
   2133     }
   2134 }
   2135 
   2136 
   2137 ZSFStringPool::~ZSFStringPool() {
   2138     if (fHash != NULL) {
   2139         uhash_close(fHash);
   2140         fHash = NULL;
   2141     }
   2142 
   2143     while (fChunks != NULL) {
   2144         ZSFStringPoolChunk *nextChunk = fChunks->fNext;
   2145         delete fChunks;
   2146         fChunks = nextChunk;
   2147     }
   2148 }
   2149 
   2150 static const UChar EmptyString = 0;
   2151 
   2152 const UChar *ZSFStringPool::get(const UChar *s, UErrorCode &status) {
   2153     const UChar *pooledString;
   2154     if (U_FAILURE(status)) {
   2155         return &EmptyString;
   2156     }
   2157 
   2158     pooledString = static_cast<UChar *>(uhash_get(fHash, s));
   2159     if (pooledString != NULL) {
   2160         return pooledString;
   2161     }
   2162 
   2163     int32_t length = u_strlen(s);
   2164     int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit;
   2165     if (remainingLength <= length) {
   2166         U_ASSERT(length < POOL_CHUNK_SIZE);
   2167         if (length >= POOL_CHUNK_SIZE) {
   2168             status = U_INTERNAL_PROGRAM_ERROR;
   2169             return &EmptyString;
   2170         }
   2171         ZSFStringPoolChunk *oldChunk = fChunks;
   2172         fChunks = new ZSFStringPoolChunk;
   2173         if (fChunks == NULL) {
   2174             status = U_MEMORY_ALLOCATION_ERROR;
   2175             return &EmptyString;
   2176         }
   2177         fChunks->fNext = oldChunk;
   2178     }
   2179 
   2180     UChar *destString = &fChunks->fStrings[fChunks->fLimit];
   2181     u_strcpy(destString, s);
   2182     fChunks->fLimit += (length + 1);
   2183     uhash_put(fHash, destString, destString, &status);
   2184     return destString;
   2185 }
   2186 
   2187 
   2188 //
   2189 //  ZSFStringPool::adopt()   Put a string into the hash, but do not copy the string data
   2190 //                           into the pool's storage.  Used for strings from resource bundles,
   2191 //                           which will perisist for the life of the zone string formatter, and
   2192 //                           therefore can be used directly without copying.
   2193 const UChar *ZSFStringPool::adopt(const UChar * s, UErrorCode &status) {
   2194     const UChar *pooledString;
   2195     if (U_FAILURE(status)) {
   2196         return &EmptyString;
   2197     }
   2198     if (s != NULL) {
   2199         pooledString = static_cast<UChar *>(uhash_get(fHash, s));
   2200         if (pooledString == NULL) {
   2201             UChar *ncs = const_cast<UChar *>(s);
   2202             uhash_put(fHash, ncs, ncs, &status);
   2203         }
   2204     }
   2205     return s;
   2206 }
   2207 
   2208 
   2209 const UChar *ZSFStringPool::get(const UnicodeString &s, UErrorCode &status) {
   2210     UnicodeString &nonConstStr = const_cast<UnicodeString &>(s);
   2211     return this->get(nonConstStr.getTerminatedBuffer(), status);
   2212 }
   2213 
   2214 /*
   2215  * freeze().   Close the hash table that maps to the pooled strings.
   2216  *             After freezing, the pool can not be searched or added to,
   2217  *             but all existing references to pooled strings remain valid.
   2218  *
   2219  *             The main purpose is to recover the storage used for the hash.
   2220  */
   2221 void ZSFStringPool::freeze() {
   2222     uhash_close(fHash);
   2223     fHash = NULL;
   2224 }
   2225 
   2226 U_NAMESPACE_END
   2227 
   2228 #endif /* #if !UCONFIG_NO_FORMATTING */
   2229