]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/tznames_impl.cpp
ICU-491.11.2.tar.gz
[apple/icu.git] / icuSources / i18n / tznames_impl.cpp
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