2 *******************************************************************************
3 * Copyright (C) 2007-2008, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
8 #include "unicode/utypes.h"
10 #if !UCONFIG_NO_FORMATTING
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"
32 * global ZoneStringFormatCache stuffs
34 static UMTX gZSFCacheLock
= NULL
;
35 static U_NAMESPACE_QUALIFIER ZSFCache
*gZoneStringFormatCache
= NULL
;
39 * ZoneStringFormatCache cleanup callback func
41 static UBool U_CALLCONV
zoneStringFormat_cleanup(void)
43 umtx_destroy(&gZSFCacheLock
);
44 if (gZoneStringFormatCache
!= NULL
) {
45 delete gZoneStringFormatCache
;
46 gZoneStringFormatCache
= NULL
;
48 gZoneStringFormatCache
= NULL
;
53 * Deleter for ZoneStringInfo
55 static void U_CALLCONV
56 deleteZoneStringInfo(void *obj
) {
57 delete (U_NAMESPACE_QUALIFIER ZoneStringInfo
*)obj
;
61 * Deleter for ZoneStrings
63 static void U_CALLCONV
64 deleteZoneStrings(void *obj
) {
65 delete (U_NAMESPACE_QUALIFIER ZoneStrings
*)obj
;
71 #define ZID_KEY_MAX 128
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";
86 #define MZID_PREFIX_LEN 5
87 static const char gMetazoneIdPrefix
[] = "meta:";
89 #define MAX_METAZONES_PER_ZONE 10
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"
95 static const double kDstCheckRange
= (double)184*U_MILLIS_PER_DAY
;
98 getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type
) {
102 typeIdx
= ZSIDX_LOCATION
;
105 typeIdx
= ZSIDX_LONG_GENERIC
;
108 typeIdx
= ZSIDX_SHORT_GENERIC
;
111 typeIdx
= ZSIDX_LONG_STANDARD
;
114 typeIdx
= ZSIDX_SHORT_STANDARD
;
117 typeIdx
= ZSIDX_LONG_DAYLIGHT
;
120 typeIdx
= ZSIDX_SHORT_DAYLIGHT
;
127 getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx
) {
133 case ZSIDX_LONG_GENERIC
:
136 case ZSIDX_SHORT_GENERIC
:
137 type
= GENERIC_SHORT
;
139 case ZSIDX_LONG_STANDARD
:
140 type
= STANDARD_LONG
;
142 case ZSIDX_SHORT_STANDARD
:
143 type
= STANDARD_SHORT
;
145 case ZSIDX_LONG_DAYLIGHT
:
146 type
= DAYLIGHT_LONG
;
149 case ZSIDX_SHORT_DAYLIGHT
:
150 type
= DAYLIGHT_SHORT
;
157 #define DEFAULT_CHARACTERNODE_CAPACITY 1
159 // ----------------------------------------------------------------------------
160 void CharacterNode::clear() {
161 uprv_memset(this, 0, sizeof(*this));
164 void CharacterNode::deleteValues() {
165 if (fValues
== NULL
) {
167 } else if (!fHasValuesVector
) {
168 deleteZoneStringInfo(fValues
);
170 delete (UVector
*)fValues
;
175 CharacterNode::addValue(void *value
, UErrorCode
&status
) {
176 if (U_FAILURE(status
)) {
177 deleteZoneStringInfo(value
);
180 if (fValues
== NULL
) {
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
);
192 values
->addElement(fValues
, status
);
194 fHasValuesVector
= TRUE
;
196 // Add the new value.
197 ((UVector
*)fValues
)->addElement(value
, status
);
201 //----------------------------------------------------------------------------
202 // Virtual destructor to avoid warning
203 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
206 // ----------------------------------------------------------------------------
207 TextTrieMap::TextTrieMap(UBool ignoreCase
)
208 : fIgnoreCase(ignoreCase
), fNodes(NULL
), fNodesCapacity(0), fNodesCount(0) {
211 TextTrieMap::~TextTrieMap() {
213 for (index
= 0; index
< fNodesCount
; ++index
) {
214 fNodes
[index
].deleteValues();
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.
228 UnicodeString foldedKey
;
229 const UChar
*keyBuffer
;
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();
237 keyBuffer
= key
.getBuffer();
238 keyLength
= key
.length();
241 CharacterNode
*node
= fNodes
;
243 for (index
= 0; index
< keyLength
; ++index
) {
244 node
= addChildNode(node
, keyBuffer
[index
], status
);
246 node
->addValue(value
, status
);
250 TextTrieMap::growNodes() {
251 if (fNodesCapacity
== 0xffff) {
252 return FALSE
; // We use 16-bit node indexes.
254 int32_t newCapacity
= fNodesCapacity
* 2;
255 if (newCapacity
> 0xffff) {
256 newCapacity
= 0xffff;
258 CharacterNode
*newNodes
= (CharacterNode
*)uprv_malloc(newCapacity
* sizeof(CharacterNode
));
259 if (newNodes
== NULL
) {
262 uprv_memcpy(newNodes
, fNodes
, fNodesCount
* sizeof(CharacterNode
));
265 fNodesCapacity
= newCapacity
;
270 TextTrieMap::addChildNode(CharacterNode
*parent
, UChar c
, UErrorCode
&status
) {
271 if (U_FAILURE(status
)) {
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
) {
282 } else if (childCharacter
> c
) {
285 prevIndex
= nodeIndex
;
286 nodeIndex
= current
->fNextSibling
;
289 // Ensure capacity. Grow fNodes[] if needed.
290 if (fNodesCount
== fNodesCapacity
) {
291 int32_t parentIndex
= (parent
- fNodes
);
293 status
= U_MEMORY_ALLOCATION_ERROR
;
296 parent
= fNodes
+ parentIndex
;
299 // Insert a new child node with c in sorted order.
300 CharacterNode
*node
= fNodes
+ fNodesCount
;
302 node
->fCharacter
= c
;
303 node
->fNextSibling
= nodeIndex
;
304 if (prevIndex
== 0) {
305 parent
->fFirstChild
= (uint16_t)fNodesCount
;
307 fNodes
[prevIndex
].fNextSibling
= (uint16_t)fNodesCount
;
314 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
) {
322 } else if (childCharacter
> c
) {
325 nodeIndex
= current
->fNextSibling
;
331 TextTrieMap::search(const UnicodeString
&text
, int32_t start
,
332 TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
333 if (fNodes
== NULL
) {
336 search(fNodes
, text
, start
, start
, handler
, status
);
340 TextTrieMap::search(CharacterNode
*node
, const UnicodeString
&text
, int32_t start
,
341 int32_t index
, TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
342 if (U_FAILURE(status
)) {
345 if (node
->hasValues()) {
346 if (!handler
->handleMatch(index
- start
, node
, status
)) {
349 if (U_FAILURE(status
)) {
353 UChar32 c
= text
.char32At(index
);
355 // size of character may grow after fold operation
356 UnicodeString
tmp(c
);
359 while (tmpidx
< tmp
.length()) {
360 c
= tmp
.char32At(tmpidx
);
361 node
= getChildNode(node
, c
);
365 tmpidx
= tmp
.moveIndex32(tmpidx
, 1);
368 node
= getChildNode(node
, c
);
371 search(node
, text
, start
, index
+1, handler
, status
);
375 // ----------------------------------------------------------------------------
376 ZoneStringInfo::ZoneStringInfo(const UnicodeString
&id
, const UnicodeString
&str
,
377 TimeZoneTranslationType type
)
378 : fId(id
), fStr(str
), fType(type
) {
381 ZoneStringInfo::~ZoneStringInfo() {
383 // ----------------------------------------------------------------------------
384 ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode
&status
)
390 ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() {
395 ZoneStringSearchResultHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
396 if (U_FAILURE(status
)) {
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
) {
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
;
422 // not found in the current list
423 fResults
.addElement(zsinfo
, status
);
424 fMatchLen
[getTimeZoneTranslationTypeIndex(zsinfo
->fType
)] = matchLength
;
432 ZoneStringSearchResultHandler::countMatches(void) {
433 return fResults
.size();
436 const ZoneStringInfo
*
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
)];
447 ZoneStringSearchResultHandler::clear(void) {
448 fResults
.removeAllElements();
449 for (int32_t i
= 0; i
< (int32_t)(sizeof(fMatchLen
)/sizeof(fMatchLen
[0])); i
++) {
453 // ----------------------------------------------------------------------------
454 ZoneStringFormat::ZoneStringFormat(const UnicodeString
* const* strings
,
455 int32_t rowCount
, int32_t columnCount
, UErrorCode
&status
)
457 fTzidToStrings(uhash_compareUnicodeString
, NULL
, status
),
458 fMzidToStrings(uhash_compareUnicodeString
, NULL
, status
),
459 fZoneStringsTrie(TRUE
)
461 if (U_FAILURE(status
)) {
464 fLocale
.setToBogus();
465 if (strings
== NULL
|| columnCount
<= 0 || rowCount
<= 0) {
466 status
= U_ILLEGAL_ARGUMENT_ERROR
;
470 fTzidToStrings
.setValueDeleter(deleteZoneStrings
);
472 for (int32_t row
= 0; row
< rowCount
; row
++) {
473 if (strings
[row
][0].isEmpty()) {
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;
482 typeIdx
= ZSIDX_LONG_STANDARD
;
485 typeIdx
= ZSIDX_SHORT_STANDARD
;
488 typeIdx
= ZSIDX_LONG_DAYLIGHT
;
491 typeIdx
= ZSIDX_SHORT_DAYLIGHT
;
494 typeIdx
= ZSIDX_LOCATION
;
497 typeIdx
= ZSIDX_LONG_GENERIC
;
500 typeIdx
= ZSIDX_SHORT_GENERIC
;
504 names
[typeIdx
].setTo(strings
[row
][col
]);
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
)) {
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
)) {
530 ZoneStringFormat::ZoneStringFormat(const Locale
&locale
, UErrorCode
&status
)
532 fTzidToStrings(uhash_compareUnicodeString
, NULL
, status
),
533 fMzidToStrings(uhash_compareUnicodeString
, NULL
, status
),
534 fZoneStringsTrie(TRUE
)
536 if (U_FAILURE(status
)) {
539 fTzidToStrings
.setValueDeleter(deleteZoneStrings
);
540 fMzidToStrings
.setValueDeleter(deleteZoneStrings
);
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
;
554 StringEnumeration
*tzids
= NULL
;
555 MessageFormat
*fallbackFmt
= NULL
;
556 MessageFormat
*regionFmt
= NULL
;
558 UResourceBundle
*zoneItem
= NULL
;
559 UResourceBundle
*metazoneItem
= NULL
;
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];
566 UnicodeString region
;
569 fallbackFmt
= getFallbackFormat(locale
, status
);
570 if (U_FAILURE(status
)) {
573 regionFmt
= getRegionFormat(locale
, status
);
574 if (U_FAILURE(status
)) {
578 tzids
= TimeZone::createEnumeration();
580 while ((tzid
= tzids
->next(NULL
, status
))) {
581 if (U_FAILURE(status
)) {
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
;
593 if (utzid
!= canonicalID
) {
597 uprv_strcpy(zidkey
, tzid
);
599 // Replace '/' with ':'
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
);
616 status
= U_ZERO_ERROR
;
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
);
626 // Compose location format string
627 UnicodeString location
;
628 UnicodeString country
;
630 UnicodeString countryCode
;
631 ZoneMeta::getCanonicalCountry(utzid
, countryCode
);
632 if (countryCode
.isEmpty()) {
633 zstrarray
[ZSIDX_LOCATION
] = NULL
;
635 const UChar
* tmpCity
= getZoneStringFromBundle(zoneItem
, gExemplarCityTag
);
636 if (tmpCity
!= NULL
) {
637 city
.setTo(TRUE
, tmpCity
, -1);
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 /*' '*/);
647 getLocalizedCountry(countryCode
, locale
, country
);
648 UnicodeString singleCountry
;
649 ZoneMeta::getSingleCountry(utzid
, singleCountry
);
651 if (singleCountry
.isEmpty()) {
652 Formattable params
[] = {
656 fallbackFmt
->format(params
, 2, location
, fpos
, status
);
658 // If the zone is only one zone in the country, do not add city
659 Formattable params
[] = {
662 regionFmt
->format(params
, 1, location
, fpos
, status
);
664 if (U_FAILURE(status
)) {
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
);
673 zstrarray
[ZSIDX_LOCATION
] = location
.getTerminatedBuffer();
676 UBool commonlyUsed
= isCommonlyUsed(zoneItem
);
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
;
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
;
707 int32_t lastNonNullIdx
= ZSIDX_COUNT
- 1;
708 while (lastNonNullIdx
>= 0) {
709 if (mzstrarray
[lastNonNullIdx
] != NULL
) {
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];
720 UnicodeString preferredIdForLocale
;
721 ZoneMeta::getZoneIdByMetazone(mzid
, region
, preferredIdForLocale
);
723 for (int32_t typeidx
= 0; typeidx
<= lastNonNullIdx
; typeidx
++) {
724 if (mzstrarray
[typeidx
] != NULL
) {
725 strings_mz
[typeidx
].setTo(TRUE
, mzstrarray
[typeidx
], -1);
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
)) {
737 tmp_mzStrings
= new ZoneStrings(strings_mz
, lastNonNullIdx
+ 1, mzCommonlyUsed
, NULL
, 0, 0);
739 // Create ZoneStrings with empty contents
740 tmp_mzStrings
= new ZoneStrings(NULL
, 0, FALSE
, NULL
, 0, 0);
743 fMzidToStrings
.put(mzid
, tmp_mzStrings
, status
);
744 if (U_FAILURE(status
)) {
748 mzStrings
= tmp_mzStrings
;
751 // Compose generic partial location format
755 mzStrings
->getString(ZSIDX_LONG_GENERIC
, lg
);
756 mzStrings
->getString(ZSIDX_SHORT_GENERIC
, sg
);
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
) {
763 addMzPartialLocationNames
= FALSE
;
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
;
776 // Use city for the location
777 locationPart
= &city
;
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();
785 if (locationPart
!= NULL
) {
788 Formattable params
[] = {
789 Formattable(*locationPart
),
792 fallbackFmt
->format(params
, 2, mzPartialLoc
[mzPartialLocIdx
][1], fpos
, status
);
795 Formattable params
[] = {
796 Formattable(*locationPart
),
799 fallbackFmt
->format(params
, 2, mzPartialLoc
[mzPartialLocIdx
][2], fpos
, status
);
800 if (mzStrings
->isShortFormatCommonlyUsed()) {
801 mzPartialLoc
[mzPartialLocIdx
][3].setTo(TRUE
, gCommonlyUsedTrue
, -1);
804 if (U_FAILURE(status
)) {
813 // Collected names for a zone
815 // Create UnicodeString array for localized zone strings
816 int32_t lastIdx
= ZSIDX_COUNT
- 1;
817 while (lastIdx
>= 0) {
818 if (zstrarray
[lastIdx
] != NULL
) {
823 UnicodeString
*strings
= NULL
;
824 int32_t stringsCount
= lastIdx
+ 1;
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);
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
)) {
845 // Create UnicodeString array for generic partial location strings
846 UnicodeString
**genericPartialLocationNames
= NULL
;
847 int32_t genericPartialRowCount
= mzPartialLocIdx
;
848 int32_t genericPartialColCount
= 4;
850 if (genericPartialRowCount
!= 0) {
851 genericPartialLocationNames
= (UnicodeString
**)uprv_malloc(genericPartialRowCount
* sizeof(UnicodeString
*));
852 if (genericPartialLocationNames
== NULL
) {
853 status
= U_MEMORY_ALLOCATION_ERROR
;
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
);
878 // Finally, create ZoneStrings instance and put it into the tzidToStinrgs map
879 ZoneStrings
*zstrings
= new ZoneStrings(strings
, stringsCount
, commonlyUsed
,
880 genericPartialLocationNames
, genericPartialRowCount
, genericPartialColCount
);
882 fTzidToStrings
.put(utzid
, zstrings
, status
);
883 if (U_FAILURE(status
)) {
890 if (fallbackFmt
!= NULL
) {
893 if (regionFmt
!= NULL
) {
899 ures_close(zoneItem
);
900 ures_close(metazoneItem
);
901 ures_close(zoneStringsArray
);
904 ZoneStringFormat::~ZoneStringFormat() {
907 SafeZoneStringFormatPtr
*
908 ZoneStringFormat::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
);
914 umtx_unlock(&gZSFCacheLock
);
916 return gZoneStringFormatCache
->get(locale
, status
);
921 ZoneStringFormat::createZoneStringsArray(UDate date
, int32_t &rowCount
, int32_t &colCount
, UErrorCode
&status
) const {
922 if (U_FAILURE(status
)) {
925 UnicodeString
**result
= NULL
;
929 // Collect canonical time zone IDs
930 UVector
canonicalIDs(uhash_deleteUnicodeString
, uhash_compareUnicodeString
, status
);
931 if (U_FAILURE(status
)) {
934 StringEnumeration
*tzids
= TimeZone::createEnumeration();
936 while ((tzid
= tzids
->unext(NULL
, status
))) {
937 if (U_FAILURE(status
)) {
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
;
949 if (utzid
== canonicalID
) {
950 canonicalIDs
.addElement(new UnicodeString(utzid
), status
);
951 if (U_FAILURE(status
)) {
960 result
= (UnicodeString
**)uprv_malloc(canonicalIDs
.size() * sizeof(UnicodeString
*));
961 if (result
== NULL
) {
962 status
= U_MEMORY_ALLOCATION_ERROR
;
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]);
978 rowCount
= canonicalIDs
.size();
984 ZoneStringFormat::getSpecificLongString(const Calendar
&cal
, UnicodeString
&result
,
985 UErrorCode
&status
) const {
987 if (U_FAILURE(status
)) {
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
);
996 return getString(tzid
, ZSIDX_LONG_DAYLIGHT
, date
, FALSE
/*not used*/, result
);
1001 ZoneStringFormat::getSpecificShortString(const Calendar
&cal
, UBool commonlyUsedOnly
,
1002 UnicodeString
&result
, UErrorCode
&status
) const {
1004 if (U_FAILURE(status
)) {
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
);
1013 return getString(tzid
, ZSIDX_SHORT_DAYLIGHT
, date
, commonlyUsedOnly
, result
);
1018 ZoneStringFormat::getGenericLongString(const Calendar
&cal
, UnicodeString
&result
,
1019 UErrorCode
&status
) const {
1020 return getGenericString(cal
, FALSE
/*long*/, FALSE
/* not used */, result
, status
);
1024 ZoneStringFormat::getGenericShortString(const Calendar
&cal
, UBool commonlyUsedOnly
,
1025 UnicodeString
&result
, UErrorCode
&status
) const {
1026 return getGenericString(cal
, TRUE
/*short*/, commonlyUsedOnly
, result
, status
);
1030 ZoneStringFormat::getGenericLocationString(const Calendar
&cal
, UnicodeString
&result
,
1031 UErrorCode
&status
) const {
1033 cal
.getTimeZone().getID(tzid
);
1034 UDate date
= cal
.getTime(status
);
1035 return getString(tzid
, ZSIDX_LOCATION
, date
, FALSE
/*not used*/, result
);
1038 const ZoneStringInfo
*
1039 ZoneStringFormat::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
);
1044 const ZoneStringInfo
*
1045 ZoneStringFormat::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
);
1050 const ZoneStringInfo
*
1051 ZoneStringFormat::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
);
1056 const ZoneStringInfo
*
1057 ZoneStringFormat::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
);
1062 const ZoneStringInfo
*
1063 ZoneStringFormat::findGenericLocation(const UnicodeString
&text
, int32_t start
,
1064 int32_t &matchLength
, UErrorCode
&status
) const {
1065 return find(text
, start
, LOCATION
, matchLength
, status
);
1069 ZoneStringFormat::getString(const UnicodeString
&tzid
, TimeZoneTranslationTypeIndex typeIdx
, UDate date
,
1070 UBool commonlyUsedOnly
, UnicodeString
& result
) const {
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
);
1082 if (fTzidToStrings
.count() > 0) {
1083 ZoneStrings
*zstrings
= (ZoneStrings
*)fTzidToStrings
.get(canonicalID
);
1084 if (zstrings
!= NULL
) {
1086 case ZSIDX_LONG_STANDARD
:
1087 case ZSIDX_LONG_DAYLIGHT
:
1088 case ZSIDX_LONG_GENERIC
:
1089 case ZSIDX_LOCATION
:
1090 zstrings
->getString(typeIdx
, result
);
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
);
1103 if (result
.isEmpty() && fMzidToStrings
.count() > 0 && typeIdx
!= ZSIDX_LOCATION
) {
1106 ZoneMeta::getMetazoneID(canonicalID
, date
, mzid
);
1107 if (!mzid
.isEmpty()) {
1108 ZoneStrings
*mzstrings
= (ZoneStrings
*)fMzidToStrings
.get(mzid
);
1109 if (mzstrings
!= NULL
) {
1111 case ZSIDX_LONG_STANDARD
:
1112 case ZSIDX_LONG_DAYLIGHT
:
1113 case ZSIDX_LONG_GENERIC
:
1114 case ZSIDX_LOCATION
:
1115 mzstrings
->getString(typeIdx
, result
);
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
);
1133 ZoneStringFormat::getGenericString(const Calendar
&cal
, UBool isShort
, UBool commonlyUsedOnly
,
1134 UnicodeString
&result
, UErrorCode
&status
) const {
1136 UDate time
= cal
.getTime(status
);
1137 if (U_FAILURE(status
)) {
1140 const TimeZone
&tz
= cal
.getTimeZone();
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
);
1153 ZoneStrings
*zstrings
= NULL
;
1154 if (fTzidToStrings
.count() > 0) {
1155 zstrings
= (ZoneStrings
*)fTzidToStrings
.get(canonicalID
);
1156 if (zstrings
!= NULL
) {
1158 if (!commonlyUsedOnly
|| zstrings
->isShortFormatCommonlyUsed()) {
1159 zstrings
->getString(ZSIDX_SHORT_GENERIC
, result
);
1162 zstrings
->getString(ZSIDX_LONG_GENERIC
, result
);
1166 if (result
.isEmpty() && fMzidToStrings
.count() > 0) {
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
)) {
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
;
1190 TimeZoneTransition before
;
1191 UBool beforTrs
= btz
->getPreviousTransition(time
, TRUE
, before
);
1193 && (time
- before
.getTime() < kDstCheckRange
)
1194 && before
.getFrom()->getDSTSavings() != 0) {
1195 useStandard
= FALSE
;
1197 TimeZoneTransition after
;
1198 UBool afterTrs
= btz
->getNextTransition(time
, FALSE
, after
);
1200 && (after
.getTime() - time
< kDstCheckRange
)
1201 && after
.getTo()->getDSTSavings() != 0) {
1202 useStandard
= FALSE
;
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
);
1210 useStandard
= FALSE
;
1212 tmptz
->getOffset(time
+ kDstCheckRange
, FALSE
, raw
, sav
, status
);
1214 useStandard
= FALSE
;
1217 if (U_FAILURE(status
)) {
1226 getString(canonicalID
, (isShort
? ZSIDX_SHORT_STANDARD
: ZSIDX_LONG_STANDARD
),
1227 time
, commonlyUsedOnly
, result
);
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
) {
1243 if (result
.isEmpty()) {
1244 ZoneStrings
*mzstrings
= (ZoneStrings
*)fMzidToStrings
.get(mzid
);
1245 if (mzstrings
!= NULL
) {
1247 if (!commonlyUsedOnly
|| mzstrings
->isShortFormatCommonlyUsed()) {
1248 mzstrings
->getString(ZSIDX_SHORT_GENERIC
, result
);
1251 mzstrings
->getString(ZSIDX_LONG_GENERIC
, result
);
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
)) {
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
1272 preferredZone
->getOffset(time
+ raw
+ sav
, TRUE
, prfRaw
, prfSav
, status
);
1273 delete preferredZone
;
1275 if (U_FAILURE(status
)) {
1279 if ((raw
!= prfRaw
|| sav
!= prfSav
) && zstrings
!= NULL
) {
1280 // Use generic partial location string as fallback
1281 zstrings
->getGenericPartialLocationString(mzid
, isShort
, commonlyUsedOnly
, result
);
1288 if (result
.isEmpty()) {
1289 // Use location format as the final fallback
1290 getString(canonicalID
, ZSIDX_LOCATION
, time
, FALSE
/*not used*/, result
);
1297 ZoneStringFormat::getGenericPartialLocationString(const UnicodeString
&tzid
, UBool isShort
,
1298 UDate date
, UBool commonlyUsedOnly
, UnicodeString
&result
) const {
1300 if (fTzidToStrings
.count() <= 0) {
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.
1313 ZoneMeta::getMetazoneID(canonicalID
, date
, mzid
);
1315 if (!mzid
.isEmpty()) {
1316 ZoneStrings
*zstrings
= (ZoneStrings
*)fTzidToStrings
.get(canonicalID
);
1317 if (zstrings
!= NULL
) {
1318 zstrings
->getGenericPartialLocationString(mzid
, isShort
, commonlyUsedOnly
, result
);
1324 const ZoneStringInfo
*
1325 ZoneStringFormat::find(const UnicodeString
&text
, int32_t start
, int32_t types
,
1326 int32_t &matchLength
, UErrorCode
&status
) const {
1328 if (U_FAILURE(status
)) {
1331 if (fZoneStringsTrie
.isEmpty()) {
1334 const ZoneStringInfo
*result
= NULL
;
1335 const ZoneStringInfo
*fallback
= NULL
;
1336 int32_t fallbackMatchLen
= 0;
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
) {
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()) {
1358 } else if (result
== NULL
) {
1359 if (fallback
== NULL
|| fallbackMatchLen
< tmpMatchLen
) {
1361 fallbackMatchLen
= tmpMatchLen
;
1362 } else if (fallbackMatchLen
== tmpMatchLen
) {
1363 if (tmp
->isGeneric() && !fallback
->isGeneric()) {
1369 if (result
== NULL
&& fallback
!= NULL
) {
1371 matchLength
= fallbackMatchLen
;
1379 ZoneStringFormat::getRegion(UnicodeString
®ion
) 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
));
1388 ZoneStringFormat::getFallbackFormat(const Locale
&locale
, UErrorCode
&status
) {
1389 if (U_FAILURE(status
)) {
1392 UnicodeString
pattern(TRUE
, gDefFallbackPattern
, -1);
1393 UResourceBundle
*zoneStringsArray
= ures_open(NULL
, locale
.getName(), &status
);
1394 zoneStringsArray
= ures_getByKeyWithFallback(zoneStringsArray
, gZoneStringsTag
, zoneStringsArray
, &status
);
1396 const UChar
*flbkfmt
= ures_getStringByKeyWithFallback(zoneStringsArray
, gFallbackFormatTag
, &len
, &status
);
1397 if (U_SUCCESS(status
)) {
1398 pattern
.setTo(flbkfmt
);
1400 status
= U_ZERO_ERROR
;
1402 ures_close(zoneStringsArray
);
1404 MessageFormat
*fallbackFmt
= new MessageFormat(pattern
, status
);
1409 ZoneStringFormat::getRegionFormat(const Locale
& locale
, UErrorCode
&status
) {
1410 if (U_FAILURE(status
)) {
1413 UnicodeString
pattern(TRUE
, gDefRegionPattern
, -1);
1414 UResourceBundle
*zoneStringsArray
= ures_open(NULL
, locale
.getName(), &status
);
1415 zoneStringsArray
= ures_getByKeyWithFallback(zoneStringsArray
, gZoneStringsTag
, zoneStringsArray
, &status
);
1417 const UChar
*regionfmt
= ures_getStringByKeyWithFallback(zoneStringsArray
, gRegionFormatTag
, &len
, &status
);
1418 if (U_SUCCESS(status
)) {
1419 pattern
.setTo(regionfmt
);
1421 status
= U_ZERO_ERROR
;
1423 ures_close(zoneStringsArray
);
1425 MessageFormat
*regionFmt
= new MessageFormat(pattern
, status
);
1430 ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle
*zoneitem
, const char *key
) {
1431 const UChar
*str
= NULL
;
1432 if (zoneitem
!= NULL
) {
1433 UErrorCode status
= U_ZERO_ERROR
;
1435 str
= ures_getStringByKeyWithFallback(zoneitem
, key
, &len
, &status
);
1436 if (U_FAILURE(status
)) {
1444 ZoneStringFormat::isCommonlyUsed(const UResourceBundle
*zoneitem
) {
1445 if (zoneitem
== NULL
) {
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
)) {
1455 commonlyUsed
= TRUE
;
1459 return commonlyUsed
;
1463 ZoneStringFormat::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;
1482 Locale
tmpLoc(tmpLocStr
);
1483 tmpLoc
.getDisplayCountry(locale
, displayCountry
);
1487 ures_close(localeBundle
);
1489 if (displayCountry
.isEmpty()) {
1490 // Use the country code as the fallback
1491 displayCountry
.setTo(countryCode
);
1493 return displayCountry
;
1496 // ----------------------------------------------------------------------------
1498 * This constructor adopts the input UnicodeString arrays.
1500 ZoneStrings::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
) {
1507 ZoneStrings::~ZoneStrings() {
1508 if (fStrings
!= NULL
) {
1511 if (fGenericPartialLocationStrings
!= NULL
) {
1512 for (int32_t i
= 0; i
< fGenericPartialLocationRowCount
; i
++) {
1513 delete[] fGenericPartialLocationStrings
[i
];
1515 uprv_free(fGenericPartialLocationStrings
);
1521 ZoneStrings::getString(int32_t typeIdx
, UnicodeString
&result
) const {
1522 if (typeIdx
>= 0 && typeIdx
< fStringsCount
) {
1523 result
.setTo(fStrings
[typeIdx
]);
1531 ZoneStrings::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
) {
1538 if (fGenericPartialLocationColCount
>= 3) {
1539 if (!commonlyUsedOnly
||
1540 fGenericPartialLocationColCount
== 3 || fGenericPartialLocationStrings
[i
][3].length() != 0) {
1541 result
.setTo(fGenericPartialLocationStrings
[i
][2]);
1546 result
.setTo(fGenericPartialLocationStrings
[i
][1]);
1559 // --------------------------------------------------------------
1560 SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry
*cacheEntry
)
1561 : fCacheEntry(cacheEntry
) {
1564 SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() {
1565 fCacheEntry
->delRef();
1568 const ZoneStringFormat
*
1569 SafeZoneStringFormatPtr::get() const {
1570 return fCacheEntry
->getZoneStringFormat();
1573 ZSFCacheEntry::ZSFCacheEntry(const Locale
&locale
, ZoneStringFormat
*zsf
, ZSFCacheEntry
*next
)
1574 : fLocale(locale
), fZoneStringFormat(zsf
),
1575 fNext(next
), fRefCount(1)
1579 ZSFCacheEntry::~ZSFCacheEntry () {
1580 delete fZoneStringFormat
;
1583 const ZoneStringFormat
*
1584 ZSFCacheEntry::getZoneStringFormat(void) {
1585 return (const ZoneStringFormat
*)fZoneStringFormat
;
1589 ZSFCacheEntry::delRef(void) {
1590 umtx_lock(&gZSFCacheLock
);
1592 umtx_unlock(&gZSFCacheLock
);
1595 ZSFCache::ZSFCache(int32_t capacity
)
1596 : fCapacity(capacity
), fFirst(NULL
) {
1599 ZSFCache::~ZSFCache() {
1600 ZSFCacheEntry
*entry
= fFirst
;
1602 ZSFCacheEntry
*next
= entry
->fNext
;
1608 SafeZoneStringFormatPtr
*
1609 ZSFCache::get(const Locale
&locale
, UErrorCode
&status
) {
1610 SafeZoneStringFormatPtr
*result
= NULL
;
1612 // Search the cache entry list
1613 ZSFCacheEntry
*entry
= NULL
;
1614 ZSFCacheEntry
*next
, *prev
;
1616 umtx_lock(&gZSFCacheLock
);
1620 next
= entry
->fNext
;
1621 if (entry
->fLocale
== locale
) {
1622 // Add reference count
1625 // move the entry to the top
1626 if (entry
!= fFirst
) {
1628 entry
->fNext
= fFirst
;
1636 umtx_unlock(&gZSFCacheLock
);
1638 // Create a new ZoneStringFormat
1639 if (entry
== NULL
) {
1640 ZoneStringFormat
*zsf
= new ZoneStringFormat(locale
, status
);
1641 if (U_FAILURE(status
)) {
1646 status
= U_MEMORY_ALLOCATION_ERROR
;
1649 // Now add the new entry
1650 umtx_lock(&gZSFCacheLock
);
1651 // Make sure no other threads already created the one for the same locale
1655 next
= entry
->fNext
;
1656 if (entry
->fLocale
== locale
) {
1657 // Add reference count
1660 // move the entry to the top
1661 if (entry
!= fFirst
) {
1663 entry
->fNext
= fFirst
;
1671 if (entry
== NULL
) {
1672 // Add the new one to the top
1674 entry
= new ZSFCacheEntry(locale
, zsf
, next
);
1679 umtx_unlock(&gZSFCacheLock
);
1682 result
= new SafeZoneStringFormatPtr(entry
);
1684 // Now, delete unused cache entries beyond the capacity
1685 umtx_lock(&gZSFCacheLock
);
1690 next
= entry
->fNext
;
1691 if (idx
>= fCapacity
&& entry
->fRefCount
== 0) {
1692 if (entry
== fFirst
) {
1704 umtx_unlock(&gZSFCacheLock
);
1711 #endif /* #if !UCONFIG_NO_FORMATTING */