• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
zoneStringFormat_cleanup(void)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
deleteZoneStringInfo(void * obj)58 deleteZoneStringInfo(void *obj) {
59     delete (U_NAMESPACE_QUALIFIER ZoneStringInfo*)obj;
60 }
61 
62 /**
63  * Deleter for ZoneStrings
64  */
65 static void U_CALLCONV
deleteZoneStrings(void * obj)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
getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type)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
getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx)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 // ----------------------------------------------------------------------------
clear()163 void CharacterNode::clear() {
164     uprv_memset(this, 0, sizeof(*this));
165 }
166 
deleteValues()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
addValue(void * value,UErrorCode & status)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
~TextTrieMapSearchResultHandler()206 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
207 }
208 
209 // ----------------------------------------------------------------------------
TextTrieMap(UBool ignoreCase)210 TextTrieMap::TextTrieMap(UBool ignoreCase)
211 : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0),
212   fLazyContents(NULL), fIsEmpty(TRUE) {
213 }
214 
~TextTrieMap()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 
isEmpty() const230 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
put(const UnicodeString & key,void * value,ZSFStringPool & sp,UErrorCode & status)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
putImpl(const UnicodeString & key,void * value,UErrorCode & status)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
growNodes()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*
addChildNode(CharacterNode * parent,UChar c,UErrorCode & status)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*
getChildNode(CharacterNode * parent,UChar c) const356 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.
buildTrie(UErrorCode & status)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
search(const UnicodeString & text,int32_t start,TextTrieMapSearchResultHandler * handler,UErrorCode & status) const396 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
search(CharacterNode * node,const UnicodeString & text,int32_t start,int32_t index,TextTrieMapSearchResultHandler * handler,UErrorCode & status) const411 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 // ----------------------------------------------------------------------------
ZoneStringInfo(const UnicodeString & id,const UnicodeString & str,TimeZoneTranslationType type,ZSFStringPool & sp,UErrorCode & status)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 
~ZoneStringInfo()454 ZoneStringInfo::~ZoneStringInfo() {
455 }
456 
457 
458 // ----------------------------------------------------------------------------
ZoneStringSearchResultHandler(UErrorCode & status)459 ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode &status)
460 : fResults(status)
461 {
462     clear();
463 }
464 
~ZoneStringSearchResultHandler()465 ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() {
466     clear();
467 }
468 
469 UBool
handleMatch(int32_t matchLength,const CharacterNode * node,UErrorCode & status)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
countMatches(void)507 ZoneStringSearchResultHandler::countMatches(void) {
508     return fResults.size();
509 }
510 
511 const ZoneStringInfo*
getMatch(int32_t index,int32_t & matchLength)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
clear(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 // ----------------------------------------------------------------------------
ZoneStringFormat(const UnicodeString * const * strings,int32_t rowCount,int32_t columnCount,UErrorCode & status)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 
ZoneStringFormat(const Locale & locale,UErrorCode & status)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
loadZone(const UnicodeString & utzid,UErrorCode & status)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
addSingleZone(UnicodeString & utzid,UErrorCode & status)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
loadFull(UErrorCode & status)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 
~ZoneStringFormat()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*
getZoneStringFormat(const Locale & locale,UErrorCode & status)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**
createZoneStringsArray(UDate date,int32_t & rowCount,int32_t & colCount,UErrorCode & status) const1190 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&
getSpecificLongString(const Calendar & cal,UnicodeString & result,UErrorCode & status) const1256 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&
getSpecificShortString(const Calendar & cal,UBool commonlyUsedOnly,UnicodeString & result,UErrorCode & status) const1273 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&
getGenericLongString(const Calendar & cal,UnicodeString & result,UErrorCode & status) const1290 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&
getGenericShortString(const Calendar & cal,UBool commonlyUsedOnly,UnicodeString & result,UErrorCode & status) const1296 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&
getGenericLocationString(const Calendar & cal,UnicodeString & result,UErrorCode & status) const1302 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*
findSpecificLong(const UnicodeString & text,int32_t start,int32_t & matchLength,UErrorCode & status) const1311 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*
findSpecificShort(const UnicodeString & text,int32_t start,int32_t & matchLength,UErrorCode & status) const1317 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*
findGenericLong(const UnicodeString & text,int32_t start,int32_t & matchLength,UErrorCode & status) const1323 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*
findGenericShort(const UnicodeString & text,int32_t start,int32_t & matchLength,UErrorCode & status) const1329 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*
findGenericLocation(const UnicodeString & text,int32_t start,int32_t & matchLength,UErrorCode & status) const1335 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&
getString(const UnicodeString & tzid,TimeZoneTranslationTypeIndex typeIdx,UDate date,UBool commonlyUsedOnly,UnicodeString & result) const1341 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&
getGenericString(const Calendar & cal,UBool isShort,UBool commonlyUsedOnly,UnicodeString & result,UErrorCode & status) const1414 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&
getGenericPartialLocationString(const UnicodeString & tzid,UBool isShort,UDate date,UBool commonlyUsedOnly,UnicodeString & result) const1584 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*
find(const UnicodeString & text,int32_t start,int32_t types,int32_t & matchLength,UErrorCode & status) const1619 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*
subFind(const UnicodeString & text,int32_t start,int32_t types,int32_t & matchLength,UErrorCode & status) const1660 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&
getRegion(UnicodeString & region) const1715 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*
getFallbackFormat(const Locale & locale,UErrorCode & status)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*
getRegionFormat(const Locale & locale,UErrorCode & status)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*
getZoneStringFromBundle(const UResourceBundle * zoneitem,const char * key)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
isCommonlyUsed(const UResourceBundle * zoneitem)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&
getLocalizedCountry(const UnicodeString & countryCode,const Locale & locale,UnicodeString & displayCountry)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  */
ZoneStrings(UnicodeString * strings,int32_t stringsCount,UBool commonlyUsed,UnicodeString ** genericPartialLocationStrings,int32_t genericRowCount,int32_t genericColCount,ZSFStringPool & sp,UErrorCode & status)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 
~ZoneStrings()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&
getString(int32_t typeIdx,UnicodeString & result) const1905 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&
getGenericPartialLocationString(const UnicodeString & mzid,UBool isShort,UBool commonlyUsedOnly,UnicodeString & result) const1915 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 // --------------------------------------------------------------
SafeZoneStringFormatPtr(ZSFCacheEntry * cacheEntry)1945 SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry)
1946 : fCacheEntry(cacheEntry) {
1947 }
1948 
~SafeZoneStringFormatPtr()1949 SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() {
1950     fCacheEntry->delRef();
1951 }
1952 
1953 const ZoneStringFormat*
get() const1954 SafeZoneStringFormatPtr::get() const {
1955     return fCacheEntry->getZoneStringFormat();
1956 }
1957 
ZSFCacheEntry(const Locale & locale,ZoneStringFormat * zsf,ZSFCacheEntry * next)1958 ZSFCacheEntry::ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next)
1959 : fLocale(locale), fZoneStringFormat(zsf),
1960  fNext(next), fRefCount(1)
1961 {
1962 }
1963 
~ZSFCacheEntry()1964 ZSFCacheEntry::~ZSFCacheEntry () {
1965     delete fZoneStringFormat;
1966 }
1967 
1968 const ZoneStringFormat*
getZoneStringFormat(void)1969 ZSFCacheEntry::getZoneStringFormat(void) {
1970     return (const ZoneStringFormat*)fZoneStringFormat;
1971 }
1972 
1973 void
delRef(void)1974 ZSFCacheEntry::delRef(void) {
1975     umtx_lock(&gZSFCacheLock);
1976     --fRefCount;
1977     umtx_unlock(&gZSFCacheLock);
1978 }
1979 
ZSFCache(int32_t capacity)1980 ZSFCache::ZSFCache(int32_t capacity)
1981 : fCapacity(capacity), fFirst(NULL) {
1982 }
1983 
~ZSFCache()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*
get(const Locale & locale,UErrorCode & status)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 
ZSFStringPoolChunk()2110 ZSFStringPoolChunk::ZSFStringPoolChunk() {
2111     fNext = NULL;
2112     fLimit = 0;
2113 }
2114 
ZSFStringPool(UErrorCode & status)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 
~ZSFStringPool()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 
get(const UChar * s,UErrorCode & status)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.
adopt(const UChar * s,UErrorCode & status)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 
get(const UnicodeString & s,UErrorCode & status)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  */
freeze()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