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 "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,
49    UTZNM_EXEMPLAR_LOCATION,
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().
286static UMutex TextTrieMutex = U_MUTEX_INITIALIZER;
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);
493    virtual const UChar* getName(UTimeZoneNameType type);
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;
548    case UTZNM_EXEMPLAR_LOCATION:   // implemeted by subclass
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
599    static TZNames* createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID);
600    virtual const UChar* getName(UTimeZoneNameType type);
601
602private:
603    TZNames(const UChar** names);
604    const UChar* fLocationName;
605    UChar* fLocationNameOwned;
606};
607
608TZNames::TZNames(const UChar** names)
609: ZNames(names), fLocationName(NULL), fLocationNameOwned(NULL) {
610}
611
612TZNames::~TZNames() {
613    if (fLocationNameOwned) {
614        uprv_free(fLocationNameOwned);
615    }
616}
617
618const UChar*
619TZNames::getName(UTimeZoneNameType type) {
620    if (type == UTZNM_EXEMPLAR_LOCATION) {
621        return fLocationName;
622    }
623    return ZNames::getName(type);
624}
625
626TZNames*
627TZNames::createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID) {
628    if (rb == NULL || key == NULL || *key == 0) {
629        return NULL;
630    }
631
632    const UChar** names = loadData(rb, key);
633    const UChar* locationName = NULL;
634    UChar* locationNameOwned = NULL;
635
636    UErrorCode status = U_ZERO_ERROR;
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            }
658        }
659    }
660
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            }
668        }
669        tznames->fLocationName = locationName;
670        tznames->fLocationNameOwned = locationNameOwned;
671    }
672
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
874static UMutex gLock = U_MUTEX_INITIALIZER;
875
876TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
877: fLocale(locale),
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();
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
1052    umtx_lock(&gLock);
1053    {
1054        znames = nonConstThis->loadMetaZoneNames(mzID);
1055    }
1056    umtx_unlock(&gLock);
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
1077    umtx_lock(&gLock);
1078    {
1079        tznames = nonConstThis->loadTimeZoneNames(tzID);
1080    }
1081    umtx_unlock(&gLock);
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 {
1094    name.setToBogus();  // cleanup result.
1095    const UChar* locName = NULL;
1096    TZNames *tznames = NULL;
1097    TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1098
1099    umtx_lock(&gLock);
1100    {
1101        tznames = nonConstThis->loadTimeZoneNames(tzID);
1102    }
1103    umtx_unlock(&gLock);
1104
1105    if (tznames != NULL) {
1106        locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION);
1107    }
1108    if (locName != NULL) {
1109        name.setTo(TRUE, locName, -1);
1110    }
1111
1112    return name;
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);
1230        tznames = TZNames::createInstance(fZoneStrings, key, tzID);
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
1283    umtx_lock(&gLock);
1284    {
1285        fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1286    }
1287    umtx_unlock(&gLock);
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
1303    umtx_lock(&gLock);
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    }
1327    umtx_unlock(&gLock);
1328
1329    if (U_FAILURE(status)) {
1330        return NULL;
1331    }
1332
1333    umtx_lock(&gLock);
1334    {
1335        // now try it again
1336        fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1337    }
1338    umtx_unlock(&gLock);
1339
1340    return handler.getMatches(maxLen);
1341}
1342
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
1369U_NAMESPACE_END
1370
1371
1372#endif /* #if !UCONFIG_NO_FORMATTING */
1373
1374//eof
1375