2 *******************************************************************************
3 * Copyright (C) 2007-2010, 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"
34 * global ZoneStringFormatCache stuffs
36 static UMTX gZSFCacheLock
= NULL
;
37 static U_NAMESPACE_QUALIFIER ZSFCache
*gZoneStringFormatCache
= NULL
;
41 * ZoneStringFormatCache cleanup callback func
43 static UBool U_CALLCONV
zoneStringFormat_cleanup(void)
45 umtx_destroy(&gZSFCacheLock
);
46 if (gZoneStringFormatCache
!= NULL
) {
47 delete gZoneStringFormatCache
;
48 gZoneStringFormatCache
= NULL
;
50 gZoneStringFormatCache
= NULL
;
55 * Deleter for ZoneStringInfo
57 static void U_CALLCONV
58 deleteZoneStringInfo(void *obj
) {
59 delete (U_NAMESPACE_QUALIFIER ZoneStringInfo
*)obj
;
63 * Deleter for ZoneStrings
65 static void U_CALLCONV
66 deleteZoneStrings(void *obj
) {
67 delete (U_NAMESPACE_QUALIFIER ZoneStrings
*)obj
;
73 #define ZID_KEY_MAX 128
75 static const char gCountriesTag
[] = "Countries";
76 static const char gZoneStringsTag
[] = "zoneStrings";
77 static const char gShortGenericTag
[] = "sg";
78 static const char gShortStandardTag
[] = "ss";
79 static const char gShortDaylightTag
[] = "sd";
80 static const char gLongGenericTag
[] = "lg";
81 static const char gLongStandardTag
[] = "ls";
82 static const char gLongDaylightTag
[] = "ld";
83 static const char gExemplarCityTag
[] = "ec";
84 static const char gCommonlyUsedTag
[] = "cu";
85 static const char gFallbackFormatTag
[] = "fallbackFormat";
86 static const char gRegionFormatTag
[] = "regionFormat";
88 #define MZID_PREFIX_LEN 5
89 static const char gMetazoneIdPrefix
[] = "meta:";
91 #define MAX_METAZONES_PER_ZONE 10
93 static const UChar gDefFallbackPattern
[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
94 static const UChar gDefRegionPattern
[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
95 static const UChar gCommonlyUsedTrue
[] = {0x31, 0x00}; // "1"
97 static const double kDstCheckRange
= (double)184*U_MILLIS_PER_DAY
;
100 getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type
) {
104 typeIdx
= ZSIDX_LOCATION
;
107 typeIdx
= ZSIDX_LONG_GENERIC
;
110 typeIdx
= ZSIDX_SHORT_GENERIC
;
113 typeIdx
= ZSIDX_LONG_STANDARD
;
116 typeIdx
= ZSIDX_SHORT_STANDARD
;
119 typeIdx
= ZSIDX_LONG_DAYLIGHT
;
122 typeIdx
= ZSIDX_SHORT_DAYLIGHT
;
129 getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx
) {
135 case ZSIDX_LONG_GENERIC
:
138 case ZSIDX_SHORT_GENERIC
:
139 type
= GENERIC_SHORT
;
141 case ZSIDX_LONG_STANDARD
:
142 type
= STANDARD_LONG
;
144 case ZSIDX_SHORT_STANDARD
:
145 type
= STANDARD_SHORT
;
147 case ZSIDX_LONG_DAYLIGHT
:
148 type
= DAYLIGHT_LONG
;
151 case ZSIDX_SHORT_DAYLIGHT
:
152 type
= DAYLIGHT_SHORT
;
160 #define DEFAULT_CHARACTERNODE_CAPACITY 1
162 // ----------------------------------------------------------------------------
163 void CharacterNode::clear() {
164 uprv_memset(this, 0, sizeof(*this));
167 void CharacterNode::deleteValues() {
168 if (fValues
== NULL
) {
170 } else if (!fHasValuesVector
) {
171 deleteZoneStringInfo(fValues
);
173 delete (UVector
*)fValues
;
178 CharacterNode::addValue(void *value
, UErrorCode
&status
) {
179 if (U_FAILURE(status
)) {
180 deleteZoneStringInfo(value
);
183 if (fValues
== NULL
) {
186 // At least one value already.
187 if (!fHasValuesVector
) {
188 // There is only one value so far, and not in a vector yet.
189 // Create a vector and add the old value.
190 UVector
*values
= new UVector(deleteZoneStringInfo
, NULL
, DEFAULT_CHARACTERNODE_CAPACITY
, status
);
191 if (U_FAILURE(status
)) {
192 deleteZoneStringInfo(value
);
195 values
->addElement(fValues
, status
);
197 fHasValuesVector
= TRUE
;
199 // Add the new value.
200 ((UVector
*)fValues
)->addElement(value
, status
);
204 //----------------------------------------------------------------------------
205 // Virtual destructor to avoid warning
206 TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
209 // ----------------------------------------------------------------------------
210 TextTrieMap::TextTrieMap(UBool ignoreCase
)
211 : fIgnoreCase(ignoreCase
), fNodes(NULL
), fNodesCapacity(0), fNodesCount(0),
212 fLazyContents(NULL
), fIsEmpty(TRUE
) {
215 TextTrieMap::~TextTrieMap() {
217 for (index
= 0; index
< fNodesCount
; ++index
) {
218 fNodes
[index
].deleteValues();
221 if (fLazyContents
!= NULL
) {
222 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
223 ZoneStringInfo
*zsinf
= (ZoneStringInfo
*)fLazyContents
->elementAt(i
+1);
226 delete fLazyContents
;
230 int32_t TextTrieMap::isEmpty() const {
231 // Use a separate field for fIsEmpty because it will remain unchanged once the
232 // Trie is built, while fNodes and fLazyContents change with the lazy init
233 // of the nodes structure. Trying to test the changing fields has
234 // thread safety complications.
239 // We defer actually building the TextTrieMap node structure until the first time a
240 // search is performed. put() simply saves the parameters in case we do
241 // eventually need to build it.
244 TextTrieMap::put(const UnicodeString
&key
, void *value
, ZSFStringPool
&sp
, UErrorCode
&status
) {
246 if (fLazyContents
== NULL
) {
247 fLazyContents
= new UVector(status
);
248 if (fLazyContents
== NULL
) {
249 status
= U_MEMORY_ALLOCATION_ERROR
;
252 if (U_FAILURE(status
)) {
255 UChar
*s
= const_cast<UChar
*>(sp
.get(key
, status
));
256 fLazyContents
->addElement(s
, status
);
257 fLazyContents
->addElement(value
, status
);
262 TextTrieMap::putImpl(const UnicodeString
&key
, void *value
, UErrorCode
&status
) {
263 if (fNodes
== NULL
) {
264 fNodesCapacity
= 512;
265 fNodes
= (CharacterNode
*)uprv_malloc(fNodesCapacity
* sizeof(CharacterNode
));
266 fNodes
[0].clear(); // Init root node.
270 UnicodeString foldedKey
;
271 const UChar
*keyBuffer
;
274 // Ok to use fastCopyFrom() because we discard the copy when we return.
275 foldedKey
.fastCopyFrom(key
).foldCase();
276 keyBuffer
= foldedKey
.getBuffer();
277 keyLength
= foldedKey
.length();
279 keyBuffer
= key
.getBuffer();
280 keyLength
= key
.length();
283 CharacterNode
*node
= fNodes
;
285 for (index
= 0; index
< keyLength
; ++index
) {
286 node
= addChildNode(node
, keyBuffer
[index
], status
);
288 node
->addValue(value
, status
);
292 TextTrieMap::growNodes() {
293 if (fNodesCapacity
== 0xffff) {
294 return FALSE
; // We use 16-bit node indexes.
296 int32_t newCapacity
= fNodesCapacity
+ 1000;
297 if (newCapacity
> 0xffff) {
298 newCapacity
= 0xffff;
300 CharacterNode
*newNodes
= (CharacterNode
*)uprv_malloc(newCapacity
* sizeof(CharacterNode
));
301 if (newNodes
== NULL
) {
304 uprv_memcpy(newNodes
, fNodes
, fNodesCount
* sizeof(CharacterNode
));
307 fNodesCapacity
= newCapacity
;
312 TextTrieMap::addChildNode(CharacterNode
*parent
, UChar c
, UErrorCode
&status
) {
313 if (U_FAILURE(status
)) {
316 // Linear search of the sorted list of children.
317 uint16_t prevIndex
= 0;
318 uint16_t nodeIndex
= parent
->fFirstChild
;
319 while (nodeIndex
> 0) {
320 CharacterNode
*current
= fNodes
+ nodeIndex
;
321 UChar childCharacter
= current
->fCharacter
;
322 if (childCharacter
== c
) {
324 } else if (childCharacter
> c
) {
327 prevIndex
= nodeIndex
;
328 nodeIndex
= current
->fNextSibling
;
331 // Ensure capacity. Grow fNodes[] if needed.
332 if (fNodesCount
== fNodesCapacity
) {
333 int32_t parentIndex
= (int32_t)(parent
- fNodes
);
335 status
= U_MEMORY_ALLOCATION_ERROR
;
338 parent
= fNodes
+ parentIndex
;
341 // Insert a new child node with c in sorted order.
342 CharacterNode
*node
= fNodes
+ fNodesCount
;
344 node
->fCharacter
= c
;
345 node
->fNextSibling
= nodeIndex
;
346 if (prevIndex
== 0) {
347 parent
->fFirstChild
= (uint16_t)fNodesCount
;
349 fNodes
[prevIndex
].fNextSibling
= (uint16_t)fNodesCount
;
356 TextTrieMap::getChildNode(CharacterNode
*parent
, UChar c
) const {
357 // Linear search of the sorted list of children.
358 uint16_t nodeIndex
= parent
->fFirstChild
;
359 while (nodeIndex
> 0) {
360 CharacterNode
*current
= fNodes
+ nodeIndex
;
361 UChar childCharacter
= current
->fCharacter
;
362 if (childCharacter
== c
) {
364 } else if (childCharacter
> c
) {
367 nodeIndex
= current
->fNextSibling
;
372 // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
373 static UMTX TextTrieMutex
;
375 // buildTrie() - The Trie node structure is needed. Create it from the data that was
376 // saved at the time the ZoneStringFormatter was created. The Trie is only
377 // needed for parsing operations, which are less common than formatting,
378 // and the Trie is big, which is why its creation is deferred until first use.
379 void TextTrieMap::buildTrie(UErrorCode
&status
) {
380 umtx_lock(&TextTrieMutex
);
381 if (fLazyContents
!= NULL
) {
382 for (int32_t i
=0; i
<fLazyContents
->size(); i
+=2) {
383 const UChar
*key
= (UChar
*)fLazyContents
->elementAt(i
);
384 void *val
= fLazyContents
->elementAt(i
+1);
385 UnicodeString
keyString(TRUE
, key
, -1); // Aliasing UnicodeString constructor.
386 putImpl(keyString
, val
, status
);
388 delete fLazyContents
;
389 fLazyContents
= NULL
;
391 umtx_unlock(&TextTrieMutex
);
396 TextTrieMap::search(const UnicodeString
&text
, int32_t start
,
397 TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
398 UBool trieNeedsInitialization
= FALSE
;
399 UMTX_CHECK(&TextTrieMutex
, fLazyContents
!= NULL
, trieNeedsInitialization
);
400 if (trieNeedsInitialization
) {
401 TextTrieMap
*nonConstThis
= const_cast<TextTrieMap
*>(this);
402 nonConstThis
->buildTrie(status
);
404 if (fNodes
== NULL
) {
407 search(fNodes
, text
, start
, start
, handler
, status
);
411 TextTrieMap::search(CharacterNode
*node
, const UnicodeString
&text
, int32_t start
,
412 int32_t index
, TextTrieMapSearchResultHandler
*handler
, UErrorCode
&status
) const {
413 if (U_FAILURE(status
)) {
416 if (node
->hasValues()) {
417 if (!handler
->handleMatch(index
- start
, node
, status
)) {
420 if (U_FAILURE(status
)) {
424 UChar32 c
= text
.char32At(index
);
426 // size of character may grow after fold operation
427 UnicodeString
tmp(c
);
430 while (tmpidx
< tmp
.length()) {
431 c
= tmp
.char32At(tmpidx
);
432 node
= getChildNode(node
, c
);
436 tmpidx
= tmp
.moveIndex32(tmpidx
, 1);
439 node
= getChildNode(node
, c
);
442 search(node
, text
, start
, index
+1, handler
, status
);
446 // ----------------------------------------------------------------------------
447 ZoneStringInfo::ZoneStringInfo(const UnicodeString
&id
, const UnicodeString
&str
,
448 TimeZoneTranslationType type
, ZSFStringPool
&sp
, UErrorCode
&status
)
450 fId
= sp
.get(id
, status
);
451 fStr
= sp
.get(str
, status
);
454 ZoneStringInfo::~ZoneStringInfo() {
458 // ----------------------------------------------------------------------------
459 ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode
&status
)
465 ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() {
470 ZoneStringSearchResultHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
471 if (U_FAILURE(status
)) {
474 if (node
->hasValues()) {
475 int32_t valuesCount
= node
->countValues();
476 for (int32_t i
= 0; i
< valuesCount
; i
++) {
477 ZoneStringInfo
*zsinfo
= (ZoneStringInfo
*)node
->getValue(i
);
478 if (zsinfo
== NULL
) {
481 // Update the results
482 UBool foundType
= FALSE
;
483 for (int32_t j
= 0; j
< fResults
.size(); j
++) {
484 ZoneStringInfo
*tmp
= (ZoneStringInfo
*)fResults
.elementAt(j
);
485 if (zsinfo
->fType
== tmp
->fType
) {
486 int32_t lenidx
= getTimeZoneTranslationTypeIndex(tmp
->fType
);
487 if (matchLength
> fMatchLen
[lenidx
]) {
488 // Same type, longer match
489 fResults
.setElementAt(zsinfo
, j
);
490 fMatchLen
[lenidx
] = matchLength
;
497 // not found in the current list
498 fResults
.addElement(zsinfo
, status
);
499 fMatchLen
[getTimeZoneTranslationTypeIndex(zsinfo
->fType
)] = matchLength
;
507 ZoneStringSearchResultHandler::countMatches(void) {
508 return fResults
.size();
511 const ZoneStringInfo
*
512 ZoneStringSearchResultHandler::getMatch(int32_t index
, int32_t &matchLength
) {
513 ZoneStringInfo
*zsinfo
= NULL
;
514 if (index
< fResults
.size()) {
515 zsinfo
= (ZoneStringInfo
*)fResults
.elementAt(index
);
516 matchLength
= fMatchLen
[getTimeZoneTranslationTypeIndex(zsinfo
->fType
)];
522 ZoneStringSearchResultHandler::clear(void) {
523 fResults
.removeAllElements();
524 for (int32_t i
= 0; i
< (int32_t)(sizeof(fMatchLen
)/sizeof(fMatchLen
[0])); i
++) {
529 // Mutex for protecting the lazy load of a zone ID (or a full load) to ZoneStringFormat structures.
530 static UMTX ZoneStringFormatMutex
;
533 // ----------------------------------------------------------------------------
534 ZoneStringFormat::ZoneStringFormat(const UnicodeString
* const* strings
,
535 int32_t rowCount
, int32_t columnCount
, UErrorCode
&status
)
537 fTzidToStrings(NULL
),
538 fMzidToStrings(NULL
),
539 fZoneStringsTrie(TRUE
),
541 fZoneStringsArray(NULL
),
544 fIsFullyLoaded(FALSE
)
546 if (U_FAILURE(status
)) {
549 fLocale
.setToBogus();
550 if (strings
== NULL
|| columnCount
<= 0 || rowCount
<= 0) {
551 status
= U_ILLEGAL_ARGUMENT_ERROR
;
554 fTzidToStrings
= uhash_open(uhash_hashUChars
, // key hash function
555 uhash_compareUChars
, // key comparison function
556 NULL
, // Value comparison function
558 fMzidToStrings
= uhash_open(uhash_hashUChars
,
563 uhash_setValueDeleter(fTzidToStrings
, deleteZoneStrings
);
564 uhash_setValueDeleter(fMzidToStrings
, deleteZoneStrings
);
566 for (int32_t row
= 0; row
< rowCount
; row
++) {
567 if (strings
[row
][0].isEmpty()) {
570 UnicodeString
*names
= new UnicodeString
[ZSIDX_COUNT
];
571 for (int32_t col
= 1; col
< columnCount
; col
++) {
572 if (!strings
[row
][col
].isEmpty()) {
573 int32_t typeIdx
= -1;
576 typeIdx
= ZSIDX_LONG_STANDARD
;
579 typeIdx
= ZSIDX_SHORT_STANDARD
;
582 typeIdx
= ZSIDX_LONG_DAYLIGHT
;
585 typeIdx
= ZSIDX_SHORT_DAYLIGHT
;
588 typeIdx
= ZSIDX_LOCATION
;
591 typeIdx
= ZSIDX_LONG_GENERIC
;
594 typeIdx
= ZSIDX_SHORT_GENERIC
;
598 names
[typeIdx
].setTo(strings
[row
][col
]);
600 // Put the name into the trie
601 int32_t type
= getTimeZoneTranslationType((TimeZoneTranslationTypeIndex
)typeIdx
);
602 ZoneStringInfo
*zsinf
= new ZoneStringInfo(strings
[row
][0],
604 (TimeZoneTranslationType
)type
,
607 fZoneStringsTrie
.put(strings
[row
][col
], zsinf
, fStringPool
, status
);
608 if (U_FAILURE(status
)) {
615 // Note: ZoneStrings constructor adopts and delete the names array.
616 ZoneStrings
*zstrings
= new ZoneStrings(names
, ZSIDX_COUNT
, TRUE
, NULL
, 0, 0,
617 fStringPool
, status
);
618 UChar
*utzid
= const_cast<UChar
*>(fStringPool
.get(strings
[row
][0], status
));
619 uhash_put(fTzidToStrings
, utzid
, zstrings
, &status
);
620 if (U_FAILURE(status
)) {
625 fStringPool
.freeze();
626 fIsFullyLoaded
= TRUE
;
633 ZoneStringFormat::ZoneStringFormat(const Locale
&locale
, UErrorCode
&status
)
635 fTzidToStrings(NULL
),
636 fMzidToStrings(NULL
),
637 fZoneStringsTrie(TRUE
),
639 fZoneStringsArray(NULL
),
642 fIsFullyLoaded(FALSE
)
644 if (U_FAILURE(status
)) {
647 fTzidToStrings
= uhash_open(uhash_hashUChars
, // key hash function
648 uhash_compareUChars
, // key comparison function
649 NULL
, // Value comparison function
651 fMzidToStrings
= uhash_open(uhash_hashUChars
, // key hash function
652 uhash_compareUChars
, // key comparison function
653 NULL
, // Value comparison function
655 uhash_setValueDeleter(fTzidToStrings
, deleteZoneStrings
);
656 uhash_setValueDeleter(fMzidToStrings
, deleteZoneStrings
);
659 // Load only a single zone
661 ZoneStringFormat::loadZone(const UnicodeString
&utzid
, UErrorCode
&status
)
663 if (fIsFullyLoaded
) {
667 if (U_FAILURE(status
)) {
671 umtx_lock(&ZoneStringFormatMutex
);
673 if (fZoneStringsArray
== NULL
) {
674 fZoneStringsArray
= ures_open(U_ICUDATA_ZONE
, fLocale
.getName(), &status
);
675 fZoneStringsArray
= ures_getByKeyWithFallback(fZoneStringsArray
, gZoneStringsTag
, fZoneStringsArray
, &status
);
676 if (U_FAILURE(status
)) {
677 // If no locale bundles are available, zoneStrings will be null.
678 // We still want to go through the rest of zone strings initialization,
679 // because generic location format is generated from tzid for the case.
680 // The rest of code should work even zoneStrings is null.
681 status
= U_ZERO_ERROR
;
682 ures_close(fZoneStringsArray
);
683 fZoneStringsArray
= NULL
;
687 // Skip non-canonical IDs
688 UnicodeString canonicalID
;
689 TimeZone::getCanonicalID(utzid
, canonicalID
, status
);
690 if (U_FAILURE(status
)) {
691 // Ignore unknown ID - we should not get here, but just in case.
692 // status = U_ZERO_ERROR;
693 umtx_unlock(&ZoneStringFormatMutex
);
697 if (U_SUCCESS(status
)) {
698 if (uhash_count(fTzidToStrings
) > 0) {
699 ZoneStrings
*zstrings
= (ZoneStrings
*)uhash_get(fTzidToStrings
, canonicalID
.getTerminatedBuffer());
700 if (zstrings
!= NULL
) {
701 umtx_unlock(&ZoneStringFormatMutex
);
702 return; // We already about this one
707 addSingleZone(canonicalID
, status
);
709 umtx_unlock(&ZoneStringFormatMutex
);
712 // Load only a single zone
714 ZoneStringFormat::addSingleZone(UnicodeString
&utzid
, UErrorCode
&status
)
716 if (U_FAILURE(status
)) {
720 if (uhash_count(fTzidToStrings
) > 0) {
721 ZoneStrings
*zstrings
= (ZoneStrings
*)uhash_get(fTzidToStrings
, utzid
.getTerminatedBuffer());
722 if (zstrings
!= NULL
) {
723 return; // We already about this one
727 MessageFormat
*fallbackFmt
= NULL
;
728 MessageFormat
*regionFmt
= NULL
;
730 fallbackFmt
= getFallbackFormat(fLocale
, status
);
731 if (U_FAILURE(status
)) {
734 regionFmt
= getRegionFormat(fLocale
, status
);
735 if (U_FAILURE(status
)) {
741 char zidkey
[ZID_KEY_MAX
+1];
742 char tzid
[ZID_KEY_MAX
+1];
743 utzid
.extract(0, utzid
.length(), zidkey
, ZID_KEY_MAX
, US_INV
);
744 utzid
.extract(0, utzid
.length(), tzid
, ZID_KEY_MAX
, US_INV
);
746 const UChar
*zstrarray
[ZSIDX_COUNT
];
747 const UChar
*mzstrarray
[ZSIDX_COUNT
];
748 UnicodeString mzPartialLoc
[MAX_METAZONES_PER_ZONE
][4];
750 // Replace '/' with ':'
761 if (fZoneStringsArray
!= NULL
) {
762 fZoneItem
= ures_getByKeyWithFallback(fZoneStringsArray
, zidkey
, fZoneItem
, &status
);
763 if (U_FAILURE(status
)) {
764 // If failed to open the zone item, create only location string
765 ures_close(fZoneItem
);
767 status
= U_ZERO_ERROR
;
771 UnicodeString region
;
774 zstrarray
[ZSIDX_LONG_STANDARD
] = getZoneStringFromBundle(fZoneItem
, gLongStandardTag
);
775 zstrarray
[ZSIDX_SHORT_STANDARD
] = getZoneStringFromBundle(fZoneItem
, gShortStandardTag
);
776 zstrarray
[ZSIDX_LONG_DAYLIGHT
] = getZoneStringFromBundle(fZoneItem
, gLongDaylightTag
);
777 zstrarray
[ZSIDX_SHORT_DAYLIGHT
] = getZoneStringFromBundle(fZoneItem
, gShortDaylightTag
);
778 zstrarray
[ZSIDX_LONG_GENERIC
] = getZoneStringFromBundle(fZoneItem
, gLongGenericTag
);
779 zstrarray
[ZSIDX_SHORT_GENERIC
] = getZoneStringFromBundle(fZoneItem
, gShortGenericTag
);
781 // Compose location format string
782 UnicodeString location
;
783 UnicodeString country
;
785 UnicodeString countryCode
;
786 ZoneMeta::getCanonicalCountry(utzid
, countryCode
);
787 if (!countryCode
.isEmpty()) {
788 const UChar
* tmpCity
= getZoneStringFromBundle(fZoneItem
, gExemplarCityTag
);
789 if (tmpCity
!= NULL
) {
790 city
.setTo(TRUE
, tmpCity
, -1);
792 city
.setTo(UnicodeString(pCity
, -1, US_INV
));
793 // Replace '_' with ' '
794 for (int32_t i
= 0; i
< city
.length(); i
++) {
795 if (city
.charAt(i
) == (UChar
)0x5F /*'_'*/) {
796 city
.setCharAt(i
, (UChar
)0x20 /*' '*/);
800 getLocalizedCountry(countryCode
, fLocale
, country
);
801 UnicodeString singleCountry
;
802 ZoneMeta::getSingleCountry(utzid
, singleCountry
);
804 if (singleCountry
.isEmpty()) {
805 Formattable params
[] = {
809 fallbackFmt
->format(params
, 2, location
, fpos
, status
);
811 // If the zone is only one zone in the country, do not add city
812 Formattable params
[] = {
815 regionFmt
->format(params
, 1, location
, fpos
, status
);
817 if (U_FAILURE(status
)) {
821 zstrarray
[ZSIDX_LOCATION
] = location
.getTerminatedBuffer();
823 if (uprv_strlen(tzid
) > 4 && uprv_strncmp(tzid
, "Etc/", 4) == 0) {
824 // "Etc/xxx" is not associated with a specific location, so localized
825 // GMT format is always used as generic location format.
826 zstrarray
[ZSIDX_LOCATION
] = NULL
;
828 // When a new time zone ID, which is actually associated with a specific
829 // location, is added in tzdata, but the current CLDR data does not have
830 // the information yet, ICU creates a generic location string based on
831 // the ID. This implementation supports canonical time zone round trip
832 // with format pattern "VVVV". See #6602 for the details.
833 UnicodeString
loc(utzid
);
834 int32_t slashIdx
= loc
.lastIndexOf((UChar
)0x2f);
835 if (slashIdx
== -1) {
836 // A time zone ID without slash in the tz database is not
837 // associated with a specific location. For instances,
838 // MET, CET, EET and WET fall into this category.
839 // In this case, we still use GMT format as fallback.
840 zstrarray
[ZSIDX_LOCATION
] = NULL
;
843 Formattable params
[] = {
846 regionFmt
->format(params
, 1, location
, fpos
, status
);
847 if (U_FAILURE(status
)) {
850 zstrarray
[ZSIDX_LOCATION
] = location
.getTerminatedBuffer();
855 UBool commonlyUsed
= isCommonlyUsed(fZoneItem
);
857 // Resolve metazones used by this zone
858 int32_t mzPartialLocIdx
= 0;
859 const UVector
*metazoneMappings
= ZoneMeta::getMetazoneMappings(utzid
);
860 if (metazoneMappings
!= NULL
) {
861 for (int32_t i
= 0; i
< metazoneMappings
->size(); i
++) {
862 const OlsonToMetaMappingEntry
*mzmap
=
863 (const OlsonToMetaMappingEntry
*)metazoneMappings
->elementAt(i
);
864 UnicodeString
mzid(mzmap
->mzid
);
865 const ZoneStrings
*mzStrings
=
866 (const ZoneStrings
*)uhash_get(fMzidToStrings
, mzid
.getTerminatedBuffer());
867 if (mzStrings
== NULL
) {
868 // If the metazone strings are not yet processed, do it now.
869 char mzidkey
[ZID_KEY_MAX
];
870 uprv_strcpy(mzidkey
, gMetazoneIdPrefix
);
871 u_UCharsToChars(mzmap
->mzid
, mzidkey
+ MZID_PREFIX_LEN
, u_strlen(mzmap
->mzid
) + 1);
872 fMetazoneItem
= ures_getByKeyWithFallback(fZoneStringsArray
, mzidkey
, fMetazoneItem
, &status
);
873 if (U_FAILURE(status
)) {
874 // No resources available for this metazone
875 // Resource bundle will be cleaned up after end of the loop.
876 status
= U_ZERO_ERROR
;
879 UBool mzCommonlyUsed
= isCommonlyUsed(fMetazoneItem
);
880 mzstrarray
[ZSIDX_LONG_STANDARD
] = getZoneStringFromBundle(fMetazoneItem
, gLongStandardTag
);
881 mzstrarray
[ZSIDX_SHORT_STANDARD
] = getZoneStringFromBundle(fMetazoneItem
, gShortStandardTag
);
882 mzstrarray
[ZSIDX_LONG_DAYLIGHT
] = getZoneStringFromBundle(fMetazoneItem
, gLongDaylightTag
);
883 mzstrarray
[ZSIDX_SHORT_DAYLIGHT
] = getZoneStringFromBundle(fMetazoneItem
, gShortDaylightTag
);
884 mzstrarray
[ZSIDX_LONG_GENERIC
] = getZoneStringFromBundle(fMetazoneItem
, gLongGenericTag
);
885 mzstrarray
[ZSIDX_SHORT_GENERIC
] = getZoneStringFromBundle(fMetazoneItem
, gShortGenericTag
);
886 mzstrarray
[ZSIDX_LOCATION
] = NULL
;
888 int32_t lastNonNullIdx
= ZSIDX_COUNT
- 1;
889 while (lastNonNullIdx
>= 0) {
890 if (mzstrarray
[lastNonNullIdx
] != NULL
) {
895 UnicodeString
*strings_mz
= NULL
;
896 ZoneStrings
*tmp_mzStrings
= NULL
;
897 if (lastNonNullIdx
>= 0) {
898 // Create UnicodeString array and put strings to the zone string trie
899 strings_mz
= new UnicodeString
[lastNonNullIdx
+ 1];
901 UnicodeString preferredIdForLocale
;
902 ZoneMeta::getZoneIdByMetazone(mzid
, region
, preferredIdForLocale
);
904 for (int32_t typeidx
= 0; typeidx
<= lastNonNullIdx
; typeidx
++) {
905 if (mzstrarray
[typeidx
] != NULL
) {
906 strings_mz
[typeidx
].setTo(TRUE
, mzstrarray
[typeidx
], -1);
908 // Add a metazone string to the zone string trie
909 int32_t type
= getTimeZoneTranslationType((TimeZoneTranslationTypeIndex
)typeidx
);
910 ZoneStringInfo
*zsinfo
= new ZoneStringInfo(
911 preferredIdForLocale
,
913 (TimeZoneTranslationType
)type
,
916 fZoneStringsTrie
.put(strings_mz
[typeidx
], zsinfo
, fStringPool
, status
);
917 if (U_FAILURE(status
)) {
923 // Note: ZoneStrings constructor adopts and deletes the strings_mz array.
924 tmp_mzStrings
= new ZoneStrings(strings_mz
, lastNonNullIdx
+ 1,
925 mzCommonlyUsed
, NULL
, 0, 0, fStringPool
, status
);
927 // Create ZoneStrings with empty contents
928 tmp_mzStrings
= new ZoneStrings(NULL
, 0, FALSE
, NULL
, 0, 0, fStringPool
, status
);
931 UChar
*umzid
= const_cast<UChar
*>(fStringPool
.get(mzid
, status
));
932 uhash_put(fMzidToStrings
, umzid
, tmp_mzStrings
, &status
);
933 if (U_FAILURE(status
)) {
937 mzStrings
= tmp_mzStrings
;
940 // Compose generic partial location format
944 mzStrings
->getString(ZSIDX_LONG_GENERIC
, lg
);
945 mzStrings
->getString(ZSIDX_SHORT_GENERIC
, sg
);
947 if (!lg
.isEmpty() || !sg
.isEmpty()) {
948 UBool addMzPartialLocationNames
= TRUE
;
949 for (int32_t j
= 0; j
< mzPartialLocIdx
; j
++) {
950 if (mzPartialLoc
[j
][0] == mzid
) {
952 addMzPartialLocationNames
= FALSE
;
956 if (addMzPartialLocationNames
) {
957 UnicodeString
*locationPart
= NULL
;
958 // Check if the zone is the preferred zone for the territory associated with the zone
959 UnicodeString preferredID
;
960 ZoneMeta::getZoneIdByMetazone(mzid
, countryCode
, preferredID
);
961 if (utzid
== preferredID
) {
962 // Use country for the location
963 locationPart
= &country
;
965 // Use city for the location
966 locationPart
= &city
;
968 // Reset the partial location string array
969 mzPartialLoc
[mzPartialLocIdx
][0].setTo(mzid
);
970 mzPartialLoc
[mzPartialLocIdx
][1].remove();
971 mzPartialLoc
[mzPartialLocIdx
][2].remove();
972 mzPartialLoc
[mzPartialLocIdx
][3].remove();
974 if (locationPart
->length() != 0) {
977 Formattable params
[] = {
978 Formattable(*locationPart
),
981 fallbackFmt
->format(params
, 2, mzPartialLoc
[mzPartialLocIdx
][1], fpos
, status
);
984 Formattable params
[] = {
985 Formattable(*locationPart
),
988 fallbackFmt
->format(params
, 2, mzPartialLoc
[mzPartialLocIdx
][2], fpos
, status
);
989 if (mzStrings
->isShortFormatCommonlyUsed()) {
990 mzPartialLoc
[mzPartialLocIdx
][3].setTo(TRUE
, gCommonlyUsedTrue
, -1);
993 if (U_FAILURE(status
)) {
1002 // Collected names for a zone
1004 // Create UnicodeString array for localized zone strings
1005 int32_t lastIdx
= ZSIDX_COUNT
- 1;
1006 while (lastIdx
>= 0) {
1007 if (zstrarray
[lastIdx
] != NULL
) {
1012 UnicodeString
*strings
= NULL
;
1013 int32_t stringsCount
= lastIdx
+ 1;
1015 if (stringsCount
> 0) {
1016 strings
= new UnicodeString
[stringsCount
];
1017 for (int32_t i
= 0; i
< stringsCount
; i
++) {
1018 if (zstrarray
[i
] != NULL
) {
1019 strings
[i
].setTo(zstrarray
[i
], -1);
1021 // Add names to the trie
1022 int32_t type
= getTimeZoneTranslationType((TimeZoneTranslationTypeIndex
)i
);
1023 ZoneStringInfo
*zsinfo
= new ZoneStringInfo(utzid
,
1025 (TimeZoneTranslationType
)type
,
1028 fZoneStringsTrie
.put(strings
[i
], zsinfo
, fStringPool
, status
);
1029 if (U_FAILURE(status
)) {
1038 // Create UnicodeString array for generic partial location strings
1039 UnicodeString
**genericPartialLocationNames
= NULL
;
1040 int32_t genericPartialRowCount
= mzPartialLocIdx
;
1041 int32_t genericPartialColCount
= 4;
1043 if (genericPartialRowCount
!= 0) {
1044 genericPartialLocationNames
=
1045 (UnicodeString
**)uprv_malloc(genericPartialRowCount
* sizeof(UnicodeString
*));
1046 if (genericPartialLocationNames
== NULL
) {
1047 status
= U_MEMORY_ALLOCATION_ERROR
;
1051 for (int32_t i
= 0; i
< genericPartialRowCount
; i
++) {
1052 genericPartialLocationNames
[i
] = new UnicodeString
[genericPartialColCount
];
1053 for (int32_t j
= 0; j
< genericPartialColCount
; j
++) {
1054 genericPartialLocationNames
[i
][j
].setTo(mzPartialLoc
[i
][j
]);
1055 // Add names to the trie
1056 if ((j
== 1 || j
== 2) &&!genericPartialLocationNames
[i
][j
].isEmpty()) {
1057 ZoneStringInfo
*zsinfo
;
1058 TimeZoneTranslationType type
= (j
== 1) ? GENERIC_LONG
: GENERIC_SHORT
;
1059 zsinfo
= new ZoneStringInfo(utzid
, genericPartialLocationNames
[i
][j
], type
,
1060 fStringPool
, status
);
1061 fZoneStringsTrie
.put(genericPartialLocationNames
[i
][j
], zsinfo
, fStringPool
, status
);
1062 if (U_FAILURE(status
)) {
1063 delete[] genericPartialLocationNames
[i
];
1064 uprv_free(genericPartialLocationNames
);
1073 // Finally, create ZoneStrings instance and put it into the tzidToStinrgs map
1074 ZoneStrings
*zstrings
= new ZoneStrings(strings
, stringsCount
, commonlyUsed
,
1075 genericPartialLocationNames
, genericPartialRowCount
,
1076 genericPartialColCount
, fStringPool
, status
);
1078 UChar
*uutzid
= const_cast<UChar
*>(fStringPool
.get(utzid
, status
));
1079 uhash_put(fTzidToStrings
, uutzid
, zstrings
, &status
);
1080 if (U_FAILURE(status
)) {
1087 if (fallbackFmt
!= NULL
) {
1090 if (regionFmt
!= NULL
) {
1093 // fStringPool.freeze();
1097 ZoneStringFormat::loadFull(UErrorCode
&status
)
1099 if (U_FAILURE(status
)) {
1102 if (fIsFullyLoaded
) {
1106 umtx_lock(&ZoneStringFormatMutex
);
1108 if (fZoneStringsArray
== NULL
) {
1109 fZoneStringsArray
= ures_open(U_ICUDATA_ZONE
, fLocale
.getName(), &status
);
1110 fZoneStringsArray
= ures_getByKeyWithFallback(fZoneStringsArray
, gZoneStringsTag
, fZoneStringsArray
, &status
);
1111 if (U_FAILURE(status
)) {
1112 // If no locale bundles are available, zoneStrings will be null.
1113 // We still want to go through the rest of zone strings initialization,
1114 // because generic location format is generated from tzid for the case.
1115 // The rest of code should work even zoneStrings is null.
1116 status
= U_ZERO_ERROR
;
1117 ures_close(fZoneStringsArray
);
1118 fZoneStringsArray
= NULL
;
1122 StringEnumeration
*tzids
= NULL
;
1124 tzids
= TimeZone::createEnumeration();
1126 while ((tzid
= tzids
->next(NULL
, status
))) {
1127 if (U_FAILURE(status
)) {
1130 // Skip non-canonical IDs
1131 UnicodeString
utzid(tzid
, -1, US_INV
);
1132 UnicodeString canonicalID
;
1133 TimeZone::getCanonicalID(utzid
, canonicalID
, status
);
1134 if (U_FAILURE(status
)) {
1135 // Ignore unknown ID - we should not get here, but just in case.
1136 status
= U_ZERO_ERROR
;
1140 if (U_SUCCESS(status
)) {
1141 if (uhash_count(fTzidToStrings
) > 0) {
1142 ZoneStrings
*zstrings
= (ZoneStrings
*)uhash_get(fTzidToStrings
, canonicalID
.getTerminatedBuffer());
1143 if (zstrings
!= NULL
) {
1144 continue; // We already about this one
1149 addSingleZone(canonicalID
, status
);
1151 if (U_FAILURE(status
)) {
1156 fIsFullyLoaded
= TRUE
;
1159 if (tzids
!= NULL
) {
1162 fStringPool
.freeze();
1164 umtx_unlock(&ZoneStringFormatMutex
);
1168 ZoneStringFormat::~ZoneStringFormat() {
1169 uhash_close(fTzidToStrings
);
1170 uhash_close(fMzidToStrings
);
1171 ures_close(fZoneItem
);
1172 ures_close(fMetazoneItem
);
1173 ures_close(fZoneStringsArray
);
1176 SafeZoneStringFormatPtr
*
1177 ZoneStringFormat::getZoneStringFormat(const Locale
& locale
, UErrorCode
&status
) {
1178 umtx_lock(&gZSFCacheLock
);
1179 if (gZoneStringFormatCache
== NULL
) {
1180 gZoneStringFormatCache
= new ZSFCache(10 /* capacity */);
1181 ucln_i18n_registerCleanup(UCLN_I18N_ZSFORMAT
, zoneStringFormat_cleanup
);
1183 umtx_unlock(&gZSFCacheLock
);
1185 return gZoneStringFormatCache
->get(locale
, status
);
1190 ZoneStringFormat::createZoneStringsArray(UDate date
, int32_t &rowCount
, int32_t &colCount
, UErrorCode
&status
) const {
1191 if (U_FAILURE(status
)) {
1194 ZoneStringFormat
*nonConstThis
= const_cast<ZoneStringFormat
*>(this);
1195 nonConstThis
->loadFull(status
);
1197 UnicodeString
**result
= NULL
;
1201 // Collect canonical time zone IDs
1202 UVector
canonicalIDs(uhash_deleteUnicodeString
, uhash_compareUnicodeString
, status
);
1203 if (U_FAILURE(status
)) {
1206 StringEnumeration
*tzids
= TimeZone::createEnumeration();
1208 while ((tzid
= tzids
->unext(NULL
, status
))) {
1209 if (U_FAILURE(status
)) {
1213 UnicodeString
utzid(tzid
);
1214 UnicodeString canonicalID
;
1215 TimeZone::getCanonicalID(UnicodeString(tzid
), canonicalID
, status
);
1216 if (U_FAILURE(status
)) {
1217 // Ignore unknown ID - we should not get here, but just in case.
1218 status
= U_ZERO_ERROR
;
1221 if (utzid
== canonicalID
) {
1222 canonicalIDs
.addElement(new UnicodeString(utzid
), status
);
1223 if (U_FAILURE(status
)) {
1232 result
= (UnicodeString
**)uprv_malloc(canonicalIDs
.size() * sizeof(UnicodeString
*));
1233 if (result
== NULL
) {
1234 status
= U_MEMORY_ALLOCATION_ERROR
;
1237 for (int32_t i
= 0; i
< canonicalIDs
.size(); i
++) {
1238 result
[i
] = new UnicodeString
[8];
1239 UnicodeString
*id
= (UnicodeString
*)canonicalIDs
.elementAt(i
);
1240 result
[i
][0].setTo(*id
);
1241 getLongStandard(*id
, date
, result
[i
][1]);
1242 getShortStandard(*id
, date
, FALSE
, result
[i
][2]);
1243 getLongDaylight(*id
, date
, result
[i
][3]);
1244 getShortDaylight(*id
, date
, FALSE
, result
[i
][4]);
1245 getGenericLocation(*id
, result
[i
][5]);
1246 getLongGenericNonLocation(*id
, date
, result
[i
][6]);
1247 getShortGenericNonLocation(*id
, date
, FALSE
, result
[i
][7]);
1250 rowCount
= canonicalIDs
.size();
1256 ZoneStringFormat::getSpecificLongString(const Calendar
&cal
, UnicodeString
&result
,
1257 UErrorCode
&status
) const {
1259 if (U_FAILURE(status
)) {
1263 cal
.getTimeZone().getID(tzid
);
1264 UDate date
= cal
.getTime(status
);
1265 if (cal
.get(UCAL_DST_OFFSET
, status
) == 0) {
1266 return getString(tzid
, ZSIDX_LONG_STANDARD
, date
, FALSE
/*not used*/, result
);
1268 return getString(tzid
, ZSIDX_LONG_DAYLIGHT
, date
, FALSE
/*not used*/, result
);
1273 ZoneStringFormat::getSpecificShortString(const Calendar
&cal
, UBool commonlyUsedOnly
,
1274 UnicodeString
&result
, UErrorCode
&status
) const {
1276 if (U_FAILURE(status
)) {
1280 cal
.getTimeZone().getID(tzid
);
1281 UDate date
= cal
.getTime(status
);
1282 if (cal
.get(UCAL_DST_OFFSET
, status
) == 0) {
1283 return getString(tzid
, ZSIDX_SHORT_STANDARD
, date
, commonlyUsedOnly
, result
);
1285 return getString(tzid
, ZSIDX_SHORT_DAYLIGHT
, date
, commonlyUsedOnly
, result
);
1290 ZoneStringFormat::getGenericLongString(const Calendar
&cal
, UnicodeString
&result
,
1291 UErrorCode
&status
) const {
1292 return getGenericString(cal
, FALSE
/*long*/, FALSE
/* not used */, result
, status
);
1296 ZoneStringFormat::getGenericShortString(const Calendar
&cal
, UBool commonlyUsedOnly
,
1297 UnicodeString
&result
, UErrorCode
&status
) const {
1298 return getGenericString(cal
, TRUE
/*short*/, commonlyUsedOnly
, result
, status
);
1302 ZoneStringFormat::getGenericLocationString(const Calendar
&cal
, UnicodeString
&result
,
1303 UErrorCode
&status
) const {
1305 cal
.getTimeZone().getID(tzid
);
1306 UDate date
= cal
.getTime(status
);
1307 return getString(tzid
, ZSIDX_LOCATION
, date
, FALSE
/*not used*/, result
);
1310 const ZoneStringInfo
*
1311 ZoneStringFormat::findSpecificLong(const UnicodeString
&text
, int32_t start
,
1312 int32_t &matchLength
, UErrorCode
&status
) const {
1313 return find(text
, start
, STANDARD_LONG
| DAYLIGHT_LONG
, matchLength
, status
);
1316 const ZoneStringInfo
*
1317 ZoneStringFormat::findSpecificShort(const UnicodeString
&text
, int32_t start
,
1318 int32_t &matchLength
, UErrorCode
&status
) const {
1319 return find(text
, start
, STANDARD_SHORT
| DAYLIGHT_SHORT
, matchLength
, status
);
1322 const ZoneStringInfo
*
1323 ZoneStringFormat::findGenericLong(const UnicodeString
&text
, int32_t start
,
1324 int32_t &matchLength
, UErrorCode
&status
) const {
1325 return find(text
, start
, GENERIC_LONG
| STANDARD_LONG
| LOCATION
, matchLength
, status
);
1328 const ZoneStringInfo
*
1329 ZoneStringFormat::findGenericShort(const UnicodeString
&text
, int32_t start
,
1330 int32_t &matchLength
, UErrorCode
&status
) const {
1331 return find(text
, start
, GENERIC_SHORT
| STANDARD_SHORT
| LOCATION
, matchLength
, status
);
1334 const ZoneStringInfo
*
1335 ZoneStringFormat::findGenericLocation(const UnicodeString
&text
, int32_t start
,
1336 int32_t &matchLength
, UErrorCode
&status
) const {
1337 return find(text
, start
, LOCATION
, matchLength
, status
);
1341 ZoneStringFormat::getString(const UnicodeString
&tzid
, TimeZoneTranslationTypeIndex typeIdx
, UDate date
,
1342 UBool commonlyUsedOnly
, UnicodeString
& result
) const {
1343 UErrorCode status
= U_ZERO_ERROR
;
1345 if (!fIsFullyLoaded
) {
1347 ZoneStringFormat
*nonConstThis
= const_cast<ZoneStringFormat
*>(this);
1348 nonConstThis
->loadZone(tzid
, status
);
1351 // ICU's own array does not have entries for aliases
1352 UnicodeString canonicalID
;
1353 TimeZone::getCanonicalID(tzid
, canonicalID
, status
);
1354 if (U_FAILURE(status
)) {
1355 // Unknown ID, but users might have their own data.
1356 canonicalID
.setTo(tzid
);
1359 if (uhash_count(fTzidToStrings
) > 0) {
1360 ZoneStrings
*zstrings
= (ZoneStrings
*)uhash_get(fTzidToStrings
, canonicalID
.getTerminatedBuffer());
1361 if (zstrings
!= NULL
) {
1363 case ZSIDX_LONG_STANDARD
:
1364 case ZSIDX_LONG_DAYLIGHT
:
1365 case ZSIDX_LONG_GENERIC
:
1366 case ZSIDX_LOCATION
:
1367 zstrings
->getString(typeIdx
, result
);
1369 case ZSIDX_SHORT_STANDARD
:
1370 case ZSIDX_SHORT_DAYLIGHT
:
1371 case ZSIDX_COUNT
: //added to avoid warning
1372 case ZSIDX_SHORT_GENERIC
:
1373 if (!commonlyUsedOnly
|| zstrings
->isShortFormatCommonlyUsed()) {
1374 zstrings
->getString(typeIdx
, result
);
1382 if (result
.isEmpty() && uhash_count(fMzidToStrings
) > 0 && typeIdx
!= ZSIDX_LOCATION
) {
1385 ZoneMeta::getMetazoneID(canonicalID
, date
, mzid
);
1386 if (!mzid
.isEmpty()) {
1387 ZoneStrings
*mzstrings
= (ZoneStrings
*)uhash_get(fMzidToStrings
, mzid
.getTerminatedBuffer());
1388 if (mzstrings
!= NULL
) {
1390 case ZSIDX_LONG_STANDARD
:
1391 case ZSIDX_LONG_DAYLIGHT
:
1392 case ZSIDX_LONG_GENERIC
:
1393 case ZSIDX_LOCATION
:
1394 mzstrings
->getString(typeIdx
, result
);
1396 case ZSIDX_SHORT_STANDARD
:
1397 case ZSIDX_SHORT_DAYLIGHT
:
1398 case ZSIDX_COUNT
: //added to avoid warning
1399 case ZSIDX_SHORT_GENERIC
:
1400 if (!commonlyUsedOnly
|| mzstrings
->isShortFormatCommonlyUsed()) {
1401 mzstrings
->getString(typeIdx
, result
);
1414 ZoneStringFormat::getGenericString(const Calendar
&cal
, UBool isShort
, UBool commonlyUsedOnly
,
1415 UnicodeString
&result
, UErrorCode
&status
) const {
1417 UDate time
= cal
.getTime(status
);
1418 if (U_FAILURE(status
)) {
1421 const TimeZone
&tz
= cal
.getTimeZone();
1425 if (!fIsFullyLoaded
) {
1427 ZoneStringFormat
*nonConstThis
= const_cast<ZoneStringFormat
*>(this);
1428 nonConstThis
->loadZone(tzid
, status
);
1431 // ICU's own array does not have entries for aliases
1432 UnicodeString canonicalID
;
1433 TimeZone::getCanonicalID(tzid
, canonicalID
, status
);
1434 if (U_FAILURE(status
)) {
1435 // Unknown ID, but users might have their own data.
1436 status
= U_ZERO_ERROR
;
1437 canonicalID
.setTo(tzid
);
1440 ZoneStrings
*zstrings
= NULL
;
1441 if (uhash_count(fTzidToStrings
) > 0) {
1442 zstrings
= (ZoneStrings
*)uhash_get(fTzidToStrings
, canonicalID
.getTerminatedBuffer());
1443 if (zstrings
!= NULL
) {
1445 if (!commonlyUsedOnly
|| zstrings
->isShortFormatCommonlyUsed()) {
1446 zstrings
->getString(ZSIDX_SHORT_GENERIC
, result
);
1449 zstrings
->getString(ZSIDX_LONG_GENERIC
, result
);
1453 if (result
.isEmpty() && uhash_count(fMzidToStrings
) > 0) {
1457 ZoneMeta::getMetazoneID(canonicalID
, time
, mzid
);
1458 if (!mzid
.isEmpty()) {
1459 UBool useStandard
= FALSE
;
1460 sav
= cal
.get(UCAL_DST_OFFSET
, status
);
1461 if (U_FAILURE(status
)) {
1466 // Check if the zone actually uses daylight saving time around the time
1467 TimeZone
*tmptz
= tz
.clone();
1468 BasicTimeZone
*btz
= NULL
;
1469 if (dynamic_cast<OlsonTimeZone
*>(tmptz
) != NULL
1470 || dynamic_cast<SimpleTimeZone
*>(tmptz
) != NULL
1471 || dynamic_cast<RuleBasedTimeZone
*>(tmptz
) != NULL
1472 || dynamic_cast<VTimeZone
*>(tmptz
) != NULL
) {
1473 btz
= (BasicTimeZone
*)tmptz
;
1477 TimeZoneTransition before
;
1478 UBool beforTrs
= btz
->getPreviousTransition(time
, TRUE
, before
);
1480 && (time
- before
.getTime() < kDstCheckRange
)
1481 && before
.getFrom()->getDSTSavings() != 0) {
1482 useStandard
= FALSE
;
1484 TimeZoneTransition after
;
1485 UBool afterTrs
= btz
->getNextTransition(time
, FALSE
, after
);
1487 && (after
.getTime() - time
< kDstCheckRange
)
1488 && after
.getTo()->getDSTSavings() != 0) {
1489 useStandard
= FALSE
;
1493 // If not BasicTimeZone... only if the instance is not an ICU's implementation.
1494 // We may get a wrong answer in edge case, but it should practically work OK.
1495 tmptz
->getOffset(time
- kDstCheckRange
, FALSE
, raw
, sav
, status
);
1497 useStandard
= FALSE
;
1499 tmptz
->getOffset(time
+ kDstCheckRange
, FALSE
, raw
, sav
, status
);
1501 useStandard
= FALSE
;
1504 if (U_FAILURE(status
)) {
1513 getString(canonicalID
, (isShort
? ZSIDX_SHORT_STANDARD
: ZSIDX_LONG_STANDARD
),
1514 time
, commonlyUsedOnly
, result
);
1517 // In CLDR 1.5.1, a same localization is used for both generic and standard
1518 // for some metazones in some locales. This is actually data bugs and should
1519 // be resolved in later versions of CLDR. For now, we check if the standard
1520 // name is different from its generic name below.
1521 if (!result
.isEmpty()) {
1522 UnicodeString genericNonLocation
;
1523 getString(canonicalID
, (isShort
? ZSIDX_SHORT_GENERIC
: ZSIDX_LONG_GENERIC
),
1524 time
, commonlyUsedOnly
, genericNonLocation
);
1525 if (!genericNonLocation
.isEmpty() && result
== genericNonLocation
) {
1530 if (result
.isEmpty()) {
1531 ZoneStrings
*mzstrings
= (ZoneStrings
*)uhash_get(fMzidToStrings
, mzid
.getTerminatedBuffer());
1532 if (mzstrings
!= NULL
) {
1534 if (!commonlyUsedOnly
|| mzstrings
->isShortFormatCommonlyUsed()) {
1535 mzstrings
->getString(ZSIDX_SHORT_GENERIC
, result
);
1538 mzstrings
->getString(ZSIDX_LONG_GENERIC
, result
);
1541 if (!result
.isEmpty()) {
1542 // Check if the offsets at the given time matches the preferred zone's offsets
1543 UnicodeString preferredId
;
1544 UnicodeString region
;
1545 ZoneMeta::getZoneIdByMetazone(mzid
, getRegion(region
), preferredId
);
1546 if (canonicalID
!= preferredId
) {
1547 // Check if the offsets at the given time are identical with the preferred zone
1548 raw
= cal
.get(UCAL_ZONE_OFFSET
, status
);
1549 if (U_FAILURE(status
)) {
1553 TimeZone
*preferredZone
= TimeZone::createTimeZone(preferredId
);
1554 int32_t prfRaw
, prfSav
;
1555 // Check offset in preferred time zone with wall time.
1556 // With getOffset(time, false, preferredOffsets),
1557 // you may get incorrect results because of time overlap at DST->STD
1559 preferredZone
->getOffset(time
+ raw
+ sav
, TRUE
, prfRaw
, prfSav
, status
);
1560 delete preferredZone
;
1562 if (U_FAILURE(status
)) {
1566 if ((raw
!= prfRaw
|| sav
!= prfSav
) && zstrings
!= NULL
) {
1567 // Use generic partial location string as fallback
1568 zstrings
->getGenericPartialLocationString(mzid
, isShort
, commonlyUsedOnly
, result
);
1575 if (result
.isEmpty()) {
1576 // Use location format as the final fallback
1577 getString(canonicalID
, ZSIDX_LOCATION
, time
, FALSE
/*not used*/, result
);
1584 ZoneStringFormat::getGenericPartialLocationString(const UnicodeString
&tzid
, UBool isShort
,
1585 UDate date
, UBool commonlyUsedOnly
, UnicodeString
&result
) const {
1586 UErrorCode status
= U_ZERO_ERROR
;
1588 if (!fIsFullyLoaded
) {
1590 ZoneStringFormat
*nonConstThis
= const_cast<ZoneStringFormat
*>(this);
1591 nonConstThis
->loadZone(tzid
, status
);
1594 if (uhash_count(fTzidToStrings
) <= 0) {
1598 UnicodeString canonicalID
;
1599 TimeZone::getCanonicalID(tzid
, canonicalID
, status
);
1600 if (U_FAILURE(status
)) {
1601 // Unknown ID, so no corresponding meta data.
1606 ZoneMeta::getMetazoneID(canonicalID
, date
, mzid
);
1608 if (!mzid
.isEmpty()) {
1609 ZoneStrings
*zstrings
= (ZoneStrings
*)uhash_get(fTzidToStrings
, canonicalID
.getTerminatedBuffer());
1610 if (zstrings
!= NULL
) {
1611 zstrings
->getGenericPartialLocationString(mzid
, isShort
, commonlyUsedOnly
, result
);
1617 // This method does lazy zone string loading
1618 const ZoneStringInfo
*
1619 ZoneStringFormat::find(const UnicodeString
&text
, int32_t start
, int32_t types
,
1620 int32_t &matchLength
, UErrorCode
&status
) const {
1622 if (U_FAILURE(status
)) {
1626 const ZoneStringInfo
* result
= subFind(text
, start
, types
, matchLength
, status
);
1627 if (fIsFullyLoaded
) {
1630 // When zone string data is partially loaded,
1631 // this method return the result only when
1632 // the input text is fully consumed.
1633 if (result
!= NULL
) {
1634 UnicodeString tmpString
;
1635 matchLength
= (result
->getString(tmpString
)).length();
1636 if (text
.length() - start
== matchLength
) {
1641 // Now load all zone strings
1642 ZoneStringFormat
*nonConstThis
= const_cast<ZoneStringFormat
*>(this);
1643 nonConstThis
->loadFull(status
);
1645 return subFind(text
, start
, types
, matchLength
, status
);
1650 * Find a prefix matching time zone for the given zone string types.
1651 * @param text The text contains a time zone string
1652 * @param start The start index within the text
1653 * @param types The bit mask representing a set of requested types
1654 * @return If any zone string matched for the requested types, returns a
1655 * ZoneStringInfo for the longest match. If no matches are found for
1656 * the requested types, returns a ZoneStringInfo for the longest match
1657 * for any other types. If nothing matches at all, returns null.
1659 const ZoneStringInfo
*
1660 ZoneStringFormat::subFind(const UnicodeString
&text
, int32_t start
, int32_t types
,
1661 int32_t &matchLength
, UErrorCode
&status
) const {
1663 if (U_FAILURE(status
)) {
1666 if (fZoneStringsTrie
.isEmpty()) {
1670 const ZoneStringInfo
*result
= NULL
;
1671 const ZoneStringInfo
*fallback
= NULL
;
1672 int32_t fallbackMatchLen
= 0;
1674 ZoneStringSearchResultHandler
handler(status
);
1675 fZoneStringsTrie
.search(text
, start
, (TextTrieMapSearchResultHandler
*)&handler
, status
);
1676 if (U_SUCCESS(status
)) {
1677 int32_t numMatches
= handler
.countMatches();
1678 for (int32_t i
= 0; i
< numMatches
; i
++) {
1679 int32_t tmpMatchLen
= 0; // init. output only param to silence gcc
1680 const ZoneStringInfo
*tmp
= handler
.getMatch(i
, tmpMatchLen
);
1681 if ((types
& tmp
->fType
) != 0) {
1682 if (result
== NULL
|| matchLength
< tmpMatchLen
) {
1684 matchLength
= tmpMatchLen
;
1685 } else if (matchLength
== tmpMatchLen
) {
1686 // Tie breaker - there are some examples that a
1687 // long standard name is identical with a location
1688 // name - for example, "Uruguay Time". In this case,
1689 // we interpret it as generic, not specific.
1690 if (tmp
->isGeneric() && !result
->isGeneric()) {
1694 } else if (result
== NULL
) {
1695 if (fallback
== NULL
|| fallbackMatchLen
< tmpMatchLen
) {
1697 fallbackMatchLen
= tmpMatchLen
;
1698 } else if (fallbackMatchLen
== tmpMatchLen
) {
1699 if (tmp
->isGeneric() && !fallback
->isGeneric()) {
1705 if (result
== NULL
&& fallback
!= NULL
) {
1707 matchLength
= fallbackMatchLen
;
1715 ZoneStringFormat::getRegion(UnicodeString
®ion
) const {
1716 const char* country
= fLocale
.getCountry();
1717 // TODO: Utilize addLikelySubtag in Locale to resolve default region
1718 // when the implementation is ready.
1719 region
.setTo(UnicodeString(country
, -1, US_INV
));
1724 ZoneStringFormat::getFallbackFormat(const Locale
&locale
, UErrorCode
&status
) {
1725 if (U_FAILURE(status
)) {
1728 UnicodeString
pattern(TRUE
, gDefFallbackPattern
, -1);
1729 UResourceBundle
*zoneStringsArray
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &status
);
1730 zoneStringsArray
= ures_getByKeyWithFallback(zoneStringsArray
, gZoneStringsTag
, zoneStringsArray
, &status
);
1732 const UChar
*flbkfmt
= ures_getStringByKeyWithFallback(zoneStringsArray
, gFallbackFormatTag
, &len
, &status
);
1733 if (U_SUCCESS(status
)) {
1734 pattern
.setTo(flbkfmt
);
1736 status
= U_ZERO_ERROR
;
1738 ures_close(zoneStringsArray
);
1740 MessageFormat
*fallbackFmt
= new MessageFormat(pattern
, status
);
1745 ZoneStringFormat::getRegionFormat(const Locale
& locale
, UErrorCode
&status
) {
1746 if (U_FAILURE(status
)) {
1749 UnicodeString
pattern(TRUE
, gDefRegionPattern
, -1);
1750 UResourceBundle
*zoneStringsArray
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &status
);
1751 zoneStringsArray
= ures_getByKeyWithFallback(zoneStringsArray
, gZoneStringsTag
, zoneStringsArray
, &status
);
1753 const UChar
*regionfmt
= ures_getStringByKeyWithFallback(zoneStringsArray
, gRegionFormatTag
, &len
, &status
);
1754 if (U_SUCCESS(status
)) {
1755 pattern
.setTo(regionfmt
);
1757 status
= U_ZERO_ERROR
;
1759 ures_close(zoneStringsArray
);
1761 MessageFormat
*regionFmt
= new MessageFormat(pattern
, status
);
1766 ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle
*zoneitem
, const char *key
) {
1767 const UChar
*str
= NULL
;
1768 if (zoneitem
!= NULL
) {
1769 UErrorCode status
= U_ZERO_ERROR
;
1771 str
= ures_getStringByKeyWithFallback(zoneitem
, key
, &len
, &status
);
1772 str
= fStringPool
.adopt(str
, status
);
1773 if (U_FAILURE(status
)) {
1781 ZoneStringFormat::isCommonlyUsed(const UResourceBundle
*zoneitem
) {
1782 if (zoneitem
== NULL
) {
1786 UBool commonlyUsed
= FALSE
;
1787 UErrorCode status
= U_ZERO_ERROR
;
1788 UResourceBundle
*cuRes
= ures_getByKey(zoneitem
, gCommonlyUsedTag
, NULL
, &status
);
1789 int32_t cuValue
= ures_getInt(cuRes
, &status
);
1790 if (U_SUCCESS(status
)) {
1792 commonlyUsed
= TRUE
;
1796 return commonlyUsed
;
1800 ZoneStringFormat::getLocalizedCountry(const UnicodeString
&countryCode
, const Locale
&locale
, UnicodeString
&displayCountry
) {
1801 // We do not want to use display country names only from the target language bundle
1802 // Note: we should do this in better way.
1803 displayCountry
.remove();
1804 int32_t ccLen
= countryCode
.length();
1805 if (ccLen
> 0 && ccLen
< ULOC_COUNTRY_CAPACITY
) {
1806 UErrorCode status
= U_ZERO_ERROR
;
1807 UResourceBundle
*localeBundle
= ures_open(NULL
, locale
.getName(), &status
);
1808 if (U_SUCCESS(status
)) {
1809 const char *bundleLocStr
= ures_getLocale(localeBundle
, &status
);
1810 if (U_SUCCESS(status
) && uprv_strlen(bundleLocStr
) > 0) {
1811 Locale
bundleLoc(bundleLocStr
);
1812 if (uprv_strcmp(bundleLocStr
, "root") != 0 &&
1813 uprv_strcmp(bundleLoc
.getLanguage(), locale
.getLanguage()) == 0) {
1814 // Create a fake locale strings
1815 char tmpLocStr
[ULOC_COUNTRY_CAPACITY
+ 3];
1816 uprv_strcpy(tmpLocStr
, "xx_");
1817 u_UCharsToChars(countryCode
.getBuffer(), &tmpLocStr
[3], ccLen
);
1818 tmpLocStr
[3 + ccLen
] = 0;
1820 Locale
tmpLoc(tmpLocStr
);
1821 tmpLoc
.getDisplayCountry(locale
, displayCountry
);
1825 ures_close(localeBundle
);
1827 if (displayCountry
.isEmpty()) {
1828 // Use the country code as the fallback
1829 displayCountry
.setTo(countryCode
);
1831 return displayCountry
;
1834 // ----------------------------------------------------------------------------
1836 * ZoneStrings constructor adopts (and promptly copies and deletes)
1837 * the input UnicodeString arrays.
1839 ZoneStrings::ZoneStrings(UnicodeString
*strings
,
1840 int32_t stringsCount
,
1842 UnicodeString
**genericPartialLocationStrings
,
1843 int32_t genericRowCount
,
1844 int32_t genericColCount
,
1848 fStringsCount(stringsCount
),
1849 fIsCommonlyUsed(commonlyUsed
),
1850 fGenericPartialLocationStrings(NULL
),
1851 fGenericPartialLocationRowCount(genericRowCount
),
1852 fGenericPartialLocationColCount(genericColCount
)
1854 if (U_FAILURE(status
)) {
1858 if (strings
!= NULL
) {
1859 fStrings
= (const UChar
**)uprv_malloc(sizeof(const UChar
**) * stringsCount
);
1860 if (fStrings
== NULL
) {
1861 status
= U_MEMORY_ALLOCATION_ERROR
;
1864 for (i
=0; i
<fStringsCount
; i
++) {
1865 fStrings
[i
] = sp
.get(strings
[i
], status
);
1869 if (genericPartialLocationStrings
!= NULL
) {
1870 fGenericPartialLocationStrings
=
1871 (const UChar
***)uprv_malloc(sizeof(const UChar
***) * genericRowCount
);
1872 if (fGenericPartialLocationStrings
== NULL
) {
1873 status
= U_MEMORY_ALLOCATION_ERROR
;
1876 for (i
=0; i
< fGenericPartialLocationRowCount
; i
++) {
1877 fGenericPartialLocationStrings
[i
] =
1878 (const UChar
**)uprv_malloc(sizeof(const UChar
**) * genericColCount
);
1879 if (fGenericPartialLocationStrings
[i
] == NULL
) {
1880 status
= U_MEMORY_ALLOCATION_ERROR
;
1881 continue; // Continue so that fGenericPartialLocationStrings will not contain uninitialized junk,
1882 } // which would crash the destructor.
1883 for (j
=0; j
<genericColCount
; j
++) {
1884 fGenericPartialLocationStrings
[i
][j
] =
1885 sp
.get(genericPartialLocationStrings
[i
][j
], status
);
1887 delete[] genericPartialLocationStrings
[i
];
1889 uprv_free(genericPartialLocationStrings
);
1893 ZoneStrings::~ZoneStrings() {
1894 uprv_free(fStrings
);
1895 if (fGenericPartialLocationStrings
!= NULL
) {
1896 for (int32_t i
= 0; i
< fGenericPartialLocationRowCount
; i
++) {
1897 uprv_free(fGenericPartialLocationStrings
[i
]);
1899 uprv_free(fGenericPartialLocationStrings
);
1905 ZoneStrings::getString(int32_t typeIdx
, UnicodeString
&result
) const {
1906 if (typeIdx
>= 0 && typeIdx
< fStringsCount
) {
1907 result
.setTo(fStrings
[typeIdx
], -1);
1915 ZoneStrings::getGenericPartialLocationString(const UnicodeString
&mzid
, UBool isShort
,
1916 UBool commonlyUsedOnly
, UnicodeString
&result
) const {
1917 UBool isSet
= FALSE
;
1918 if (fGenericPartialLocationColCount
>= 2) {
1919 for (int32_t i
= 0; i
< fGenericPartialLocationRowCount
; i
++) {
1920 if (mzid
.compare(fGenericPartialLocationStrings
[i
][0], -1) == 0) {
1922 if (fGenericPartialLocationColCount
>= 3) {
1923 if (!commonlyUsedOnly
||
1924 fGenericPartialLocationColCount
== 3 ||
1925 fGenericPartialLocationStrings
[i
][3][0] != 0) {
1926 result
.setTo(fGenericPartialLocationStrings
[i
][2], -1);
1931 result
.setTo(fGenericPartialLocationStrings
[i
][1], -1);
1944 // --------------------------------------------------------------
1945 SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry
*cacheEntry
)
1946 : fCacheEntry(cacheEntry
) {
1949 SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() {
1950 fCacheEntry
->delRef();
1953 const ZoneStringFormat
*
1954 SafeZoneStringFormatPtr::get() const {
1955 return fCacheEntry
->getZoneStringFormat();
1958 ZSFCacheEntry::ZSFCacheEntry(const Locale
&locale
, ZoneStringFormat
*zsf
, ZSFCacheEntry
*next
)
1959 : fLocale(locale
), fZoneStringFormat(zsf
),
1960 fNext(next
), fRefCount(1)
1964 ZSFCacheEntry::~ZSFCacheEntry () {
1965 delete fZoneStringFormat
;
1968 const ZoneStringFormat
*
1969 ZSFCacheEntry::getZoneStringFormat(void) {
1970 return (const ZoneStringFormat
*)fZoneStringFormat
;
1974 ZSFCacheEntry::delRef(void) {
1975 umtx_lock(&gZSFCacheLock
);
1977 umtx_unlock(&gZSFCacheLock
);
1980 ZSFCache::ZSFCache(int32_t capacity
)
1981 : fCapacity(capacity
), fFirst(NULL
) {
1984 ZSFCache::~ZSFCache() {
1985 ZSFCacheEntry
*entry
= fFirst
;
1987 ZSFCacheEntry
*next
= entry
->fNext
;
1993 SafeZoneStringFormatPtr
*
1994 ZSFCache::get(const Locale
&locale
, UErrorCode
&status
) {
1995 SafeZoneStringFormatPtr
*result
= NULL
;
1997 // Search the cache entry list
1998 ZSFCacheEntry
*entry
= NULL
;
1999 ZSFCacheEntry
*next
, *prev
;
2001 umtx_lock(&gZSFCacheLock
);
2005 next
= entry
->fNext
;
2006 if (entry
->fLocale
== locale
) {
2007 // Add reference count
2010 // move the entry to the top
2011 if (entry
!= fFirst
) {
2013 entry
->fNext
= fFirst
;
2021 umtx_unlock(&gZSFCacheLock
);
2023 // Create a new ZoneStringFormat
2024 if (entry
== NULL
) {
2025 ZoneStringFormat
*zsf
= new ZoneStringFormat(locale
, status
);
2026 if (U_FAILURE(status
)) {
2031 status
= U_MEMORY_ALLOCATION_ERROR
;
2035 // Now add the new entry
2036 umtx_lock(&gZSFCacheLock
);
2037 // Make sure no other threads already created the one for the same locale
2041 next
= entry
->fNext
;
2042 if (entry
->fLocale
== locale
) {
2043 // Add reference count
2046 // move the entry to the top
2047 if (entry
!= fFirst
) {
2049 entry
->fNext
= fFirst
;
2057 if (entry
== NULL
) {
2058 // Add the new one to the top
2060 entry
= new ZSFCacheEntry(locale
, zsf
, next
);
2065 umtx_unlock(&gZSFCacheLock
);
2068 result
= new SafeZoneStringFormatPtr(entry
);
2070 // Now, delete unused cache entries beyond the capacity
2071 umtx_lock(&gZSFCacheLock
);
2076 next
= entry
->fNext
;
2077 if (idx
>= fCapacity
&& entry
->fRefCount
== 0) {
2078 if (entry
== fFirst
) {
2090 umtx_unlock(&gZSFCacheLock
);
2097 * Zone String Formatter String Pool Implementation
2099 * String pool for (UChar *) strings. Avoids having repeated copies of the same string.
2102 static const int32_t POOL_CHUNK_SIZE
= 2000;
2103 struct ZSFStringPoolChunk
: public UMemory
{
2104 ZSFStringPoolChunk
*fNext
; // Ptr to next pool chunk
2105 int32_t fLimit
; // Index to start of unused area at end of fStrings
2106 UChar fStrings
[POOL_CHUNK_SIZE
]; // Strings array
2107 ZSFStringPoolChunk();
2110 ZSFStringPoolChunk::ZSFStringPoolChunk() {
2115 ZSFStringPool::ZSFStringPool(UErrorCode
&status
) {
2118 if (U_FAILURE(status
)) {
2121 fChunks
= new ZSFStringPoolChunk
;
2122 if (fChunks
== NULL
) {
2123 status
= U_MEMORY_ALLOCATION_ERROR
;
2127 fHash
= uhash_open(uhash_hashUChars
/* keyHash */,
2128 uhash_compareUChars
/* keyComp */,
2129 uhash_compareUChars
/* valueComp */,
2131 if (U_FAILURE(status
)) {
2137 ZSFStringPool::~ZSFStringPool() {
2138 if (fHash
!= NULL
) {
2143 while (fChunks
!= NULL
) {
2144 ZSFStringPoolChunk
*nextChunk
= fChunks
->fNext
;
2146 fChunks
= nextChunk
;
2150 static const UChar EmptyString
= 0;
2152 const UChar
*ZSFStringPool::get(const UChar
*s
, UErrorCode
&status
) {
2153 const UChar
*pooledString
;
2154 if (U_FAILURE(status
)) {
2155 return &EmptyString
;
2158 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
2159 if (pooledString
!= NULL
) {
2160 return pooledString
;
2163 int32_t length
= u_strlen(s
);
2164 int32_t remainingLength
= POOL_CHUNK_SIZE
- fChunks
->fLimit
;
2165 if (remainingLength
<= length
) {
2166 U_ASSERT(length
< POOL_CHUNK_SIZE
);
2167 if (length
>= POOL_CHUNK_SIZE
) {
2168 status
= U_INTERNAL_PROGRAM_ERROR
;
2169 return &EmptyString
;
2171 ZSFStringPoolChunk
*oldChunk
= fChunks
;
2172 fChunks
= new ZSFStringPoolChunk
;
2173 if (fChunks
== NULL
) {
2174 status
= U_MEMORY_ALLOCATION_ERROR
;
2175 return &EmptyString
;
2177 fChunks
->fNext
= oldChunk
;
2180 UChar
*destString
= &fChunks
->fStrings
[fChunks
->fLimit
];
2181 u_strcpy(destString
, s
);
2182 fChunks
->fLimit
+= (length
+ 1);
2183 uhash_put(fHash
, destString
, destString
, &status
);
2189 // ZSFStringPool::adopt() Put a string into the hash, but do not copy the string data
2190 // into the pool's storage. Used for strings from resource bundles,
2191 // which will perisist for the life of the zone string formatter, and
2192 // therefore can be used directly without copying.
2193 const UChar
*ZSFStringPool::adopt(const UChar
* s
, UErrorCode
&status
) {
2194 const UChar
*pooledString
;
2195 if (U_FAILURE(status
)) {
2196 return &EmptyString
;
2199 pooledString
= static_cast<UChar
*>(uhash_get(fHash
, s
));
2200 if (pooledString
== NULL
) {
2201 UChar
*ncs
= const_cast<UChar
*>(s
);
2202 uhash_put(fHash
, ncs
, ncs
, &status
);
2209 const UChar
*ZSFStringPool::get(const UnicodeString
&s
, UErrorCode
&status
) {
2210 UnicodeString
&nonConstStr
= const_cast<UnicodeString
&>(s
);
2211 return this->get(nonConstStr
.getTerminatedBuffer(), status
);
2215 * freeze(). Close the hash table that maps to the pooled strings.
2216 * After freezing, the pool can not be searched or added to,
2217 * but all existing references to pooled strings remain valid.
2219 * The main purpose is to recover the storage used for the hash.
2221 void ZSFStringPool::freeze() {
2228 #endif /* #if !UCONFIG_NO_FORMATTING */