• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2007-2008, 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 
31 /**
32  * global ZoneStringFormatCache stuffs
33  */
34 static UMTX gZSFCacheLock = NULL;
35 static U_NAMESPACE_QUALIFIER ZSFCache *gZoneStringFormatCache = NULL;
36 
37 U_CDECL_BEGIN
38 /**
39  * ZoneStringFormatCache cleanup callback func
40  */
zoneStringFormat_cleanup(void)41 static UBool U_CALLCONV zoneStringFormat_cleanup(void)
42 {
43     umtx_destroy(&gZSFCacheLock);
44     if (gZoneStringFormatCache != NULL) {
45         delete gZoneStringFormatCache;
46         gZoneStringFormatCache = NULL;
47     }
48     gZoneStringFormatCache = NULL;
49     return TRUE;
50 }
51 
52 /**
53  * Deleter for ZoneStringInfo
54  */
55 static void U_CALLCONV
deleteZoneStringInfo(void * obj)56 deleteZoneStringInfo(void *obj) {
57     delete (U_NAMESPACE_QUALIFIER ZoneStringInfo*)obj;
58 }
59 
60 /**
61  * Deleter for ZoneStrings
62  */
63 static void U_CALLCONV
deleteZoneStrings(void * obj)64 deleteZoneStrings(void *obj) {
65     delete (U_NAMESPACE_QUALIFIER ZoneStrings*)obj;
66 }
67 U_CDECL_END
68 
69 U_NAMESPACE_BEGIN
70 
71 #define ZID_KEY_MAX 128
72 
73 static const char gCountriesTag[]       = "Countries";
74 static const char gZoneStringsTag[]     = "zoneStrings";
75 static const char gShortGenericTag[]    = "sg";
76 static const char gShortStandardTag[]   = "ss";
77 static const char gShortDaylightTag[]   = "sd";
78 static const char gLongGenericTag[]     = "lg";
79 static const char gLongStandardTag[]    = "ls";
80 static const char gLongDaylightTag[]    = "ld";
81 static const char gExemplarCityTag[]    = "ec";
82 static const char gCommonlyUsedTag[]    = "cu";
83 static const char gFallbackFormatTag[]  = "fallbackFormat";
84 static const char gRegionFormatTag[]    = "regionFormat";
85 
86 #define MZID_PREFIX_LEN 5
87 static const char gMetazoneIdPrefix[]   = "meta:";
88 
89 #define MAX_METAZONES_PER_ZONE 10
90 
91 static const UChar gDefFallbackPattern[]    = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
92 static const UChar gDefRegionPattern[]      = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
93 static const UChar gCommonlyUsedTrue[]      = {0x31, 0x00}; // "1"
94 
95 static const double kDstCheckRange      = (double)184*U_MILLIS_PER_DAY;
96 
97 static int32_t
getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type)98 getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type) {
99     int32_t typeIdx = 0;
100     switch (type) {
101         case LOCATION:
102             typeIdx = ZSIDX_LOCATION;
103             break;
104         case GENERIC_LONG:
105             typeIdx = ZSIDX_LONG_GENERIC;
106             break;
107         case GENERIC_SHORT:
108             typeIdx = ZSIDX_SHORT_GENERIC;
109             break;
110         case STANDARD_LONG:
111             typeIdx = ZSIDX_LONG_STANDARD;
112             break;
113         case STANDARD_SHORT:
114             typeIdx = ZSIDX_SHORT_STANDARD;
115             break;
116         case DAYLIGHT_LONG:
117             typeIdx = ZSIDX_LONG_DAYLIGHT;
118             break;
119         case DAYLIGHT_SHORT:
120             typeIdx = ZSIDX_SHORT_DAYLIGHT;
121             break;
122     }
123     return typeIdx;
124 }
125 
126 static int32_t
getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx)127 getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx) {
128     int32_t type = 0;
129     switch (typeIdx) {
130         case ZSIDX_LOCATION:
131             type = LOCATION;
132             break;
133         case ZSIDX_LONG_GENERIC:
134             type = GENERIC_LONG;
135             break;
136         case ZSIDX_SHORT_GENERIC:
137             type = GENERIC_SHORT;
138             break;
139         case ZSIDX_LONG_STANDARD:
140             type = STANDARD_LONG;
141             break;
142         case ZSIDX_SHORT_STANDARD:
143             type = STANDARD_SHORT;
144             break;
145         case ZSIDX_LONG_DAYLIGHT:
146             type = DAYLIGHT_LONG;
147             break;
148         case ZSIDX_COUNT:
149         case ZSIDX_SHORT_DAYLIGHT:
150             type = DAYLIGHT_SHORT;
151             break;
152 
153     }
154     return type;
155 }
156 
157 #define DEFAULT_CHARACTERNODE_CAPACITY 1
158 
159 // ----------------------------------------------------------------------------
clear()160 void CharacterNode::clear() {
161     uprv_memset(this, 0, sizeof(*this));
162 }
163 
deleteValues()164 void CharacterNode::deleteValues() {
165     if (fValues == NULL) {
166         // Do nothing.
167     } else if (!fHasValuesVector) {
168         deleteZoneStringInfo(fValues);
169     } else {
170         delete (UVector *)fValues;
171     }
172 }
173 
174 void
addValue(void * value,UErrorCode & status)175 CharacterNode::addValue(void *value, UErrorCode &status) {
176     if (U_FAILURE(status)) {
177         deleteZoneStringInfo(value);
178         return;
179     }
180     if (fValues == NULL) {
181         fValues = value;
182     } else {
183         // At least one value already.
184         if (!fHasValuesVector) {
185             // There is only one value so far, and not in a vector yet.
186             // Create a vector and add the old value.
187             UVector *values = new UVector(deleteZoneStringInfo, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status);
188             if (U_FAILURE(status)) {
189                 deleteZoneStringInfo(value);
190                 return;
191             }
192             values->addElement(fValues, status);
193             fValues = values;
194             fHasValuesVector = TRUE;
195         }
196         // Add the new value.
197         ((UVector *)fValues)->addElement(value, status);
198     }
199 }
200 
201 //----------------------------------------------------------------------------
202 // Virtual destructor to avoid warning
~TextTrieMapSearchResultHandler()203 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
204 }
205 
206 // ----------------------------------------------------------------------------
TextTrieMap(UBool ignoreCase)207 TextTrieMap::TextTrieMap(UBool ignoreCase)
208 : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0) {
209 }
210 
~TextTrieMap()211 TextTrieMap::~TextTrieMap() {
212     int32_t index;
213     for (index = 0; index < fNodesCount; ++index) {
214         fNodes[index].deleteValues();
215     }
216     uprv_free(fNodes);
217 }
218 
219 void
put(const UnicodeString & key,void * value,UErrorCode & status)220 TextTrieMap::put(const UnicodeString &key, void *value, UErrorCode &status) {
221     if (fNodes == NULL) {
222         fNodesCapacity = 512;
223         fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
224         fNodes[0].clear();  // Init root node.
225         fNodesCount = 1;
226     }
227 
228     UnicodeString foldedKey;
229     const UChar *keyBuffer;
230     int32_t keyLength;
231     if (fIgnoreCase) {
232         // Ok to use fastCopyFrom() because we discard the copy when we return.
233         foldedKey.fastCopyFrom(key).foldCase();
234         keyBuffer = foldedKey.getBuffer();
235         keyLength = foldedKey.length();
236     } else {
237         keyBuffer = key.getBuffer();
238         keyLength = key.length();
239     }
240 
241     CharacterNode *node = fNodes;
242     int32_t index;
243     for (index = 0; index < keyLength; ++index) {
244         node = addChildNode(node, keyBuffer[index], status);
245     }
246     node->addValue(value, status);
247 }
248 
249 UBool
growNodes()250 TextTrieMap::growNodes() {
251     if (fNodesCapacity == 0xffff) {
252         return FALSE;  // We use 16-bit node indexes.
253     }
254     int32_t newCapacity = fNodesCapacity * 2;
255     if (newCapacity > 0xffff) {
256         newCapacity = 0xffff;
257     }
258     CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode));
259     if (newNodes == NULL) {
260         return FALSE;
261     }
262     uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode));
263     uprv_free(fNodes);
264     fNodes = newNodes;
265     fNodesCapacity = newCapacity;
266     return TRUE;
267 }
268 
269 CharacterNode*
addChildNode(CharacterNode * parent,UChar c,UErrorCode & status)270 TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) {
271     if (U_FAILURE(status)) {
272         return NULL;
273     }
274     // Linear search of the sorted list of children.
275     uint16_t prevIndex = 0;
276     uint16_t nodeIndex = parent->fFirstChild;
277     while (nodeIndex > 0) {
278         CharacterNode *current = fNodes + nodeIndex;
279         UChar childCharacter = current->fCharacter;
280         if (childCharacter == c) {
281             return current;
282         } else if (childCharacter > c) {
283             break;
284         }
285         prevIndex = nodeIndex;
286         nodeIndex = current->fNextSibling;
287     }
288 
289     // Ensure capacity. Grow fNodes[] if needed.
290     if (fNodesCount == fNodesCapacity) {
291         int32_t parentIndex = (parent - fNodes);
292         if (!growNodes()) {
293             status = U_MEMORY_ALLOCATION_ERROR;
294             return NULL;
295         }
296         parent = fNodes + parentIndex;
297     }
298 
299     // Insert a new child node with c in sorted order.
300     CharacterNode *node = fNodes + fNodesCount;
301     node->clear();
302     node->fCharacter = c;
303     node->fNextSibling = nodeIndex;
304     if (prevIndex == 0) {
305         parent->fFirstChild = (uint16_t)fNodesCount;
306     } else {
307         fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount;
308     }
309     ++fNodesCount;
310     return node;
311 }
312 
313 CharacterNode*
getChildNode(CharacterNode * parent,UChar c) const314 TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
315     // Linear search of the sorted list of children.
316     uint16_t nodeIndex = parent->fFirstChild;
317     while (nodeIndex > 0) {
318         CharacterNode *current = fNodes + nodeIndex;
319         UChar childCharacter = current->fCharacter;
320         if (childCharacter == c) {
321             return current;
322         } else if (childCharacter > c) {
323             break;
324         }
325         nodeIndex = current->fNextSibling;
326     }
327     return NULL;
328 }
329 
330 void
search(const UnicodeString & text,int32_t start,TextTrieMapSearchResultHandler * handler,UErrorCode & status) const331 TextTrieMap::search(const UnicodeString &text, int32_t start,
332                   TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
333     if (fNodes == NULL) {
334         return;
335     }
336     search(fNodes, text, start, start, handler, status);
337 }
338 
339 void
search(CharacterNode * node,const UnicodeString & text,int32_t start,int32_t index,TextTrieMapSearchResultHandler * handler,UErrorCode & status) const340 TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
341                   int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
342     if (U_FAILURE(status)) {
343         return;
344     }
345     if (node->hasValues()) {
346         if (!handler->handleMatch(index - start, node, status)) {
347             return;
348         }
349         if (U_FAILURE(status)) {
350             return;
351         }
352     }
353     UChar32 c = text.char32At(index);
354     if (fIgnoreCase) {
355         // size of character may grow after fold operation
356         UnicodeString tmp(c);
357         tmp.foldCase();
358         int32_t tmpidx = 0;
359         while (tmpidx < tmp.length()) {
360             c = tmp.char32At(tmpidx);
361             node = getChildNode(node, c);
362             if (node == NULL) {
363                 break;
364             }
365             tmpidx = tmp.moveIndex32(tmpidx, 1);
366         }
367     } else {
368         node = getChildNode(node, c);
369     }
370     if (node != NULL) {
371         search(node, text, start, index+1, handler, status);
372     }
373 }
374 
375 // ----------------------------------------------------------------------------
ZoneStringInfo(const UnicodeString & id,const UnicodeString & str,TimeZoneTranslationType type)376 ZoneStringInfo::ZoneStringInfo(const UnicodeString &id, const UnicodeString &str,
377                                TimeZoneTranslationType type)
378 : fId(id), fStr(str), fType(type) {
379 }
380 
~ZoneStringInfo()381 ZoneStringInfo::~ZoneStringInfo() {
382 }
383 // ----------------------------------------------------------------------------
ZoneStringSearchResultHandler(UErrorCode & status)384 ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode &status)
385 : fResults(status)
386 {
387     clear();
388 }
389 
~ZoneStringSearchResultHandler()390 ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() {
391     clear();
392 }
393 
394 UBool
handleMatch(int32_t matchLength,const CharacterNode * node,UErrorCode & status)395 ZoneStringSearchResultHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
396     if (U_FAILURE(status)) {
397         return FALSE;
398     }
399     if (node->hasValues()) {
400         int32_t valuesCount = node->countValues();
401         for (int32_t i = 0; i < valuesCount; i++) {
402             ZoneStringInfo *zsinfo = (ZoneStringInfo*)node->getValue(i);
403             if (zsinfo == NULL) {
404                 break;
405             }
406             // Update the results
407             UBool foundType = FALSE;
408             for (int32_t j = 0; j < fResults.size(); j++) {
409                 ZoneStringInfo *tmp = (ZoneStringInfo*)fResults.elementAt(j);
410                 if (zsinfo->fType == tmp->fType) {
411                     int32_t lenidx = getTimeZoneTranslationTypeIndex(tmp->fType);
412                     if (matchLength > fMatchLen[lenidx]) {
413                         // Same type, longer match
414                         fResults.setElementAt(zsinfo, j);
415                         fMatchLen[lenidx] = matchLength;
416                     }
417                     foundType = TRUE;
418                     break;
419                 }
420             }
421             if (!foundType) {
422                 // not found in the current list
423                 fResults.addElement(zsinfo, status);
424                 fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)] = matchLength;
425             }
426         }
427     }
428     return TRUE;
429 }
430 
431 int32_t
countMatches(void)432 ZoneStringSearchResultHandler::countMatches(void) {
433     return fResults.size();
434 }
435 
436 const ZoneStringInfo*
getMatch(int32_t index,int32_t & matchLength)437 ZoneStringSearchResultHandler::getMatch(int32_t index, int32_t &matchLength) {
438     ZoneStringInfo *zsinfo = NULL;
439     if (index < fResults.size()) {
440         zsinfo = (ZoneStringInfo*)fResults.elementAt(index);
441         matchLength = fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)];
442     }
443     return zsinfo;
444 }
445 
446 void
clear(void)447 ZoneStringSearchResultHandler::clear(void) {
448     fResults.removeAllElements();
449     for (int32_t i = 0; i < (int32_t)(sizeof(fMatchLen)/sizeof(fMatchLen[0])); i++) {
450         fMatchLen[i] = 0;
451     }
452 }
453 // ----------------------------------------------------------------------------
ZoneStringFormat(const UnicodeString * const * strings,int32_t rowCount,int32_t columnCount,UErrorCode & status)454 ZoneStringFormat::ZoneStringFormat(const UnicodeString* const* strings,
455                                    int32_t rowCount, int32_t columnCount, UErrorCode &status)
456 : fLocale(""),
457   fTzidToStrings(uhash_compareUnicodeString, NULL, status),
458   fMzidToStrings(uhash_compareUnicodeString, NULL, status),
459   fZoneStringsTrie(TRUE)
460 {
461     if (U_FAILURE(status)) {
462         return;
463     }
464     fLocale.setToBogus();
465     if (strings == NULL || columnCount <= 0 || rowCount <= 0) {
466         status = U_ILLEGAL_ARGUMENT_ERROR;
467         return;
468     }
469 
470     fTzidToStrings.setValueDeleter(deleteZoneStrings);
471 
472     for (int32_t row = 0; row < rowCount; row++) {
473         if (strings[row][0].isEmpty()) {
474             continue;
475         }
476         UnicodeString *names = new UnicodeString[ZSIDX_COUNT];
477         for (int32_t col = 1; col < columnCount; col++) {
478             if (!strings[row][col].isEmpty()) {
479                 int32_t typeIdx = -1;
480                 switch (col) {
481                     case 1:
482                         typeIdx = ZSIDX_LONG_STANDARD;
483                         break;
484                     case 2:
485                         typeIdx = ZSIDX_SHORT_STANDARD;
486                         break;
487                     case 3:
488                         typeIdx = ZSIDX_LONG_DAYLIGHT;
489                         break;
490                     case 4:
491                         typeIdx = ZSIDX_SHORT_DAYLIGHT;
492                         break;
493                     case 5:
494                         typeIdx = ZSIDX_LOCATION;
495                         break;
496                     case 6:
497                         typeIdx = ZSIDX_LONG_GENERIC;
498                         break;
499                     case 7:
500                         typeIdx = ZSIDX_SHORT_GENERIC;
501                         break;
502                 }
503                 if (typeIdx != -1) {
504                     names[typeIdx].setTo(strings[row][col]);
505 
506                     // Put the name into the trie
507                     int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeIdx);
508                     ZoneStringInfo *zsinf = new ZoneStringInfo(strings[row][0], strings[row][col], (TimeZoneTranslationType)type);
509                     fZoneStringsTrie.put(strings[row][col], zsinf, status);
510                     if (U_FAILURE(status)) {
511                         delete zsinf;
512                         goto error_cleanup;
513                     }
514                 }
515             }
516         }
517         ZoneStrings *zstrings = new ZoneStrings(names, ZSIDX_COUNT, TRUE, NULL, 0, 0);
518         fTzidToStrings.put(strings[row][0], zstrings, status);
519         if (U_FAILURE(status)) {
520             delete zstrings;
521             goto error_cleanup;
522         }
523     }
524     return;
525 
526 error_cleanup:
527     return;
528 }
529 
ZoneStringFormat(const Locale & locale,UErrorCode & status)530 ZoneStringFormat::ZoneStringFormat(const Locale &locale, UErrorCode &status)
531 : fLocale(locale),
532   fTzidToStrings(uhash_compareUnicodeString, NULL, status),
533   fMzidToStrings(uhash_compareUnicodeString, NULL, status),
534   fZoneStringsTrie(TRUE)
535 {
536     if (U_FAILURE(status)) {
537         return;
538     }
539     fTzidToStrings.setValueDeleter(deleteZoneStrings);
540     fMzidToStrings.setValueDeleter(deleteZoneStrings);
541 
542     UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status);
543     zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
544     if (U_FAILURE(status)) {
545         // If no locale bundles are available, zoneStrings will be null.
546         // We still want to go through the rest of zone strings initialization,
547         // because generic location format is generated from tzid for the case.
548         // The rest of code should work even zoneStrings is null.
549         status = U_ZERO_ERROR;
550         ures_close(zoneStringsArray);
551         zoneStringsArray = NULL;
552     }
553 
554     StringEnumeration *tzids = NULL;
555     MessageFormat *fallbackFmt = NULL;
556     MessageFormat *regionFmt = NULL;
557 
558     UResourceBundle *zoneItem = NULL;
559     UResourceBundle *metazoneItem = NULL;
560 
561     char zidkey[ZID_KEY_MAX];
562     const UChar *zstrarray[ZSIDX_COUNT];
563     const UChar *mzstrarray[ZSIDX_COUNT];
564     UnicodeString mzPartialLoc[MAX_METAZONES_PER_ZONE][4];
565 
566     UnicodeString region;
567     getRegion(region);
568 
569     fallbackFmt = getFallbackFormat(locale, status);
570     if (U_FAILURE(status)) {
571         goto error_cleanup;
572     }
573     regionFmt = getRegionFormat(locale, status);
574     if (U_FAILURE(status)) {
575         goto error_cleanup;
576     }
577 
578     tzids = TimeZone::createEnumeration();
579     const char *tzid;
580     while ((tzid = tzids->next(NULL, status))) {
581         if (U_FAILURE(status)) {
582             goto error_cleanup;
583         }
584         // Skip non-canonical IDs
585         UnicodeString utzid(tzid, -1, US_INV);
586         UnicodeString canonicalID;
587         ZoneMeta::getCanonicalID(utzid, canonicalID);
588         if (utzid != canonicalID) {
589             continue;
590         }
591 
592         uprv_strcpy(zidkey, tzid);
593 
594         // Replace '/' with ':'
595         char *pCity = NULL;
596         char *p = zidkey;
597         while (*p) {
598             if (*p == '/') {
599                 *p = ':';
600                 pCity = p + 1;
601             }
602             p++;
603         }
604 
605         if (zoneStringsArray != NULL) {
606             zoneItem = ures_getByKeyWithFallback(zoneStringsArray, zidkey, zoneItem, &status);
607             if (U_FAILURE(status)) {
608                 // If failed to open the zone item, create only location string
609                 ures_close(zoneItem);
610                 zoneItem = NULL;
611                 status = U_ZERO_ERROR;
612             }
613         }
614         zstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(zoneItem, gLongStandardTag);
615         zstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(zoneItem, gShortStandardTag);
616         zstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gLongDaylightTag);
617         zstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gShortDaylightTag);
618         zstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(zoneItem, gLongGenericTag);
619         zstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(zoneItem, gShortGenericTag);
620 
621         // Compose location format string
622         UnicodeString location;
623         UnicodeString country;
624         UnicodeString city;
625         UnicodeString countryCode;
626         ZoneMeta::getCanonicalCountry(utzid, countryCode);
627         if (countryCode.isEmpty()) {
628             zstrarray[ZSIDX_LOCATION] = NULL;
629         } else {
630             const UChar* tmpCity = getZoneStringFromBundle(zoneItem, gExemplarCityTag);
631             if (tmpCity != NULL) {
632                 city.setTo(TRUE, tmpCity, -1);
633             } else {
634                 city.setTo(UnicodeString(pCity, -1, US_INV));
635                 // Replace '_' with ' '
636                 for (int32_t i = 0; i < city.length(); i++) {
637                     if (city.charAt(i) == (UChar)0x5F /*'_'*/) {
638                         city.setCharAt(i, (UChar)0x20 /*' '*/);
639                     }
640                 }
641             }
642             getLocalizedCountry(countryCode, locale, country);
643             UnicodeString singleCountry;
644             ZoneMeta::getSingleCountry(utzid, singleCountry);
645             FieldPosition fpos;
646             if (singleCountry.isEmpty()) {
647                 Formattable params [] = {
648                     Formattable(city),
649                     Formattable(country)
650                 };
651                 fallbackFmt->format(params, 2, location, fpos, status);
652             } else {
653                 // If the zone is only one zone in the country, do not add city
654                 Formattable params [] = {
655                     Formattable(country)
656                 };
657                 regionFmt->format(params, 1, location, fpos, status);
658             }
659             if (U_FAILURE(status)) {
660                 goto error_cleanup;
661             }
662 
663             // Workaround for reducing UMR warning in Purify.
664             // Append NULL before calling getTerminatedBuffer()
665             int32_t locLen = location.length();
666             location.append((UChar)0).truncate(locLen);
667 
668             zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer();
669         }
670 
671         UBool commonlyUsed = isCommonlyUsed(zoneItem);
672 
673         // Resolve metazones used by this zone
674         int32_t mzPartialLocIdx = 0;
675         const UVector *metazoneMappings = ZoneMeta::getMetazoneMappings(utzid);
676         if (metazoneMappings != NULL) {
677             for (int32_t i = 0; i < metazoneMappings->size(); i++) {
678                 const OlsonToMetaMappingEntry *mzmap = (const OlsonToMetaMappingEntry*)metazoneMappings->elementAt(i);
679                 UnicodeString mzid(mzmap->mzid);
680                 const ZoneStrings *mzStrings = (const ZoneStrings*)fMzidToStrings.get(mzid);
681                 if (mzStrings == NULL) {
682                     // If the metazone strings are not yet processed, do it now.
683                     char mzidkey[ZID_KEY_MAX];
684                     uprv_strcpy(mzidkey, gMetazoneIdPrefix);
685                     u_UCharsToChars(mzmap->mzid, mzidkey + MZID_PREFIX_LEN, u_strlen(mzmap->mzid) + 1);
686                     metazoneItem = ures_getByKeyWithFallback(zoneStringsArray, mzidkey, metazoneItem, &status);
687                     if (U_FAILURE(status)) {
688                         // No resources available for this metazone
689                         // Resource bundle will be cleaned up after end of the loop.
690                         status = U_ZERO_ERROR;
691                         continue;
692                     }
693                     UBool mzCommonlyUsed = isCommonlyUsed(metazoneItem);
694                     mzstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(metazoneItem, gLongStandardTag);
695                     mzstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(metazoneItem, gShortStandardTag);
696                     mzstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gLongDaylightTag);
697                     mzstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gShortDaylightTag);
698                     mzstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(metazoneItem, gLongGenericTag);
699                     mzstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(metazoneItem, gShortGenericTag);
700                     mzstrarray[ZSIDX_LOCATION] = NULL;
701 
702                     int32_t lastNonNullIdx = ZSIDX_COUNT - 1;
703                     while (lastNonNullIdx >= 0) {
704                         if (mzstrarray[lastNonNullIdx] != NULL) {
705                             break;
706                         }
707                         lastNonNullIdx--;
708                     }
709                     UnicodeString *strings_mz = NULL;
710                     ZoneStrings *tmp_mzStrings = NULL;
711                     if (lastNonNullIdx >= 0) {
712                         // Create UnicodeString array and put strings to the zone string trie
713                         strings_mz = new UnicodeString[lastNonNullIdx + 1];
714 
715                         UnicodeString preferredIdForLocale;
716                         ZoneMeta::getZoneIdByMetazone(mzid, region, preferredIdForLocale);
717 
718                         for (int32_t typeidx = 0; typeidx <= lastNonNullIdx; typeidx++) {
719                             if (mzstrarray[typeidx] != NULL) {
720                                 strings_mz[typeidx].setTo(TRUE, mzstrarray[typeidx], -1);
721 
722                                 // Add a metazone string to the zone string trie
723                                 int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeidx);
724                                 ZoneStringInfo *zsinfo = new ZoneStringInfo(preferredIdForLocale, strings_mz[typeidx], (TimeZoneTranslationType)type);
725                                 fZoneStringsTrie.put(strings_mz[typeidx], zsinfo, status);
726                                 if (U_FAILURE(status)) {
727                                     delete []strings_mz;
728                                     goto error_cleanup;
729                                 }
730                             }
731                         }
732                         tmp_mzStrings = new ZoneStrings(strings_mz, lastNonNullIdx + 1, mzCommonlyUsed, NULL, 0, 0);
733                     } else {
734                         // Create ZoneStrings with empty contents
735                         tmp_mzStrings = new ZoneStrings(NULL, 0, FALSE, NULL, 0, 0);
736                     }
737 
738                     fMzidToStrings.put(mzid, tmp_mzStrings, status);
739                     if (U_FAILURE(status)) {
740                         goto error_cleanup;
741                     }
742 
743                     mzStrings = tmp_mzStrings;
744                 }
745 
746                 // Compose generic partial location format
747                 UnicodeString lg;
748                 UnicodeString sg;
749 
750                 mzStrings->getString(ZSIDX_LONG_GENERIC, lg);
751                 mzStrings->getString(ZSIDX_SHORT_GENERIC, sg);
752 
753                 if (!lg.isEmpty() || !sg.isEmpty()) {
754                     UBool addMzPartialLocationNames = TRUE;
755                     for (int32_t j = 0; j < mzPartialLocIdx; j++) {
756                         if (mzPartialLoc[j][0] == mzid) {
757                             // already processed
758                             addMzPartialLocationNames = FALSE;
759                             break;
760                         }
761                     }
762                     if (addMzPartialLocationNames) {
763                         UnicodeString *locationPart = NULL;
764                         // Check if the zone is the preferred zone for the territory associated with the zone
765                         UnicodeString preferredID;
766                         ZoneMeta::getZoneIdByMetazone(mzid, countryCode, preferredID);
767                         if (utzid == preferredID) {
768                             // Use country for the location
769                             locationPart = &country;
770                         } else {
771                             // Use city for the location
772                             locationPart = &city;
773                         }
774                         // Reset the partial location string array
775                         mzPartialLoc[mzPartialLocIdx][0].setTo(mzid);
776                         mzPartialLoc[mzPartialLocIdx][1].remove();
777                         mzPartialLoc[mzPartialLocIdx][2].remove();
778                         mzPartialLoc[mzPartialLocIdx][3].remove();
779 
780                         if (locationPart != NULL) {
781                             FieldPosition fpos;
782                             if (!lg.isEmpty()) {
783                                 Formattable params [] = {
784                                     Formattable(*locationPart),
785                                     Formattable(lg)
786                                 };
787                                 fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][1], fpos, status);
788                             }
789                             if (!sg.isEmpty()) {
790                                 Formattable params [] = {
791                                     Formattable(*locationPart),
792                                     Formattable(sg)
793                                 };
794                                 fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][2], fpos, status);
795                                 if (mzStrings->isShortFormatCommonlyUsed()) {
796                                     mzPartialLoc[mzPartialLocIdx][3].setTo(TRUE, gCommonlyUsedTrue, -1);
797                                 }
798                             }
799                             if (U_FAILURE(status)) {
800                                 goto error_cleanup;
801                             }
802                         }
803                         mzPartialLocIdx++;
804                     }
805                 }
806             }
807         }
808         // Collected names for a zone
809 
810         // Create UnicodeString array for localized zone strings
811         int32_t lastIdx = ZSIDX_COUNT - 1;
812         while (lastIdx >= 0) {
813             if (zstrarray[lastIdx] != NULL) {
814                 break;
815             }
816             lastIdx--;
817         }
818         UnicodeString *strings = NULL;
819         int32_t stringsCount = lastIdx + 1;
820 
821         if (stringsCount > 0) {
822             strings = new UnicodeString[stringsCount];
823             for (int32_t i = 0; i < stringsCount; i++) {
824                 if (zstrarray[i] != NULL) {
825                     strings[i].setTo(zstrarray[i], -1);
826 
827                     // Add names to the trie
828                     int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)i);
829                     ZoneStringInfo *zsinfo = new ZoneStringInfo(utzid, strings[i], (TimeZoneTranslationType)type);
830                     fZoneStringsTrie.put(strings[i], zsinfo, status);
831                     if (U_FAILURE(status)) {
832                         delete zsinfo;
833                         delete[] strings;
834                         goto error_cleanup;
835                     }
836                 }
837             }
838         }
839 
840         // Create UnicodeString array for generic partial location strings
841         UnicodeString **genericPartialLocationNames = NULL;
842         int32_t genericPartialRowCount = mzPartialLocIdx;
843         int32_t genericPartialColCount = 4;
844 
845         if (genericPartialRowCount != 0) {
846             genericPartialLocationNames = (UnicodeString**)uprv_malloc(genericPartialRowCount * sizeof(UnicodeString*));
847             if (genericPartialLocationNames == NULL) {
848                 status = U_MEMORY_ALLOCATION_ERROR;
849                 delete[] strings;
850                 goto error_cleanup;
851             }
852             for (int32_t i = 0; i < genericPartialRowCount; i++) {
853                 genericPartialLocationNames[i] = new UnicodeString[genericPartialColCount];
854                 for (int32_t j = 0; j < genericPartialColCount; j++) {
855                     genericPartialLocationNames[i][j].setTo(mzPartialLoc[i][j]);
856                     // Add names to the trie
857                     if ((j == 1 || j == 2) &&!genericPartialLocationNames[i][j].isEmpty()) {
858                         ZoneStringInfo *zsinfo;
859                         TimeZoneTranslationType type = (j == 1) ? GENERIC_LONG : GENERIC_SHORT;
860                         zsinfo = new ZoneStringInfo(utzid, genericPartialLocationNames[i][j], type);
861                         fZoneStringsTrie.put(genericPartialLocationNames[i][j], zsinfo, status);
862                         if (U_FAILURE(status)) {
863                             delete[] genericPartialLocationNames[i];
864                             uprv_free(genericPartialLocationNames);
865                             delete[] strings;
866                             goto error_cleanup;
867                         }
868                     }
869                 }
870             }
871         }
872 
873         // Finally, create ZoneStrings instance and put it into the tzidToStinrgs map
874         ZoneStrings *zstrings = new ZoneStrings(strings, stringsCount, commonlyUsed,
875             genericPartialLocationNames, genericPartialRowCount, genericPartialColCount);
876 
877         fTzidToStrings.put(utzid, zstrings, status);
878         if (U_FAILURE(status)) {
879             delete zstrings;
880             goto error_cleanup;
881         }
882     }
883 
884 error_cleanup:
885     if (fallbackFmt != NULL) {
886         delete fallbackFmt;
887     }
888     if (regionFmt != NULL) {
889         delete regionFmt;
890     }
891     if (tzids != NULL) {
892         delete tzids;
893     }
894     ures_close(zoneItem);
895     ures_close(metazoneItem);
896     ures_close(zoneStringsArray);
897 }
898 
~ZoneStringFormat()899 ZoneStringFormat::~ZoneStringFormat() {
900 }
901 
902 SafeZoneStringFormatPtr*
getZoneStringFormat(const Locale & locale,UErrorCode & status)903 ZoneStringFormat::getZoneStringFormat(const Locale& locale, UErrorCode &status) {
904     umtx_lock(&gZSFCacheLock);
905     if (gZoneStringFormatCache == NULL) {
906         gZoneStringFormatCache = new ZSFCache(10 /* capacity */);
907         ucln_i18n_registerCleanup(UCLN_I18N_ZSFORMAT, zoneStringFormat_cleanup);
908     }
909     umtx_unlock(&gZSFCacheLock);
910 
911     return gZoneStringFormatCache->get(locale, status);
912 }
913 
914 
915 UnicodeString**
createZoneStringsArray(UDate date,int32_t & rowCount,int32_t & colCount,UErrorCode & status) const916 ZoneStringFormat::createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const {
917     if (U_FAILURE(status)) {
918         return NULL;
919     }
920     UnicodeString **result = NULL;
921     rowCount = 0;
922     colCount = 0;
923 
924     // Collect canonical time zone IDs
925     UVector canonicalIDs(uhash_deleteUnicodeString, uhash_compareUnicodeString, status);
926     if (U_FAILURE(status)) {
927         return NULL;
928     }
929     StringEnumeration *tzids = TimeZone::createEnumeration();
930     const UChar *tzid;
931     while ((tzid = tzids->unext(NULL, status))) {
932         if (U_FAILURE(status)) {
933             delete tzids;
934             return NULL;
935         }
936         UnicodeString utzid(tzid);
937         UnicodeString canonicalID;
938         ZoneMeta::getCanonicalID(UnicodeString(tzid), canonicalID);
939         if (utzid == canonicalID) {
940             canonicalIDs.addElement(new UnicodeString(utzid), status);
941             if (U_FAILURE(status)) {
942                 delete tzids;
943                 return NULL;
944             }
945         }
946     }
947     delete tzids;
948 
949     // Allocate array
950     result = (UnicodeString**)uprv_malloc(canonicalIDs.size() * sizeof(UnicodeString*));
951     if (result == NULL) {
952         status = U_MEMORY_ALLOCATION_ERROR;
953         return NULL;
954     }
955     for (int32_t i = 0; i < canonicalIDs.size(); i++) {
956         result[i] = new UnicodeString[8];
957         UnicodeString *id = (UnicodeString*)canonicalIDs.elementAt(i);
958         result[i][0].setTo(*id);
959         getLongStandard(*id, date, result[i][1]);
960         getShortStandard(*id, date, FALSE, result[i][2]);
961         getLongDaylight(*id, date, result[i][3]);
962         getShortDaylight(*id, date, FALSE, result[i][4]);
963         getGenericLocation(*id, result[i][5]);
964         getLongGenericNonLocation(*id, date, result[i][6]);
965         getShortGenericNonLocation(*id, date, FALSE, result[i][7]);
966     }
967 
968     rowCount = canonicalIDs.size();
969     colCount = 8;
970     return result;
971 }
972 
973 UnicodeString&
getSpecificLongString(const Calendar & cal,UnicodeString & result,UErrorCode & status) const974 ZoneStringFormat::getSpecificLongString(const Calendar &cal, UnicodeString &result,
975                                         UErrorCode &status) const {
976     result.remove();
977     if (U_FAILURE(status)) {
978         return result;
979     }
980     UnicodeString tzid;
981     cal.getTimeZone().getID(tzid);
982     UDate date = cal.getTime(status);
983     if (cal.get(UCAL_DST_OFFSET, status) == 0) {
984         return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /*not used*/, result);
985     } else {
986         return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /*not used*/, result);
987     }
988 }
989 
990 UnicodeString&
getSpecificShortString(const Calendar & cal,UBool commonlyUsedOnly,UnicodeString & result,UErrorCode & status) const991 ZoneStringFormat::getSpecificShortString(const Calendar &cal, UBool commonlyUsedOnly,
992                                          UnicodeString &result, UErrorCode &status) const {
993     result.remove();
994     if (U_FAILURE(status)) {
995         return result;
996     }
997     UnicodeString tzid;
998     cal.getTimeZone().getID(tzid);
999     UDate date = cal.getTime(status);
1000     if (cal.get(UCAL_DST_OFFSET, status) == 0) {
1001         return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result);
1002     } else {
1003         return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result);
1004     }
1005 }
1006 
1007 UnicodeString&
getGenericLongString(const Calendar & cal,UnicodeString & result,UErrorCode & status) const1008 ZoneStringFormat::getGenericLongString(const Calendar &cal, UnicodeString &result,
1009                                        UErrorCode &status) const {
1010     return getGenericString(cal, FALSE /*long*/, FALSE /* not used */, result, status);
1011 }
1012 
1013 UnicodeString&
getGenericShortString(const Calendar & cal,UBool commonlyUsedOnly,UnicodeString & result,UErrorCode & status) const1014 ZoneStringFormat::getGenericShortString(const Calendar &cal, UBool commonlyUsedOnly,
1015                                         UnicodeString &result, UErrorCode &status) const {
1016     return getGenericString(cal, TRUE /*short*/, commonlyUsedOnly, result, status);
1017 }
1018 
1019 UnicodeString&
getGenericLocationString(const Calendar & cal,UnicodeString & result,UErrorCode & status) const1020 ZoneStringFormat::getGenericLocationString(const Calendar &cal, UnicodeString &result,
1021                                            UErrorCode &status) const {
1022     UnicodeString tzid;
1023     cal.getTimeZone().getID(tzid);
1024     UDate date = cal.getTime(status);
1025     return getString(tzid, ZSIDX_LOCATION, date, FALSE /*not used*/, result);
1026 }
1027 
1028 const ZoneStringInfo*
findSpecificLong(const UnicodeString & text,int32_t start,int32_t & matchLength,UErrorCode & status) const1029 ZoneStringFormat::findSpecificLong(const UnicodeString &text, int32_t start,
1030                                    int32_t &matchLength, UErrorCode &status) const {
1031     return find(text, start, STANDARD_LONG | DAYLIGHT_LONG, matchLength, status);
1032 }
1033 
1034 const ZoneStringInfo*
findSpecificShort(const UnicodeString & text,int32_t start,int32_t & matchLength,UErrorCode & status) const1035 ZoneStringFormat::findSpecificShort(const UnicodeString &text, int32_t start,
1036                                     int32_t &matchLength, UErrorCode &status) const {
1037     return find(text, start, STANDARD_SHORT | DAYLIGHT_SHORT, matchLength, status);
1038 }
1039 
1040 const ZoneStringInfo*
findGenericLong(const UnicodeString & text,int32_t start,int32_t & matchLength,UErrorCode & status) const1041 ZoneStringFormat::findGenericLong(const UnicodeString &text, int32_t start,
1042                                   int32_t &matchLength, UErrorCode &status) const {
1043     return find(text, start, GENERIC_LONG | STANDARD_LONG | LOCATION, matchLength, status);
1044 }
1045 
1046 const ZoneStringInfo*
findGenericShort(const UnicodeString & text,int32_t start,int32_t & matchLength,UErrorCode & status) const1047 ZoneStringFormat::findGenericShort(const UnicodeString &text, int32_t start,
1048                                    int32_t &matchLength, UErrorCode &status) const {
1049     return find(text, start, GENERIC_SHORT | STANDARD_SHORT | LOCATION, matchLength, status);
1050 }
1051 
1052 const ZoneStringInfo*
findGenericLocation(const UnicodeString & text,int32_t start,int32_t & matchLength,UErrorCode & status) const1053 ZoneStringFormat::findGenericLocation(const UnicodeString &text, int32_t start,
1054                                       int32_t &matchLength, UErrorCode &status) const {
1055     return find(text, start, LOCATION, matchLength, status);
1056 }
1057 
1058 UnicodeString&
getString(const UnicodeString & tzid,TimeZoneTranslationTypeIndex typeIdx,UDate date,UBool commonlyUsedOnly,UnicodeString & result) const1059 ZoneStringFormat::getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date,
1060                             UBool commonlyUsedOnly, UnicodeString& result) const {
1061     result.remove();
1062 
1063     // ICU's own array does not have entries for aliases
1064     UnicodeString canonicalID;
1065     UErrorCode status = U_ZERO_ERROR;
1066     ZoneMeta::getCanonicalID(tzid, canonicalID);
1067 
1068     if (fTzidToStrings.count() > 0) {
1069         ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID);
1070         if (zstrings != NULL) {
1071             switch (typeIdx) {
1072                 case ZSIDX_LONG_STANDARD:
1073                 case ZSIDX_LONG_DAYLIGHT:
1074                 case ZSIDX_LONG_GENERIC:
1075                 case ZSIDX_LOCATION:
1076                     zstrings->getString(typeIdx, result);
1077                     break;
1078                 case ZSIDX_SHORT_STANDARD:
1079                 case ZSIDX_SHORT_DAYLIGHT:
1080                 case ZSIDX_COUNT: //added to avoid warning
1081                 case ZSIDX_SHORT_GENERIC:
1082                     if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
1083                         zstrings->getString(typeIdx, result);
1084                     }
1085                     break;
1086             }
1087         }
1088     }
1089     if (result.isEmpty() && fMzidToStrings.count() > 0 && typeIdx != ZSIDX_LOCATION) {
1090         // Try metazone
1091         UnicodeString mzid;
1092         ZoneMeta::getMetazoneID(canonicalID, date, mzid);
1093         if (!mzid.isEmpty()) {
1094             ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings.get(mzid);
1095             if (mzstrings != NULL) {
1096                 switch (typeIdx) {
1097                     case ZSIDX_LONG_STANDARD:
1098                     case ZSIDX_LONG_DAYLIGHT:
1099                     case ZSIDX_LONG_GENERIC:
1100                     case ZSIDX_LOCATION:
1101                         mzstrings->getString(typeIdx, result);
1102                         break;
1103                     case ZSIDX_SHORT_STANDARD:
1104                     case ZSIDX_SHORT_DAYLIGHT:
1105                     case ZSIDX_COUNT: //added to avoid warning
1106                     case ZSIDX_SHORT_GENERIC:
1107                         if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
1108                             mzstrings->getString(typeIdx, result);
1109                         }
1110                         break;
1111                 }
1112             }
1113         }
1114     }
1115     return result;
1116 }
1117 
1118 UnicodeString&
getGenericString(const Calendar & cal,UBool isShort,UBool commonlyUsedOnly,UnicodeString & result,UErrorCode & status) const1119 ZoneStringFormat::getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly,
1120                                    UnicodeString &result, UErrorCode &status) const {
1121     result.remove();
1122     UDate time = cal.getTime(status);
1123     if (U_FAILURE(status)) {
1124         return result;
1125     }
1126     const TimeZone &tz = cal.getTimeZone();
1127     UnicodeString tzid;
1128     tz.getID(tzid);
1129 
1130     // ICU's own array does not have entries for aliases
1131     UnicodeString canonicalID;
1132     ZoneMeta::getCanonicalID(tzid, canonicalID);
1133 
1134     ZoneStrings *zstrings = NULL;
1135     if (fTzidToStrings.count() > 0) {
1136         zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID);
1137         if (zstrings != NULL) {
1138             if (isShort) {
1139                 if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
1140                     zstrings->getString(ZSIDX_SHORT_GENERIC, result);
1141                 }
1142             } else {
1143                 zstrings->getString(ZSIDX_LONG_GENERIC, result);
1144             }
1145         }
1146     }
1147     if (result.isEmpty() && fMzidToStrings.count() > 0) {
1148         // try metazone
1149         int32_t raw, sav;
1150         UnicodeString mzid;
1151         ZoneMeta::getMetazoneID(canonicalID, time, mzid);
1152         if (!mzid.isEmpty()) {
1153             UBool useStandard = FALSE;
1154             sav = cal.get(UCAL_DST_OFFSET, status);
1155             if (U_FAILURE(status)) {
1156                 return result;
1157             }
1158             if (sav == 0) {
1159                 useStandard = TRUE;
1160                 // Check if the zone actually uses daylight saving time around the time
1161                 TimeZone *tmptz = tz.clone();
1162                 BasicTimeZone *btz = NULL;
1163                 if (tmptz->getDynamicClassID() == OlsonTimeZone::getStaticClassID()
1164                     || tmptz->getDynamicClassID() == SimpleTimeZone::getStaticClassID()
1165                     || tmptz->getDynamicClassID() == RuleBasedTimeZone::getStaticClassID()
1166                     || tmptz->getDynamicClassID() == VTimeZone::getStaticClassID()) {
1167                     btz = (BasicTimeZone*)tmptz;
1168                 }
1169 
1170                 if (btz != NULL) {
1171                     TimeZoneTransition before;
1172                     UBool beforTrs = btz->getPreviousTransition(time, TRUE, before);
1173                     if (beforTrs
1174                             && (time - before.getTime() < kDstCheckRange)
1175                             && before.getFrom()->getDSTSavings() != 0) {
1176                         useStandard = FALSE;
1177                     } else {
1178                         TimeZoneTransition after;
1179                         UBool afterTrs = btz->getNextTransition(time, FALSE, after);
1180                         if (afterTrs
1181                                 && (after.getTime() - time < kDstCheckRange)
1182                                 && after.getTo()->getDSTSavings() != 0) {
1183                             useStandard = FALSE;
1184                         }
1185                     }
1186                 } else {
1187                     // If not BasicTimeZone... only if the instance is not an ICU's implementation.
1188                     // We may get a wrong answer in edge case, but it should practically work OK.
1189                     tmptz->getOffset(time - kDstCheckRange, FALSE, raw, sav, status);
1190                     if (sav != 0) {
1191                         useStandard = FALSE;
1192                     } else {
1193                         tmptz->getOffset(time + kDstCheckRange, FALSE, raw, sav, status);
1194                         if (sav != 0){
1195                             useStandard = FALSE;
1196                         }
1197                     }
1198                     if (U_FAILURE(status)) {
1199                         delete tmptz;
1200                         result.remove();
1201                         return result;
1202                     }
1203                 }
1204                 delete tmptz;
1205             }
1206             if (useStandard) {
1207                 getString(canonicalID, (isShort ? ZSIDX_SHORT_STANDARD : ZSIDX_LONG_STANDARD),
1208                     time, commonlyUsedOnly, result);
1209 
1210                 // Note:
1211                 // In CLDR 1.5.1, a same localization is used for both generic and standard
1212                 // for some metazones in some locales.  This is actually data bugs and should
1213                 // be resolved in later versions of CLDR.  For now, we check if the standard
1214                 // name is different from its generic name below.
1215                 if (!result.isEmpty()) {
1216                     UnicodeString genericNonLocation;
1217                     getString(canonicalID, (isShort ? ZSIDX_SHORT_GENERIC : ZSIDX_LONG_GENERIC),
1218                         time, commonlyUsedOnly, genericNonLocation);
1219                     if (!genericNonLocation.isEmpty() && result == genericNonLocation) {
1220                         result.remove();
1221                     }
1222                 }
1223             }
1224             if (result.isEmpty()) {
1225                 ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings.get(mzid);
1226                 if (mzstrings != NULL) {
1227                     if (isShort) {
1228                         if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
1229                             mzstrings->getString(ZSIDX_SHORT_GENERIC, result);
1230                         }
1231                     } else {
1232                         mzstrings->getString(ZSIDX_LONG_GENERIC, result);
1233                     }
1234                 }
1235                 if (!result.isEmpty()) {
1236                     // Check if the offsets at the given time matches the preferred zone's offsets
1237                     UnicodeString preferredId;
1238                     UnicodeString region;
1239                     ZoneMeta::getZoneIdByMetazone(mzid, getRegion(region), preferredId);
1240                     if (canonicalID != preferredId) {
1241                         // Check if the offsets at the given time are identical with the preferred zone
1242                         raw = cal.get(UCAL_ZONE_OFFSET, status);
1243                         if (U_FAILURE(status)) {
1244                             result.remove();
1245                             return result;
1246                         }
1247                         TimeZone *preferredZone = TimeZone::createTimeZone(preferredId);
1248                         int32_t prfRaw, prfSav;
1249                         // Check offset in preferred time zone with wall time.
1250                         // With getOffset(time, false, preferredOffsets),
1251                         // you may get incorrect results because of time overlap at DST->STD
1252                         // transition.
1253                         preferredZone->getOffset(time + raw + sav, TRUE, prfRaw, prfSav, status);
1254                         delete preferredZone;
1255 
1256                         if (U_FAILURE(status)) {
1257                             result.remove();
1258                             return result;
1259                         }
1260                         if ((raw != prfRaw || sav != prfSav) && zstrings != NULL) {
1261                             // Use generic partial location string as fallback
1262                             zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
1263                         }
1264                     }
1265                 }
1266             }
1267         }
1268     }
1269     if (result.isEmpty()) {
1270         // Use location format as the final fallback
1271         getString(canonicalID, ZSIDX_LOCATION, time, FALSE /*not used*/, result);
1272     }
1273 
1274     return result;
1275 }
1276 
1277 UnicodeString&
getGenericPartialLocationString(const UnicodeString & tzid,UBool isShort,UDate date,UBool commonlyUsedOnly,UnicodeString & result) const1278 ZoneStringFormat::getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort,
1279                                                   UDate date, UBool commonlyUsedOnly, UnicodeString &result) const {
1280     result.remove();
1281     if (fTzidToStrings.count() <= 0) {
1282         return result;
1283     }
1284 
1285     UnicodeString canonicalID;
1286     UErrorCode status = U_ZERO_ERROR;
1287     ZoneMeta::getCanonicalID(tzid, canonicalID);
1288 
1289     UnicodeString mzid;
1290     ZoneMeta::getMetazoneID(canonicalID, date, mzid);
1291 
1292     if (!mzid.isEmpty()) {
1293         ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID);
1294         if (zstrings != NULL) {
1295             zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
1296         }
1297     }
1298     return result;
1299 }
1300 
1301 const ZoneStringInfo*
find(const UnicodeString & text,int32_t start,int32_t types,int32_t & matchLength,UErrorCode & status) const1302 ZoneStringFormat::find(const UnicodeString &text, int32_t start, int32_t types,
1303                        int32_t &matchLength, UErrorCode &status) const {
1304     matchLength = 0;
1305     if (U_FAILURE(status)) {
1306         return NULL;
1307     }
1308     if (fZoneStringsTrie.isEmpty()) {
1309         return NULL;
1310     }
1311     const ZoneStringInfo *result = NULL;
1312     const ZoneStringInfo *fallback = NULL;
1313     int32_t fallbackMatchLen = 0;
1314 
1315     ZoneStringSearchResultHandler handler(status);
1316     fZoneStringsTrie.search(text, start, (TextTrieMapSearchResultHandler*)&handler, status);
1317     if (U_SUCCESS(status)) {
1318         int32_t numMatches = handler.countMatches();
1319         for (int32_t i = 0; i < numMatches; i++) {
1320             int32_t tmpMatchLen;
1321             const ZoneStringInfo *tmp = handler.getMatch(i, tmpMatchLen);
1322             if ((types & tmp->fType) != 0) {
1323                 if (result == NULL || matchLength < tmpMatchLen) {
1324                     result = tmp;
1325                     matchLength = tmpMatchLen;
1326                 } else if (matchLength == tmpMatchLen) {
1327                     // Tie breaker - there are some examples that a
1328                     // long standard name is identical with a location
1329                     // name - for example, "Uruguay Time".  In this case,
1330                     // we interpret it as generic, not specific.
1331                     if (tmp->isGeneric() && !result->isGeneric()) {
1332                         result = tmp;
1333                     }
1334                 }
1335             } else if (result == NULL) {
1336                 if (fallback == NULL || fallbackMatchLen < tmpMatchLen) {
1337                     fallback = tmp;
1338                     fallbackMatchLen = tmpMatchLen;
1339                 } else if (fallbackMatchLen == tmpMatchLen) {
1340                     if (tmp->isGeneric() && !fallback->isGeneric()) {
1341                         fallback = tmp;
1342                     }
1343                 }
1344             }
1345         }
1346         if (result == NULL && fallback != NULL) {
1347             result = fallback;
1348             matchLength = fallbackMatchLen;
1349         }
1350     }
1351     return result;
1352 }
1353 
1354 
1355 UnicodeString&
getRegion(UnicodeString & region) const1356 ZoneStringFormat::getRegion(UnicodeString &region) const {
1357     const char* country = fLocale.getCountry();
1358     // TODO: Utilize addLikelySubtag in Locale to resolve default region
1359     // when the implementation is ready.
1360     region.setTo(UnicodeString(country, -1, US_INV));
1361     return region;
1362 }
1363 
1364 MessageFormat*
getFallbackFormat(const Locale & locale,UErrorCode & status)1365 ZoneStringFormat::getFallbackFormat(const Locale &locale, UErrorCode &status) {
1366     if (U_FAILURE(status)) {
1367         return NULL;
1368     }
1369     UnicodeString pattern(TRUE, gDefFallbackPattern, -1);
1370     UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status);
1371     zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
1372     int32_t len;
1373     const UChar *flbkfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gFallbackFormatTag, &len, &status);
1374     if (U_SUCCESS(status)) {
1375         pattern.setTo(flbkfmt);
1376     } else {
1377         status = U_ZERO_ERROR;
1378     }
1379     ures_close(zoneStringsArray);
1380 
1381     MessageFormat *fallbackFmt = new MessageFormat(pattern, status);
1382     return fallbackFmt;
1383 }
1384 
1385 MessageFormat*
getRegionFormat(const Locale & locale,UErrorCode & status)1386 ZoneStringFormat::getRegionFormat(const Locale& locale, UErrorCode &status) {
1387     if (U_FAILURE(status)) {
1388         return NULL;
1389     }
1390     UnicodeString pattern(TRUE, gDefRegionPattern, -1);
1391     UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status);
1392     zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
1393     int32_t len;
1394     const UChar *regionfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gRegionFormatTag, &len, &status);
1395     if (U_SUCCESS(status)) {
1396         pattern.setTo(regionfmt);
1397     } else {
1398         status = U_ZERO_ERROR;
1399     }
1400     ures_close(zoneStringsArray);
1401 
1402     MessageFormat *regionFmt = new MessageFormat(pattern, status);
1403     return regionFmt;
1404 }
1405 
1406 const UChar*
getZoneStringFromBundle(const UResourceBundle * zoneitem,const char * key)1407 ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key) {
1408     const UChar *str = NULL;
1409     if (zoneitem != NULL) {
1410         UErrorCode status = U_ZERO_ERROR;
1411         int32_t len;
1412         str = ures_getStringByKeyWithFallback(zoneitem, key, &len, &status);
1413         if (U_FAILURE(status)) {
1414             str = NULL;
1415         }
1416     }
1417     return str;
1418 }
1419 
1420 UBool
isCommonlyUsed(const UResourceBundle * zoneitem)1421 ZoneStringFormat::isCommonlyUsed(const UResourceBundle *zoneitem) {
1422     if (zoneitem == NULL) {
1423         return TRUE;
1424     }
1425 
1426     UBool commonlyUsed = FALSE;
1427     UErrorCode status = U_ZERO_ERROR;
1428     UResourceBundle *cuRes = ures_getByKey(zoneitem, gCommonlyUsedTag, NULL, &status);
1429     int32_t cuValue = ures_getInt(cuRes, &status);
1430     if (U_SUCCESS(status)) {
1431         if (cuValue == 1) {
1432             commonlyUsed = TRUE;
1433         }
1434     }
1435     ures_close(cuRes);
1436     return commonlyUsed;
1437 }
1438 
1439 UnicodeString&
getLocalizedCountry(const UnicodeString & countryCode,const Locale & locale,UnicodeString & displayCountry)1440 ZoneStringFormat::getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale, UnicodeString &displayCountry) {
1441     // We do not want to use display country names only from the target language bundle
1442     // Note: we should do this in better way.
1443     displayCountry.remove();
1444     int32_t ccLen = countryCode.length();
1445     if (ccLen > 0 && ccLen < ULOC_COUNTRY_CAPACITY) {
1446         UErrorCode status = U_ZERO_ERROR;
1447         UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
1448         if (U_SUCCESS(status)) {
1449             const char *bundleLocStr = ures_getLocale(localeBundle, &status);
1450             if (U_SUCCESS(status) && uprv_strlen(bundleLocStr) > 0) {
1451                 Locale bundleLoc(bundleLocStr);
1452                 if (uprv_strcmp(bundleLocStr, "root") != 0 && uprv_strcmp(bundleLoc.getLanguage(), locale.getLanguage()) == 0) {
1453                     // Create a fake locale strings
1454                     char tmpLocStr[ULOC_COUNTRY_CAPACITY + 3];
1455                     uprv_strcpy(tmpLocStr, "xx_");
1456                     u_UCharsToChars(countryCode.getBuffer(), &tmpLocStr[3], ccLen);
1457                     tmpLocStr[3 + ccLen] = 0;
1458 
1459                     Locale tmpLoc(tmpLocStr);
1460                     tmpLoc.getDisplayCountry(locale, displayCountry);
1461                 }
1462             }
1463         }
1464         ures_close(localeBundle);
1465     }
1466     if (displayCountry.isEmpty()) {
1467         // Use the country code as the fallback
1468         displayCountry.setTo(countryCode);
1469     }
1470     return displayCountry;
1471 }
1472 
1473 // ----------------------------------------------------------------------------
1474 /*
1475  * This constructor adopts the input UnicodeString arrays.
1476  */
ZoneStrings(UnicodeString * strings,int32_t stringsCount,UBool commonlyUsed,UnicodeString ** genericPartialLocationStrings,int32_t genericRowCount,int32_t genericColCount)1477 ZoneStrings::ZoneStrings(UnicodeString *strings, int32_t stringsCount, UBool commonlyUsed,
1478        UnicodeString **genericPartialLocationStrings, int32_t genericRowCount, int32_t genericColCount)
1479 : fStrings(strings), fStringsCount(stringsCount), fIsCommonlyUsed(commonlyUsed),
1480   fGenericPartialLocationStrings(genericPartialLocationStrings),
1481   fGenericPartialLocationRowCount(genericRowCount), fGenericPartialLocationColCount(genericColCount) {
1482 }
1483 
~ZoneStrings()1484 ZoneStrings::~ZoneStrings() {
1485     if (fStrings != NULL) {
1486         delete[] fStrings;
1487     }
1488     if (fGenericPartialLocationStrings != NULL) {
1489         for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
1490             delete[] fGenericPartialLocationStrings[i];
1491         }
1492         uprv_free(fGenericPartialLocationStrings);
1493     }
1494 }
1495 
1496 
1497 UnicodeString&
getString(int32_t typeIdx,UnicodeString & result) const1498 ZoneStrings::getString(int32_t typeIdx, UnicodeString &result) const {
1499     if (typeIdx >= 0 && typeIdx < fStringsCount) {
1500         result.setTo(fStrings[typeIdx]);
1501     } else {
1502         result.remove();
1503     }
1504     return result;
1505 }
1506 
1507 UnicodeString&
getGenericPartialLocationString(const UnicodeString & mzid,UBool isShort,UBool commonlyUsedOnly,UnicodeString & result) const1508 ZoneStrings::getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort,
1509                                         UBool commonlyUsedOnly, UnicodeString &result) const {
1510     UBool isSet = FALSE;
1511     if (fGenericPartialLocationColCount >= 2) {
1512         for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
1513             if (fGenericPartialLocationStrings[i][0] == mzid) {
1514                 if (isShort) {
1515                     if (fGenericPartialLocationColCount >= 3) {
1516                         if (!commonlyUsedOnly ||
1517                             fGenericPartialLocationColCount == 3 || fGenericPartialLocationStrings[i][3].length() != 0) {
1518                             result.setTo(fGenericPartialLocationStrings[i][2]);
1519                             isSet = TRUE;
1520                         }
1521                     }
1522                 } else {
1523                     result.setTo(fGenericPartialLocationStrings[i][1]);
1524                     isSet = TRUE;
1525                 }
1526                 break;
1527             }
1528         }
1529     }
1530     if (!isSet) {
1531         result.remove();
1532     }
1533     return result;
1534 }
1535 
1536 // --------------------------------------------------------------
SafeZoneStringFormatPtr(ZSFCacheEntry * cacheEntry)1537 SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry)
1538 : fCacheEntry(cacheEntry) {
1539 }
1540 
~SafeZoneStringFormatPtr()1541 SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() {
1542     fCacheEntry->delRef();
1543 }
1544 
1545 const ZoneStringFormat*
get() const1546 SafeZoneStringFormatPtr::get() const {
1547     return fCacheEntry->getZoneStringFormat();
1548 }
1549 
ZSFCacheEntry(const Locale & locale,ZoneStringFormat * zsf,ZSFCacheEntry * next)1550 ZSFCacheEntry::ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next)
1551 : fLocale(locale), fZoneStringFormat(zsf),
1552  fNext(next), fRefCount(1)
1553 {
1554 }
1555 
~ZSFCacheEntry()1556 ZSFCacheEntry::~ZSFCacheEntry () {
1557     delete fZoneStringFormat;
1558 }
1559 
1560 const ZoneStringFormat*
getZoneStringFormat(void)1561 ZSFCacheEntry::getZoneStringFormat(void) {
1562     return (const ZoneStringFormat*)fZoneStringFormat;
1563 }
1564 
1565 void
delRef(void)1566 ZSFCacheEntry::delRef(void) {
1567     umtx_lock(&gZSFCacheLock);
1568     --fRefCount;
1569     umtx_unlock(&gZSFCacheLock);
1570 }
1571 
ZSFCache(int32_t capacity)1572 ZSFCache::ZSFCache(int32_t capacity)
1573 : fCapacity(capacity), fFirst(NULL) {
1574 }
1575 
~ZSFCache()1576 ZSFCache::~ZSFCache() {
1577     ZSFCacheEntry *entry = fFirst;
1578     while (entry) {
1579         ZSFCacheEntry *next = entry->fNext;
1580         delete entry;
1581         entry = next;
1582     }
1583 }
1584 
1585 SafeZoneStringFormatPtr*
get(const Locale & locale,UErrorCode & status)1586 ZSFCache::get(const Locale &locale, UErrorCode &status) {
1587     SafeZoneStringFormatPtr *result = NULL;
1588 
1589     // Search the cache entry list
1590     ZSFCacheEntry *entry = NULL;
1591     ZSFCacheEntry *next, *prev;
1592 
1593     umtx_lock(&gZSFCacheLock);
1594     entry = fFirst;
1595     prev = NULL;
1596     while (entry) {
1597         next = entry->fNext;
1598         if (entry->fLocale == locale) {
1599             // Add reference count
1600             entry->fRefCount++;
1601 
1602             // move the entry to the top
1603             if (entry != fFirst) {
1604                 prev->fNext = next;
1605                 entry->fNext = fFirst;
1606                 fFirst = entry;
1607             }
1608             break;
1609         }
1610         prev = entry;
1611         entry = next;
1612     }
1613     umtx_unlock(&gZSFCacheLock);
1614 
1615     // Create a new ZoneStringFormat
1616     if (entry == NULL) {
1617         ZoneStringFormat *zsf = new ZoneStringFormat(locale, status);
1618         if (U_FAILURE(status)) {
1619             delete zsf;
1620             return NULL;
1621         }
1622         if (zsf == NULL) {
1623             status = U_MEMORY_ALLOCATION_ERROR;
1624             return NULL;
1625         }
1626         // Now add the new entry
1627         umtx_lock(&gZSFCacheLock);
1628         // Make sure no other threads already created the one for the same locale
1629         entry = fFirst;
1630         prev = NULL;
1631         while (entry) {
1632             next = entry->fNext;
1633             if (entry->fLocale == locale) {
1634                 // Add reference count
1635                 entry->fRefCount++;
1636 
1637                 // move the entry to the top
1638                 if (entry != fFirst) {
1639                     prev->fNext = next;
1640                     entry->fNext = fFirst;
1641                     fFirst = entry;
1642                 }
1643                 break;
1644             }
1645             prev = entry;
1646             entry = next;
1647         }
1648         if (entry == NULL) {
1649             // Add the new one to the top
1650             next = fFirst;
1651             entry = new ZSFCacheEntry(locale, zsf, next);
1652             fFirst = entry;
1653         } else {
1654             delete zsf;
1655         }
1656         umtx_unlock(&gZSFCacheLock);
1657     }
1658 
1659     result = new SafeZoneStringFormatPtr(entry);
1660 
1661     // Now, delete unused cache entries beyond the capacity
1662     umtx_lock(&gZSFCacheLock);
1663     entry = fFirst;
1664     prev = NULL;
1665     int32_t idx = 1;
1666     while (entry) {
1667         next = entry->fNext;
1668         if (idx >= fCapacity && entry->fRefCount == 0) {
1669             if (entry == fFirst) {
1670                 fFirst = next;
1671             } else {
1672                 prev->fNext = next;
1673             }
1674             delete entry;
1675         } else {
1676             prev = entry;
1677         }
1678         entry = next;
1679         idx++;
1680     }
1681     umtx_unlock(&gZSFCacheLock);
1682 
1683     return result;
1684 }
1685 
1686 U_NAMESPACE_END
1687 
1688 #endif /* #if !UCONFIG_NO_FORMATTING */
1689