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