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 ®ion) 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