]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/zstrfmt.cpp
ICU-400.42.tar.gz
[apple/icu.git] / icuSources / i18n / zstrfmt.cpp
CommitLineData
46f4442e
A
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 */
34static UMTX gZSFCacheLock = NULL;
35static U_NAMESPACE_QUALIFIER ZSFCache *gZoneStringFormatCache = NULL;
36
37U_CDECL_BEGIN
38/**
39 * ZoneStringFormatCache cleanup callback func
40 */
41static 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 */
55static void U_CALLCONV
56deleteZoneStringInfo(void *obj) {
57 delete (U_NAMESPACE_QUALIFIER ZoneStringInfo*)obj;
58}
59
60/**
61 * Deleter for ZoneStrings
62 */
63static void U_CALLCONV
64deleteZoneStrings(void *obj) {
65 delete (U_NAMESPACE_QUALIFIER ZoneStrings*)obj;
66}
67U_CDECL_END
68
69U_NAMESPACE_BEGIN
70
71#define ZID_KEY_MAX 128
72
73static const char gCountriesTag[] = "Countries";
74static const char gZoneStringsTag[] = "zoneStrings";
75static const char gShortGenericTag[] = "sg";
76static const char gShortStandardTag[] = "ss";
77static const char gShortDaylightTag[] = "sd";
78static const char gLongGenericTag[] = "lg";
79static const char gLongStandardTag[] = "ls";
80static const char gLongDaylightTag[] = "ld";
81static const char gExemplarCityTag[] = "ec";
82static const char gCommonlyUsedTag[] = "cu";
83static const char gFallbackFormatTag[] = "fallbackFormat";
84static const char gRegionFormatTag[] = "regionFormat";
85
86#define MZID_PREFIX_LEN 5
87static const char gMetazoneIdPrefix[] = "meta:";
88
89#define MAX_METAZONES_PER_ZONE 10
90
91static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
92static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
93static const UChar gCommonlyUsedTrue[] = {0x31, 0x00}; // "1"
94
95static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY;
96
97static int32_t
98getTimeZoneTranslationTypeIndex(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
126static int32_t
127getTimeZoneTranslationType(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// ----------------------------------------------------------------------------
160void CharacterNode::clear() {
161 uprv_memset(this, 0, sizeof(*this));
162}
163
164void 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
174void
175CharacterNode::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
203TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
204}
205
206// ----------------------------------------------------------------------------
207TextTrieMap::TextTrieMap(UBool ignoreCase)
208: fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0) {
209}
210
211TextTrieMap::~TextTrieMap() {
212 int32_t index;
213 for (index = 0; index < fNodesCount; ++index) {
214 fNodes[index].deleteValues();
215 }
216 uprv_free(fNodes);
217}
218
219void
220TextTrieMap::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
249UBool
250TextTrieMap::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
269CharacterNode*
270TextTrieMap::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
313CharacterNode*
314TextTrieMap::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
330void
331TextTrieMap::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
339void
340TextTrieMap::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// ----------------------------------------------------------------------------
376ZoneStringInfo::ZoneStringInfo(const UnicodeString &id, const UnicodeString &str,
377 TimeZoneTranslationType type)
378: fId(id), fStr(str), fType(type) {
379}
380
381ZoneStringInfo::~ZoneStringInfo() {
382}
383// ----------------------------------------------------------------------------
384ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode &status)
385: fResults(status)
386{
387 clear();
388}
389
390ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() {
391 clear();
392}
393
394UBool
395ZoneStringSearchResultHandler::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
431int32_t
432ZoneStringSearchResultHandler::countMatches(void) {
433 return fResults.size();
434}
435
436const ZoneStringInfo*
437ZoneStringSearchResultHandler::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
446void
447ZoneStringSearchResultHandler::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// ----------------------------------------------------------------------------
454ZoneStringFormat::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
526error_cleanup:
527 return;
528}
529
530ZoneStringFormat::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 TimeZone::getCanonicalID(utzid, canonicalID, status);
588 if (U_FAILURE(status)) {
589 // Ignore unknown ID - we should not get here, but just in case.
590 status = U_ZERO_ERROR;
591 continue;
592 }
593 if (utzid != canonicalID) {
594 continue;
595 }
596
597 uprv_strcpy(zidkey, tzid);
598
599 // Replace '/' with ':'
600 char *pCity = NULL;
601 char *p = zidkey;
602 while (*p) {
603 if (*p == '/') {
604 *p = ':';
605 pCity = p + 1;
606 }
607 p++;
608 }
609
610 if (zoneStringsArray != NULL) {
611 zoneItem = ures_getByKeyWithFallback(zoneStringsArray, zidkey, zoneItem, &status);
612 if (U_FAILURE(status)) {
613 // If failed to open the zone item, create only location string
614 ures_close(zoneItem);
615 zoneItem = NULL;
616 status = U_ZERO_ERROR;
617 }
618 }
619 zstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(zoneItem, gLongStandardTag);
620 zstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(zoneItem, gShortStandardTag);
621 zstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gLongDaylightTag);
622 zstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gShortDaylightTag);
623 zstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(zoneItem, gLongGenericTag);
624 zstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(zoneItem, gShortGenericTag);
625
626 // Compose location format string
627 UnicodeString location;
628 UnicodeString country;
629 UnicodeString city;
630 UnicodeString countryCode;
631 ZoneMeta::getCanonicalCountry(utzid, countryCode);
632 if (countryCode.isEmpty()) {
633 zstrarray[ZSIDX_LOCATION] = NULL;
634 } else {
635 const UChar* tmpCity = getZoneStringFromBundle(zoneItem, gExemplarCityTag);
636 if (tmpCity != NULL) {
637 city.setTo(TRUE, tmpCity, -1);
638 } else {
639 city.setTo(UnicodeString(pCity, -1, US_INV));
640 // Replace '_' with ' '
641 for (int32_t i = 0; i < city.length(); i++) {
642 if (city.charAt(i) == (UChar)0x5F /*'_'*/) {
643 city.setCharAt(i, (UChar)0x20 /*' '*/);
644 }
645 }
646 }
647 getLocalizedCountry(countryCode, locale, country);
648 UnicodeString singleCountry;
649 ZoneMeta::getSingleCountry(utzid, singleCountry);
650 FieldPosition fpos;
651 if (singleCountry.isEmpty()) {
652 Formattable params [] = {
653 Formattable(city),
654 Formattable(country)
655 };
656 fallbackFmt->format(params, 2, location, fpos, status);
657 } else {
658 // If the zone is only one zone in the country, do not add city
659 Formattable params [] = {
660 Formattable(country)
661 };
662 regionFmt->format(params, 1, location, fpos, status);
663 }
664 if (U_FAILURE(status)) {
665 goto error_cleanup;
666 }
667
668 // Workaround for reducing UMR warning in Purify.
669 // Append NULL before calling getTerminatedBuffer()
670 int32_t locLen = location.length();
671 location.append((UChar)0).truncate(locLen);
672
673 zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer();
674 }
675
676 UBool commonlyUsed = isCommonlyUsed(zoneItem);
677
678 // Resolve metazones used by this zone
679 int32_t mzPartialLocIdx = 0;
680 const UVector *metazoneMappings = ZoneMeta::getMetazoneMappings(utzid);
681 if (metazoneMappings != NULL) {
682 for (int32_t i = 0; i < metazoneMappings->size(); i++) {
683 const OlsonToMetaMappingEntry *mzmap = (const OlsonToMetaMappingEntry*)metazoneMappings->elementAt(i);
684 UnicodeString mzid(mzmap->mzid);
685 const ZoneStrings *mzStrings = (const ZoneStrings*)fMzidToStrings.get(mzid);
686 if (mzStrings == NULL) {
687 // If the metazone strings are not yet processed, do it now.
688 char mzidkey[ZID_KEY_MAX];
689 uprv_strcpy(mzidkey, gMetazoneIdPrefix);
690 u_UCharsToChars(mzmap->mzid, mzidkey + MZID_PREFIX_LEN, u_strlen(mzmap->mzid) + 1);
691 metazoneItem = ures_getByKeyWithFallback(zoneStringsArray, mzidkey, metazoneItem, &status);
692 if (U_FAILURE(status)) {
693 // No resources available for this metazone
694 // Resource bundle will be cleaned up after end of the loop.
695 status = U_ZERO_ERROR;
696 continue;
697 }
698 UBool mzCommonlyUsed = isCommonlyUsed(metazoneItem);
699 mzstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(metazoneItem, gLongStandardTag);
700 mzstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(metazoneItem, gShortStandardTag);
701 mzstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gLongDaylightTag);
702 mzstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gShortDaylightTag);
703 mzstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(metazoneItem, gLongGenericTag);
704 mzstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(metazoneItem, gShortGenericTag);
705 mzstrarray[ZSIDX_LOCATION] = NULL;
706
707 int32_t lastNonNullIdx = ZSIDX_COUNT - 1;
708 while (lastNonNullIdx >= 0) {
709 if (mzstrarray[lastNonNullIdx] != NULL) {
710 break;
711 }
712 lastNonNullIdx--;
713 }
714 UnicodeString *strings_mz = NULL;
715 ZoneStrings *tmp_mzStrings = NULL;
716 if (lastNonNullIdx >= 0) {
717 // Create UnicodeString array and put strings to the zone string trie
718 strings_mz = new UnicodeString[lastNonNullIdx + 1];
719
720 UnicodeString preferredIdForLocale;
721 ZoneMeta::getZoneIdByMetazone(mzid, region, preferredIdForLocale);
722
723 for (int32_t typeidx = 0; typeidx <= lastNonNullIdx; typeidx++) {
724 if (mzstrarray[typeidx] != NULL) {
725 strings_mz[typeidx].setTo(TRUE, mzstrarray[typeidx], -1);
726
727 // Add a metazone string to the zone string trie
728 int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeidx);
729 ZoneStringInfo *zsinfo = new ZoneStringInfo(preferredIdForLocale, strings_mz[typeidx], (TimeZoneTranslationType)type);
730 fZoneStringsTrie.put(strings_mz[typeidx], zsinfo, status);
731 if (U_FAILURE(status)) {
732 delete []strings_mz;
733 goto error_cleanup;
734 }
735 }
736 }
737 tmp_mzStrings = new ZoneStrings(strings_mz, lastNonNullIdx + 1, mzCommonlyUsed, NULL, 0, 0);
738 } else {
739 // Create ZoneStrings with empty contents
740 tmp_mzStrings = new ZoneStrings(NULL, 0, FALSE, NULL, 0, 0);
741 }
742
743 fMzidToStrings.put(mzid, tmp_mzStrings, status);
744 if (U_FAILURE(status)) {
745 goto error_cleanup;
746 }
747
748 mzStrings = tmp_mzStrings;
749 }
750
751 // Compose generic partial location format
752 UnicodeString lg;
753 UnicodeString sg;
754
755 mzStrings->getString(ZSIDX_LONG_GENERIC, lg);
756 mzStrings->getString(ZSIDX_SHORT_GENERIC, sg);
757
758 if (!lg.isEmpty() || !sg.isEmpty()) {
759 UBool addMzPartialLocationNames = TRUE;
760 for (int32_t j = 0; j < mzPartialLocIdx; j++) {
761 if (mzPartialLoc[j][0] == mzid) {
762 // already processed
763 addMzPartialLocationNames = FALSE;
764 break;
765 }
766 }
767 if (addMzPartialLocationNames) {
768 UnicodeString *locationPart = NULL;
769 // Check if the zone is the preferred zone for the territory associated with the zone
770 UnicodeString preferredID;
771 ZoneMeta::getZoneIdByMetazone(mzid, countryCode, preferredID);
772 if (utzid == preferredID) {
773 // Use country for the location
774 locationPart = &country;
775 } else {
776 // Use city for the location
777 locationPart = &city;
778 }
779 // Reset the partial location string array
780 mzPartialLoc[mzPartialLocIdx][0].setTo(mzid);
781 mzPartialLoc[mzPartialLocIdx][1].remove();
782 mzPartialLoc[mzPartialLocIdx][2].remove();
783 mzPartialLoc[mzPartialLocIdx][3].remove();
784
785 if (locationPart != NULL) {
786 FieldPosition fpos;
787 if (!lg.isEmpty()) {
788 Formattable params [] = {
789 Formattable(*locationPart),
790 Formattable(lg)
791 };
792 fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][1], fpos, status);
793 }
794 if (!sg.isEmpty()) {
795 Formattable params [] = {
796 Formattable(*locationPart),
797 Formattable(sg)
798 };
799 fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][2], fpos, status);
800 if (mzStrings->isShortFormatCommonlyUsed()) {
801 mzPartialLoc[mzPartialLocIdx][3].setTo(TRUE, gCommonlyUsedTrue, -1);
802 }
803 }
804 if (U_FAILURE(status)) {
805 goto error_cleanup;
806 }
807 }
808 mzPartialLocIdx++;
809 }
810 }
811 }
812 }
813 // Collected names for a zone
814
815 // Create UnicodeString array for localized zone strings
816 int32_t lastIdx = ZSIDX_COUNT - 1;
817 while (lastIdx >= 0) {
818 if (zstrarray[lastIdx] != NULL) {
819 break;
820 }
821 lastIdx--;
822 }
823 UnicodeString *strings = NULL;
824 int32_t stringsCount = lastIdx + 1;
825
826 if (stringsCount > 0) {
827 strings = new UnicodeString[stringsCount];
828 for (int32_t i = 0; i < stringsCount; i++) {
829 if (zstrarray[i] != NULL) {
830 strings[i].setTo(zstrarray[i], -1);
831
832 // Add names to the trie
833 int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)i);
834 ZoneStringInfo *zsinfo = new ZoneStringInfo(utzid, strings[i], (TimeZoneTranslationType)type);
835 fZoneStringsTrie.put(strings[i], zsinfo, status);
836 if (U_FAILURE(status)) {
837 delete zsinfo;
838 delete[] strings;
839 goto error_cleanup;
840 }
841 }
842 }
843 }
844
845 // Create UnicodeString array for generic partial location strings
846 UnicodeString **genericPartialLocationNames = NULL;
847 int32_t genericPartialRowCount = mzPartialLocIdx;
848 int32_t genericPartialColCount = 4;
849
850 if (genericPartialRowCount != 0) {
851 genericPartialLocationNames = (UnicodeString**)uprv_malloc(genericPartialRowCount * sizeof(UnicodeString*));
852 if (genericPartialLocationNames == NULL) {
853 status = U_MEMORY_ALLOCATION_ERROR;
854 delete[] strings;
855 goto error_cleanup;
856 }
857 for (int32_t i = 0; i < genericPartialRowCount; i++) {
858 genericPartialLocationNames[i] = new UnicodeString[genericPartialColCount];
859 for (int32_t j = 0; j < genericPartialColCount; j++) {
860 genericPartialLocationNames[i][j].setTo(mzPartialLoc[i][j]);
861 // Add names to the trie
862 if ((j == 1 || j == 2) &&!genericPartialLocationNames[i][j].isEmpty()) {
863 ZoneStringInfo *zsinfo;
864 TimeZoneTranslationType type = (j == 1) ? GENERIC_LONG : GENERIC_SHORT;
865 zsinfo = new ZoneStringInfo(utzid, genericPartialLocationNames[i][j], type);
866 fZoneStringsTrie.put(genericPartialLocationNames[i][j], zsinfo, status);
867 if (U_FAILURE(status)) {
868 delete[] genericPartialLocationNames[i];
869 uprv_free(genericPartialLocationNames);
870 delete[] strings;
871 goto error_cleanup;
872 }
873 }
874 }
875 }
876 }
877
878 // Finally, create ZoneStrings instance and put it into the tzidToStinrgs map
879 ZoneStrings *zstrings = new ZoneStrings(strings, stringsCount, commonlyUsed,
880 genericPartialLocationNames, genericPartialRowCount, genericPartialColCount);
881
882 fTzidToStrings.put(utzid, zstrings, status);
883 if (U_FAILURE(status)) {
884 delete zstrings;
885 goto error_cleanup;
886 }
887 }
888
889error_cleanup:
890 if (fallbackFmt != NULL) {
891 delete fallbackFmt;
892 }
893 if (regionFmt != NULL) {
894 delete regionFmt;
895 }
896 if (tzids != NULL) {
897 delete tzids;
898 }
899 ures_close(zoneItem);
900 ures_close(metazoneItem);
901 ures_close(zoneStringsArray);
902}
903
904ZoneStringFormat::~ZoneStringFormat() {
905}
906
907SafeZoneStringFormatPtr*
908ZoneStringFormat::getZoneStringFormat(const Locale& locale, UErrorCode &status) {
909 umtx_lock(&gZSFCacheLock);
910 if (gZoneStringFormatCache == NULL) {
911 gZoneStringFormatCache = new ZSFCache(10 /* capacity */);
912 ucln_i18n_registerCleanup(UCLN_I18N_ZSFORMAT, zoneStringFormat_cleanup);
913 }
914 umtx_unlock(&gZSFCacheLock);
915
916 return gZoneStringFormatCache->get(locale, status);
917}
918
919
920UnicodeString**
921ZoneStringFormat::createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const {
922 if (U_FAILURE(status)) {
923 return NULL;
924 }
925 UnicodeString **result = NULL;
926 rowCount = 0;
927 colCount = 0;
928
929 // Collect canonical time zone IDs
930 UVector canonicalIDs(uhash_deleteUnicodeString, uhash_compareUnicodeString, status);
931 if (U_FAILURE(status)) {
932 return NULL;
933 }
934 StringEnumeration *tzids = TimeZone::createEnumeration();
935 const UChar *tzid;
936 while ((tzid = tzids->unext(NULL, status))) {
937 if (U_FAILURE(status)) {
938 delete tzids;
939 return NULL;
940 }
941 UnicodeString utzid(tzid);
942 UnicodeString canonicalID;
943 TimeZone::getCanonicalID(UnicodeString(tzid), canonicalID, status);
944 if (U_FAILURE(status)) {
945 // Ignore unknown ID - we should not get here, but just in case.
946 status = U_ZERO_ERROR;
947 continue;
948 }
949 if (utzid == canonicalID) {
950 canonicalIDs.addElement(new UnicodeString(utzid), status);
951 if (U_FAILURE(status)) {
952 delete tzids;
953 return NULL;
954 }
955 }
956 }
957 delete tzids;
958
959 // Allocate array
960 result = (UnicodeString**)uprv_malloc(canonicalIDs.size() * sizeof(UnicodeString*));
961 if (result == NULL) {
962 status = U_MEMORY_ALLOCATION_ERROR;
963 return NULL;
964 }
965 for (int32_t i = 0; i < canonicalIDs.size(); i++) {
966 result[i] = new UnicodeString[8];
967 UnicodeString *id = (UnicodeString*)canonicalIDs.elementAt(i);
968 result[i][0].setTo(*id);
969 getLongStandard(*id, date, result[i][1]);
970 getShortStandard(*id, date, FALSE, result[i][2]);
971 getLongDaylight(*id, date, result[i][3]);
972 getShortDaylight(*id, date, FALSE, result[i][4]);
973 getGenericLocation(*id, result[i][5]);
974 getLongGenericNonLocation(*id, date, result[i][6]);
975 getShortGenericNonLocation(*id, date, FALSE, result[i][7]);
976 }
977
978 rowCount = canonicalIDs.size();
979 colCount = 8;
980 return result;
981}
982
983UnicodeString&
984ZoneStringFormat::getSpecificLongString(const Calendar &cal, UnicodeString &result,
985 UErrorCode &status) const {
986 result.remove();
987 if (U_FAILURE(status)) {
988 return result;
989 }
990 UnicodeString tzid;
991 cal.getTimeZone().getID(tzid);
992 UDate date = cal.getTime(status);
993 if (cal.get(UCAL_DST_OFFSET, status) == 0) {
994 return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /*not used*/, result);
995 } else {
996 return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /*not used*/, result);
997 }
998}
999
1000UnicodeString&
1001ZoneStringFormat::getSpecificShortString(const Calendar &cal, UBool commonlyUsedOnly,
1002 UnicodeString &result, UErrorCode &status) const {
1003 result.remove();
1004 if (U_FAILURE(status)) {
1005 return result;
1006 }
1007 UnicodeString tzid;
1008 cal.getTimeZone().getID(tzid);
1009 UDate date = cal.getTime(status);
1010 if (cal.get(UCAL_DST_OFFSET, status) == 0) {
1011 return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result);
1012 } else {
1013 return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result);
1014 }
1015}
1016
1017UnicodeString&
1018ZoneStringFormat::getGenericLongString(const Calendar &cal, UnicodeString &result,
1019 UErrorCode &status) const {
1020 return getGenericString(cal, FALSE /*long*/, FALSE /* not used */, result, status);
1021}
1022
1023UnicodeString&
1024ZoneStringFormat::getGenericShortString(const Calendar &cal, UBool commonlyUsedOnly,
1025 UnicodeString &result, UErrorCode &status) const {
1026 return getGenericString(cal, TRUE /*short*/, commonlyUsedOnly, result, status);
1027}
1028
1029UnicodeString&
1030ZoneStringFormat::getGenericLocationString(const Calendar &cal, UnicodeString &result,
1031 UErrorCode &status) const {
1032 UnicodeString tzid;
1033 cal.getTimeZone().getID(tzid);
1034 UDate date = cal.getTime(status);
1035 return getString(tzid, ZSIDX_LOCATION, date, FALSE /*not used*/, result);
1036}
1037
1038const ZoneStringInfo*
1039ZoneStringFormat::findSpecificLong(const UnicodeString &text, int32_t start,
1040 int32_t &matchLength, UErrorCode &status) const {
1041 return find(text, start, STANDARD_LONG | DAYLIGHT_LONG, matchLength, status);
1042}
1043
1044const ZoneStringInfo*
1045ZoneStringFormat::findSpecificShort(const UnicodeString &text, int32_t start,
1046 int32_t &matchLength, UErrorCode &status) const {
1047 return find(text, start, STANDARD_SHORT | DAYLIGHT_SHORT, matchLength, status);
1048}
1049
1050const ZoneStringInfo*
1051ZoneStringFormat::findGenericLong(const UnicodeString &text, int32_t start,
1052 int32_t &matchLength, UErrorCode &status) const {
1053 return find(text, start, GENERIC_LONG | STANDARD_LONG | LOCATION, matchLength, status);
1054}
1055
1056const ZoneStringInfo*
1057ZoneStringFormat::findGenericShort(const UnicodeString &text, int32_t start,
1058 int32_t &matchLength, UErrorCode &status) const {
1059 return find(text, start, GENERIC_SHORT | STANDARD_SHORT | LOCATION, matchLength, status);
1060}
1061
1062const ZoneStringInfo*
1063ZoneStringFormat::findGenericLocation(const UnicodeString &text, int32_t start,
1064 int32_t &matchLength, UErrorCode &status) const {
1065 return find(text, start, LOCATION, matchLength, status);
1066}
1067
1068UnicodeString&
1069ZoneStringFormat::getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date,
1070 UBool commonlyUsedOnly, UnicodeString& result) const {
1071 result.remove();
1072
1073 // ICU's own array does not have entries for aliases
1074 UnicodeString canonicalID;
1075 UErrorCode status = U_ZERO_ERROR;
1076 TimeZone::getCanonicalID(tzid, canonicalID, status);
1077 if (U_FAILURE(status)) {
1078 // Unknown ID, but users might have their own data.
1079 canonicalID.setTo(tzid);
1080 }
1081
1082 if (fTzidToStrings.count() > 0) {
1083 ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID);
1084 if (zstrings != NULL) {
1085 switch (typeIdx) {
1086 case ZSIDX_LONG_STANDARD:
1087 case ZSIDX_LONG_DAYLIGHT:
1088 case ZSIDX_LONG_GENERIC:
1089 case ZSIDX_LOCATION:
1090 zstrings->getString(typeIdx, result);
1091 break;
1092 case ZSIDX_SHORT_STANDARD:
1093 case ZSIDX_SHORT_DAYLIGHT:
1094 case ZSIDX_COUNT: //added to avoid warning
1095 case ZSIDX_SHORT_GENERIC:
1096 if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
1097 zstrings->getString(typeIdx, result);
1098 }
1099 break;
1100 }
1101 }
1102 }
1103 if (result.isEmpty() && fMzidToStrings.count() > 0 && typeIdx != ZSIDX_LOCATION) {
1104 // Try metazone
1105 UnicodeString mzid;
1106 ZoneMeta::getMetazoneID(canonicalID, date, mzid);
1107 if (!mzid.isEmpty()) {
1108 ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings.get(mzid);
1109 if (mzstrings != NULL) {
1110 switch (typeIdx) {
1111 case ZSIDX_LONG_STANDARD:
1112 case ZSIDX_LONG_DAYLIGHT:
1113 case ZSIDX_LONG_GENERIC:
1114 case ZSIDX_LOCATION:
1115 mzstrings->getString(typeIdx, result);
1116 break;
1117 case ZSIDX_SHORT_STANDARD:
1118 case ZSIDX_SHORT_DAYLIGHT:
1119 case ZSIDX_COUNT: //added to avoid warning
1120 case ZSIDX_SHORT_GENERIC:
1121 if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
1122 mzstrings->getString(typeIdx, result);
1123 }
1124 break;
1125 }
1126 }
1127 }
1128 }
1129 return result;
1130}
1131
1132UnicodeString&
1133ZoneStringFormat::getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly,
1134 UnicodeString &result, UErrorCode &status) const {
1135 result.remove();
1136 UDate time = cal.getTime(status);
1137 if (U_FAILURE(status)) {
1138 return result;
1139 }
1140 const TimeZone &tz = cal.getTimeZone();
1141 UnicodeString tzid;
1142 tz.getID(tzid);
1143
1144 // ICU's own array does not have entries for aliases
1145 UnicodeString canonicalID;
1146 TimeZone::getCanonicalID(tzid, canonicalID, status);
1147 if (U_FAILURE(status)) {
1148 // Unknown ID, but users might have their own data.
1149 status = U_ZERO_ERROR;
1150 canonicalID.setTo(tzid);
1151 }
1152
1153 ZoneStrings *zstrings = NULL;
1154 if (fTzidToStrings.count() > 0) {
1155 zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID);
1156 if (zstrings != NULL) {
1157 if (isShort) {
1158 if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
1159 zstrings->getString(ZSIDX_SHORT_GENERIC, result);
1160 }
1161 } else {
1162 zstrings->getString(ZSIDX_LONG_GENERIC, result);
1163 }
1164 }
1165 }
1166 if (result.isEmpty() && fMzidToStrings.count() > 0) {
1167 // try metazone
1168 int32_t raw, sav;
1169 UnicodeString mzid;
1170 ZoneMeta::getMetazoneID(canonicalID, time, mzid);
1171 if (!mzid.isEmpty()) {
1172 UBool useStandard = FALSE;
1173 sav = cal.get(UCAL_DST_OFFSET, status);
1174 if (U_FAILURE(status)) {
1175 return result;
1176 }
1177 if (sav == 0) {
1178 useStandard = TRUE;
1179 // Check if the zone actually uses daylight saving time around the time
1180 TimeZone *tmptz = tz.clone();
1181 BasicTimeZone *btz = NULL;
1182 if (tmptz->getDynamicClassID() == OlsonTimeZone::getStaticClassID()
1183 || tmptz->getDynamicClassID() == SimpleTimeZone::getStaticClassID()
1184 || tmptz->getDynamicClassID() == RuleBasedTimeZone::getStaticClassID()
1185 || tmptz->getDynamicClassID() == VTimeZone::getStaticClassID()) {
1186 btz = (BasicTimeZone*)tmptz;
1187 }
1188
1189 if (btz != NULL) {
1190 TimeZoneTransition before;
1191 UBool beforTrs = btz->getPreviousTransition(time, TRUE, before);
1192 if (beforTrs
1193 && (time - before.getTime() < kDstCheckRange)
1194 && before.getFrom()->getDSTSavings() != 0) {
1195 useStandard = FALSE;
1196 } else {
1197 TimeZoneTransition after;
1198 UBool afterTrs = btz->getNextTransition(time, FALSE, after);
1199 if (afterTrs
1200 && (after.getTime() - time < kDstCheckRange)
1201 && after.getTo()->getDSTSavings() != 0) {
1202 useStandard = FALSE;
1203 }
1204 }
1205 } else {
1206 // If not BasicTimeZone... only if the instance is not an ICU's implementation.
1207 // We may get a wrong answer in edge case, but it should practically work OK.
1208 tmptz->getOffset(time - kDstCheckRange, FALSE, raw, sav, status);
1209 if (sav != 0) {
1210 useStandard = FALSE;
1211 } else {
1212 tmptz->getOffset(time + kDstCheckRange, FALSE, raw, sav, status);
1213 if (sav != 0){
1214 useStandard = FALSE;
1215 }
1216 }
1217 if (U_FAILURE(status)) {
1218 delete tmptz;
1219 result.remove();
1220 return result;
1221 }
1222 }
1223 delete tmptz;
1224 }
1225 if (useStandard) {
1226 getString(canonicalID, (isShort ? ZSIDX_SHORT_STANDARD : ZSIDX_LONG_STANDARD),
1227 time, commonlyUsedOnly, result);
1228
1229 // Note:
1230 // In CLDR 1.5.1, a same localization is used for both generic and standard
1231 // for some metazones in some locales. This is actually data bugs and should
1232 // be resolved in later versions of CLDR. For now, we check if the standard
1233 // name is different from its generic name below.
1234 if (!result.isEmpty()) {
1235 UnicodeString genericNonLocation;
1236 getString(canonicalID, (isShort ? ZSIDX_SHORT_GENERIC : ZSIDX_LONG_GENERIC),
1237 time, commonlyUsedOnly, genericNonLocation);
1238 if (!genericNonLocation.isEmpty() && result == genericNonLocation) {
1239 result.remove();
1240 }
1241 }
1242 }
1243 if (result.isEmpty()) {
1244 ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings.get(mzid);
1245 if (mzstrings != NULL) {
1246 if (isShort) {
1247 if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
1248 mzstrings->getString(ZSIDX_SHORT_GENERIC, result);
1249 }
1250 } else {
1251 mzstrings->getString(ZSIDX_LONG_GENERIC, result);
1252 }
1253 }
1254 if (!result.isEmpty()) {
1255 // Check if the offsets at the given time matches the preferred zone's offsets
1256 UnicodeString preferredId;
1257 UnicodeString region;
1258 ZoneMeta::getZoneIdByMetazone(mzid, getRegion(region), preferredId);
1259 if (canonicalID != preferredId) {
1260 // Check if the offsets at the given time are identical with the preferred zone
1261 raw = cal.get(UCAL_ZONE_OFFSET, status);
1262 if (U_FAILURE(status)) {
1263 result.remove();
1264 return result;
1265 }
1266 TimeZone *preferredZone = TimeZone::createTimeZone(preferredId);
1267 int32_t prfRaw, prfSav;
1268 // Check offset in preferred time zone with wall time.
1269 // With getOffset(time, false, preferredOffsets),
1270 // you may get incorrect results because of time overlap at DST->STD
1271 // transition.
1272 preferredZone->getOffset(time + raw + sav, TRUE, prfRaw, prfSav, status);
1273 delete preferredZone;
1274
1275 if (U_FAILURE(status)) {
1276 result.remove();
1277 return result;
1278 }
1279 if ((raw != prfRaw || sav != prfSav) && zstrings != NULL) {
1280 // Use generic partial location string as fallback
1281 zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
1282 }
1283 }
1284 }
1285 }
1286 }
1287 }
1288 if (result.isEmpty()) {
1289 // Use location format as the final fallback
1290 getString(canonicalID, ZSIDX_LOCATION, time, FALSE /*not used*/, result);
1291 }
1292
1293 return result;
1294}
1295
1296UnicodeString&
1297ZoneStringFormat::getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort,
1298 UDate date, UBool commonlyUsedOnly, UnicodeString &result) const {
1299 result.remove();
1300 if (fTzidToStrings.count() <= 0) {
1301 return result;
1302 }
1303
1304 UnicodeString canonicalID;
1305 UErrorCode status = U_ZERO_ERROR;
1306 TimeZone::getCanonicalID(tzid, canonicalID, status);
1307 if (U_FAILURE(status)) {
1308 // Unknown ID, so no corresponding meta data.
1309 return result;
1310 }
1311
1312 UnicodeString mzid;
1313 ZoneMeta::getMetazoneID(canonicalID, date, mzid);
1314
1315 if (!mzid.isEmpty()) {
1316 ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID);
1317 if (zstrings != NULL) {
1318 zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
1319 }
1320 }
1321 return result;
1322}
1323
1324const ZoneStringInfo*
1325ZoneStringFormat::find(const UnicodeString &text, int32_t start, int32_t types,
1326 int32_t &matchLength, UErrorCode &status) const {
1327 matchLength = 0;
1328 if (U_FAILURE(status)) {
1329 return NULL;
1330 }
1331 if (fZoneStringsTrie.isEmpty()) {
1332 return NULL;
1333 }
1334 const ZoneStringInfo *result = NULL;
1335 const ZoneStringInfo *fallback = NULL;
1336 int32_t fallbackMatchLen = 0;
1337
1338 ZoneStringSearchResultHandler handler(status);
1339 fZoneStringsTrie.search(text, start, (TextTrieMapSearchResultHandler*)&handler, status);
1340 if (U_SUCCESS(status)) {
1341 int32_t numMatches = handler.countMatches();
1342 for (int32_t i = 0; i < numMatches; i++) {
1343 int32_t tmpMatchLen;
1344 const ZoneStringInfo *tmp = handler.getMatch(i, tmpMatchLen);
1345 if ((types & tmp->fType) != 0) {
1346 if (result == NULL || matchLength < tmpMatchLen) {
1347 result = tmp;
1348 matchLength = tmpMatchLen;
1349 } else if (matchLength == tmpMatchLen) {
1350 // Tie breaker - there are some examples that a
1351 // long standard name is identical with a location
1352 // name - for example, "Uruguay Time". In this case,
1353 // we interpret it as generic, not specific.
1354 if (tmp->isGeneric() && !result->isGeneric()) {
1355 result = tmp;
1356 }
1357 }
1358 } else if (result == NULL) {
1359 if (fallback == NULL || fallbackMatchLen < tmpMatchLen) {
1360 fallback = tmp;
1361 fallbackMatchLen = tmpMatchLen;
1362 } else if (fallbackMatchLen == tmpMatchLen) {
1363 if (tmp->isGeneric() && !fallback->isGeneric()) {
1364 fallback = tmp;
1365 }
1366 }
1367 }
1368 }
1369 if (result == NULL && fallback != NULL) {
1370 result = fallback;
1371 matchLength = fallbackMatchLen;
1372 }
1373 }
1374 return result;
1375}
1376
1377
1378UnicodeString&
1379ZoneStringFormat::getRegion(UnicodeString &region) const {
1380 const char* country = fLocale.getCountry();
1381 // TODO: Utilize addLikelySubtag in Locale to resolve default region
1382 // when the implementation is ready.
1383 region.setTo(UnicodeString(country, -1, US_INV));
1384 return region;
1385}
1386
1387MessageFormat*
1388ZoneStringFormat::getFallbackFormat(const Locale &locale, UErrorCode &status) {
1389 if (U_FAILURE(status)) {
1390 return NULL;
1391 }
1392 UnicodeString pattern(TRUE, gDefFallbackPattern, -1);
1393 UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status);
1394 zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
1395 int32_t len;
1396 const UChar *flbkfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gFallbackFormatTag, &len, &status);
1397 if (U_SUCCESS(status)) {
1398 pattern.setTo(flbkfmt);
1399 } else {
1400 status = U_ZERO_ERROR;
1401 }
1402 ures_close(zoneStringsArray);
1403
1404 MessageFormat *fallbackFmt = new MessageFormat(pattern, status);
1405 return fallbackFmt;
1406}
1407
1408MessageFormat*
1409ZoneStringFormat::getRegionFormat(const Locale& locale, UErrorCode &status) {
1410 if (U_FAILURE(status)) {
1411 return NULL;
1412 }
1413 UnicodeString pattern(TRUE, gDefRegionPattern, -1);
1414 UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status);
1415 zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
1416 int32_t len;
1417 const UChar *regionfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gRegionFormatTag, &len, &status);
1418 if (U_SUCCESS(status)) {
1419 pattern.setTo(regionfmt);
1420 } else {
1421 status = U_ZERO_ERROR;
1422 }
1423 ures_close(zoneStringsArray);
1424
1425 MessageFormat *regionFmt = new MessageFormat(pattern, status);
1426 return regionFmt;
1427}
1428
1429const UChar*
1430ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key) {
1431 const UChar *str = NULL;
1432 if (zoneitem != NULL) {
1433 UErrorCode status = U_ZERO_ERROR;
1434 int32_t len;
1435 str = ures_getStringByKeyWithFallback(zoneitem, key, &len, &status);
1436 if (U_FAILURE(status)) {
1437 str = NULL;
1438 }
1439 }
1440 return str;
1441}
1442
1443UBool
1444ZoneStringFormat::isCommonlyUsed(const UResourceBundle *zoneitem) {
1445 if (zoneitem == NULL) {
1446 return TRUE;
1447 }
1448
1449 UBool commonlyUsed = FALSE;
1450 UErrorCode status = U_ZERO_ERROR;
1451 UResourceBundle *cuRes = ures_getByKey(zoneitem, gCommonlyUsedTag, NULL, &status);
1452 int32_t cuValue = ures_getInt(cuRes, &status);
1453 if (U_SUCCESS(status)) {
1454 if (cuValue == 1) {
1455 commonlyUsed = TRUE;
1456 }
1457 }
1458 ures_close(cuRes);
1459 return commonlyUsed;
1460}
1461
1462UnicodeString&
1463ZoneStringFormat::getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale, UnicodeString &displayCountry) {
1464 // We do not want to use display country names only from the target language bundle
1465 // Note: we should do this in better way.
1466 displayCountry.remove();
1467 int32_t ccLen = countryCode.length();
1468 if (ccLen > 0 && ccLen < ULOC_COUNTRY_CAPACITY) {
1469 UErrorCode status = U_ZERO_ERROR;
1470 UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
1471 if (U_SUCCESS(status)) {
1472 const char *bundleLocStr = ures_getLocale(localeBundle, &status);
1473 if (U_SUCCESS(status) && uprv_strlen(bundleLocStr) > 0) {
1474 Locale bundleLoc(bundleLocStr);
1475 if (uprv_strcmp(bundleLocStr, "root") != 0 && uprv_strcmp(bundleLoc.getLanguage(), locale.getLanguage()) == 0) {
1476 // Create a fake locale strings
1477 char tmpLocStr[ULOC_COUNTRY_CAPACITY + 3];
1478 uprv_strcpy(tmpLocStr, "xx_");
1479 u_UCharsToChars(countryCode.getBuffer(), &tmpLocStr[3], ccLen);
1480 tmpLocStr[3 + ccLen] = 0;
1481
1482 Locale tmpLoc(tmpLocStr);
1483 tmpLoc.getDisplayCountry(locale, displayCountry);
1484 }
1485 }
1486 }
1487 ures_close(localeBundle);
1488 }
1489 if (displayCountry.isEmpty()) {
1490 // Use the country code as the fallback
1491 displayCountry.setTo(countryCode);
1492 }
1493 return displayCountry;
1494}
1495
1496// ----------------------------------------------------------------------------
1497/*
1498 * This constructor adopts the input UnicodeString arrays.
1499 */
1500ZoneStrings::ZoneStrings(UnicodeString *strings, int32_t stringsCount, UBool commonlyUsed,
1501 UnicodeString **genericPartialLocationStrings, int32_t genericRowCount, int32_t genericColCount)
1502: fStrings(strings), fStringsCount(stringsCount), fIsCommonlyUsed(commonlyUsed),
1503 fGenericPartialLocationStrings(genericPartialLocationStrings),
1504 fGenericPartialLocationRowCount(genericRowCount), fGenericPartialLocationColCount(genericColCount) {
1505}
1506
1507ZoneStrings::~ZoneStrings() {
1508 if (fStrings != NULL) {
1509 delete[] fStrings;
1510 }
1511 if (fGenericPartialLocationStrings != NULL) {
1512 for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
1513 delete[] fGenericPartialLocationStrings[i];
1514 }
1515 uprv_free(fGenericPartialLocationStrings);
1516 }
1517}
1518
1519
1520UnicodeString&
1521ZoneStrings::getString(int32_t typeIdx, UnicodeString &result) const {
1522 if (typeIdx >= 0 && typeIdx < fStringsCount) {
1523 result.setTo(fStrings[typeIdx]);
1524 } else {
1525 result.remove();
1526 }
1527 return result;
1528}
1529
1530UnicodeString&
1531ZoneStrings::getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort,
1532 UBool commonlyUsedOnly, UnicodeString &result) const {
1533 UBool isSet = FALSE;
1534 if (fGenericPartialLocationColCount >= 2) {
1535 for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
1536 if (fGenericPartialLocationStrings[i][0] == mzid) {
1537 if (isShort) {
1538 if (fGenericPartialLocationColCount >= 3) {
1539 if (!commonlyUsedOnly ||
1540 fGenericPartialLocationColCount == 3 || fGenericPartialLocationStrings[i][3].length() != 0) {
1541 result.setTo(fGenericPartialLocationStrings[i][2]);
1542 isSet = TRUE;
1543 }
1544 }
1545 } else {
1546 result.setTo(fGenericPartialLocationStrings[i][1]);
1547 isSet = TRUE;
1548 }
1549 break;
1550 }
1551 }
1552 }
1553 if (!isSet) {
1554 result.remove();
1555 }
1556 return result;
1557}
1558
1559// --------------------------------------------------------------
1560SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry)
1561: fCacheEntry(cacheEntry) {
1562}
1563
1564SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() {
1565 fCacheEntry->delRef();
1566}
1567
1568const ZoneStringFormat*
1569SafeZoneStringFormatPtr::get() const {
1570 return fCacheEntry->getZoneStringFormat();
1571}
1572
1573ZSFCacheEntry::ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next)
1574: fLocale(locale), fZoneStringFormat(zsf),
1575 fNext(next), fRefCount(1)
1576{
1577}
1578
1579ZSFCacheEntry::~ZSFCacheEntry () {
1580 delete fZoneStringFormat;
1581}
1582
1583const ZoneStringFormat*
1584ZSFCacheEntry::getZoneStringFormat(void) {
1585 return (const ZoneStringFormat*)fZoneStringFormat;
1586}
1587
1588void
1589ZSFCacheEntry::delRef(void) {
1590 umtx_lock(&gZSFCacheLock);
1591 --fRefCount;
1592 umtx_unlock(&gZSFCacheLock);
1593}
1594
1595ZSFCache::ZSFCache(int32_t capacity)
1596: fCapacity(capacity), fFirst(NULL) {
1597}
1598
1599ZSFCache::~ZSFCache() {
1600 ZSFCacheEntry *entry = fFirst;
1601 while (entry) {
1602 ZSFCacheEntry *next = entry->fNext;
1603 delete entry;
1604 entry = next;
1605 }
1606}
1607
1608SafeZoneStringFormatPtr*
1609ZSFCache::get(const Locale &locale, UErrorCode &status) {
1610 SafeZoneStringFormatPtr *result = NULL;
1611
1612 // Search the cache entry list
1613 ZSFCacheEntry *entry = NULL;
1614 ZSFCacheEntry *next, *prev;
1615
1616 umtx_lock(&gZSFCacheLock);
1617 entry = fFirst;
1618 prev = NULL;
1619 while (entry) {
1620 next = entry->fNext;
1621 if (entry->fLocale == locale) {
1622 // Add reference count
1623 entry->fRefCount++;
1624
1625 // move the entry to the top
1626 if (entry != fFirst) {
1627 prev->fNext = next;
1628 entry->fNext = fFirst;
1629 fFirst = entry;
1630 }
1631 break;
1632 }
1633 prev = entry;
1634 entry = next;
1635 }
1636 umtx_unlock(&gZSFCacheLock);
1637
1638 // Create a new ZoneStringFormat
1639 if (entry == NULL) {
1640 ZoneStringFormat *zsf = new ZoneStringFormat(locale, status);
1641 if (U_FAILURE(status)) {
1642 delete zsf;
1643 return NULL;
1644 }
1645 if (zsf == NULL) {
1646 status = U_MEMORY_ALLOCATION_ERROR;
1647 return NULL;
1648 }
1649 // Now add the new entry
1650 umtx_lock(&gZSFCacheLock);
1651 // Make sure no other threads already created the one for the same locale
1652 entry = fFirst;
1653 prev = NULL;
1654 while (entry) {
1655 next = entry->fNext;
1656 if (entry->fLocale == locale) {
1657 // Add reference count
1658 entry->fRefCount++;
1659
1660 // move the entry to the top
1661 if (entry != fFirst) {
1662 prev->fNext = next;
1663 entry->fNext = fFirst;
1664 fFirst = entry;
1665 }
1666 break;
1667 }
1668 prev = entry;
1669 entry = next;
1670 }
1671 if (entry == NULL) {
1672 // Add the new one to the top
1673 next = fFirst;
1674 entry = new ZSFCacheEntry(locale, zsf, next);
1675 fFirst = entry;
1676 } else {
1677 delete zsf;
1678 }
1679 umtx_unlock(&gZSFCacheLock);
1680 }
1681
1682 result = new SafeZoneStringFormatPtr(entry);
1683
1684 // Now, delete unused cache entries beyond the capacity
1685 umtx_lock(&gZSFCacheLock);
1686 entry = fFirst;
1687 prev = NULL;
1688 int32_t idx = 1;
1689 while (entry) {
1690 next = entry->fNext;
1691 if (idx >= fCapacity && entry->fRefCount == 0) {
1692 if (entry == fFirst) {
1693 fFirst = next;
1694 } else {
1695 prev->fNext = next;
1696 }
1697 delete entry;
1698 } else {
1699 prev = entry;
1700 }
1701 entry = next;
1702 idx++;
1703 }
1704 umtx_unlock(&gZSFCacheLock);
1705
1706 return result;
1707}
1708
1709U_NAMESPACE_END
1710
1711#endif /* #if !UCONFIG_NO_FORMATTING */