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
32U_NAMESPACE_BEGIN
33
34#define ZID_KEY_MAX  128
35#define MZ_PREFIX_LEN 5
36
37static const char gZoneStrings[]        = "zoneStrings";
38static const char gMZPrefix[]           = "meta:";
39
40static const char* KEYS[]               = {"lg", "ls", "ld", "sg", "ss", "sd"};
41static const int32_t KEYS_SIZE = (sizeof KEYS / sizeof KEYS[0]);
42
43static const char gEcTag[]              = "ec";
44
45static const char EMPTY[]               = "<empty>";   // place holder for empty ZNames/TZNames
46
47static 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// ---------------------------------------------------
59void CharacterNode::clear() {
60    uprv_memset(this, 0, sizeof(*this));
61}
62
63void 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
75void
76CharacterNode::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// ---------------------------------------------------
109TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
110}
111
112// ---------------------------------------------------
113// TextTrieMap class implementation
114// ---------------------------------------------------
115TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter)
116: fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0),
117  fLazyContents(NULL), fIsEmpty(TRUE), fValueDeleter(valueDeleter) {
118}
119
120TextTrieMap::~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
136int32_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//
149void
150TextTrieMap::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.
157void
158TextTrieMap::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
175void
176TextTrieMap::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
205UBool
206TextTrieMap::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
225CharacterNode*
226TextTrieMap::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
269CharacterNode*
270TextTrieMap::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().
287static 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.
293void 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
306void
307TextTrieMap::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
327void
328TextTrieMap::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// ---------------------------------------------------
366static const int32_t POOL_CHUNK_SIZE = 2000;
367struct 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
374ZNStringPoolChunk::ZNStringPoolChunk() {
375    fNext = NULL;
376    fLimit = 0;
377}
378
379ZNStringPool::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
400ZNStringPool::~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
413static const UChar EmptyString = 0;
414
415const 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.
456const 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
472const 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 */
484void ZNStringPool::freeze() {
485    uhash_close(fHash);
486    fHash = NULL;
487}
488
489
490// ---------------------------------------------------
491// ZNames - names common for time zone and meta zone
492// ---------------------------------------------------
493class ZNames : public UMemory {
494public:
495    virtual ~ZNames();
496
497    static ZNames* createInstance(UResourceBundle* rb, const char* key);
498    virtual const UChar* getName(UTimeZoneNameType type);
499
500protected:
501    ZNames(const UChar** names);
502    static const UChar** loadData(UResourceBundle* rb, const char* key);
503
504private:
505    const UChar** fNames;
506};
507
508ZNames::ZNames(const UChar** names)
509: fNames(names) {
510}
511
512ZNames::~ZNames() {
513    if (fNames != NULL) {
514        uprv_free(fNames);
515    }
516}
517
518ZNames*
519ZNames::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
528const UChar*
529ZNames::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
560const UChar**
561ZNames::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// ---------------------------------------------------
600class TZNames : public ZNames {
601public:
602    virtual ~TZNames();
603
604    static TZNames* createInstance(UResourceBundle* rb, const char* key, const UnicodeString& tzID);
605    virtual const UChar* getName(UTimeZoneNameType type);
606
607private:
608    TZNames(const UChar** names);
609    const UChar* fLocationName;
610    UChar* fLocationNameOwned;
611};
612
613TZNames::TZNames(const UChar** names)
614: ZNames(names), fLocationName(NULL), fLocationNameOwned(NULL) {
615}
616
617TZNames::~TZNames() {
618    if (fLocationNameOwned) {
619        uprv_free(fLocationNameOwned);
620    }
621}
622
623const UChar*
624TZNames::getName(UTimeZoneNameType type) {
625    if (type == UTZNM_EXEMPLAR_LOCATION) {
626        return fLocationName;
627    }
628    return ZNames::getName(type);
629}
630
631TZNames*
632TZNames::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// ---------------------------------------------------
684class MetaZoneIDsEnumeration : public StringEnumeration {
685public:
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;
695private:
696    int32_t fLen;
697    int32_t fPos;
698    const UVector* fMetaZoneIDs;
699    UVector *fLocalVector;
700};
701
702UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)
703
704MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
705: fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) {
706}
707
708MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs)
709: fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) {
710    fLen = fMetaZoneIDs->size();
711}
712
713MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector *mzIDs)
714: fLen(0), fPos(0), fMetaZoneIDs(mzIDs), fLocalVector(mzIDs) {
715    if (fMetaZoneIDs) {
716        fLen = fMetaZoneIDs->size();
717    }
718}
719
720const UnicodeString*
721MetaZoneIDsEnumeration::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
729void
730MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) {
731    fPos = 0;
732}
733
734int32_t
735MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const {
736    return fLen;
737}
738
739MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
740    if (fLocalVector) {
741        delete fLocalVector;
742    }
743}
744
745U_CDECL_BEGIN
746/**
747 * ZNameInfo stores zone name information in the trie
748 */
749typedef 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 */
758typedef struct ZMatchInfo {
759    const ZNameInfo*    znameInfo;
760    int32_t             matchLength;
761} ZMatchInfo;
762U_CDECL_END
763
764
765// ---------------------------------------------------
766// ZNameSearchHandler
767// ---------------------------------------------------
768class ZNameSearchHandler : public TextTrieMapSearchResultHandler {
769public:
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
776private:
777    uint32_t fTypes;
778    int32_t fMaxMatchLen;
779    TimeZoneNames::MatchInfoCollection* fResults;
780};
781
782ZNameSearchHandler::ZNameSearchHandler(uint32_t types)
783: fTypes(types), fMaxMatchLen(0), fResults(NULL) {
784}
785
786ZNameSearchHandler::~ZNameSearchHandler() {
787    if (fResults != NULL) {
788        delete fResults;
789    }
790}
791
792UBool
793ZNameSearchHandler::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
830TimeZoneNames::MatchInfoCollection*
831ZNameSearchHandler::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
849U_CDECL_BEGIN
850/**
851 * Deleter for ZNames
852 */
853static void U_CALLCONV
854deleteZNames(void *obj) {
855    if (obj != EMPTY) {
856        delete (ZNames *)obj;
857    }
858}
859/**
860 * Deleter for TZNames
861 */
862static void U_CALLCONV
863deleteTZNames(void *obj) {
864    if (obj != EMPTY) {
865        delete (TZNames *)obj;
866    }
867}
868
869/**
870 * Deleter for ZNameInfo
871 */
872static void U_CALLCONV
873deleteZNameInfo(void *obj) {
874    uprv_free(obj);
875}
876
877U_CDECL_END
878
879static UMutex gLock = U_MUTEX_INITIALIZER;
880
881TimeZoneNamesImpl::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
891void
892TimeZoneNamesImpl::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 */
934void
935TimeZoneNamesImpl::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
952TimeZoneNamesImpl::~TimeZoneNamesImpl() {
953    cleanup();
954}
955
956void
957TimeZoneNamesImpl::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
972UBool
973TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const {
974    if (this == &other) {
975        return TRUE;
976    }
977    // No implementation for now
978    return FALSE;
979}
980
981TimeZoneNames*
982TimeZoneNamesImpl::clone() const {
983    UErrorCode status = U_ZERO_ERROR;
984    return new TimeZoneNamesImpl(fLocale, status);
985}
986
987StringEnumeration*
988TimeZoneNamesImpl::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
999StringEnumeration*
1000TimeZoneNamesImpl::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
1033UnicodeString&
1034TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
1035    ZoneMeta::getMetazoneID(tzID, date, mzID);
1036    return mzID;
1037}
1038
1039UnicodeString&
1040TimeZoneNamesImpl::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
1045UnicodeString&
1046TimeZoneNamesImpl::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
1072UnicodeString&
1073TimeZoneNamesImpl::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
1097UnicodeString&
1098TimeZoneNamesImpl::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
1122static 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 */
1140ZNames*
1141TimeZoneNamesImpl::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 */
1209TZNames*
1210TimeZoneNamesImpl::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
1282TimeZoneNames::MatchInfoCollection*
1283TimeZoneNamesImpl::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
1348static const UChar gEtcPrefix[]         = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1349static const int32_t gEtcPrefixLen      = 4;
1350static const UChar gSystemVPrefix[]     = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1351static const int32_t gSystemVPrefixLen  = 8;
1352static const UChar gRiyadh8[]           = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1353static const int32_t gRiyadh8Len       = 7;
1354
1355UnicodeString& U_EXPORT2
1356TimeZoneNamesImpl::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
1374U_NAMESPACE_END
1375
1376
1377#endif /* #if !UCONFIG_NO_FORMATTING */
1378
1379//eof
1380