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