]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/tznames_impl.cpp
ICU-511.25.tar.gz
[apple/icu.git] / icuSources / i18n / tznames_impl.cpp
CommitLineData
4388f060
A
1/*
2*******************************************************************************
51004dcb 3* Copyright (C) 2011-2013, International Business Machines Corporation and
4388f060
A
4* others. All Rights Reserved.
5*******************************************************************************
6*
7* File TZNAMES_IMPL.CPP
8*
9*******************************************************************************
10*/
11
12#include "unicode/utypes.h"
13
14#if !UCONFIG_NO_FORMATTING
15
16#include "unicode/ustring.h"
17#include "unicode/timezone.h"
18
19#include "tznames_impl.h"
20#include "cmemory.h"
21#include "cstring.h"
22#include "uassert.h"
23#include "uresimp.h"
24#include "ureslocs.h"
25#include "zonemeta.h"
26#include "ucln_in.h"
27#include "uvector.h"
28#include "olsontz.h"
29
30
31U_NAMESPACE_BEGIN
32
33#define ZID_KEY_MAX 128
34#define MZ_PREFIX_LEN 5
35
36static const char gZoneStrings[] = "zoneStrings";
37static const char gMZPrefix[] = "meta:";
38
39static const char* KEYS[] = {"lg", "ls", "ld", "sg", "ss", "sd"};
40static const int32_t KEYS_SIZE = (sizeof KEYS / sizeof KEYS[0]);
41
42static const char gEcTag[] = "ec";
43
44static const char EMPTY[] = "<empty>"; // place holder for empty ZNames/TZNames
45
46static const UTimeZoneNameType ALL_NAME_TYPES[] = {
47 UTZNM_LONG_GENERIC, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT,
48 UTZNM_SHORT_GENERIC, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT,
51004dcb 49 UTZNM_EXEMPLAR_LOCATION,
4388f060
A
50 UTZNM_UNKNOWN // unknown as the last one
51};
52
53#define DEFAULT_CHARACTERNODE_CAPACITY 1
54
55// ---------------------------------------------------
56// CharacterNode class implementation
57// ---------------------------------------------------
58void CharacterNode::clear() {
59 uprv_memset(this, 0, sizeof(*this));
60}
61
62void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) {
63 if (fValues == NULL) {
64 // Do nothing.
65 } else if (!fHasValuesVector) {
66 if (valueDeleter) {
67 valueDeleter(fValues);
68 }
69 } else {
70 delete (UVector *)fValues;
71 }
72}
73
74void
75CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) {
76 if (U_FAILURE(status)) {
77 if (valueDeleter) {
78 valueDeleter(value);
79 }
80 return;
81 }
82 if (fValues == NULL) {
83 fValues = value;
84 } else {
85 // At least one value already.
86 if (!fHasValuesVector) {
87 // There is only one value so far, and not in a vector yet.
88 // Create a vector and add the old value.
89 UVector *values = new UVector(valueDeleter, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status);
90 if (U_FAILURE(status)) {
91 if (valueDeleter) {
92 valueDeleter(value);
93 }
94 return;
95 }
96 values->addElement(fValues, status);
97 fValues = values;
98 fHasValuesVector = TRUE;
99 }
100 // Add the new value.
101 ((UVector *)fValues)->addElement(value, status);
102 }
103}
104
105// ---------------------------------------------------
106// TextTrieMapSearchResultHandler class implementation
107// ---------------------------------------------------
108TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
109}
110
111// ---------------------------------------------------
112// TextTrieMap class implementation
113// ---------------------------------------------------
114TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter)
115: fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0),
116 fLazyContents(NULL), fIsEmpty(TRUE), fValueDeleter(valueDeleter) {
117}
118
119TextTrieMap::~TextTrieMap() {
120 int32_t index;
121 for (index = 0; index < fNodesCount; ++index) {
122 fNodes[index].deleteValues(fValueDeleter);
123 }
124 uprv_free(fNodes);
125 if (fLazyContents != NULL) {
126 for (int32_t i=0; i<fLazyContents->size(); i+=2) {
127 if (fValueDeleter) {
128 fValueDeleter(fLazyContents->elementAt(i+1));
129 }
130 }
131 delete fLazyContents;
132 }
133}
134
135int32_t TextTrieMap::isEmpty() const {
136 // Use a separate field for fIsEmpty because it will remain unchanged once the
137 // Trie is built, while fNodes and fLazyContents change with the lazy init
138 // of the nodes structure. Trying to test the changing fields has
139 // thread safety complications.
140 return fIsEmpty;
141}
142
143
144// We defer actually building the TextTrieMap node structure until the first time a
145// search is performed. put() simply saves the parameters in case we do
146// eventually need to build it.
147//
148void
149TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) {
150 const UChar *s = sp.get(key, status);
151 put(s, value, status);
152}
153
154// This method is for designed for a persistent key, such as string key stored in
155// resource bundle.
156void
157TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) {
158 fIsEmpty = FALSE;
159 if (fLazyContents == NULL) {
160 fLazyContents = new UVector(status);
161 if (fLazyContents == NULL) {
162 status = U_MEMORY_ALLOCATION_ERROR;
163 }
164 }
165 if (U_FAILURE(status)) {
166 return;
167 }
168 U_ASSERT(fLazyContents != NULL);
169 UChar *s = const_cast<UChar *>(key);
170 fLazyContents->addElement(s, status);
171 fLazyContents->addElement(value, status);
172}
173
174void
175TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) {
176 if (fNodes == NULL) {
177 fNodesCapacity = 512;
178 fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
179 fNodes[0].clear(); // Init root node.
180 fNodesCount = 1;
181 }
182
183 UnicodeString foldedKey;
184 const UChar *keyBuffer;
185 int32_t keyLength;
186 if (fIgnoreCase) {
187 // Ok to use fastCopyFrom() because we discard the copy when we return.
188 foldedKey.fastCopyFrom(key).foldCase();
189 keyBuffer = foldedKey.getBuffer();
190 keyLength = foldedKey.length();
191 } else {
192 keyBuffer = key.getBuffer();
193 keyLength = key.length();
194 }
195
196 CharacterNode *node = fNodes;
197 int32_t index;
198 for (index = 0; index < keyLength; ++index) {
199 node = addChildNode(node, keyBuffer[index], status);
200 }
201 node->addValue(value, fValueDeleter, status);
202}
203
204UBool
205TextTrieMap::growNodes() {
206 if (fNodesCapacity == 0xffff) {
207 return FALSE; // We use 16-bit node indexes.
208 }
209 int32_t newCapacity = fNodesCapacity + 1000;
210 if (newCapacity > 0xffff) {
211 newCapacity = 0xffff;
212 }
213 CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode));
214 if (newNodes == NULL) {
215 return FALSE;
216 }
217 uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode));
218 uprv_free(fNodes);
219 fNodes = newNodes;
220 fNodesCapacity = newCapacity;
221 return TRUE;
222}
223
224CharacterNode*
225TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) {
226 if (U_FAILURE(status)) {
227 return NULL;
228 }
229 // Linear search of the sorted list of children.
230 uint16_t prevIndex = 0;
231 uint16_t nodeIndex = parent->fFirstChild;
232 while (nodeIndex > 0) {
233 CharacterNode *current = fNodes + nodeIndex;
234 UChar childCharacter = current->fCharacter;
235 if (childCharacter == c) {
236 return current;
237 } else if (childCharacter > c) {
238 break;
239 }
240 prevIndex = nodeIndex;
241 nodeIndex = current->fNextSibling;
242 }
243
244 // Ensure capacity. Grow fNodes[] if needed.
245 if (fNodesCount == fNodesCapacity) {
246 int32_t parentIndex = (int32_t)(parent - fNodes);
247 if (!growNodes()) {
248 status = U_MEMORY_ALLOCATION_ERROR;
249 return NULL;
250 }
251 parent = fNodes + parentIndex;
252 }
253
254 // Insert a new child node with c in sorted order.
255 CharacterNode *node = fNodes + fNodesCount;
256 node->clear();
257 node->fCharacter = c;
258 node->fNextSibling = nodeIndex;
259 if (prevIndex == 0) {
260 parent->fFirstChild = (uint16_t)fNodesCount;
261 } else {
262 fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount;
263 }
264 ++fNodesCount;
265 return node;
266}
267
268CharacterNode*
269TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
270 // Linear search of the sorted list of children.
271 uint16_t nodeIndex = parent->fFirstChild;
272 while (nodeIndex > 0) {
273 CharacterNode *current = fNodes + nodeIndex;
274 UChar childCharacter = current->fCharacter;
275 if (childCharacter == c) {
276 return current;
277 } else if (childCharacter > c) {
278 break;
279 }
280 nodeIndex = current->fNextSibling;
281 }
282 return NULL;
283}
284
285// Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
51004dcb 286static UMutex TextTrieMutex = U_MUTEX_INITIALIZER;
4388f060
A
287
288// buildTrie() - The Trie node structure is needed. Create it from the data that was
289// saved at the time the ZoneStringFormatter was created. The Trie is only
290// needed for parsing operations, which are less common than formatting,
291// and the Trie is big, which is why its creation is deferred until first use.
292void TextTrieMap::buildTrie(UErrorCode &status) {
293 umtx_lock(&TextTrieMutex);
294 if (fLazyContents != NULL) {
295 for (int32_t i=0; i<fLazyContents->size(); i+=2) {
296 const UChar *key = (UChar *)fLazyContents->elementAt(i);
297 void *val = fLazyContents->elementAt(i+1);
298 UnicodeString keyString(TRUE, key, -1); // Aliasing UnicodeString constructor.
299 putImpl(keyString, val, status);
300 }
301 delete fLazyContents;
302 fLazyContents = NULL;
303 }
304 umtx_unlock(&TextTrieMutex);
305}
306
307void
308TextTrieMap::search(const UnicodeString &text, int32_t start,
309 TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
310 UBool trieNeedsInitialization = FALSE;
311 UMTX_CHECK(&TextTrieMutex, fLazyContents != NULL, trieNeedsInitialization);
312 if (trieNeedsInitialization) {
313 TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
314 nonConstThis->buildTrie(status);
315 }
316 if (fNodes == NULL) {
317 return;
318 }
319 search(fNodes, text, start, start, handler, status);
320}
321
322void
323TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
324 int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
325 if (U_FAILURE(status)) {
326 return;
327 }
328 if (node->hasValues()) {
329 if (!handler->handleMatch(index - start, node, status)) {
330 return;
331 }
332 if (U_FAILURE(status)) {
333 return;
334 }
335 }
336 UChar32 c = text.char32At(index);
337 if (fIgnoreCase) {
338 // size of character may grow after fold operation
339 UnicodeString tmp(c);
340 tmp.foldCase();
341 int32_t tmpidx = 0;
342 while (tmpidx < tmp.length()) {
343 c = tmp.char32At(tmpidx);
344 node = getChildNode(node, c);
345 if (node == NULL) {
346 break;
347 }
348 tmpidx = tmp.moveIndex32(tmpidx, 1);
349 }
350 } else {
351 node = getChildNode(node, c);
352 }
353 if (node != NULL) {
354 search(node, text, start, index+1, handler, status);
355 }
356}
357
358// ---------------------------------------------------
359// ZNStringPool class implementation
360// ---------------------------------------------------
361static const int32_t POOL_CHUNK_SIZE = 2000;
362struct ZNStringPoolChunk: public UMemory {
363 ZNStringPoolChunk *fNext; // Ptr to next pool chunk
364 int32_t fLimit; // Index to start of unused area at end of fStrings
365 UChar fStrings[POOL_CHUNK_SIZE]; // Strings array
366 ZNStringPoolChunk();
367};
368
369ZNStringPoolChunk::ZNStringPoolChunk() {
370 fNext = NULL;
371 fLimit = 0;
372}
373
374ZNStringPool::ZNStringPool(UErrorCode &status) {
375 fChunks = NULL;
376 fHash = NULL;
377 if (U_FAILURE(status)) {
378 return;
379 }
380 fChunks = new ZNStringPoolChunk;
381 if (fChunks == NULL) {
382 status = U_MEMORY_ALLOCATION_ERROR;
383 return;
384 }
385
386 fHash = uhash_open(uhash_hashUChars /* keyHash */,
387 uhash_compareUChars /* keyComp */,
388 uhash_compareUChars /* valueComp */,
389 &status);
390 if (U_FAILURE(status)) {
391 return;
392 }
393}
394
395ZNStringPool::~ZNStringPool() {
396 if (fHash != NULL) {
397 uhash_close(fHash);
398 fHash = NULL;
399 }
400
401 while (fChunks != NULL) {
402 ZNStringPoolChunk *nextChunk = fChunks->fNext;
403 delete fChunks;
404 fChunks = nextChunk;
405 }
406}
407
408static const UChar EmptyString = 0;
409
410const UChar *ZNStringPool::get(const UChar *s, UErrorCode &status) {
411 const UChar *pooledString;
412 if (U_FAILURE(status)) {
413 return &EmptyString;
414 }
415
416 pooledString = static_cast<UChar *>(uhash_get(fHash, s));
417 if (pooledString != NULL) {
418 return pooledString;
419 }
420
421 int32_t length = u_strlen(s);
422 int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit;
423 if (remainingLength <= length) {
424 U_ASSERT(length < POOL_CHUNK_SIZE);
425 if (length >= POOL_CHUNK_SIZE) {
426 status = U_INTERNAL_PROGRAM_ERROR;
427 return &EmptyString;
428 }
429 ZNStringPoolChunk *oldChunk = fChunks;
430 fChunks = new ZNStringPoolChunk;
431 if (fChunks == NULL) {
432 status = U_MEMORY_ALLOCATION_ERROR;
433 return &EmptyString;
434 }
435 fChunks->fNext = oldChunk;
436 }
437
438 UChar *destString = &fChunks->fStrings[fChunks->fLimit];
439 u_strcpy(destString, s);
440 fChunks->fLimit += (length + 1);
441 uhash_put(fHash, destString, destString, &status);
442 return destString;
443}
444
445
446//
447// ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
448// into the pool's storage. Used for strings from resource bundles,
449// which will perisist for the life of the zone string formatter, and
450// therefore can be used directly without copying.
451const UChar *ZNStringPool::adopt(const UChar * s, UErrorCode &status) {
452 const UChar *pooledString;
453 if (U_FAILURE(status)) {
454 return &EmptyString;
455 }
456 if (s != NULL) {
457 pooledString = static_cast<UChar *>(uhash_get(fHash, s));
458 if (pooledString == NULL) {
459 UChar *ncs = const_cast<UChar *>(s);
460 uhash_put(fHash, ncs, ncs, &status);
461 }
462 }
463 return s;
464}
465
466
467const UChar *ZNStringPool::get(const UnicodeString &s, UErrorCode &status) {
468 UnicodeString &nonConstStr = const_cast<UnicodeString &>(s);
469 return this->get(nonConstStr.getTerminatedBuffer(), status);
470}
471
472/*
473 * freeze(). Close the hash table that maps to the pooled strings.
474 * After freezing, the pool can not be searched or added to,
475 * but all existing references to pooled strings remain valid.
476 *
477 * The main purpose is to recover the storage used for the hash.
478 */
479void ZNStringPool::freeze() {
480 uhash_close(fHash);
481 fHash = NULL;
482}
483
484
485// ---------------------------------------------------
486// ZNames - names common for time zone and meta zone
487// ---------------------------------------------------
488class ZNames : public UMemory {
489public:
490 virtual ~ZNames();
491
492 static ZNames* createInstance(UResourceBundle* rb, const char* key);
51004dcb 493 virtual const UChar* getName(UTimeZoneNameType type);
4388f060
A
494
495protected:
496 ZNames(const UChar** names);
497 static const UChar** loadData(UResourceBundle* rb, const char* key);
498
499private:
500 const UChar** fNames;
501};
502
503ZNames::ZNames(const UChar** names)
504: fNames(names) {
505}
506
507ZNames::~ZNames() {
508 if (fNames != NULL) {
509 uprv_free(fNames);
510 }
511}
512
513ZNames*
514ZNames::createInstance(UResourceBundle* rb, const char* key) {
515 const UChar** names = loadData(rb, key);
516 if (names == NULL) {
517 // No names data available
518 return NULL;
519 }
520 return new ZNames(names);
521}
522
523const UChar*
524ZNames::getName(UTimeZoneNameType type) {
525 if (fNames == NULL) {
526 return NULL;
527 }
528 const UChar *name = NULL;
529 switch(type) {
530 case UTZNM_LONG_GENERIC:
531 name = fNames[0];
532 break;
533 case UTZNM_LONG_STANDARD:
534 name = fNames[1];
535 break;
536 case UTZNM_LONG_DAYLIGHT:
537 name = fNames[2];
538 break;
539 case UTZNM_SHORT_GENERIC:
540 name = fNames[3];
541 break;
542 case UTZNM_SHORT_STANDARD:
543 name = fNames[4];
544 break;
545 case UTZNM_SHORT_DAYLIGHT:
546 name = fNames[5];
547 break;
51004dcb 548 case UTZNM_EXEMPLAR_LOCATION: // implemeted by subclass
4388f060
A
549 default:
550 name = NULL;
551 }
552 return name;
553}
554
555const UChar**
556ZNames::loadData(UResourceBundle* rb, const char* key) {
557 if (rb == NULL || key == NULL || *key == 0) {
558 return NULL;
559 }
560
561 UErrorCode status = U_ZERO_ERROR;
562 const UChar **names = NULL;
563
564 UResourceBundle* rbTable = NULL;
565 rbTable = ures_getByKeyWithFallback(rb, key, rbTable, &status);
566 if (U_SUCCESS(status)) {
567 names = (const UChar **)uprv_malloc(sizeof(const UChar*) * KEYS_SIZE);
568 if (names != NULL) {
569 UBool isEmpty = TRUE;
570 for (int32_t i = 0; i < KEYS_SIZE; i++) {
571 status = U_ZERO_ERROR;
572 int32_t len = 0;
573 const UChar *value = ures_getStringByKeyWithFallback(rbTable, KEYS[i], &len, &status);
574 if (U_FAILURE(status) || len == 0) {
575 names[i] = NULL;
576 } else {
577 names[i] = value;
578 isEmpty = FALSE;
579 }
580 }
581 if (isEmpty) {
582 // No need to keep the names array
583 uprv_free(names);
584 names = NULL;
585 }
586 }
587 }
588 ures_close(rbTable);
589 return names;
590}
591
592// ---------------------------------------------------
593// TZNames - names for a time zone
594// ---------------------------------------------------
595class TZNames : public ZNames {
596public:
597 virtual ~TZNames();
598
51004dcb
A
599 static TZNames* createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID);
600 virtual const UChar* getName(UTimeZoneNameType type);
4388f060
A
601
602private:
51004dcb 603 TZNames(const UChar** names);
4388f060 604 const UChar* fLocationName;
51004dcb 605 UChar* fLocationNameOwned;
4388f060
A
606};
607
51004dcb
A
608TZNames::TZNames(const UChar** names)
609: ZNames(names), fLocationName(NULL), fLocationNameOwned(NULL) {
4388f060
A
610}
611
612TZNames::~TZNames() {
51004dcb
A
613 if (fLocationNameOwned) {
614 uprv_free(fLocationNameOwned);
615 }
4388f060
A
616}
617
618const UChar*
51004dcb
A
619TZNames::getName(UTimeZoneNameType type) {
620 if (type == UTZNM_EXEMPLAR_LOCATION) {
621 return fLocationName;
622 }
623 return ZNames::getName(type);
4388f060
A
624}
625
626TZNames*
51004dcb 627TZNames::createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID) {
4388f060
A
628 if (rb == NULL || key == NULL || *key == 0) {
629 return NULL;
630 }
51004dcb
A
631
632 const UChar** names = loadData(rb, key);
633 const UChar* locationName = NULL;
634 UChar* locationNameOwned = NULL;
635
4388f060 636 UErrorCode status = U_ZERO_ERROR;
51004dcb
A
637 int32_t len = 0;
638
639 UResourceBundle* table = ures_getByKeyWithFallback(rb, key, NULL, &status);
640 locationName = ures_getStringByKeyWithFallback(table, gEcTag, &len, &status);
641 // ignore missing resource here
642 status = U_ZERO_ERROR;
643
644 ures_close(table);
645
646 if (locationName == NULL) {
647 UnicodeString tmpName;
648 int32_t tmpNameLen = 0;
649 TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, tmpName);
650 tmpNameLen = tmpName.length();
651
652 if (tmpNameLen > 0) {
653 locationNameOwned = (UChar*) uprv_malloc(sizeof(UChar) * (tmpNameLen + 1));
654 if (locationNameOwned) {
655 tmpName.extract(locationNameOwned, tmpNameLen + 1, status);
656 locationName = locationNameOwned;
657 }
4388f060 658 }
51004dcb 659 }
4388f060 660
51004dcb
A
661 TZNames* tznames = NULL;
662 if (locationName != NULL || names != NULL) {
663 tznames = new TZNames(names);
664 if (tznames == NULL) {
665 if (locationNameOwned) {
666 uprv_free(locationNameOwned);
667 }
4388f060 668 }
51004dcb
A
669 tznames->fLocationName = locationName;
670 tznames->fLocationNameOwned = locationNameOwned;
4388f060 671 }
51004dcb 672
4388f060
A
673 return tznames;
674}
675
676// ---------------------------------------------------
677// The meta zone ID enumeration class
678// ---------------------------------------------------
679class MetaZoneIDsEnumeration : public StringEnumeration {
680public:
681 MetaZoneIDsEnumeration();
682 MetaZoneIDsEnumeration(const UVector& mzIDs);
683 MetaZoneIDsEnumeration(UVector* mzIDs);
684 virtual ~MetaZoneIDsEnumeration();
685 static UClassID U_EXPORT2 getStaticClassID(void);
686 virtual UClassID getDynamicClassID(void) const;
687 virtual const UnicodeString* snext(UErrorCode& status);
688 virtual void reset(UErrorCode& status);
689 virtual int32_t count(UErrorCode& status) const;
690private:
691 int32_t fLen;
692 int32_t fPos;
693 const UVector* fMetaZoneIDs;
694 UVector *fLocalVector;
695};
696
697UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)
698
699MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
700: fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) {
701}
702
703MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs)
704: fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) {
705 fLen = fMetaZoneIDs->size();
706}
707
708MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector *mzIDs)
709: fLen(0), fPos(0), fMetaZoneIDs(mzIDs), fLocalVector(mzIDs) {
710 if (fMetaZoneIDs) {
711 fLen = fMetaZoneIDs->size();
712 }
713}
714
715const UnicodeString*
716MetaZoneIDsEnumeration::snext(UErrorCode& status) {
717 if (U_SUCCESS(status) && fMetaZoneIDs != NULL && fPos < fLen) {
718 unistr.setTo((const UChar*)fMetaZoneIDs->elementAt(fPos++), -1);
719 return &unistr;
720 }
721 return NULL;
722}
723
724void
725MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) {
726 fPos = 0;
727}
728
729int32_t
730MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const {
731 return fLen;
732}
733
734MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
735 if (fLocalVector) {
736 delete fLocalVector;
737 }
738}
739
740U_CDECL_BEGIN
741/**
742 * ZNameInfo stores zone name information in the trie
743 */
744typedef struct ZNameInfo {
745 UTimeZoneNameType type;
746 const UChar* tzID;
747 const UChar* mzID;
748} ZNameInfo;
749
750/**
751 * ZMatchInfo stores zone name match information used by find method
752 */
753typedef struct ZMatchInfo {
754 const ZNameInfo* znameInfo;
755 int32_t matchLength;
756} ZMatchInfo;
757U_CDECL_END
758
759
760// ---------------------------------------------------
761// ZNameSearchHandler
762// ---------------------------------------------------
763class ZNameSearchHandler : public TextTrieMapSearchResultHandler {
764public:
765 ZNameSearchHandler(uint32_t types);
766 virtual ~ZNameSearchHandler();
767
768 UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
769 TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
770
771private:
772 uint32_t fTypes;
773 int32_t fMaxMatchLen;
774 TimeZoneNames::MatchInfoCollection* fResults;
775};
776
777ZNameSearchHandler::ZNameSearchHandler(uint32_t types)
778: fTypes(types), fMaxMatchLen(0), fResults(NULL) {
779}
780
781ZNameSearchHandler::~ZNameSearchHandler() {
782 if (fResults != NULL) {
783 delete fResults;
784 }
785}
786
787UBool
788ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
789 if (U_FAILURE(status)) {
790 return FALSE;
791 }
792 if (node->hasValues()) {
793 int32_t valuesCount = node->countValues();
794 for (int32_t i = 0; i < valuesCount; i++) {
795 ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
796 if (nameinfo == NULL) {
797 break;
798 }
799 if ((nameinfo->type & fTypes) != 0) {
800 // matches a requested type
801 if (fResults == NULL) {
802 fResults = new TimeZoneNames::MatchInfoCollection();
803 if (fResults == NULL) {
804 status = U_MEMORY_ALLOCATION_ERROR;
805 }
806 }
807 if (U_SUCCESS(status)) {
808 U_ASSERT(fResults != NULL);
809 if (nameinfo->tzID) {
810 fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status);
811 } else {
812 U_ASSERT(nameinfo->mzID);
813 fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status);
814 }
815 if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
816 fMaxMatchLen = matchLength;
817 }
818 }
819 }
820 }
821 }
822 return TRUE;
823}
824
825TimeZoneNames::MatchInfoCollection*
826ZNameSearchHandler::getMatches(int32_t& maxMatchLen) {
827 // give the ownership to the caller
828 TimeZoneNames::MatchInfoCollection* results = fResults;
829 maxMatchLen = fMaxMatchLen;
830
831 // reset
832 fResults = NULL;
833 fMaxMatchLen = 0;
834 return results;
835}
836
837// ---------------------------------------------------
838// TimeZoneNamesImpl
839//
840// TimeZoneNames implementation class. This is the main
841// part of this module.
842// ---------------------------------------------------
843
844U_CDECL_BEGIN
845/**
846 * Deleter for ZNames
847 */
848static void U_CALLCONV
849deleteZNames(void *obj) {
850 if (obj != EMPTY) {
851 delete (ZNames *)obj;
852 }
853}
854/**
855 * Deleter for TZNames
856 */
857static void U_CALLCONV
858deleteTZNames(void *obj) {
859 if (obj != EMPTY) {
860 delete (TZNames *)obj;
861 }
862}
863
864/**
865 * Deleter for ZNameInfo
866 */
867static void U_CALLCONV
868deleteZNameInfo(void *obj) {
869 uprv_free(obj);
870}
871
872U_CDECL_END
873
51004dcb
A
874static UMutex gLock = U_MUTEX_INITIALIZER;
875
4388f060
A
876TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
877: fLocale(locale),
4388f060
A
878 fZoneStrings(NULL),
879 fTZNamesMap(NULL),
880 fMZNamesMap(NULL),
881 fNamesTrieFullyLoaded(FALSE),
882 fNamesTrie(TRUE, deleteZNameInfo) {
883 initialize(locale, status);
884}
885
886void
887TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
888 if (U_FAILURE(status)) {
889 return;
890 }
891
892 // Load zoneStrings bundle
893 UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning..
894 fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
895 fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts);
896 if (U_FAILURE(tmpsts)) {
897 status = tmpsts;
898 cleanup();
899 return;
900 }
901
902 // Initialize hashtables holding time zone/meta zone names
903 fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
904 fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
905 if (U_FAILURE(status)) {
906 cleanup();
907 return;
908 }
909
910 uhash_setValueDeleter(fMZNamesMap, deleteZNames);
911 uhash_setValueDeleter(fTZNamesMap, deleteTZNames);
912 // no key deleters for name maps
913
914 // preload zone strings for the default zone
915 TimeZone *tz = TimeZone::createDefault();
916 const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
917 if (tzID != NULL) {
918 loadStrings(UnicodeString(tzID));
919 }
920 delete tz;
921
922 return;
923}
924
925/*
926 * This method updates the cache and must be called with a lock,
927 * except initializer.
928 */
929void
930TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID) {
931 loadTimeZoneNames(tzCanonicalID);
932
933 UErrorCode status = U_ZERO_ERROR;
934 StringEnumeration *mzIDs = getAvailableMetaZoneIDs(tzCanonicalID, status);
935 if (U_SUCCESS(status) && mzIDs != NULL) {
936 const UnicodeString *mzID;
937 while ((mzID = mzIDs->snext(status))) {
938 if (U_FAILURE(status)) {
939 break;
940 }
941 loadMetaZoneNames(*mzID);
942 }
943 delete mzIDs;
944 }
945}
946
947TimeZoneNamesImpl::~TimeZoneNamesImpl() {
948 cleanup();
4388f060
A
949}
950
951void
952TimeZoneNamesImpl::cleanup() {
953 if (fZoneStrings != NULL) {
954 ures_close(fZoneStrings);
955 fZoneStrings = NULL;
956 }
957 if (fMZNamesMap != NULL) {
958 uhash_close(fMZNamesMap);
959 fMZNamesMap = NULL;
960 }
961 if (fTZNamesMap != NULL) {
962 uhash_close(fTZNamesMap);
963 fTZNamesMap = NULL;
964 }
965}
966
967UBool
968TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const {
969 if (this == &other) {
970 return TRUE;
971 }
972 // No implementation for now
973 return FALSE;
974}
975
976TimeZoneNames*
977TimeZoneNamesImpl::clone() const {
978 UErrorCode status = U_ZERO_ERROR;
979 return new TimeZoneNamesImpl(fLocale, status);
980}
981
982StringEnumeration*
983TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
984 if (U_FAILURE(status)) {
985 return NULL;
986 }
987 const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs();
988 if (mzIDs == NULL) {
989 return new MetaZoneIDsEnumeration();
990 }
991 return new MetaZoneIDsEnumeration(*mzIDs);
992}
993
994StringEnumeration*
995TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
996 if (U_FAILURE(status)) {
997 return NULL;
998 }
999 const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID);
1000 if (mappings == NULL) {
1001 return new MetaZoneIDsEnumeration();
1002 }
1003
1004 MetaZoneIDsEnumeration *senum = NULL;
1005 UVector* mzIDs = new UVector(NULL, uhash_compareUChars, status);
1006 if (mzIDs == NULL) {
1007 status = U_MEMORY_ALLOCATION_ERROR;
1008 }
1009 if (U_SUCCESS(status)) {
1010 U_ASSERT(mzIDs != NULL);
1011 for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) {
1012
1013 OlsonToMetaMappingEntry *map = (OlsonToMetaMappingEntry *)mappings->elementAt(i);
1014 const UChar *mzID = map->mzid;
1015 if (!mzIDs->contains((void *)mzID)) {
1016 mzIDs->addElement((void *)mzID, status);
1017 }
1018 }
1019 if (U_SUCCESS(status)) {
1020 senum = new MetaZoneIDsEnumeration(mzIDs);
1021 } else {
1022 delete mzIDs;
1023 }
1024 }
1025 return senum;
1026}
1027
1028UnicodeString&
1029TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
1030 ZoneMeta::getMetazoneID(tzID, date, mzID);
1031 return mzID;
1032}
1033
1034UnicodeString&
1035TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
1036 ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID);
1037 return tzID;
1038}
1039
1040UnicodeString&
1041TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
1042 UTimeZoneNameType type,
1043 UnicodeString& name) const {
1044 name.setToBogus(); // cleanup result.
1045 if (mzID.isEmpty()) {
1046 return name;
1047 }
1048
1049 ZNames *znames = NULL;
1050 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1051
51004dcb 1052 umtx_lock(&gLock);
4388f060
A
1053 {
1054 znames = nonConstThis->loadMetaZoneNames(mzID);
1055 }
51004dcb 1056 umtx_unlock(&gLock);
4388f060
A
1057
1058 if (znames != NULL) {
1059 const UChar* s = znames->getName(type);
1060 if (s != NULL) {
1061 name.setTo(TRUE, s, -1);
1062 }
1063 }
1064 return name;
1065}
1066
1067UnicodeString&
1068TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
1069 name.setToBogus(); // cleanup result.
1070 if (tzID.isEmpty()) {
1071 return name;
1072 }
1073
1074 TZNames *tznames = NULL;
1075 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1076
51004dcb 1077 umtx_lock(&gLock);
4388f060
A
1078 {
1079 tznames = nonConstThis->loadTimeZoneNames(tzID);
1080 }
51004dcb 1081 umtx_unlock(&gLock);
4388f060
A
1082
1083 if (tznames != NULL) {
1084 const UChar *s = tznames->getName(type);
1085 if (s != NULL) {
1086 name.setTo(TRUE, s, -1);
1087 }
1088 }
1089 return name;
1090}
1091
1092UnicodeString&
1093TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
51004dcb 1094 name.setToBogus(); // cleanup result.
4388f060
A
1095 const UChar* locName = NULL;
1096 TZNames *tznames = NULL;
1097 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1098
51004dcb 1099 umtx_lock(&gLock);
4388f060
A
1100 {
1101 tznames = nonConstThis->loadTimeZoneNames(tzID);
1102 }
51004dcb 1103 umtx_unlock(&gLock);
4388f060
A
1104
1105 if (tznames != NULL) {
51004dcb 1106 locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION);
4388f060
A
1107 }
1108 if (locName != NULL) {
1109 name.setTo(TRUE, locName, -1);
4388f060
A
1110 }
1111
51004dcb 1112 return name;
4388f060
A
1113}
1114
1115
1116// Merge the MZ_PREFIX and mzId
1117static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
1118 if (mzID.isEmpty()) {
1119 result[0] = '\0';
1120 return;
1121 }
1122
1123 char mzIdChar[ZID_KEY_MAX + 1];
1124 int32_t keyLen;
1125 int32_t prefixLen = uprv_strlen(gMZPrefix);
1126 keyLen = mzID.extract(0, mzID.length(), mzIdChar, ZID_KEY_MAX + 1, US_INV);
1127 uprv_memcpy((void *)result, (void *)gMZPrefix, prefixLen);
1128 uprv_memcpy((void *)(result + prefixLen), (void *)mzIdChar, keyLen);
1129 result[keyLen + prefixLen] = '\0';
1130}
1131
1132/*
1133 * This method updates the cache and must be called with a lock
1134 */
1135ZNames*
1136TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID) {
1137 if (mzID.length() > (ZID_KEY_MAX - MZ_PREFIX_LEN)) {
1138 return NULL;
1139 }
1140
1141 ZNames *znames = NULL;
1142
1143 UErrorCode status = U_ZERO_ERROR;
1144 UChar mzIDKey[ZID_KEY_MAX + 1];
1145 mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
1146 U_ASSERT(status == U_ZERO_ERROR); // already checked length above
1147 mzIDKey[mzID.length()] = 0;
1148
1149 void *cacheVal = uhash_get(fMZNamesMap, mzIDKey);
1150 if (cacheVal == NULL) {
1151 char key[ZID_KEY_MAX + 1];
1152 mergeTimeZoneKey(mzID, key);
1153 znames = ZNames::createInstance(fZoneStrings, key);
1154
1155 if (znames == NULL) {
1156 cacheVal = (void *)EMPTY;
1157 } else {
1158 cacheVal = znames;
1159 }
1160 // Use the persistent ID as the resource key, so we can
1161 // avoid duplications.
1162 const UChar* newKey = ZoneMeta::findMetaZoneID(mzID);
1163 if (newKey != NULL) {
1164 uhash_put(fMZNamesMap, (void *)newKey, cacheVal, &status);
1165 if (U_FAILURE(status)) {
1166 if (znames != NULL) {
1167 delete znames;
1168 }
1169 } else if (znames != NULL) {
1170 // put the name info into the trie
1171 for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) {
1172 const UChar* name = znames->getName(ALL_NAME_TYPES[i]);
1173 if (name != NULL) {
1174 ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
1175 if (nameinfo != NULL) {
1176 nameinfo->type = ALL_NAME_TYPES[i];
1177 nameinfo->tzID = NULL;
1178 nameinfo->mzID = newKey;
1179 fNamesTrie.put(name, nameinfo, status);
1180 }
1181 }
1182 }
1183 }
1184
1185 } else {
1186 // Should never happen with a valid input
1187 if (znames != NULL) {
1188 // It's not possible that we get a valid ZNames with unknown ID.
1189 // But just in case..
1190 delete znames;
1191 znames = NULL;
1192 }
1193 }
1194 } else if (cacheVal != EMPTY) {
1195 znames = (ZNames *)cacheVal;
1196 }
1197
1198 return znames;
1199}
1200
1201/*
1202 * This method updates the cache and must be called with a lock
1203 */
1204TZNames*
1205TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID) {
1206 if (tzID.length() > ZID_KEY_MAX) {
1207 return NULL;
1208 }
1209
1210 TZNames *tznames = NULL;
1211
1212 UErrorCode status = U_ZERO_ERROR;
1213 UChar tzIDKey[ZID_KEY_MAX + 1];
1214 int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
1215 U_ASSERT(status == U_ZERO_ERROR); // already checked length above
1216 tzIDKey[tzIDKeyLen] = 0;
1217
1218 void *cacheVal = uhash_get(fTZNamesMap, tzIDKey);
1219 if (cacheVal == NULL) {
1220 char key[ZID_KEY_MAX + 1];
1221 UErrorCode status = U_ZERO_ERROR;
1222 // Replace "/" with ":".
1223 UnicodeString uKey(tzID);
1224 for (int32_t i = 0; i < uKey.length(); i++) {
1225 if (uKey.charAt(i) == (UChar)0x2F) {
1226 uKey.setCharAt(i, (UChar)0x3A);
1227 }
1228 }
1229 uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
51004dcb 1230 tznames = TZNames::createInstance(fZoneStrings, key, tzID);
4388f060
A
1231
1232 if (tznames == NULL) {
1233 cacheVal = (void *)EMPTY;
1234 } else {
1235 cacheVal = tznames;
1236 }
1237 // Use the persistent ID as the resource key, so we can
1238 // avoid duplications.
1239 const UChar* newKey = ZoneMeta::findTimeZoneID(tzID);
1240 if (newKey != NULL) {
1241 uhash_put(fTZNamesMap, (void *)newKey, cacheVal, &status);
1242 if (U_FAILURE(status)) {
1243 if (tznames != NULL) {
1244 delete tznames;
1245 }
1246 } else if (tznames != NULL) {
1247 // put the name info into the trie
1248 for (int32_t i = 0; ALL_NAME_TYPES[i] != UTZNM_UNKNOWN; i++) {
1249 const UChar* name = tznames->getName(ALL_NAME_TYPES[i]);
1250 if (name != NULL) {
1251 ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
1252 if (nameinfo != NULL) {
1253 nameinfo->type = ALL_NAME_TYPES[i];
1254 nameinfo->tzID = newKey;
1255 nameinfo->mzID = NULL;
1256 fNamesTrie.put(name, nameinfo, status);
1257 }
1258 }
1259 }
1260 }
1261 } else {
1262 // Should never happen with a valid input
1263 if (tznames != NULL) {
1264 // It's not possible that we get a valid TZNames with unknown ID.
1265 // But just in case..
1266 delete tznames;
1267 tznames = NULL;
1268 }
1269 }
1270 } else if (cacheVal != EMPTY) {
1271 tznames = (TZNames *)cacheVal;
1272 }
1273
1274 return tznames;
1275}
1276
1277TimeZoneNames::MatchInfoCollection*
1278TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
1279 ZNameSearchHandler handler(types);
1280
1281 TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1282
51004dcb 1283 umtx_lock(&gLock);
4388f060
A
1284 {
1285 fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1286 }
51004dcb 1287 umtx_unlock(&gLock);
4388f060
A
1288
1289 if (U_FAILURE(status)) {
1290 return NULL;
1291 }
1292
1293 int32_t maxLen = 0;
1294 TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen);
1295 if (matches != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) {
1296 // perfect match
1297 return matches;
1298 }
1299
1300 delete matches;
1301
1302 // All names are not yet loaded into the trie
51004dcb 1303 umtx_lock(&gLock);
4388f060
A
1304 {
1305 if (!fNamesTrieFullyLoaded) {
1306 const UnicodeString *id;
1307
1308 // load strings for all zones
1309 StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
1310 if (U_SUCCESS(status)) {
1311 while ((id = tzIDs->snext(status))) {
1312 if (U_FAILURE(status)) {
1313 break;
1314 }
1315 // loadStrings also load related metazone strings
1316 nonConstThis->loadStrings(*id);
1317 }
1318 }
1319 if (tzIDs != NULL) {
1320 delete tzIDs;
1321 }
1322 if (U_SUCCESS(status)) {
1323 nonConstThis->fNamesTrieFullyLoaded = TRUE;
1324 }
1325 }
1326 }
51004dcb 1327 umtx_unlock(&gLock);
4388f060
A
1328
1329 if (U_FAILURE(status)) {
1330 return NULL;
1331 }
1332
51004dcb 1333 umtx_lock(&gLock);
4388f060
A
1334 {
1335 // now try it again
1336 fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1337 }
51004dcb 1338 umtx_unlock(&gLock);
4388f060
A
1339
1340 return handler.getMatches(maxLen);
1341}
1342
51004dcb
A
1343static const UChar gEtcPrefix[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1344static const int32_t gEtcPrefixLen = 4;
1345static const UChar gSystemVPrefix[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1346static const int32_t gSystemVPrefixLen = 8;
1347static const UChar gRiyadh8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1348static const int32_t gRiyadh8Len = 7;
1349
1350UnicodeString& U_EXPORT2
1351TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) {
1352 if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen)
1353 || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) {
1354 name.setToBogus();
1355 return name;
1356 }
1357
1358 int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */);
1359 if (sep > 0 && sep + 1 < tzID.length()) {
1360 name.setTo(tzID, sep + 1);
1361 name.findAndReplace(UnicodeString((UChar)0x5f /* _ */),
1362 UnicodeString((UChar)0x20 /* space */));
1363 } else {
1364 name.setToBogus();
1365 }
1366 return name;
1367}
1368
4388f060
A
1369U_NAMESPACE_END
1370
1371
1372#endif /* #if !UCONFIG_NO_FORMATTING */
1373
1374//eof