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