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