Line data Source code
1 : // © 2016 and later: Unicode, Inc. and others.
2 : // License & terms of use: http://www.unicode.org/copyright.html
3 : /*
4 : *******************************************************************************
5 : * Copyright (C) 2011-2016, International Business Machines Corporation and
6 : * others. All Rights Reserved.
7 : *******************************************************************************
8 : *
9 : * File TZNAMES_IMPL.CPP
10 : *
11 : *******************************************************************************
12 : */
13 :
14 : #include "unicode/utypes.h"
15 :
16 : #if !UCONFIG_NO_FORMATTING
17 :
18 : #include "unicode/strenum.h"
19 : #include "unicode/ustring.h"
20 : #include "unicode/timezone.h"
21 :
22 : #include "tznames_impl.h"
23 : #include "cmemory.h"
24 : #include "cstring.h"
25 : #include "uassert.h"
26 : #include "mutex.h"
27 : #include "resource.h"
28 : #include "uresimp.h"
29 : #include "ureslocs.h"
30 : #include "zonemeta.h"
31 : #include "ucln_in.h"
32 : #include "uvector.h"
33 : #include "olsontz.h"
34 :
35 : U_NAMESPACE_BEGIN
36 :
37 : #define ZID_KEY_MAX 128
38 : #define MZ_PREFIX_LEN 5
39 :
40 : static const char gZoneStrings[] = "zoneStrings";
41 : static const char gMZPrefix[] = "meta:";
42 :
43 : static const char EMPTY[] = "<empty>"; // place holder for empty ZNames
44 : static const char DUMMY_LOADER[] = "<dummy>"; // place holder for dummy ZNamesLoader
45 : static const UChar NO_NAME[] = { 0 }; // for empty no-fallback time zone names
46 :
47 : // stuff for TZDBTimeZoneNames
48 : static const char* TZDBNAMES_KEYS[] = {"ss", "sd"};
49 : static const int32_t TZDBNAMES_KEYS_SIZE = UPRV_LENGTHOF(TZDBNAMES_KEYS);
50 :
51 : static UMutex gTZDBNamesMapLock = U_MUTEX_INITIALIZER;
52 : static UMutex gDataMutex = U_MUTEX_INITIALIZER;
53 :
54 : static UHashtable* gTZDBNamesMap = NULL;
55 : static icu::UInitOnce gTZDBNamesMapInitOnce = U_INITONCE_INITIALIZER;
56 :
57 : static TextTrieMap* gTZDBNamesTrie = NULL;
58 : static icu::UInitOnce gTZDBNamesTrieInitOnce = U_INITONCE_INITIALIZER;
59 :
60 : // The order in which strings are stored may be different than the order in the public enum.
61 : enum UTimeZoneNameTypeIndex {
62 : UTZNM_INDEX_UNKNOWN = -1,
63 : UTZNM_INDEX_EXEMPLAR_LOCATION,
64 : UTZNM_INDEX_LONG_GENERIC,
65 : UTZNM_INDEX_LONG_STANDARD,
66 : UTZNM_INDEX_LONG_DAYLIGHT,
67 : UTZNM_INDEX_SHORT_GENERIC,
68 : UTZNM_INDEX_SHORT_STANDARD,
69 : UTZNM_INDEX_SHORT_DAYLIGHT,
70 : UTZNM_INDEX_COUNT
71 : };
72 : static const UChar* EMPTY_NAMES[UTZNM_INDEX_COUNT] = {0,0,0,0,0,0,0};
73 :
74 : U_CDECL_BEGIN
75 0 : static UBool U_CALLCONV tzdbTimeZoneNames_cleanup(void) {
76 0 : if (gTZDBNamesMap != NULL) {
77 0 : uhash_close(gTZDBNamesMap);
78 0 : gTZDBNamesMap = NULL;
79 : }
80 0 : gTZDBNamesMapInitOnce.reset();
81 :
82 0 : if (gTZDBNamesTrie != NULL) {
83 0 : delete gTZDBNamesTrie;
84 0 : gTZDBNamesTrie = NULL;
85 : }
86 0 : gTZDBNamesTrieInitOnce.reset();
87 :
88 0 : return TRUE;
89 : }
90 : U_CDECL_END
91 :
92 : /**
93 : * ZNameInfo stores zone name information in the trie
94 : */
95 : struct ZNameInfo {
96 : UTimeZoneNameType type;
97 : const UChar* tzID;
98 : const UChar* mzID;
99 : };
100 :
101 : /**
102 : * ZMatchInfo stores zone name match information used by find method
103 : */
104 : struct ZMatchInfo {
105 : const ZNameInfo* znameInfo;
106 : int32_t matchLength;
107 : };
108 :
109 : // Helper functions
110 : static void mergeTimeZoneKey(const UnicodeString& mzID, char* result);
111 :
112 : #define DEFAULT_CHARACTERNODE_CAPACITY 1
113 :
114 : // ---------------------------------------------------
115 : // CharacterNode class implementation
116 : // ---------------------------------------------------
117 0 : void CharacterNode::clear() {
118 0 : uprv_memset(this, 0, sizeof(*this));
119 0 : }
120 :
121 0 : void CharacterNode::deleteValues(UObjectDeleter *valueDeleter) {
122 0 : if (fValues == NULL) {
123 : // Do nothing.
124 0 : } else if (!fHasValuesVector) {
125 0 : if (valueDeleter) {
126 0 : valueDeleter(fValues);
127 : }
128 : } else {
129 0 : delete (UVector *)fValues;
130 : }
131 0 : }
132 :
133 : void
134 0 : CharacterNode::addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status) {
135 0 : if (U_FAILURE(status)) {
136 0 : if (valueDeleter) {
137 0 : valueDeleter(value);
138 : }
139 0 : return;
140 : }
141 0 : if (fValues == NULL) {
142 0 : fValues = value;
143 : } else {
144 : // At least one value already.
145 0 : if (!fHasValuesVector) {
146 : // There is only one value so far, and not in a vector yet.
147 : // Create a vector and add the old value.
148 0 : UVector *values = new UVector(valueDeleter, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status);
149 0 : if (U_FAILURE(status)) {
150 0 : if (valueDeleter) {
151 0 : valueDeleter(value);
152 : }
153 0 : return;
154 : }
155 0 : values->addElement(fValues, status);
156 0 : fValues = values;
157 0 : fHasValuesVector = TRUE;
158 : }
159 : // Add the new value.
160 0 : ((UVector *)fValues)->addElement(value, status);
161 : }
162 : }
163 :
164 : // ---------------------------------------------------
165 : // TextTrieMapSearchResultHandler class implementation
166 : // ---------------------------------------------------
167 0 : TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){
168 0 : }
169 :
170 : // ---------------------------------------------------
171 : // TextTrieMap class implementation
172 : // ---------------------------------------------------
173 0 : TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleter)
174 : : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0),
175 0 : fLazyContents(NULL), fIsEmpty(TRUE), fValueDeleter(valueDeleter) {
176 0 : }
177 :
178 0 : TextTrieMap::~TextTrieMap() {
179 : int32_t index;
180 0 : for (index = 0; index < fNodesCount; ++index) {
181 0 : fNodes[index].deleteValues(fValueDeleter);
182 : }
183 0 : uprv_free(fNodes);
184 0 : if (fLazyContents != NULL) {
185 0 : for (int32_t i=0; i<fLazyContents->size(); i+=2) {
186 0 : if (fValueDeleter) {
187 0 : fValueDeleter(fLazyContents->elementAt(i+1));
188 : }
189 : }
190 0 : delete fLazyContents;
191 : }
192 0 : }
193 :
194 0 : int32_t TextTrieMap::isEmpty() const {
195 : // Use a separate field for fIsEmpty because it will remain unchanged once the
196 : // Trie is built, while fNodes and fLazyContents change with the lazy init
197 : // of the nodes structure. Trying to test the changing fields has
198 : // thread safety complications.
199 0 : return fIsEmpty;
200 : }
201 :
202 :
203 : // We defer actually building the TextTrieMap node structure until the first time a
204 : // search is performed. put() simply saves the parameters in case we do
205 : // eventually need to build it.
206 : //
207 : void
208 0 : TextTrieMap::put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status) {
209 0 : const UChar *s = sp.get(key, status);
210 0 : put(s, value, status);
211 0 : }
212 :
213 : // This method is designed for a persistent key, such as string key stored in
214 : // resource bundle.
215 : void
216 0 : TextTrieMap::put(const UChar *key, void *value, UErrorCode &status) {
217 0 : fIsEmpty = FALSE;
218 0 : if (fLazyContents == NULL) {
219 0 : fLazyContents = new UVector(status);
220 0 : if (fLazyContents == NULL) {
221 0 : status = U_MEMORY_ALLOCATION_ERROR;
222 : }
223 : }
224 0 : if (U_FAILURE(status)) {
225 0 : if (fValueDeleter) {
226 0 : fValueDeleter((void*) key);
227 : }
228 0 : return;
229 : }
230 0 : U_ASSERT(fLazyContents != NULL);
231 :
232 0 : UChar *s = const_cast<UChar *>(key);
233 0 : fLazyContents->addElement(s, status);
234 0 : if (U_FAILURE(status)) {
235 0 : if (fValueDeleter) {
236 0 : fValueDeleter((void*) key);
237 : }
238 0 : return;
239 : }
240 :
241 0 : fLazyContents->addElement(value, status);
242 : }
243 :
244 : void
245 0 : TextTrieMap::putImpl(const UnicodeString &key, void *value, UErrorCode &status) {
246 0 : if (fNodes == NULL) {
247 0 : fNodesCapacity = 512;
248 0 : fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode));
249 0 : if (fNodes == NULL) {
250 0 : status = U_MEMORY_ALLOCATION_ERROR;
251 0 : return;
252 : }
253 0 : fNodes[0].clear(); // Init root node.
254 0 : fNodesCount = 1;
255 : }
256 :
257 0 : UnicodeString foldedKey;
258 : const UChar *keyBuffer;
259 : int32_t keyLength;
260 0 : if (fIgnoreCase) {
261 : // Ok to use fastCopyFrom() because we discard the copy when we return.
262 0 : foldedKey.fastCopyFrom(key).foldCase();
263 0 : keyBuffer = foldedKey.getBuffer();
264 0 : keyLength = foldedKey.length();
265 : } else {
266 0 : keyBuffer = key.getBuffer();
267 0 : keyLength = key.length();
268 : }
269 :
270 0 : CharacterNode *node = fNodes;
271 : int32_t index;
272 0 : for (index = 0; index < keyLength; ++index) {
273 0 : node = addChildNode(node, keyBuffer[index], status);
274 : }
275 0 : node->addValue(value, fValueDeleter, status);
276 : }
277 :
278 : UBool
279 0 : TextTrieMap::growNodes() {
280 0 : if (fNodesCapacity == 0xffff) {
281 0 : return FALSE; // We use 16-bit node indexes.
282 : }
283 0 : int32_t newCapacity = fNodesCapacity + 1000;
284 0 : if (newCapacity > 0xffff) {
285 0 : newCapacity = 0xffff;
286 : }
287 0 : CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode));
288 0 : if (newNodes == NULL) {
289 0 : return FALSE;
290 : }
291 0 : uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode));
292 0 : uprv_free(fNodes);
293 0 : fNodes = newNodes;
294 0 : fNodesCapacity = newCapacity;
295 0 : return TRUE;
296 : }
297 :
298 : CharacterNode*
299 0 : TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) {
300 0 : if (U_FAILURE(status)) {
301 0 : return NULL;
302 : }
303 : // Linear search of the sorted list of children.
304 0 : uint16_t prevIndex = 0;
305 0 : uint16_t nodeIndex = parent->fFirstChild;
306 0 : while (nodeIndex > 0) {
307 0 : CharacterNode *current = fNodes + nodeIndex;
308 0 : UChar childCharacter = current->fCharacter;
309 0 : if (childCharacter == c) {
310 0 : return current;
311 0 : } else if (childCharacter > c) {
312 0 : break;
313 : }
314 0 : prevIndex = nodeIndex;
315 0 : nodeIndex = current->fNextSibling;
316 : }
317 :
318 : // Ensure capacity. Grow fNodes[] if needed.
319 0 : if (fNodesCount == fNodesCapacity) {
320 0 : int32_t parentIndex = (int32_t)(parent - fNodes);
321 0 : if (!growNodes()) {
322 0 : status = U_MEMORY_ALLOCATION_ERROR;
323 0 : return NULL;
324 : }
325 0 : parent = fNodes + parentIndex;
326 : }
327 :
328 : // Insert a new child node with c in sorted order.
329 0 : CharacterNode *node = fNodes + fNodesCount;
330 0 : node->clear();
331 0 : node->fCharacter = c;
332 0 : node->fNextSibling = nodeIndex;
333 0 : if (prevIndex == 0) {
334 0 : parent->fFirstChild = (uint16_t)fNodesCount;
335 : } else {
336 0 : fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount;
337 : }
338 0 : ++fNodesCount;
339 0 : return node;
340 : }
341 :
342 : CharacterNode*
343 0 : TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const {
344 : // Linear search of the sorted list of children.
345 0 : uint16_t nodeIndex = parent->fFirstChild;
346 0 : while (nodeIndex > 0) {
347 0 : CharacterNode *current = fNodes + nodeIndex;
348 0 : UChar childCharacter = current->fCharacter;
349 0 : if (childCharacter == c) {
350 0 : return current;
351 0 : } else if (childCharacter > c) {
352 0 : break;
353 : }
354 0 : nodeIndex = current->fNextSibling;
355 : }
356 0 : return NULL;
357 : }
358 :
359 : // Mutex for protecting the lazy creation of the Trie node structure on the first call to search().
360 : static UMutex TextTrieMutex = U_MUTEX_INITIALIZER;
361 :
362 : // buildTrie() - The Trie node structure is needed. Create it from the data that was
363 : // saved at the time the ZoneStringFormatter was created. The Trie is only
364 : // needed for parsing operations, which are less common than formatting,
365 : // and the Trie is big, which is why its creation is deferred until first use.
366 0 : void TextTrieMap::buildTrie(UErrorCode &status) {
367 0 : if (fLazyContents != NULL) {
368 0 : for (int32_t i=0; i<fLazyContents->size(); i+=2) {
369 0 : const UChar *key = (UChar *)fLazyContents->elementAt(i);
370 0 : void *val = fLazyContents->elementAt(i+1);
371 0 : UnicodeString keyString(TRUE, key, -1); // Aliasing UnicodeString constructor.
372 0 : putImpl(keyString, val, status);
373 : }
374 0 : delete fLazyContents;
375 0 : fLazyContents = NULL;
376 : }
377 0 : }
378 :
379 : void
380 0 : TextTrieMap::search(const UnicodeString &text, int32_t start,
381 : TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
382 : {
383 : // TODO: if locking the mutex for each check proves to be a performance problem,
384 : // add a flag of type atomic_int32_t to class TextTrieMap, and use only
385 : // the ICU atomic safe functions for assigning and testing.
386 : // Don't test the pointer fLazyContents.
387 : // Don't do unless it's really required.
388 0 : Mutex lock(&TextTrieMutex);
389 0 : if (fLazyContents != NULL) {
390 0 : TextTrieMap *nonConstThis = const_cast<TextTrieMap *>(this);
391 0 : nonConstThis->buildTrie(status);
392 : }
393 : }
394 0 : if (fNodes == NULL) {
395 0 : return;
396 : }
397 0 : search(fNodes, text, start, start, handler, status);
398 : }
399 :
400 : void
401 0 : TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
402 : int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
403 0 : if (U_FAILURE(status)) {
404 0 : return;
405 : }
406 0 : if (node->hasValues()) {
407 0 : if (!handler->handleMatch(index - start, node, status)) {
408 0 : return;
409 : }
410 0 : if (U_FAILURE(status)) {
411 0 : return;
412 : }
413 : }
414 0 : UChar32 c = text.char32At(index);
415 0 : if (fIgnoreCase) {
416 : // size of character may grow after fold operation
417 0 : UnicodeString tmp(c);
418 0 : tmp.foldCase();
419 0 : int32_t tmpidx = 0;
420 0 : while (tmpidx < tmp.length()) {
421 0 : c = tmp.char32At(tmpidx);
422 0 : node = getChildNode(node, c);
423 0 : if (node == NULL) {
424 0 : break;
425 : }
426 0 : tmpidx = tmp.moveIndex32(tmpidx, 1);
427 : }
428 : } else {
429 0 : node = getChildNode(node, c);
430 : }
431 0 : if (node != NULL) {
432 0 : search(node, text, start, index+1, handler, status);
433 : }
434 : }
435 :
436 : // ---------------------------------------------------
437 : // ZNStringPool class implementation
438 : // ---------------------------------------------------
439 : static const int32_t POOL_CHUNK_SIZE = 2000;
440 : struct ZNStringPoolChunk: public UMemory {
441 : ZNStringPoolChunk *fNext; // Ptr to next pool chunk
442 : int32_t fLimit; // Index to start of unused area at end of fStrings
443 : UChar fStrings[POOL_CHUNK_SIZE]; // Strings array
444 : ZNStringPoolChunk();
445 : };
446 :
447 0 : ZNStringPoolChunk::ZNStringPoolChunk() {
448 0 : fNext = NULL;
449 0 : fLimit = 0;
450 0 : }
451 :
452 0 : ZNStringPool::ZNStringPool(UErrorCode &status) {
453 0 : fChunks = NULL;
454 0 : fHash = NULL;
455 0 : if (U_FAILURE(status)) {
456 0 : return;
457 : }
458 0 : fChunks = new ZNStringPoolChunk;
459 0 : if (fChunks == NULL) {
460 0 : status = U_MEMORY_ALLOCATION_ERROR;
461 0 : return;
462 : }
463 :
464 0 : fHash = uhash_open(uhash_hashUChars /* keyHash */,
465 : uhash_compareUChars /* keyComp */,
466 : uhash_compareUChars /* valueComp */,
467 : &status);
468 0 : if (U_FAILURE(status)) {
469 0 : return;
470 : }
471 : }
472 :
473 0 : ZNStringPool::~ZNStringPool() {
474 0 : if (fHash != NULL) {
475 0 : uhash_close(fHash);
476 0 : fHash = NULL;
477 : }
478 :
479 0 : while (fChunks != NULL) {
480 0 : ZNStringPoolChunk *nextChunk = fChunks->fNext;
481 0 : delete fChunks;
482 0 : fChunks = nextChunk;
483 : }
484 0 : }
485 :
486 : static const UChar EmptyString = 0;
487 :
488 0 : const UChar *ZNStringPool::get(const UChar *s, UErrorCode &status) {
489 : const UChar *pooledString;
490 0 : if (U_FAILURE(status)) {
491 0 : return &EmptyString;
492 : }
493 :
494 0 : pooledString = static_cast<UChar *>(uhash_get(fHash, s));
495 0 : if (pooledString != NULL) {
496 0 : return pooledString;
497 : }
498 :
499 0 : int32_t length = u_strlen(s);
500 0 : int32_t remainingLength = POOL_CHUNK_SIZE - fChunks->fLimit;
501 0 : if (remainingLength <= length) {
502 0 : U_ASSERT(length < POOL_CHUNK_SIZE);
503 0 : if (length >= POOL_CHUNK_SIZE) {
504 0 : status = U_INTERNAL_PROGRAM_ERROR;
505 0 : return &EmptyString;
506 : }
507 0 : ZNStringPoolChunk *oldChunk = fChunks;
508 0 : fChunks = new ZNStringPoolChunk;
509 0 : if (fChunks == NULL) {
510 0 : status = U_MEMORY_ALLOCATION_ERROR;
511 0 : return &EmptyString;
512 : }
513 0 : fChunks->fNext = oldChunk;
514 : }
515 :
516 0 : UChar *destString = &fChunks->fStrings[fChunks->fLimit];
517 0 : u_strcpy(destString, s);
518 0 : fChunks->fLimit += (length + 1);
519 0 : uhash_put(fHash, destString, destString, &status);
520 0 : return destString;
521 : }
522 :
523 :
524 : //
525 : // ZNStringPool::adopt() Put a string into the hash, but do not copy the string data
526 : // into the pool's storage. Used for strings from resource bundles,
527 : // which will perisist for the life of the zone string formatter, and
528 : // therefore can be used directly without copying.
529 0 : const UChar *ZNStringPool::adopt(const UChar * s, UErrorCode &status) {
530 : const UChar *pooledString;
531 0 : if (U_FAILURE(status)) {
532 0 : return &EmptyString;
533 : }
534 0 : if (s != NULL) {
535 0 : pooledString = static_cast<UChar *>(uhash_get(fHash, s));
536 0 : if (pooledString == NULL) {
537 0 : UChar *ncs = const_cast<UChar *>(s);
538 0 : uhash_put(fHash, ncs, ncs, &status);
539 : }
540 : }
541 0 : return s;
542 : }
543 :
544 :
545 0 : const UChar *ZNStringPool::get(const UnicodeString &s, UErrorCode &status) {
546 0 : UnicodeString &nonConstStr = const_cast<UnicodeString &>(s);
547 0 : return this->get(nonConstStr.getTerminatedBuffer(), status);
548 : }
549 :
550 : /*
551 : * freeze(). Close the hash table that maps to the pooled strings.
552 : * After freezing, the pool can not be searched or added to,
553 : * but all existing references to pooled strings remain valid.
554 : *
555 : * The main purpose is to recover the storage used for the hash.
556 : */
557 0 : void ZNStringPool::freeze() {
558 0 : uhash_close(fHash);
559 0 : fHash = NULL;
560 0 : }
561 :
562 :
563 : /**
564 : * This class stores name data for a meta zone or time zone.
565 : */
566 : class ZNames : public UMemory {
567 : private:
568 : friend class TimeZoneNamesImpl;
569 :
570 0 : static UTimeZoneNameTypeIndex getTZNameTypeIndex(UTimeZoneNameType type) {
571 0 : switch(type) {
572 0 : case UTZNM_EXEMPLAR_LOCATION: return UTZNM_INDEX_EXEMPLAR_LOCATION;
573 0 : case UTZNM_LONG_GENERIC: return UTZNM_INDEX_LONG_GENERIC;
574 0 : case UTZNM_LONG_STANDARD: return UTZNM_INDEX_LONG_STANDARD;
575 0 : case UTZNM_LONG_DAYLIGHT: return UTZNM_INDEX_LONG_DAYLIGHT;
576 0 : case UTZNM_SHORT_GENERIC: return UTZNM_INDEX_SHORT_GENERIC;
577 0 : case UTZNM_SHORT_STANDARD: return UTZNM_INDEX_SHORT_STANDARD;
578 0 : case UTZNM_SHORT_DAYLIGHT: return UTZNM_INDEX_SHORT_DAYLIGHT;
579 0 : default: return UTZNM_INDEX_UNKNOWN;
580 : }
581 : }
582 0 : static UTimeZoneNameType getTZNameType(UTimeZoneNameTypeIndex index) {
583 0 : switch(index) {
584 0 : case UTZNM_INDEX_EXEMPLAR_LOCATION: return UTZNM_EXEMPLAR_LOCATION;
585 0 : case UTZNM_INDEX_LONG_GENERIC: return UTZNM_LONG_GENERIC;
586 0 : case UTZNM_INDEX_LONG_STANDARD: return UTZNM_LONG_STANDARD;
587 0 : case UTZNM_INDEX_LONG_DAYLIGHT: return UTZNM_LONG_DAYLIGHT;
588 0 : case UTZNM_INDEX_SHORT_GENERIC: return UTZNM_SHORT_GENERIC;
589 0 : case UTZNM_INDEX_SHORT_STANDARD: return UTZNM_SHORT_STANDARD;
590 0 : case UTZNM_INDEX_SHORT_DAYLIGHT: return UTZNM_SHORT_DAYLIGHT;
591 0 : default: return UTZNM_UNKNOWN;
592 : }
593 : }
594 :
595 : const UChar* fNames[UTZNM_INDEX_COUNT];
596 : UBool fDidAddIntoTrie;
597 :
598 : // Whether we own the location string, if computed rather than loaded from a bundle.
599 : // A meta zone names instance never has an exemplar location string.
600 : UBool fOwnsLocationName;
601 :
602 0 : ZNames(const UChar* names[], const UChar* locationName)
603 0 : : fDidAddIntoTrie(FALSE) {
604 0 : uprv_memcpy(fNames, names, sizeof(fNames));
605 0 : if (locationName != NULL) {
606 0 : fOwnsLocationName = TRUE;
607 0 : fNames[UTZNM_INDEX_EXEMPLAR_LOCATION] = locationName;
608 : } else {
609 0 : fOwnsLocationName = FALSE;
610 : }
611 0 : }
612 :
613 : public:
614 0 : ~ZNames() {
615 0 : if (fOwnsLocationName) {
616 0 : const UChar* locationName = fNames[UTZNM_INDEX_EXEMPLAR_LOCATION];
617 0 : U_ASSERT(locationName != NULL);
618 0 : uprv_free((void*) locationName);
619 : }
620 0 : }
621 :
622 : private:
623 0 : static void* createMetaZoneAndPutInCache(UHashtable* cache, const UChar* names[],
624 : const UnicodeString& mzID, UErrorCode& status) {
625 0 : if (U_FAILURE(status)) { return NULL; }
626 0 : U_ASSERT(names != NULL);
627 :
628 : // Use the persistent ID as the resource key, so we can
629 : // avoid duplications.
630 : // TODO: Is there a more efficient way, like intern() in Java?
631 0 : void* key = (void*) ZoneMeta::findMetaZoneID(mzID);
632 : void* value;
633 0 : if (uprv_memcmp(names, EMPTY_NAMES, sizeof(EMPTY_NAMES)) == 0) {
634 0 : value = (void*) EMPTY;
635 : } else {
636 0 : value = (void*) (new ZNames(names, NULL));
637 0 : if (value == NULL) {
638 0 : status = U_MEMORY_ALLOCATION_ERROR;
639 0 : return NULL;
640 : }
641 : }
642 0 : uhash_put(cache, key, value, &status);
643 0 : return value;
644 : }
645 :
646 0 : static void* createTimeZoneAndPutInCache(UHashtable* cache, const UChar* names[],
647 : const UnicodeString& tzID, UErrorCode& status) {
648 0 : if (U_FAILURE(status)) { return NULL; }
649 0 : U_ASSERT(names != NULL);
650 :
651 : // If necessary, compute the location name from the time zone name.
652 0 : UChar* locationName = NULL;
653 0 : if (names[UTZNM_INDEX_EXEMPLAR_LOCATION] == NULL) {
654 0 : UnicodeString locationNameUniStr;
655 0 : TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, locationNameUniStr);
656 :
657 : // Copy the computed location name to the heap
658 0 : if (locationNameUniStr.length() > 0) {
659 0 : const UChar* buff = locationNameUniStr.getTerminatedBuffer();
660 0 : int32_t len = sizeof(UChar) * (locationNameUniStr.length() + 1);
661 0 : locationName = (UChar*) uprv_malloc(len);
662 0 : if (locationName == NULL) {
663 0 : status = U_MEMORY_ALLOCATION_ERROR;
664 0 : return NULL;
665 : }
666 0 : uprv_memcpy(locationName, buff, len);
667 : }
668 : }
669 :
670 : // Use the persistent ID as the resource key, so we can
671 : // avoid duplications.
672 : // TODO: Is there a more efficient way, like intern() in Java?
673 0 : void* key = (void*) ZoneMeta::findTimeZoneID(tzID);
674 0 : void* value = (void*) (new ZNames(names, locationName));
675 0 : if (value == NULL) {
676 0 : status = U_MEMORY_ALLOCATION_ERROR;
677 0 : return NULL;
678 : }
679 0 : uhash_put(cache, key, value, &status);
680 0 : return value;
681 : }
682 :
683 0 : const UChar* getName(UTimeZoneNameType type) const {
684 0 : UTimeZoneNameTypeIndex index = getTZNameTypeIndex(type);
685 0 : return index >= 0 ? fNames[index] : NULL;
686 : }
687 :
688 0 : void addAsMetaZoneIntoTrie(const UChar* mzID, TextTrieMap& trie, UErrorCode& status) {
689 0 : addNamesIntoTrie(mzID, NULL, trie, status);
690 0 : }
691 0 : void addAsTimeZoneIntoTrie(const UChar* tzID, TextTrieMap& trie, UErrorCode& status) {
692 0 : addNamesIntoTrie(NULL, tzID, trie, status);
693 0 : }
694 :
695 0 : void addNamesIntoTrie(const UChar* mzID, const UChar* tzID, TextTrieMap& trie,
696 : UErrorCode& status) {
697 0 : if (U_FAILURE(status)) { return; }
698 0 : if (fDidAddIntoTrie) { return; }
699 0 : fDidAddIntoTrie = TRUE;
700 :
701 0 : for (int32_t i = 0; i < UTZNM_INDEX_COUNT; i++) {
702 0 : const UChar* name = fNames[i];
703 0 : if (name != NULL) {
704 0 : ZNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(ZNameInfo));
705 0 : if (nameinfo == NULL) {
706 0 : status = U_MEMORY_ALLOCATION_ERROR;
707 0 : return;
708 : }
709 0 : nameinfo->mzID = mzID;
710 0 : nameinfo->tzID = tzID;
711 0 : nameinfo->type = getTZNameType((UTimeZoneNameTypeIndex)i);
712 0 : trie.put(name, nameinfo, status); // trie.put() takes ownership of the key
713 0 : if (U_FAILURE(status)) {
714 0 : return;
715 : }
716 : }
717 : }
718 : }
719 :
720 : public:
721 : struct ZNamesLoader;
722 : };
723 :
724 : struct ZNames::ZNamesLoader : public ResourceSink {
725 : const UChar *names[UTZNM_INDEX_COUNT];
726 :
727 0 : ZNamesLoader() {
728 0 : clear();
729 0 : }
730 : virtual ~ZNamesLoader();
731 :
732 : /** Reset for loading another set of names. */
733 0 : void clear() {
734 0 : uprv_memcpy(names, EMPTY_NAMES, sizeof(names));
735 0 : }
736 :
737 0 : void loadMetaZone(const UResourceBundle* zoneStrings, const UnicodeString& mzID, UErrorCode& errorCode) {
738 0 : if (U_FAILURE(errorCode)) { return; }
739 :
740 : char key[ZID_KEY_MAX + 1];
741 0 : mergeTimeZoneKey(mzID, key);
742 :
743 0 : loadNames(zoneStrings, key, errorCode);
744 : }
745 :
746 0 : void loadTimeZone(const UResourceBundle* zoneStrings, const UnicodeString& tzID, UErrorCode& errorCode) {
747 : // Replace "/" with ":".
748 0 : UnicodeString uKey(tzID);
749 0 : for (int32_t i = 0; i < uKey.length(); i++) {
750 0 : if (uKey.charAt(i) == (UChar)0x2F) {
751 0 : uKey.setCharAt(i, (UChar)0x3A);
752 : }
753 : }
754 :
755 : char key[ZID_KEY_MAX + 1];
756 0 : uKey.extract(0, uKey.length(), key, sizeof(key), US_INV);
757 :
758 0 : loadNames(zoneStrings, key, errorCode);
759 0 : }
760 :
761 0 : void loadNames(const UResourceBundle* zoneStrings, const char* key, UErrorCode& errorCode) {
762 0 : U_ASSERT(zoneStrings != NULL);
763 0 : U_ASSERT(key != NULL);
764 0 : U_ASSERT(key[0] != '\0');
765 :
766 0 : UErrorCode localStatus = U_ZERO_ERROR;
767 0 : clear();
768 0 : ures_getAllItemsWithFallback(zoneStrings, key, *this, localStatus);
769 :
770 : // Ignore errors, but propogate possible warnings.
771 0 : if (U_SUCCESS(localStatus)) {
772 0 : errorCode = localStatus;
773 : }
774 0 : }
775 :
776 0 : void setNameIfEmpty(const char* key, const ResourceValue* value, UErrorCode& errorCode) {
777 0 : UTimeZoneNameTypeIndex type = nameTypeFromKey(key);
778 0 : if (type == UTZNM_INDEX_UNKNOWN) { return; }
779 0 : if (names[type] == NULL) {
780 : int32_t length;
781 : // 'NO_NAME' indicates internally that this field should remain empty. It will be
782 : // replaced by 'NULL' in getNames()
783 0 : names[type] = (value == NULL) ? NO_NAME : value->getString(length, errorCode);
784 : }
785 : }
786 :
787 0 : virtual void put(const char* key, ResourceValue& value, UBool /*noFallback*/,
788 : UErrorCode &errorCode) {
789 0 : ResourceTable namesTable = value.getTable(errorCode);
790 0 : if (U_FAILURE(errorCode)) { return; }
791 0 : for (int32_t i = 0; namesTable.getKeyAndValue(i, key, value); ++i) {
792 0 : if (value.isNoInheritanceMarker()) {
793 0 : setNameIfEmpty(key, NULL, errorCode);
794 : } else {
795 0 : setNameIfEmpty(key, &value, errorCode);
796 : }
797 : }
798 : }
799 :
800 0 : static UTimeZoneNameTypeIndex nameTypeFromKey(const char *key) {
801 : char c0, c1;
802 0 : if ((c0 = key[0]) == 0 || (c1 = key[1]) == 0 || key[2] != 0) {
803 0 : return UTZNM_INDEX_UNKNOWN;
804 : }
805 0 : if (c0 == 'l') {
806 0 : return c1 == 'g' ? UTZNM_INDEX_LONG_GENERIC :
807 0 : c1 == 's' ? UTZNM_INDEX_LONG_STANDARD :
808 0 : c1 == 'd' ? UTZNM_INDEX_LONG_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
809 0 : } else if (c0 == 's') {
810 0 : return c1 == 'g' ? UTZNM_INDEX_SHORT_GENERIC :
811 0 : c1 == 's' ? UTZNM_INDEX_SHORT_STANDARD :
812 0 : c1 == 'd' ? UTZNM_INDEX_SHORT_DAYLIGHT : UTZNM_INDEX_UNKNOWN;
813 0 : } else if (c0 == 'e' && c1 == 'c') {
814 0 : return UTZNM_INDEX_EXEMPLAR_LOCATION;
815 : }
816 0 : return UTZNM_INDEX_UNKNOWN;
817 : }
818 :
819 : /**
820 : * Returns an array of names. It is the caller's responsibility to copy the data into a
821 : * permanent location, as the returned array is owned by the loader instance and may be
822 : * cleared or leave scope.
823 : *
824 : * This is different than Java, where the array will no longer be modified and null
825 : * may be returned.
826 : */
827 0 : const UChar** getNames() {
828 : // Remove 'NO_NAME' references in the array and replace with 'NULL'
829 0 : for (int32_t i = 0; i < UTZNM_INDEX_COUNT; ++i) {
830 0 : if (names[i] == NO_NAME) {
831 0 : names[i] = NULL;
832 : }
833 : }
834 0 : return names;
835 : }
836 : };
837 :
838 0 : ZNames::ZNamesLoader::~ZNamesLoader() {}
839 :
840 :
841 : // ---------------------------------------------------
842 : // The meta zone ID enumeration class
843 : // ---------------------------------------------------
844 : class MetaZoneIDsEnumeration : public StringEnumeration {
845 : public:
846 : MetaZoneIDsEnumeration();
847 : MetaZoneIDsEnumeration(const UVector& mzIDs);
848 : MetaZoneIDsEnumeration(UVector* mzIDs);
849 : virtual ~MetaZoneIDsEnumeration();
850 : static UClassID U_EXPORT2 getStaticClassID(void);
851 : virtual UClassID getDynamicClassID(void) const;
852 : virtual const UnicodeString* snext(UErrorCode& status);
853 : virtual void reset(UErrorCode& status);
854 : virtual int32_t count(UErrorCode& status) const;
855 : private:
856 : int32_t fLen;
857 : int32_t fPos;
858 : const UVector* fMetaZoneIDs;
859 : UVector *fLocalVector;
860 : };
861 :
862 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MetaZoneIDsEnumeration)
863 :
864 0 : MetaZoneIDsEnumeration::MetaZoneIDsEnumeration()
865 0 : : fLen(0), fPos(0), fMetaZoneIDs(NULL), fLocalVector(NULL) {
866 0 : }
867 :
868 0 : MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(const UVector& mzIDs)
869 0 : : fPos(0), fMetaZoneIDs(&mzIDs), fLocalVector(NULL) {
870 0 : fLen = fMetaZoneIDs->size();
871 0 : }
872 :
873 0 : MetaZoneIDsEnumeration::MetaZoneIDsEnumeration(UVector *mzIDs)
874 0 : : fLen(0), fPos(0), fMetaZoneIDs(mzIDs), fLocalVector(mzIDs) {
875 0 : if (fMetaZoneIDs) {
876 0 : fLen = fMetaZoneIDs->size();
877 : }
878 0 : }
879 :
880 : const UnicodeString*
881 0 : MetaZoneIDsEnumeration::snext(UErrorCode& status) {
882 0 : if (U_SUCCESS(status) && fMetaZoneIDs != NULL && fPos < fLen) {
883 0 : unistr.setTo((const UChar*)fMetaZoneIDs->elementAt(fPos++), -1);
884 0 : return &unistr;
885 : }
886 0 : return NULL;
887 : }
888 :
889 : void
890 0 : MetaZoneIDsEnumeration::reset(UErrorCode& /*status*/) {
891 0 : fPos = 0;
892 0 : }
893 :
894 : int32_t
895 0 : MetaZoneIDsEnumeration::count(UErrorCode& /*status*/) const {
896 0 : return fLen;
897 : }
898 :
899 0 : MetaZoneIDsEnumeration::~MetaZoneIDsEnumeration() {
900 0 : if (fLocalVector) {
901 0 : delete fLocalVector;
902 : }
903 0 : }
904 :
905 :
906 : // ---------------------------------------------------
907 : // ZNameSearchHandler
908 : // ---------------------------------------------------
909 : class ZNameSearchHandler : public TextTrieMapSearchResultHandler {
910 : public:
911 : ZNameSearchHandler(uint32_t types);
912 : virtual ~ZNameSearchHandler();
913 :
914 : UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
915 : TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
916 :
917 : private:
918 : uint32_t fTypes;
919 : int32_t fMaxMatchLen;
920 : TimeZoneNames::MatchInfoCollection* fResults;
921 : };
922 :
923 0 : ZNameSearchHandler::ZNameSearchHandler(uint32_t types)
924 0 : : fTypes(types), fMaxMatchLen(0), fResults(NULL) {
925 0 : }
926 :
927 0 : ZNameSearchHandler::~ZNameSearchHandler() {
928 0 : if (fResults != NULL) {
929 0 : delete fResults;
930 : }
931 0 : }
932 :
933 : UBool
934 0 : ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
935 0 : if (U_FAILURE(status)) {
936 0 : return FALSE;
937 : }
938 0 : if (node->hasValues()) {
939 0 : int32_t valuesCount = node->countValues();
940 0 : for (int32_t i = 0; i < valuesCount; i++) {
941 0 : ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
942 0 : if (nameinfo == NULL) {
943 0 : continue;
944 : }
945 0 : if ((nameinfo->type & fTypes) != 0) {
946 : // matches a requested type
947 0 : if (fResults == NULL) {
948 0 : fResults = new TimeZoneNames::MatchInfoCollection();
949 0 : if (fResults == NULL) {
950 0 : status = U_MEMORY_ALLOCATION_ERROR;
951 : }
952 : }
953 0 : if (U_SUCCESS(status)) {
954 0 : U_ASSERT(fResults != NULL);
955 0 : if (nameinfo->tzID) {
956 0 : fResults->addZone(nameinfo->type, matchLength, UnicodeString(nameinfo->tzID, -1), status);
957 : } else {
958 0 : U_ASSERT(nameinfo->mzID);
959 0 : fResults->addMetaZone(nameinfo->type, matchLength, UnicodeString(nameinfo->mzID, -1), status);
960 : }
961 0 : if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
962 0 : fMaxMatchLen = matchLength;
963 : }
964 : }
965 : }
966 : }
967 : }
968 0 : return TRUE;
969 : }
970 :
971 : TimeZoneNames::MatchInfoCollection*
972 0 : ZNameSearchHandler::getMatches(int32_t& maxMatchLen) {
973 : // give the ownership to the caller
974 0 : TimeZoneNames::MatchInfoCollection* results = fResults;
975 0 : maxMatchLen = fMaxMatchLen;
976 :
977 : // reset
978 0 : fResults = NULL;
979 0 : fMaxMatchLen = 0;
980 0 : return results;
981 : }
982 :
983 : // ---------------------------------------------------
984 : // TimeZoneNamesImpl
985 : //
986 : // TimeZoneNames implementation class. This is the main
987 : // part of this module.
988 : // ---------------------------------------------------
989 :
990 : U_CDECL_BEGIN
991 : /**
992 : * Deleter for ZNames
993 : */
994 : static void U_CALLCONV
995 0 : deleteZNames(void *obj) {
996 0 : if (obj != EMPTY) {
997 0 : delete (ZNames*) obj;
998 : }
999 0 : }
1000 :
1001 : /**
1002 : * Deleter for ZNameInfo
1003 : */
1004 : static void U_CALLCONV
1005 0 : deleteZNameInfo(void *obj) {
1006 0 : uprv_free(obj);
1007 0 : }
1008 :
1009 : U_CDECL_END
1010 :
1011 0 : TimeZoneNamesImpl::TimeZoneNamesImpl(const Locale& locale, UErrorCode& status)
1012 : : fLocale(locale),
1013 : fZoneStrings(NULL),
1014 : fTZNamesMap(NULL),
1015 : fMZNamesMap(NULL),
1016 : fNamesTrieFullyLoaded(FALSE),
1017 : fNamesFullyLoaded(FALSE),
1018 0 : fNamesTrie(TRUE, deleteZNameInfo) {
1019 0 : initialize(locale, status);
1020 0 : }
1021 :
1022 : void
1023 0 : TimeZoneNamesImpl::initialize(const Locale& locale, UErrorCode& status) {
1024 0 : if (U_FAILURE(status)) {
1025 0 : return;
1026 : }
1027 :
1028 : // Load zoneStrings bundle
1029 0 : UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning..
1030 0 : fZoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
1031 0 : fZoneStrings = ures_getByKeyWithFallback(fZoneStrings, gZoneStrings, fZoneStrings, &tmpsts);
1032 0 : if (U_FAILURE(tmpsts)) {
1033 0 : status = tmpsts;
1034 0 : cleanup();
1035 0 : return;
1036 : }
1037 :
1038 : // Initialize hashtables holding time zone/meta zone names
1039 0 : fMZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
1040 0 : fTZNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
1041 0 : if (U_FAILURE(status)) {
1042 0 : cleanup();
1043 0 : return;
1044 : }
1045 :
1046 0 : uhash_setValueDeleter(fMZNamesMap, deleteZNames);
1047 0 : uhash_setValueDeleter(fTZNamesMap, deleteZNames);
1048 : // no key deleters for name maps
1049 :
1050 : // preload zone strings for the default zone
1051 0 : TimeZone *tz = TimeZone::createDefault();
1052 0 : const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
1053 0 : if (tzID != NULL) {
1054 0 : loadStrings(UnicodeString(tzID), status);
1055 : }
1056 0 : delete tz;
1057 :
1058 0 : return;
1059 : }
1060 :
1061 : /*
1062 : * This method updates the cache and must be called with a lock,
1063 : * except initializer.
1064 : */
1065 : void
1066 0 : TimeZoneNamesImpl::loadStrings(const UnicodeString& tzCanonicalID, UErrorCode& status) {
1067 0 : loadTimeZoneNames(tzCanonicalID, status);
1068 0 : LocalPointer<StringEnumeration> mzIDs(getAvailableMetaZoneIDs(tzCanonicalID, status));
1069 0 : if (U_FAILURE(status)) { return; }
1070 0 : U_ASSERT(!mzIDs.isNull());
1071 :
1072 : const UnicodeString *mzID;
1073 0 : while ((mzID = mzIDs->snext(status)) && U_SUCCESS(status)) {
1074 0 : loadMetaZoneNames(*mzID, status);
1075 : }
1076 : }
1077 :
1078 0 : TimeZoneNamesImpl::~TimeZoneNamesImpl() {
1079 0 : cleanup();
1080 0 : }
1081 :
1082 : void
1083 0 : TimeZoneNamesImpl::cleanup() {
1084 0 : if (fZoneStrings != NULL) {
1085 0 : ures_close(fZoneStrings);
1086 0 : fZoneStrings = NULL;
1087 : }
1088 0 : if (fMZNamesMap != NULL) {
1089 0 : uhash_close(fMZNamesMap);
1090 0 : fMZNamesMap = NULL;
1091 : }
1092 0 : if (fTZNamesMap != NULL) {
1093 0 : uhash_close(fTZNamesMap);
1094 0 : fTZNamesMap = NULL;
1095 : }
1096 0 : }
1097 :
1098 : UBool
1099 0 : TimeZoneNamesImpl::operator==(const TimeZoneNames& other) const {
1100 0 : if (this == &other) {
1101 0 : return TRUE;
1102 : }
1103 : // No implementation for now
1104 0 : return FALSE;
1105 : }
1106 :
1107 : TimeZoneNames*
1108 0 : TimeZoneNamesImpl::clone() const {
1109 0 : UErrorCode status = U_ZERO_ERROR;
1110 0 : return new TimeZoneNamesImpl(fLocale, status);
1111 : }
1112 :
1113 : StringEnumeration*
1114 0 : TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const {
1115 0 : return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
1116 : }
1117 :
1118 : // static implementation of getAvailableMetaZoneIDs(UErrorCode&)
1119 : StringEnumeration*
1120 0 : TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode& status) {
1121 0 : if (U_FAILURE(status)) {
1122 0 : return NULL;
1123 : }
1124 0 : const UVector* mzIDs = ZoneMeta::getAvailableMetazoneIDs();
1125 0 : if (mzIDs == NULL) {
1126 0 : return new MetaZoneIDsEnumeration();
1127 : }
1128 0 : return new MetaZoneIDsEnumeration(*mzIDs);
1129 : }
1130 :
1131 : StringEnumeration*
1132 0 : TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
1133 0 : return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
1134 : }
1135 :
1136 : // static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&)
1137 : StringEnumeration*
1138 0 : TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) {
1139 0 : if (U_FAILURE(status)) {
1140 0 : return NULL;
1141 : }
1142 0 : const UVector* mappings = ZoneMeta::getMetazoneMappings(tzID);
1143 0 : if (mappings == NULL) {
1144 0 : return new MetaZoneIDsEnumeration();
1145 : }
1146 :
1147 0 : MetaZoneIDsEnumeration *senum = NULL;
1148 0 : UVector* mzIDs = new UVector(NULL, uhash_compareUChars, status);
1149 0 : if (mzIDs == NULL) {
1150 0 : status = U_MEMORY_ALLOCATION_ERROR;
1151 : }
1152 0 : if (U_SUCCESS(status)) {
1153 0 : U_ASSERT(mzIDs != NULL);
1154 0 : for (int32_t i = 0; U_SUCCESS(status) && i < mappings->size(); i++) {
1155 :
1156 0 : OlsonToMetaMappingEntry *map = (OlsonToMetaMappingEntry *)mappings->elementAt(i);
1157 0 : const UChar *mzID = map->mzid;
1158 0 : if (!mzIDs->contains((void *)mzID)) {
1159 0 : mzIDs->addElement((void *)mzID, status);
1160 : }
1161 : }
1162 0 : if (U_SUCCESS(status)) {
1163 0 : senum = new MetaZoneIDsEnumeration(mzIDs);
1164 : } else {
1165 0 : delete mzIDs;
1166 : }
1167 : }
1168 0 : return senum;
1169 : }
1170 :
1171 : UnicodeString&
1172 0 : TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
1173 0 : return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
1174 : }
1175 :
1176 : // static implementation of getMetaZoneID
1177 : UnicodeString&
1178 0 : TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) {
1179 0 : ZoneMeta::getMetazoneID(tzID, date, mzID);
1180 0 : return mzID;
1181 : }
1182 :
1183 : UnicodeString&
1184 0 : TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
1185 0 : return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
1186 : }
1187 :
1188 : // static implementaion of getReferenceZoneID
1189 : UnicodeString&
1190 0 : TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) {
1191 0 : ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID);
1192 0 : return tzID;
1193 : }
1194 :
1195 : UnicodeString&
1196 0 : TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID,
1197 : UTimeZoneNameType type,
1198 : UnicodeString& name) const {
1199 0 : name.setToBogus(); // cleanup result.
1200 0 : if (mzID.isEmpty()) {
1201 0 : return name;
1202 : }
1203 :
1204 0 : ZNames *znames = NULL;
1205 0 : TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1206 :
1207 : {
1208 0 : Mutex lock(&gDataMutex);
1209 0 : UErrorCode status = U_ZERO_ERROR;
1210 0 : znames = nonConstThis->loadMetaZoneNames(mzID, status);
1211 0 : if (U_FAILURE(status)) { return name; }
1212 : }
1213 :
1214 0 : if (znames != NULL) {
1215 0 : const UChar* s = znames->getName(type);
1216 0 : if (s != NULL) {
1217 0 : name.setTo(TRUE, s, -1);
1218 : }
1219 : }
1220 0 : return name;
1221 : }
1222 :
1223 : UnicodeString&
1224 0 : TimeZoneNamesImpl::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
1225 0 : name.setToBogus(); // cleanup result.
1226 0 : if (tzID.isEmpty()) {
1227 0 : return name;
1228 : }
1229 :
1230 0 : ZNames *tznames = NULL;
1231 0 : TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1232 :
1233 : {
1234 0 : Mutex lock(&gDataMutex);
1235 0 : UErrorCode status = U_ZERO_ERROR;
1236 0 : tznames = nonConstThis->loadTimeZoneNames(tzID, status);
1237 0 : if (U_FAILURE(status)) { return name; }
1238 : }
1239 :
1240 0 : if (tznames != NULL) {
1241 0 : const UChar *s = tznames->getName(type);
1242 0 : if (s != NULL) {
1243 0 : name.setTo(TRUE, s, -1);
1244 : }
1245 : }
1246 0 : return name;
1247 : }
1248 :
1249 : UnicodeString&
1250 0 : TimeZoneNamesImpl::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
1251 0 : name.setToBogus(); // cleanup result.
1252 0 : const UChar* locName = NULL;
1253 0 : ZNames *tznames = NULL;
1254 0 : TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl *>(this);
1255 :
1256 : {
1257 0 : Mutex lock(&gDataMutex);
1258 0 : UErrorCode status = U_ZERO_ERROR;
1259 0 : tznames = nonConstThis->loadTimeZoneNames(tzID, status);
1260 0 : if (U_FAILURE(status)) { return name; }
1261 : }
1262 :
1263 0 : if (tznames != NULL) {
1264 0 : locName = tznames->getName(UTZNM_EXEMPLAR_LOCATION);
1265 : }
1266 0 : if (locName != NULL) {
1267 0 : name.setTo(TRUE, locName, -1);
1268 : }
1269 :
1270 0 : return name;
1271 : }
1272 :
1273 :
1274 : // Merge the MZ_PREFIX and mzId
1275 0 : static void mergeTimeZoneKey(const UnicodeString& mzID, char* result) {
1276 0 : if (mzID.isEmpty()) {
1277 0 : result[0] = '\0';
1278 0 : return;
1279 : }
1280 :
1281 : char mzIdChar[ZID_KEY_MAX + 1];
1282 : int32_t keyLen;
1283 0 : int32_t prefixLen = uprv_strlen(gMZPrefix);
1284 0 : keyLen = mzID.extract(0, mzID.length(), mzIdChar, ZID_KEY_MAX + 1, US_INV);
1285 0 : uprv_memcpy((void *)result, (void *)gMZPrefix, prefixLen);
1286 0 : uprv_memcpy((void *)(result + prefixLen), (void *)mzIdChar, keyLen);
1287 0 : result[keyLen + prefixLen] = '\0';
1288 : }
1289 :
1290 : /*
1291 : * This method updates the cache and must be called with a lock
1292 : */
1293 : ZNames*
1294 0 : TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
1295 0 : if (U_FAILURE(status)) { return NULL; }
1296 0 : U_ASSERT(mzID.length() <= ZID_KEY_MAX - MZ_PREFIX_LEN);
1297 :
1298 : UChar mzIDKey[ZID_KEY_MAX + 1];
1299 0 : mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
1300 0 : U_ASSERT(U_SUCCESS(status)); // already checked length above
1301 0 : mzIDKey[mzID.length()] = 0;
1302 :
1303 0 : void* mznames = uhash_get(fMZNamesMap, mzIDKey);
1304 0 : if (mznames == NULL) {
1305 0 : ZNames::ZNamesLoader loader;
1306 0 : loader.loadMetaZone(fZoneStrings, mzID, status);
1307 0 : mznames = ZNames::createMetaZoneAndPutInCache(fMZNamesMap, loader.getNames(), mzID, status);
1308 0 : if (U_FAILURE(status)) { return NULL; }
1309 : }
1310 :
1311 0 : if (mznames != EMPTY) {
1312 0 : return (ZNames*)mznames;
1313 : } else {
1314 0 : return NULL;
1315 : }
1316 : }
1317 :
1318 : /*
1319 : * This method updates the cache and must be called with a lock
1320 : */
1321 : ZNames*
1322 0 : TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID, UErrorCode& status) {
1323 0 : if (U_FAILURE(status)) { return NULL; }
1324 0 : U_ASSERT(tzID.length() <= ZID_KEY_MAX);
1325 :
1326 : UChar tzIDKey[ZID_KEY_MAX + 1];
1327 0 : int32_t tzIDKeyLen = tzID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
1328 0 : U_ASSERT(U_SUCCESS(status)); // already checked length above
1329 0 : tzIDKey[tzIDKeyLen] = 0;
1330 :
1331 0 : void *tznames = uhash_get(fTZNamesMap, tzIDKey);
1332 0 : if (tznames == NULL) {
1333 0 : ZNames::ZNamesLoader loader;
1334 0 : loader.loadTimeZone(fZoneStrings, tzID, status);
1335 0 : tznames = ZNames::createTimeZoneAndPutInCache(fTZNamesMap, loader.getNames(), tzID, status);
1336 0 : if (U_FAILURE(status)) { return NULL; }
1337 : }
1338 :
1339 : // tznames is never EMPTY
1340 0 : return (ZNames*)tznames;
1341 : }
1342 :
1343 : TimeZoneNames::MatchInfoCollection*
1344 0 : TimeZoneNamesImpl::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
1345 0 : ZNameSearchHandler handler(types);
1346 : TimeZoneNames::MatchInfoCollection* matches;
1347 0 : TimeZoneNamesImpl* nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
1348 :
1349 : // Synchronize so that data is not loaded multiple times.
1350 : // TODO: Consider more fine-grained synchronization.
1351 : {
1352 0 : Mutex lock(&gDataMutex);
1353 :
1354 : // First try of lookup.
1355 0 : matches = doFind(handler, text, start, status);
1356 0 : if (U_FAILURE(status)) { return NULL; }
1357 0 : if (matches != NULL) {
1358 0 : return matches;
1359 : }
1360 :
1361 : // All names are not yet loaded into the trie.
1362 : // We may have loaded names for formatting several time zones,
1363 : // and might be parsing one of those.
1364 : // Populate the parsing trie from all of the already-loaded names.
1365 0 : nonConstThis->addAllNamesIntoTrie(status);
1366 :
1367 : // Second try of lookup.
1368 0 : matches = doFind(handler, text, start, status);
1369 0 : if (U_FAILURE(status)) { return NULL; }
1370 0 : if (matches != NULL) {
1371 0 : return matches;
1372 : }
1373 :
1374 : // There are still some names we haven't loaded into the trie yet.
1375 : // Load everything now.
1376 0 : nonConstThis->internalLoadAllDisplayNames(status);
1377 0 : nonConstThis->addAllNamesIntoTrie(status);
1378 0 : nonConstThis->fNamesTrieFullyLoaded = TRUE;
1379 0 : if (U_FAILURE(status)) { return NULL; }
1380 :
1381 : // Third try: we must return this one.
1382 0 : return doFind(handler, text, start, status);
1383 : }
1384 : }
1385 :
1386 : TimeZoneNames::MatchInfoCollection*
1387 0 : TimeZoneNamesImpl::doFind(ZNameSearchHandler& handler,
1388 : const UnicodeString& text, int32_t start, UErrorCode& status) const {
1389 :
1390 0 : fNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1391 0 : if (U_FAILURE(status)) { return NULL; }
1392 :
1393 0 : int32_t maxLen = 0;
1394 0 : TimeZoneNames::MatchInfoCollection* matches = handler.getMatches(maxLen);
1395 0 : if (matches != NULL && ((maxLen == (text.length() - start)) || fNamesTrieFullyLoaded)) {
1396 : // perfect match, or no more names available
1397 0 : return matches;
1398 : }
1399 0 : delete matches;
1400 0 : return NULL;
1401 : }
1402 :
1403 : // Caller must synchronize.
1404 0 : void TimeZoneNamesImpl::addAllNamesIntoTrie(UErrorCode& status) {
1405 0 : if (U_FAILURE(status)) return;
1406 : int32_t pos;
1407 : const UHashElement* element;
1408 :
1409 0 : pos = UHASH_FIRST;
1410 0 : while ((element = uhash_nextElement(fMZNamesMap, &pos)) != NULL) {
1411 0 : if (element->value.pointer == EMPTY) { continue; }
1412 0 : UChar* mzID = (UChar*) element->key.pointer;
1413 0 : ZNames* znames = (ZNames*) element->value.pointer;
1414 0 : znames->addAsMetaZoneIntoTrie(mzID, fNamesTrie, status);
1415 0 : if (U_FAILURE(status)) { return; }
1416 : }
1417 :
1418 0 : pos = UHASH_FIRST;
1419 0 : while ((element = uhash_nextElement(fTZNamesMap, &pos)) != NULL) {
1420 0 : if (element->value.pointer == EMPTY) { continue; }
1421 0 : UChar* tzID = (UChar*) element->key.pointer;
1422 0 : ZNames* znames = (ZNames*) element->value.pointer;
1423 0 : znames->addAsTimeZoneIntoTrie(tzID, fNamesTrie, status);
1424 0 : if (U_FAILURE(status)) { return; }
1425 : }
1426 : }
1427 :
1428 : U_CDECL_BEGIN
1429 : static void U_CALLCONV
1430 0 : deleteZNamesLoader(void* obj) {
1431 0 : if (obj == DUMMY_LOADER) { return; }
1432 0 : const ZNames::ZNamesLoader* loader = (const ZNames::ZNamesLoader*) obj;
1433 0 : delete loader;
1434 : }
1435 : U_CDECL_END
1436 :
1437 : struct TimeZoneNamesImpl::ZoneStringsLoader : public ResourceSink {
1438 : TimeZoneNamesImpl& tzn;
1439 : UHashtable* keyToLoader;
1440 :
1441 0 : ZoneStringsLoader(TimeZoneNamesImpl& _tzn, UErrorCode& status)
1442 0 : : tzn(_tzn) {
1443 0 : keyToLoader = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
1444 0 : if (U_FAILURE(status)) { return; }
1445 0 : uhash_setKeyDeleter(keyToLoader, uprv_free);
1446 0 : uhash_setValueDeleter(keyToLoader, deleteZNamesLoader);
1447 : }
1448 : virtual ~ZoneStringsLoader();
1449 :
1450 0 : void* createKey(const char* key, UErrorCode& status) {
1451 0 : int32_t len = sizeof(char) * (uprv_strlen(key) + 1);
1452 0 : char* newKey = (char*) uprv_malloc(len);
1453 0 : if (newKey == NULL) {
1454 0 : status = U_MEMORY_ALLOCATION_ERROR;
1455 0 : return NULL;
1456 : }
1457 0 : uprv_memcpy(newKey, key, len);
1458 0 : newKey[len-1] = '\0';
1459 0 : return (void*) newKey;
1460 : }
1461 :
1462 0 : UBool isMetaZone(const char* key) {
1463 0 : return (uprv_strlen(key) >= MZ_PREFIX_LEN && uprv_memcmp(key, gMZPrefix, MZ_PREFIX_LEN) == 0);
1464 : }
1465 :
1466 0 : UnicodeString mzIDFromKey(const char* key) {
1467 0 : return UnicodeString(key + MZ_PREFIX_LEN, uprv_strlen(key) - MZ_PREFIX_LEN, US_INV);
1468 : }
1469 :
1470 0 : UnicodeString tzIDFromKey(const char* key) {
1471 0 : UnicodeString tzID(key, -1, US_INV);
1472 : // Replace all colons ':' with slashes '/'
1473 0 : for (int i=0; i<tzID.length(); i++) {
1474 0 : if (tzID.charAt(i) == 0x003A) {
1475 0 : tzID.setCharAt(i, 0x002F);
1476 : }
1477 : }
1478 0 : return tzID;
1479 : }
1480 :
1481 0 : void load(UErrorCode& status) {
1482 0 : ures_getAllItemsWithFallback(tzn.fZoneStrings, "", *this, status);
1483 0 : if (U_FAILURE(status)) { return; }
1484 :
1485 0 : int32_t pos = UHASH_FIRST;
1486 : const UHashElement* element;
1487 0 : while ((element = uhash_nextElement(keyToLoader, &pos)) != NULL) {
1488 0 : if (element->value.pointer == DUMMY_LOADER) { continue; }
1489 0 : ZNames::ZNamesLoader* loader = (ZNames::ZNamesLoader*) element->value.pointer;
1490 0 : char* key = (char*) element->key.pointer;
1491 :
1492 0 : if (isMetaZone(key)) {
1493 0 : UnicodeString mzID = mzIDFromKey(key);
1494 0 : ZNames::createMetaZoneAndPutInCache(tzn.fMZNamesMap, loader->getNames(), mzID, status);
1495 : } else {
1496 0 : UnicodeString tzID = tzIDFromKey(key);
1497 0 : ZNames::createTimeZoneAndPutInCache(tzn.fTZNamesMap, loader->getNames(), tzID, status);
1498 : }
1499 0 : if (U_FAILURE(status)) { return; }
1500 : }
1501 : }
1502 :
1503 0 : void consumeNamesTable(const char *key, ResourceValue &value, UBool noFallback,
1504 : UErrorCode &status) {
1505 0 : if (U_FAILURE(status)) { return; }
1506 :
1507 0 : void* loader = uhash_get(keyToLoader, key);
1508 0 : if (loader == NULL) {
1509 0 : if (isMetaZone(key)) {
1510 0 : UnicodeString mzID = mzIDFromKey(key);
1511 0 : void* cacheVal = uhash_get(tzn.fMZNamesMap, mzID.getTerminatedBuffer());
1512 0 : if (cacheVal != NULL) {
1513 : // We have already loaded the names for this meta zone.
1514 0 : loader = (void*) DUMMY_LOADER;
1515 : } else {
1516 0 : loader = (void*) new ZNames::ZNamesLoader();
1517 0 : if (loader == NULL) {
1518 0 : status = U_MEMORY_ALLOCATION_ERROR;
1519 0 : return;
1520 : }
1521 : }
1522 : } else {
1523 0 : UnicodeString tzID = tzIDFromKey(key);
1524 0 : void* cacheVal = uhash_get(tzn.fTZNamesMap, tzID.getTerminatedBuffer());
1525 0 : if (cacheVal != NULL) {
1526 : // We have already loaded the names for this time zone.
1527 0 : loader = (void*) DUMMY_LOADER;
1528 : } else {
1529 0 : loader = (void*) new ZNames::ZNamesLoader();
1530 0 : if (loader == NULL) {
1531 0 : status = U_MEMORY_ALLOCATION_ERROR;
1532 0 : return;
1533 : }
1534 : }
1535 : }
1536 :
1537 0 : void* newKey = createKey(key, status);
1538 0 : if (U_FAILURE(status)) {
1539 0 : deleteZNamesLoader(loader);
1540 0 : return;
1541 : }
1542 :
1543 0 : uhash_put(keyToLoader, newKey, loader, &status);
1544 0 : if (U_FAILURE(status)) { return; }
1545 : }
1546 :
1547 0 : if (loader != DUMMY_LOADER) {
1548 : // Let the ZNamesLoader consume the names table.
1549 0 : ((ZNames::ZNamesLoader*)loader)->put(key, value, noFallback, status);
1550 : }
1551 : }
1552 :
1553 0 : virtual void put(const char *key, ResourceValue &value, UBool noFallback,
1554 : UErrorCode &status) {
1555 0 : ResourceTable timeZonesTable = value.getTable(status);
1556 0 : if (U_FAILURE(status)) { return; }
1557 0 : for (int32_t i = 0; timeZonesTable.getKeyAndValue(i, key, value); ++i) {
1558 0 : U_ASSERT(!value.isNoInheritanceMarker());
1559 0 : if (value.getType() == URES_TABLE) {
1560 0 : consumeNamesTable(key, value, noFallback, status);
1561 : } else {
1562 : // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard).
1563 : // All time zone fields are tables.
1564 : }
1565 0 : if (U_FAILURE(status)) { return; }
1566 : }
1567 : }
1568 : };
1569 :
1570 : // Virtual destructors must be defined out of line.
1571 0 : TimeZoneNamesImpl::ZoneStringsLoader::~ZoneStringsLoader() {
1572 0 : uhash_close(keyToLoader);
1573 0 : }
1574 :
1575 0 : void TimeZoneNamesImpl::loadAllDisplayNames(UErrorCode& status) {
1576 0 : if (U_FAILURE(status)) return;
1577 :
1578 : {
1579 0 : Mutex lock(&gDataMutex);
1580 0 : internalLoadAllDisplayNames(status);
1581 : }
1582 : }
1583 :
1584 0 : void TimeZoneNamesImpl::getDisplayNames(const UnicodeString& tzID,
1585 : const UTimeZoneNameType types[], int32_t numTypes,
1586 : UDate date, UnicodeString dest[], UErrorCode& status) const {
1587 0 : if (U_FAILURE(status)) return;
1588 :
1589 0 : if (tzID.isEmpty()) { return; }
1590 0 : void* tznames = NULL;
1591 0 : void* mznames = NULL;
1592 0 : TimeZoneNamesImpl *nonConstThis = const_cast<TimeZoneNamesImpl*>(this);
1593 :
1594 : // Load the time zone strings
1595 : {
1596 0 : Mutex lock(&gDataMutex);
1597 0 : tznames = (void*) nonConstThis->loadTimeZoneNames(tzID, status);
1598 0 : if (U_FAILURE(status)) { return; }
1599 : }
1600 0 : U_ASSERT(tznames != NULL);
1601 :
1602 : // Load the values into the dest array
1603 0 : for (int i = 0; i < numTypes; i++) {
1604 0 : UTimeZoneNameType type = types[i];
1605 0 : const UChar* name = ((ZNames*)tznames)->getName(type);
1606 0 : if (name == NULL) {
1607 0 : if (mznames == NULL) {
1608 : // Load the meta zone name
1609 0 : UnicodeString mzID;
1610 0 : getMetaZoneID(tzID, date, mzID);
1611 0 : if (mzID.isEmpty()) {
1612 0 : mznames = (void*) EMPTY;
1613 : } else {
1614 : // Load the meta zone strings
1615 : // Mutex is scoped to the "else" statement
1616 0 : Mutex lock(&gDataMutex);
1617 0 : mznames = (void*) nonConstThis->loadMetaZoneNames(mzID, status);
1618 0 : if (U_FAILURE(status)) { return; }
1619 : // Note: when the metazone doesn't exist, in Java, loadMetaZoneNames returns
1620 : // a dummy object instead of NULL.
1621 0 : if (mznames == NULL) {
1622 0 : mznames = (void*) EMPTY;
1623 : }
1624 : }
1625 : }
1626 0 : U_ASSERT(mznames != NULL);
1627 0 : if (mznames != EMPTY) {
1628 0 : name = ((ZNames*)mznames)->getName(type);
1629 : }
1630 : }
1631 0 : if (name != NULL) {
1632 0 : dest[i].setTo(TRUE, name, -1);
1633 : } else {
1634 0 : dest[i].setToBogus();
1635 : }
1636 : }
1637 : }
1638 :
1639 : // Caller must synchronize.
1640 0 : void TimeZoneNamesImpl::internalLoadAllDisplayNames(UErrorCode& status) {
1641 0 : if (!fNamesFullyLoaded) {
1642 0 : fNamesFullyLoaded = TRUE;
1643 :
1644 0 : ZoneStringsLoader loader(*this, status);
1645 0 : loader.load(status);
1646 0 : if (U_FAILURE(status)) { return; }
1647 :
1648 : const UnicodeString *id;
1649 :
1650 : // load strings for all zones
1651 : StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(
1652 0 : UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
1653 0 : if (U_SUCCESS(status)) {
1654 0 : while ((id = tzIDs->snext(status))) {
1655 0 : if (U_FAILURE(status)) {
1656 0 : break;
1657 : }
1658 0 : UnicodeString copy(*id);
1659 0 : void* value = uhash_get(fTZNamesMap, copy.getTerminatedBuffer());
1660 0 : if (value == NULL) {
1661 : // loadStrings also loads related metazone strings
1662 0 : loadStrings(*id, status);
1663 : }
1664 : }
1665 : }
1666 0 : if (tzIDs != NULL) {
1667 0 : delete tzIDs;
1668 : }
1669 : }
1670 : }
1671 :
1672 :
1673 :
1674 : static const UChar gEtcPrefix[] = { 0x45, 0x74, 0x63, 0x2F }; // "Etc/"
1675 : static const int32_t gEtcPrefixLen = 4;
1676 : static const UChar gSystemVPrefix[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F }; // "SystemV/
1677 : static const int32_t gSystemVPrefixLen = 8;
1678 : static const UChar gRiyadh8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38 }; // "Riyadh8"
1679 : static const int32_t gRiyadh8Len = 7;
1680 :
1681 : UnicodeString& U_EXPORT2
1682 0 : TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) {
1683 0 : if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen)
1684 0 : || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) {
1685 0 : name.setToBogus();
1686 0 : return name;
1687 : }
1688 :
1689 0 : int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */);
1690 0 : if (sep > 0 && sep + 1 < tzID.length()) {
1691 0 : name.setTo(tzID, sep + 1);
1692 0 : name.findAndReplace(UnicodeString((UChar)0x5f /* _ */),
1693 0 : UnicodeString((UChar)0x20 /* space */));
1694 : } else {
1695 0 : name.setToBogus();
1696 : }
1697 0 : return name;
1698 : }
1699 :
1700 : // ---------------------------------------------------
1701 : // TZDBTimeZoneNames and its supporting classes
1702 : //
1703 : // TZDBTimeZoneNames is an implementation class of
1704 : // TimeZoneNames holding the IANA tz database abbreviations.
1705 : // ---------------------------------------------------
1706 :
1707 : class TZDBNames : public UMemory {
1708 : public:
1709 : virtual ~TZDBNames();
1710 :
1711 : static TZDBNames* createInstance(UResourceBundle* rb, const char* key);
1712 : const UChar* getName(UTimeZoneNameType type) const;
1713 : const char** getParseRegions(int32_t& numRegions) const;
1714 :
1715 : protected:
1716 : TZDBNames(const UChar** names, char** regions, int32_t numRegions);
1717 :
1718 : private:
1719 : const UChar** fNames;
1720 : char** fRegions;
1721 : int32_t fNumRegions;
1722 : };
1723 :
1724 0 : TZDBNames::TZDBNames(const UChar** names, char** regions, int32_t numRegions)
1725 : : fNames(names),
1726 : fRegions(regions),
1727 0 : fNumRegions(numRegions) {
1728 0 : }
1729 :
1730 0 : TZDBNames::~TZDBNames() {
1731 0 : if (fNames != NULL) {
1732 0 : uprv_free(fNames);
1733 : }
1734 0 : if (fRegions != NULL) {
1735 0 : char **p = fRegions;
1736 0 : for (int32_t i = 0; i < fNumRegions; p++, i++) {
1737 0 : uprv_free(*p);
1738 : }
1739 0 : uprv_free(fRegions);
1740 : }
1741 0 : }
1742 :
1743 : TZDBNames*
1744 0 : TZDBNames::createInstance(UResourceBundle* rb, const char* key) {
1745 0 : if (rb == NULL || key == NULL || *key == 0) {
1746 0 : return NULL;
1747 : }
1748 :
1749 0 : UErrorCode status = U_ZERO_ERROR;
1750 :
1751 0 : const UChar **names = NULL;
1752 0 : char** regions = NULL;
1753 0 : int32_t numRegions = 0;
1754 :
1755 0 : int32_t len = 0;
1756 :
1757 0 : UResourceBundle* rbTable = NULL;
1758 0 : rbTable = ures_getByKey(rb, key, rbTable, &status);
1759 0 : if (U_FAILURE(status)) {
1760 0 : return NULL;
1761 : }
1762 :
1763 0 : names = (const UChar **)uprv_malloc(sizeof(const UChar*) * TZDBNAMES_KEYS_SIZE);
1764 0 : UBool isEmpty = TRUE;
1765 0 : if (names != NULL) {
1766 0 : for (int32_t i = 0; i < TZDBNAMES_KEYS_SIZE; i++) {
1767 0 : status = U_ZERO_ERROR;
1768 0 : const UChar *value = ures_getStringByKey(rbTable, TZDBNAMES_KEYS[i], &len, &status);
1769 0 : if (U_FAILURE(status) || len == 0) {
1770 0 : names[i] = NULL;
1771 : } else {
1772 0 : names[i] = value;
1773 0 : isEmpty = FALSE;
1774 : }
1775 : }
1776 : }
1777 :
1778 0 : if (isEmpty) {
1779 0 : if (names != NULL) {
1780 0 : uprv_free(names);
1781 : }
1782 0 : return NULL;
1783 : }
1784 :
1785 0 : UResourceBundle *regionsRes = ures_getByKey(rbTable, "parseRegions", NULL, &status);
1786 0 : UBool regionError = FALSE;
1787 0 : if (U_SUCCESS(status)) {
1788 0 : numRegions = ures_getSize(regionsRes);
1789 0 : if (numRegions > 0) {
1790 0 : regions = (char**)uprv_malloc(sizeof(char*) * numRegions);
1791 0 : if (regions != NULL) {
1792 0 : char **pRegion = regions;
1793 0 : for (int32_t i = 0; i < numRegions; i++, pRegion++) {
1794 0 : *pRegion = NULL;
1795 : }
1796 : // filling regions
1797 0 : pRegion = regions;
1798 0 : for (int32_t i = 0; i < numRegions; i++, pRegion++) {
1799 0 : status = U_ZERO_ERROR;
1800 0 : const UChar *uregion = ures_getStringByIndex(regionsRes, i, &len, &status);
1801 0 : if (U_FAILURE(status)) {
1802 0 : regionError = TRUE;
1803 0 : break;
1804 : }
1805 0 : *pRegion = (char*)uprv_malloc(sizeof(char) * (len + 1));
1806 0 : if (*pRegion == NULL) {
1807 0 : regionError = TRUE;
1808 0 : break;
1809 : }
1810 0 : u_UCharsToChars(uregion, *pRegion, len);
1811 0 : (*pRegion)[len] = 0;
1812 : }
1813 : }
1814 : }
1815 : }
1816 0 : ures_close(regionsRes);
1817 0 : ures_close(rbTable);
1818 :
1819 0 : if (regionError) {
1820 0 : if (names != NULL) {
1821 0 : uprv_free(names);
1822 : }
1823 0 : if (regions != NULL) {
1824 0 : char **p = regions;
1825 0 : for (int32_t i = 0; i < numRegions; p++, i++) {
1826 0 : uprv_free(*p);
1827 : }
1828 0 : uprv_free(regions);
1829 : }
1830 0 : return NULL;
1831 : }
1832 :
1833 0 : return new TZDBNames(names, regions, numRegions);
1834 : }
1835 :
1836 : const UChar*
1837 0 : TZDBNames::getName(UTimeZoneNameType type) const {
1838 0 : if (fNames == NULL) {
1839 0 : return NULL;
1840 : }
1841 0 : const UChar *name = NULL;
1842 0 : switch(type) {
1843 : case UTZNM_SHORT_STANDARD:
1844 0 : name = fNames[0];
1845 0 : break;
1846 : case UTZNM_SHORT_DAYLIGHT:
1847 0 : name = fNames[1];
1848 0 : break;
1849 : default:
1850 0 : name = NULL;
1851 : }
1852 0 : return name;
1853 : }
1854 :
1855 : const char**
1856 0 : TZDBNames::getParseRegions(int32_t& numRegions) const {
1857 0 : if (fRegions == NULL) {
1858 0 : numRegions = 0;
1859 : } else {
1860 0 : numRegions = fNumRegions;
1861 : }
1862 0 : return (const char**)fRegions;
1863 : }
1864 :
1865 : U_CDECL_BEGIN
1866 : /**
1867 : * TZDBNameInfo stores metazone name information for the IANA abbreviations
1868 : * in the trie
1869 : */
1870 : typedef struct TZDBNameInfo {
1871 : const UChar* mzID;
1872 : UTimeZoneNameType type;
1873 : UBool ambiguousType;
1874 : const char** parseRegions;
1875 : int32_t nRegions;
1876 : } TZDBNameInfo;
1877 : U_CDECL_END
1878 :
1879 :
1880 : class TZDBNameSearchHandler : public TextTrieMapSearchResultHandler {
1881 : public:
1882 : TZDBNameSearchHandler(uint32_t types, const char* region);
1883 : virtual ~TZDBNameSearchHandler();
1884 :
1885 : UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
1886 : TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen);
1887 :
1888 : private:
1889 : uint32_t fTypes;
1890 : int32_t fMaxMatchLen;
1891 : TimeZoneNames::MatchInfoCollection* fResults;
1892 : const char* fRegion;
1893 : };
1894 :
1895 0 : TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types, const char* region)
1896 0 : : fTypes(types), fMaxMatchLen(0), fResults(NULL), fRegion(region) {
1897 0 : }
1898 :
1899 0 : TZDBNameSearchHandler::~TZDBNameSearchHandler() {
1900 0 : if (fResults != NULL) {
1901 0 : delete fResults;
1902 : }
1903 0 : }
1904 :
1905 : UBool
1906 0 : TZDBNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
1907 0 : if (U_FAILURE(status)) {
1908 0 : return FALSE;
1909 : }
1910 :
1911 0 : TZDBNameInfo *match = NULL;
1912 0 : TZDBNameInfo *defaultRegionMatch = NULL;
1913 :
1914 0 : if (node->hasValues()) {
1915 0 : int32_t valuesCount = node->countValues();
1916 0 : for (int32_t i = 0; i < valuesCount; i++) {
1917 0 : TZDBNameInfo *ninfo = (TZDBNameInfo *)node->getValue(i);
1918 0 : if (ninfo == NULL) {
1919 0 : continue;
1920 : }
1921 0 : if ((ninfo->type & fTypes) != 0) {
1922 : // Some tz database abbreviations are ambiguous. For example,
1923 : // CST means either Central Standard Time or China Standard Time.
1924 : // Unlike CLDR time zone display names, this implementation
1925 : // does not use unique names. And TimeZoneFormat does not expect
1926 : // multiple results returned for the same time zone type.
1927 : // For this reason, this implementation resolve one among same
1928 : // zone type with a same name at this level.
1929 0 : if (ninfo->parseRegions == NULL) {
1930 : // parseRegions == null means this is the default metazone
1931 : // mapping for the abbreviation.
1932 0 : if (defaultRegionMatch == NULL) {
1933 0 : match = defaultRegionMatch = ninfo;
1934 : }
1935 : } else {
1936 0 : UBool matchRegion = FALSE;
1937 : // non-default metazone mapping for an abbreviation
1938 : // comes with applicable regions. For example, the default
1939 : // metazone mapping for "CST" is America_Central,
1940 : // but if region is one of CN/MO/TW, "CST" is parsed
1941 : // as metazone China (China Standard Time).
1942 0 : for (int32_t i = 0; i < ninfo->nRegions; i++) {
1943 0 : const char *region = ninfo->parseRegions[i];
1944 0 : if (uprv_strcmp(fRegion, region) == 0) {
1945 0 : match = ninfo;
1946 0 : matchRegion = TRUE;
1947 0 : break;
1948 : }
1949 : }
1950 0 : if (matchRegion) {
1951 0 : break;
1952 : }
1953 0 : if (match == NULL) {
1954 0 : match = ninfo;
1955 : }
1956 : }
1957 : }
1958 : }
1959 :
1960 0 : if (match != NULL) {
1961 0 : UTimeZoneNameType ntype = match->type;
1962 : // Note: Workaround for duplicated standard/daylight names
1963 : // The tz database contains a few zones sharing a
1964 : // same name for both standard time and daylight saving
1965 : // time. For example, Australia/Sydney observes DST,
1966 : // but "EST" is used for both standard and daylight.
1967 : // When both SHORT_STANDARD and SHORT_DAYLIGHT are included
1968 : // in the find operation, we cannot tell which one was
1969 : // actually matched.
1970 : // TimeZoneFormat#parse returns a matched name type (standard
1971 : // or daylight) and DateFormat implementation uses the info to
1972 : // to adjust actual time. To avoid false type information,
1973 : // this implementation replaces the name type with SHORT_GENERIC.
1974 0 : if (match->ambiguousType
1975 0 : && (ntype == UTZNM_SHORT_STANDARD || ntype == UTZNM_SHORT_DAYLIGHT)
1976 0 : && (fTypes & UTZNM_SHORT_STANDARD) != 0
1977 0 : && (fTypes & UTZNM_SHORT_DAYLIGHT) != 0) {
1978 0 : ntype = UTZNM_SHORT_GENERIC;
1979 : }
1980 :
1981 0 : if (fResults == NULL) {
1982 0 : fResults = new TimeZoneNames::MatchInfoCollection();
1983 0 : if (fResults == NULL) {
1984 0 : status = U_MEMORY_ALLOCATION_ERROR;
1985 : }
1986 : }
1987 0 : if (U_SUCCESS(status)) {
1988 0 : U_ASSERT(fResults != NULL);
1989 0 : U_ASSERT(match->mzID != NULL);
1990 0 : fResults->addMetaZone(ntype, matchLength, UnicodeString(match->mzID, -1), status);
1991 0 : if (U_SUCCESS(status) && matchLength > fMaxMatchLen) {
1992 0 : fMaxMatchLen = matchLength;
1993 : }
1994 : }
1995 : }
1996 : }
1997 0 : return TRUE;
1998 : }
1999 :
2000 : TimeZoneNames::MatchInfoCollection*
2001 0 : TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen) {
2002 : // give the ownership to the caller
2003 0 : TimeZoneNames::MatchInfoCollection* results = fResults;
2004 0 : maxMatchLen = fMaxMatchLen;
2005 :
2006 : // reset
2007 0 : fResults = NULL;
2008 0 : fMaxMatchLen = 0;
2009 0 : return results;
2010 : }
2011 :
2012 : U_CDECL_BEGIN
2013 : /**
2014 : * Deleter for TZDBNames
2015 : */
2016 : static void U_CALLCONV
2017 0 : deleteTZDBNames(void *obj) {
2018 0 : if (obj != EMPTY) {
2019 0 : delete (TZDBNames *)obj;
2020 : }
2021 0 : }
2022 :
2023 0 : static void U_CALLCONV initTZDBNamesMap(UErrorCode &status) {
2024 0 : gTZDBNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
2025 0 : if (U_FAILURE(status)) {
2026 0 : gTZDBNamesMap = NULL;
2027 0 : return;
2028 : }
2029 : // no key deleters for tzdb name maps
2030 0 : uhash_setValueDeleter(gTZDBNamesMap, deleteTZDBNames);
2031 0 : ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
2032 : }
2033 :
2034 : /**
2035 : * Deleter for TZDBNameInfo
2036 : */
2037 : static void U_CALLCONV
2038 0 : deleteTZDBNameInfo(void *obj) {
2039 0 : if (obj != NULL) {
2040 0 : uprv_free(obj);
2041 : }
2042 0 : }
2043 :
2044 0 : static void U_CALLCONV prepareFind(UErrorCode &status) {
2045 0 : if (U_FAILURE(status)) {
2046 0 : return;
2047 : }
2048 0 : gTZDBNamesTrie = new TextTrieMap(TRUE, deleteTZDBNameInfo);
2049 0 : if (gTZDBNamesTrie == NULL) {
2050 0 : status = U_MEMORY_ALLOCATION_ERROR;
2051 0 : return;
2052 : }
2053 :
2054 : const UnicodeString *mzID;
2055 0 : StringEnumeration *mzIDs = TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
2056 0 : if (U_SUCCESS(status)) {
2057 0 : while ((mzID = mzIDs->snext(status)) && U_SUCCESS(status)) {
2058 0 : const TZDBNames *names = TZDBTimeZoneNames::getMetaZoneNames(*mzID, status);
2059 0 : if (names == NULL) {
2060 0 : continue;
2061 : }
2062 0 : const UChar *std = names->getName(UTZNM_SHORT_STANDARD);
2063 0 : const UChar *dst = names->getName(UTZNM_SHORT_DAYLIGHT);
2064 0 : if (std == NULL && dst == NULL) {
2065 0 : continue;
2066 : }
2067 0 : int32_t numRegions = 0;
2068 0 : const char **parseRegions = names->getParseRegions(numRegions);
2069 :
2070 : // The tz database contains a few zones sharing a
2071 : // same name for both standard time and daylight saving
2072 : // time. For example, Australia/Sydney observes DST,
2073 : // but "EST" is used for both standard and daylight.
2074 : // we need to store the information for later processing.
2075 0 : UBool ambiguousType = (std != NULL && dst != NULL && u_strcmp(std, dst) == 0);
2076 :
2077 0 : const UChar *uMzID = ZoneMeta::findMetaZoneID(*mzID);
2078 0 : if (std != NULL) {
2079 0 : TZDBNameInfo *stdInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
2080 0 : if (stdInf == NULL) {
2081 0 : status = U_MEMORY_ALLOCATION_ERROR;
2082 0 : break;
2083 : }
2084 0 : stdInf->mzID = uMzID;
2085 0 : stdInf->type = UTZNM_SHORT_STANDARD;
2086 0 : stdInf->ambiguousType = ambiguousType;
2087 0 : stdInf->parseRegions = parseRegions;
2088 0 : stdInf->nRegions = numRegions;
2089 0 : gTZDBNamesTrie->put(std, stdInf, status);
2090 : }
2091 0 : if (U_SUCCESS(status) && dst != NULL) {
2092 0 : TZDBNameInfo *dstInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo));
2093 0 : if (dstInf == NULL) {
2094 0 : status = U_MEMORY_ALLOCATION_ERROR;
2095 0 : break;
2096 : }
2097 0 : dstInf->mzID = uMzID;
2098 0 : dstInf->type = UTZNM_SHORT_DAYLIGHT;
2099 0 : dstInf->ambiguousType = ambiguousType;
2100 0 : dstInf->parseRegions = parseRegions;
2101 0 : dstInf->nRegions = numRegions;
2102 0 : gTZDBNamesTrie->put(dst, dstInf, status);
2103 : }
2104 : }
2105 : }
2106 0 : delete mzIDs;
2107 :
2108 0 : if (U_FAILURE(status)) {
2109 0 : delete gTZDBNamesTrie;
2110 0 : gTZDBNamesTrie = NULL;
2111 0 : return;
2112 : }
2113 :
2114 0 : ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup);
2115 : }
2116 :
2117 : U_CDECL_END
2118 :
2119 0 : TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale& locale)
2120 0 : : fLocale(locale) {
2121 0 : UBool useWorld = TRUE;
2122 0 : const char* region = fLocale.getCountry();
2123 0 : int32_t regionLen = uprv_strlen(region);
2124 0 : if (regionLen == 0) {
2125 0 : UErrorCode status = U_ZERO_ERROR;
2126 : char loc[ULOC_FULLNAME_CAPACITY];
2127 0 : uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
2128 0 : regionLen = uloc_getCountry(loc, fRegion, sizeof(fRegion), &status);
2129 0 : if (U_SUCCESS(status) && regionLen < (int32_t)sizeof(fRegion)) {
2130 0 : useWorld = FALSE;
2131 : }
2132 0 : } else if (regionLen < (int32_t)sizeof(fRegion)) {
2133 0 : uprv_strcpy(fRegion, region);
2134 0 : useWorld = FALSE;
2135 : }
2136 0 : if (useWorld) {
2137 0 : uprv_strcpy(fRegion, "001");
2138 : }
2139 0 : }
2140 :
2141 0 : TZDBTimeZoneNames::~TZDBTimeZoneNames() {
2142 0 : }
2143 :
2144 : UBool
2145 0 : TZDBTimeZoneNames::operator==(const TimeZoneNames& other) const {
2146 0 : if (this == &other) {
2147 0 : return TRUE;
2148 : }
2149 : // No implementation for now
2150 0 : return FALSE;
2151 : }
2152 :
2153 : TimeZoneNames*
2154 0 : TZDBTimeZoneNames::clone() const {
2155 0 : return new TZDBTimeZoneNames(fLocale);
2156 : }
2157 :
2158 : StringEnumeration*
2159 0 : TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode& status) const {
2160 0 : return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status);
2161 : }
2162 :
2163 : StringEnumeration*
2164 0 : TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
2165 0 : return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status);
2166 : }
2167 :
2168 : UnicodeString&
2169 0 : TZDBTimeZoneNames::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
2170 0 : return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID);
2171 : }
2172 :
2173 : UnicodeString&
2174 0 : TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
2175 0 : return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID);
2176 : }
2177 :
2178 : UnicodeString&
2179 0 : TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString& mzID,
2180 : UTimeZoneNameType type,
2181 : UnicodeString& name) const {
2182 0 : name.setToBogus();
2183 0 : if (mzID.isEmpty()) {
2184 0 : return name;
2185 : }
2186 :
2187 0 : UErrorCode status = U_ZERO_ERROR;
2188 0 : const TZDBNames *tzdbNames = TZDBTimeZoneNames::getMetaZoneNames(mzID, status);
2189 0 : if (U_SUCCESS(status)) {
2190 0 : const UChar *s = tzdbNames->getName(type);
2191 0 : if (s != NULL) {
2192 0 : name.setTo(TRUE, s, -1);
2193 : }
2194 : }
2195 :
2196 0 : return name;
2197 : }
2198 :
2199 : UnicodeString&
2200 0 : TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString& /* tzID */, UTimeZoneNameType /* type */, UnicodeString& name) const {
2201 : // No abbreviations associated a zone directly for now.
2202 0 : name.setToBogus();
2203 0 : return name;
2204 : }
2205 :
2206 : TZDBTimeZoneNames::MatchInfoCollection*
2207 0 : TZDBTimeZoneNames::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
2208 0 : umtx_initOnce(gTZDBNamesTrieInitOnce, &prepareFind, status);
2209 0 : if (U_FAILURE(status)) {
2210 0 : return NULL;
2211 : }
2212 :
2213 0 : TZDBNameSearchHandler handler(types, fRegion);
2214 0 : gTZDBNamesTrie->search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
2215 0 : if (U_FAILURE(status)) {
2216 0 : return NULL;
2217 : }
2218 0 : int32_t maxLen = 0;
2219 0 : return handler.getMatches(maxLen);
2220 : }
2221 :
2222 : const TZDBNames*
2223 0 : TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) {
2224 0 : umtx_initOnce(gTZDBNamesMapInitOnce, &initTZDBNamesMap, status);
2225 0 : if (U_FAILURE(status)) {
2226 0 : return NULL;
2227 : }
2228 :
2229 0 : TZDBNames* tzdbNames = NULL;
2230 :
2231 : UChar mzIDKey[ZID_KEY_MAX + 1];
2232 0 : mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status);
2233 0 : U_ASSERT(status == U_ZERO_ERROR); // already checked length above
2234 0 : mzIDKey[mzID.length()] = 0;
2235 :
2236 0 : umtx_lock(&gTZDBNamesMapLock);
2237 : {
2238 0 : void *cacheVal = uhash_get(gTZDBNamesMap, mzIDKey);
2239 0 : if (cacheVal == NULL) {
2240 0 : UResourceBundle *zoneStringsRes = ures_openDirect(U_ICUDATA_ZONE, "tzdbNames", &status);
2241 0 : zoneStringsRes = ures_getByKey(zoneStringsRes, gZoneStrings, zoneStringsRes, &status);
2242 0 : if (U_SUCCESS(status)) {
2243 : char key[ZID_KEY_MAX + 1];
2244 0 : mergeTimeZoneKey(mzID, key);
2245 0 : tzdbNames = TZDBNames::createInstance(zoneStringsRes, key);
2246 :
2247 0 : if (tzdbNames == NULL) {
2248 0 : cacheVal = (void *)EMPTY;
2249 : } else {
2250 0 : cacheVal = tzdbNames;
2251 : }
2252 : // Use the persistent ID as the resource key, so we can
2253 : // avoid duplications.
2254 : // TODO: Is there a more efficient way, like intern() in Java?
2255 0 : void* newKey = (void*) ZoneMeta::findMetaZoneID(mzID);
2256 0 : if (newKey != NULL) {
2257 0 : uhash_put(gTZDBNamesMap, newKey, cacheVal, &status);
2258 0 : if (U_FAILURE(status)) {
2259 0 : if (tzdbNames != NULL) {
2260 0 : delete tzdbNames;
2261 0 : tzdbNames = NULL;
2262 : }
2263 : }
2264 : } else {
2265 : // Should never happen with a valid input
2266 0 : if (tzdbNames != NULL) {
2267 : // It's not possible that we get a valid tzdbNames with unknown ID.
2268 : // But just in case..
2269 0 : delete tzdbNames;
2270 0 : tzdbNames = NULL;
2271 : }
2272 : }
2273 : }
2274 0 : ures_close(zoneStringsRes);
2275 0 : } else if (cacheVal != EMPTY) {
2276 0 : tzdbNames = (TZDBNames *)cacheVal;
2277 : }
2278 : }
2279 0 : umtx_unlock(&gTZDBNamesMapLock);
2280 :
2281 0 : return tzdbNames;
2282 : }
2283 :
2284 : U_NAMESPACE_END
2285 :
2286 :
2287 : #endif /* #if !UCONFIG_NO_FORMATTING */
2288 :
2289 : //eof
|