]>
Commit | Line | Data |
---|---|---|
46f4442e A |
1 | /* |
2 | ******************************************************************************* | |
3 | * Copyright (C) 2007-2008, International Business Machines Corporation and * | |
4 | * others. All Rights Reserved. * | |
5 | ******************************************************************************* | |
6 | */ | |
7 | ||
8 | #include "unicode/utypes.h" | |
9 | ||
10 | #if !UCONFIG_NO_FORMATTING | |
11 | ||
12 | #include "zstrfmt.h" | |
13 | ||
14 | #include "unicode/ustring.h" | |
15 | #include "unicode/putil.h" | |
16 | #include "unicode/msgfmt.h" | |
17 | #include "unicode/basictz.h" | |
18 | #include "unicode/simpletz.h" | |
19 | #include "unicode/rbtz.h" | |
20 | #include "unicode/vtzone.h" | |
21 | ||
22 | #include "uvector.h" | |
23 | #include "cstring.h" | |
24 | #include "cmemory.h" | |
25 | #include "uresimp.h" | |
26 | #include "zonemeta.h" | |
27 | #include "olsontz.h" | |
28 | #include "umutex.h" | |
29 | #include "ucln_in.h" | |
30 | ||
31 | /** | |
32 | * global ZoneStringFormatCache stuffs | |
33 | */ | |
34 | static UMTX gZSFCacheLock = NULL; | |
35 | static U_NAMESPACE_QUALIFIER ZSFCache *gZoneStringFormatCache = NULL; | |
36 | ||
37 | U_CDECL_BEGIN | |
38 | /** | |
39 | * ZoneStringFormatCache cleanup callback func | |
40 | */ | |
41 | static UBool U_CALLCONV zoneStringFormat_cleanup(void) | |
42 | { | |
43 | umtx_destroy(&gZSFCacheLock); | |
44 | if (gZoneStringFormatCache != NULL) { | |
45 | delete gZoneStringFormatCache; | |
46 | gZoneStringFormatCache = NULL; | |
47 | } | |
48 | gZoneStringFormatCache = NULL; | |
49 | return TRUE; | |
50 | } | |
51 | ||
52 | /** | |
53 | * Deleter for ZoneStringInfo | |
54 | */ | |
55 | static void U_CALLCONV | |
56 | deleteZoneStringInfo(void *obj) { | |
57 | delete (U_NAMESPACE_QUALIFIER ZoneStringInfo*)obj; | |
58 | } | |
59 | ||
60 | /** | |
61 | * Deleter for ZoneStrings | |
62 | */ | |
63 | static void U_CALLCONV | |
64 | deleteZoneStrings(void *obj) { | |
65 | delete (U_NAMESPACE_QUALIFIER ZoneStrings*)obj; | |
66 | } | |
67 | U_CDECL_END | |
68 | ||
69 | U_NAMESPACE_BEGIN | |
70 | ||
71 | #define ZID_KEY_MAX 128 | |
72 | ||
73 | static const char gCountriesTag[] = "Countries"; | |
74 | static const char gZoneStringsTag[] = "zoneStrings"; | |
75 | static const char gShortGenericTag[] = "sg"; | |
76 | static const char gShortStandardTag[] = "ss"; | |
77 | static const char gShortDaylightTag[] = "sd"; | |
78 | static const char gLongGenericTag[] = "lg"; | |
79 | static const char gLongStandardTag[] = "ls"; | |
80 | static const char gLongDaylightTag[] = "ld"; | |
81 | static const char gExemplarCityTag[] = "ec"; | |
82 | static const char gCommonlyUsedTag[] = "cu"; | |
83 | static const char gFallbackFormatTag[] = "fallbackFormat"; | |
84 | static const char gRegionFormatTag[] = "regionFormat"; | |
85 | ||
86 | #define MZID_PREFIX_LEN 5 | |
87 | static const char gMetazoneIdPrefix[] = "meta:"; | |
88 | ||
89 | #define MAX_METAZONES_PER_ZONE 10 | |
90 | ||
91 | static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})" | |
92 | static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}" | |
93 | static const UChar gCommonlyUsedTrue[] = {0x31, 0x00}; // "1" | |
94 | ||
95 | static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY; | |
96 | ||
97 | static int32_t | |
98 | getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type) { | |
99 | int32_t typeIdx = 0; | |
100 | switch (type) { | |
101 | case LOCATION: | |
102 | typeIdx = ZSIDX_LOCATION; | |
103 | break; | |
104 | case GENERIC_LONG: | |
105 | typeIdx = ZSIDX_LONG_GENERIC; | |
106 | break; | |
107 | case GENERIC_SHORT: | |
108 | typeIdx = ZSIDX_SHORT_GENERIC; | |
109 | break; | |
110 | case STANDARD_LONG: | |
111 | typeIdx = ZSIDX_LONG_STANDARD; | |
112 | break; | |
113 | case STANDARD_SHORT: | |
114 | typeIdx = ZSIDX_SHORT_STANDARD; | |
115 | break; | |
116 | case DAYLIGHT_LONG: | |
117 | typeIdx = ZSIDX_LONG_DAYLIGHT; | |
118 | break; | |
119 | case DAYLIGHT_SHORT: | |
120 | typeIdx = ZSIDX_SHORT_DAYLIGHT; | |
121 | break; | |
122 | } | |
123 | return typeIdx; | |
124 | } | |
125 | ||
126 | static int32_t | |
127 | getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx) { | |
128 | int32_t type = 0; | |
129 | switch (typeIdx) { | |
130 | case ZSIDX_LOCATION: | |
131 | type = LOCATION; | |
132 | break; | |
133 | case ZSIDX_LONG_GENERIC: | |
134 | type = GENERIC_LONG; | |
135 | break; | |
136 | case ZSIDX_SHORT_GENERIC: | |
137 | type = GENERIC_SHORT; | |
138 | break; | |
139 | case ZSIDX_LONG_STANDARD: | |
140 | type = STANDARD_LONG; | |
141 | break; | |
142 | case ZSIDX_SHORT_STANDARD: | |
143 | type = STANDARD_SHORT; | |
144 | break; | |
145 | case ZSIDX_LONG_DAYLIGHT: | |
146 | type = DAYLIGHT_LONG; | |
147 | break; | |
148 | case ZSIDX_COUNT: | |
149 | case ZSIDX_SHORT_DAYLIGHT: | |
150 | type = DAYLIGHT_SHORT; | |
151 | break; | |
152 | ||
153 | } | |
154 | return type; | |
155 | } | |
156 | ||
157 | #define DEFAULT_CHARACTERNODE_CAPACITY 1 | |
158 | ||
159 | // ---------------------------------------------------------------------------- | |
160 | void CharacterNode::clear() { | |
161 | uprv_memset(this, 0, sizeof(*this)); | |
162 | } | |
163 | ||
164 | void CharacterNode::deleteValues() { | |
165 | if (fValues == NULL) { | |
166 | // Do nothing. | |
167 | } else if (!fHasValuesVector) { | |
168 | deleteZoneStringInfo(fValues); | |
169 | } else { | |
170 | delete (UVector *)fValues; | |
171 | } | |
172 | } | |
173 | ||
174 | void | |
175 | CharacterNode::addValue(void *value, UErrorCode &status) { | |
176 | if (U_FAILURE(status)) { | |
177 | deleteZoneStringInfo(value); | |
178 | return; | |
179 | } | |
180 | if (fValues == NULL) { | |
181 | fValues = value; | |
182 | } else { | |
183 | // At least one value already. | |
184 | if (!fHasValuesVector) { | |
185 | // There is only one value so far, and not in a vector yet. | |
186 | // Create a vector and add the old value. | |
187 | UVector *values = new UVector(deleteZoneStringInfo, NULL, DEFAULT_CHARACTERNODE_CAPACITY, status); | |
188 | if (U_FAILURE(status)) { | |
189 | deleteZoneStringInfo(value); | |
190 | return; | |
191 | } | |
192 | values->addElement(fValues, status); | |
193 | fValues = values; | |
194 | fHasValuesVector = TRUE; | |
195 | } | |
196 | // Add the new value. | |
197 | ((UVector *)fValues)->addElement(value, status); | |
198 | } | |
199 | } | |
200 | ||
201 | //---------------------------------------------------------------------------- | |
202 | // Virtual destructor to avoid warning | |
203 | TextTrieMapSearchResultHandler::~TextTrieMapSearchResultHandler(){ | |
204 | } | |
205 | ||
206 | // ---------------------------------------------------------------------------- | |
207 | TextTrieMap::TextTrieMap(UBool ignoreCase) | |
208 | : fIgnoreCase(ignoreCase), fNodes(NULL), fNodesCapacity(0), fNodesCount(0) { | |
209 | } | |
210 | ||
211 | TextTrieMap::~TextTrieMap() { | |
212 | int32_t index; | |
213 | for (index = 0; index < fNodesCount; ++index) { | |
214 | fNodes[index].deleteValues(); | |
215 | } | |
216 | uprv_free(fNodes); | |
217 | } | |
218 | ||
219 | void | |
220 | TextTrieMap::put(const UnicodeString &key, void *value, UErrorCode &status) { | |
221 | if (fNodes == NULL) { | |
222 | fNodesCapacity = 512; | |
223 | fNodes = (CharacterNode *)uprv_malloc(fNodesCapacity * sizeof(CharacterNode)); | |
224 | fNodes[0].clear(); // Init root node. | |
225 | fNodesCount = 1; | |
226 | } | |
227 | ||
228 | UnicodeString foldedKey; | |
229 | const UChar *keyBuffer; | |
230 | int32_t keyLength; | |
231 | if (fIgnoreCase) { | |
232 | // Ok to use fastCopyFrom() because we discard the copy when we return. | |
233 | foldedKey.fastCopyFrom(key).foldCase(); | |
234 | keyBuffer = foldedKey.getBuffer(); | |
235 | keyLength = foldedKey.length(); | |
236 | } else { | |
237 | keyBuffer = key.getBuffer(); | |
238 | keyLength = key.length(); | |
239 | } | |
240 | ||
241 | CharacterNode *node = fNodes; | |
242 | int32_t index; | |
243 | for (index = 0; index < keyLength; ++index) { | |
244 | node = addChildNode(node, keyBuffer[index], status); | |
245 | } | |
246 | node->addValue(value, status); | |
247 | } | |
248 | ||
249 | UBool | |
250 | TextTrieMap::growNodes() { | |
251 | if (fNodesCapacity == 0xffff) { | |
252 | return FALSE; // We use 16-bit node indexes. | |
253 | } | |
254 | int32_t newCapacity = fNodesCapacity * 2; | |
255 | if (newCapacity > 0xffff) { | |
256 | newCapacity = 0xffff; | |
257 | } | |
258 | CharacterNode *newNodes = (CharacterNode *)uprv_malloc(newCapacity * sizeof(CharacterNode)); | |
259 | if (newNodes == NULL) { | |
260 | return FALSE; | |
261 | } | |
262 | uprv_memcpy(newNodes, fNodes, fNodesCount * sizeof(CharacterNode)); | |
263 | uprv_free(fNodes); | |
264 | fNodes = newNodes; | |
265 | fNodesCapacity = newCapacity; | |
266 | return TRUE; | |
267 | } | |
268 | ||
269 | CharacterNode* | |
270 | TextTrieMap::addChildNode(CharacterNode *parent, UChar c, UErrorCode &status) { | |
271 | if (U_FAILURE(status)) { | |
272 | return NULL; | |
273 | } | |
274 | // Linear search of the sorted list of children. | |
275 | uint16_t prevIndex = 0; | |
276 | uint16_t nodeIndex = parent->fFirstChild; | |
277 | while (nodeIndex > 0) { | |
278 | CharacterNode *current = fNodes + nodeIndex; | |
279 | UChar childCharacter = current->fCharacter; | |
280 | if (childCharacter == c) { | |
281 | return current; | |
282 | } else if (childCharacter > c) { | |
283 | break; | |
284 | } | |
285 | prevIndex = nodeIndex; | |
286 | nodeIndex = current->fNextSibling; | |
287 | } | |
288 | ||
289 | // Ensure capacity. Grow fNodes[] if needed. | |
290 | if (fNodesCount == fNodesCapacity) { | |
291 | int32_t parentIndex = (parent - fNodes); | |
292 | if (!growNodes()) { | |
293 | status = U_MEMORY_ALLOCATION_ERROR; | |
294 | return NULL; | |
295 | } | |
296 | parent = fNodes + parentIndex; | |
297 | } | |
298 | ||
299 | // Insert a new child node with c in sorted order. | |
300 | CharacterNode *node = fNodes + fNodesCount; | |
301 | node->clear(); | |
302 | node->fCharacter = c; | |
303 | node->fNextSibling = nodeIndex; | |
304 | if (prevIndex == 0) { | |
305 | parent->fFirstChild = (uint16_t)fNodesCount; | |
306 | } else { | |
307 | fNodes[prevIndex].fNextSibling = (uint16_t)fNodesCount; | |
308 | } | |
309 | ++fNodesCount; | |
310 | return node; | |
311 | } | |
312 | ||
313 | CharacterNode* | |
314 | TextTrieMap::getChildNode(CharacterNode *parent, UChar c) const { | |
315 | // Linear search of the sorted list of children. | |
316 | uint16_t nodeIndex = parent->fFirstChild; | |
317 | while (nodeIndex > 0) { | |
318 | CharacterNode *current = fNodes + nodeIndex; | |
319 | UChar childCharacter = current->fCharacter; | |
320 | if (childCharacter == c) { | |
321 | return current; | |
322 | } else if (childCharacter > c) { | |
323 | break; | |
324 | } | |
325 | nodeIndex = current->fNextSibling; | |
326 | } | |
327 | return NULL; | |
328 | } | |
329 | ||
330 | void | |
331 | TextTrieMap::search(const UnicodeString &text, int32_t start, | |
332 | TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { | |
333 | if (fNodes == NULL) { | |
334 | return; | |
335 | } | |
336 | search(fNodes, text, start, start, handler, status); | |
337 | } | |
338 | ||
339 | void | |
340 | TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start, | |
341 | int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const { | |
342 | if (U_FAILURE(status)) { | |
343 | return; | |
344 | } | |
345 | if (node->hasValues()) { | |
346 | if (!handler->handleMatch(index - start, node, status)) { | |
347 | return; | |
348 | } | |
349 | if (U_FAILURE(status)) { | |
350 | return; | |
351 | } | |
352 | } | |
353 | UChar32 c = text.char32At(index); | |
354 | if (fIgnoreCase) { | |
355 | // size of character may grow after fold operation | |
356 | UnicodeString tmp(c); | |
357 | tmp.foldCase(); | |
358 | int32_t tmpidx = 0; | |
359 | while (tmpidx < tmp.length()) { | |
360 | c = tmp.char32At(tmpidx); | |
361 | node = getChildNode(node, c); | |
362 | if (node == NULL) { | |
363 | break; | |
364 | } | |
365 | tmpidx = tmp.moveIndex32(tmpidx, 1); | |
366 | } | |
367 | } else { | |
368 | node = getChildNode(node, c); | |
369 | } | |
370 | if (node != NULL) { | |
371 | search(node, text, start, index+1, handler, status); | |
372 | } | |
373 | } | |
374 | ||
375 | // ---------------------------------------------------------------------------- | |
376 | ZoneStringInfo::ZoneStringInfo(const UnicodeString &id, const UnicodeString &str, | |
377 | TimeZoneTranslationType type) | |
378 | : fId(id), fStr(str), fType(type) { | |
379 | } | |
380 | ||
381 | ZoneStringInfo::~ZoneStringInfo() { | |
382 | } | |
383 | // ---------------------------------------------------------------------------- | |
384 | ZoneStringSearchResultHandler::ZoneStringSearchResultHandler(UErrorCode &status) | |
385 | : fResults(status) | |
386 | { | |
387 | clear(); | |
388 | } | |
389 | ||
390 | ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() { | |
391 | clear(); | |
392 | } | |
393 | ||
394 | UBool | |
395 | ZoneStringSearchResultHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { | |
396 | if (U_FAILURE(status)) { | |
397 | return FALSE; | |
398 | } | |
399 | if (node->hasValues()) { | |
400 | int32_t valuesCount = node->countValues(); | |
401 | for (int32_t i = 0; i < valuesCount; i++) { | |
402 | ZoneStringInfo *zsinfo = (ZoneStringInfo*)node->getValue(i); | |
403 | if (zsinfo == NULL) { | |
404 | break; | |
405 | } | |
406 | // Update the results | |
407 | UBool foundType = FALSE; | |
408 | for (int32_t j = 0; j < fResults.size(); j++) { | |
409 | ZoneStringInfo *tmp = (ZoneStringInfo*)fResults.elementAt(j); | |
410 | if (zsinfo->fType == tmp->fType) { | |
411 | int32_t lenidx = getTimeZoneTranslationTypeIndex(tmp->fType); | |
412 | if (matchLength > fMatchLen[lenidx]) { | |
413 | // Same type, longer match | |
414 | fResults.setElementAt(zsinfo, j); | |
415 | fMatchLen[lenidx] = matchLength; | |
416 | } | |
417 | foundType = TRUE; | |
418 | break; | |
419 | } | |
420 | } | |
421 | if (!foundType) { | |
422 | // not found in the current list | |
423 | fResults.addElement(zsinfo, status); | |
424 | fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)] = matchLength; | |
425 | } | |
426 | } | |
427 | } | |
428 | return TRUE; | |
429 | } | |
430 | ||
431 | int32_t | |
432 | ZoneStringSearchResultHandler::countMatches(void) { | |
433 | return fResults.size(); | |
434 | } | |
435 | ||
436 | const ZoneStringInfo* | |
437 | ZoneStringSearchResultHandler::getMatch(int32_t index, int32_t &matchLength) { | |
438 | ZoneStringInfo *zsinfo = NULL; | |
439 | if (index < fResults.size()) { | |
440 | zsinfo = (ZoneStringInfo*)fResults.elementAt(index); | |
441 | matchLength = fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)]; | |
442 | } | |
443 | return zsinfo; | |
444 | } | |
445 | ||
446 | void | |
447 | ZoneStringSearchResultHandler::clear(void) { | |
448 | fResults.removeAllElements(); | |
449 | for (int32_t i = 0; i < (int32_t)(sizeof(fMatchLen)/sizeof(fMatchLen[0])); i++) { | |
450 | fMatchLen[i] = 0; | |
451 | } | |
452 | } | |
453 | // ---------------------------------------------------------------------------- | |
454 | ZoneStringFormat::ZoneStringFormat(const UnicodeString* const* strings, | |
455 | int32_t rowCount, int32_t columnCount, UErrorCode &status) | |
456 | : fLocale(""), | |
457 | fTzidToStrings(uhash_compareUnicodeString, NULL, status), | |
458 | fMzidToStrings(uhash_compareUnicodeString, NULL, status), | |
459 | fZoneStringsTrie(TRUE) | |
460 | { | |
461 | if (U_FAILURE(status)) { | |
462 | return; | |
463 | } | |
464 | fLocale.setToBogus(); | |
465 | if (strings == NULL || columnCount <= 0 || rowCount <= 0) { | |
466 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
467 | return; | |
468 | } | |
469 | ||
470 | fTzidToStrings.setValueDeleter(deleteZoneStrings); | |
471 | ||
472 | for (int32_t row = 0; row < rowCount; row++) { | |
473 | if (strings[row][0].isEmpty()) { | |
474 | continue; | |
475 | } | |
476 | UnicodeString *names = new UnicodeString[ZSIDX_COUNT]; | |
477 | for (int32_t col = 1; col < columnCount; col++) { | |
478 | if (!strings[row][col].isEmpty()) { | |
479 | int32_t typeIdx = -1; | |
480 | switch (col) { | |
481 | case 1: | |
482 | typeIdx = ZSIDX_LONG_STANDARD; | |
483 | break; | |
484 | case 2: | |
485 | typeIdx = ZSIDX_SHORT_STANDARD; | |
486 | break; | |
487 | case 3: | |
488 | typeIdx = ZSIDX_LONG_DAYLIGHT; | |
489 | break; | |
490 | case 4: | |
491 | typeIdx = ZSIDX_SHORT_DAYLIGHT; | |
492 | break; | |
493 | case 5: | |
494 | typeIdx = ZSIDX_LOCATION; | |
495 | break; | |
496 | case 6: | |
497 | typeIdx = ZSIDX_LONG_GENERIC; | |
498 | break; | |
499 | case 7: | |
500 | typeIdx = ZSIDX_SHORT_GENERIC; | |
501 | break; | |
502 | } | |
503 | if (typeIdx != -1) { | |
504 | names[typeIdx].setTo(strings[row][col]); | |
505 | ||
506 | // Put the name into the trie | |
507 | int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeIdx); | |
508 | ZoneStringInfo *zsinf = new ZoneStringInfo(strings[row][0], strings[row][col], (TimeZoneTranslationType)type); | |
509 | fZoneStringsTrie.put(strings[row][col], zsinf, status); | |
510 | if (U_FAILURE(status)) { | |
511 | delete zsinf; | |
512 | goto error_cleanup; | |
513 | } | |
514 | } | |
515 | } | |
516 | } | |
517 | ZoneStrings *zstrings = new ZoneStrings(names, ZSIDX_COUNT, TRUE, NULL, 0, 0); | |
518 | fTzidToStrings.put(strings[row][0], zstrings, status); | |
519 | if (U_FAILURE(status)) { | |
520 | delete zstrings; | |
521 | goto error_cleanup; | |
522 | } | |
523 | } | |
524 | return; | |
525 | ||
526 | error_cleanup: | |
527 | return; | |
528 | } | |
529 | ||
530 | ZoneStringFormat::ZoneStringFormat(const Locale &locale, UErrorCode &status) | |
531 | : fLocale(locale), | |
532 | fTzidToStrings(uhash_compareUnicodeString, NULL, status), | |
533 | fMzidToStrings(uhash_compareUnicodeString, NULL, status), | |
534 | fZoneStringsTrie(TRUE) | |
535 | { | |
536 | if (U_FAILURE(status)) { | |
537 | return; | |
538 | } | |
539 | fTzidToStrings.setValueDeleter(deleteZoneStrings); | |
540 | fMzidToStrings.setValueDeleter(deleteZoneStrings); | |
541 | ||
542 | UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status); | |
543 | zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status); | |
544 | if (U_FAILURE(status)) { | |
545 | // If no locale bundles are available, zoneStrings will be null. | |
546 | // We still want to go through the rest of zone strings initialization, | |
547 | // because generic location format is generated from tzid for the case. | |
548 | // The rest of code should work even zoneStrings is null. | |
549 | status = U_ZERO_ERROR; | |
550 | ures_close(zoneStringsArray); | |
551 | zoneStringsArray = NULL; | |
552 | } | |
553 | ||
554 | StringEnumeration *tzids = NULL; | |
555 | MessageFormat *fallbackFmt = NULL; | |
556 | MessageFormat *regionFmt = NULL; | |
557 | ||
558 | UResourceBundle *zoneItem = NULL; | |
559 | UResourceBundle *metazoneItem = NULL; | |
560 | ||
561 | char zidkey[ZID_KEY_MAX]; | |
562 | const UChar *zstrarray[ZSIDX_COUNT]; | |
563 | const UChar *mzstrarray[ZSIDX_COUNT]; | |
564 | UnicodeString mzPartialLoc[MAX_METAZONES_PER_ZONE][4]; | |
565 | ||
566 | UnicodeString region; | |
567 | getRegion(region); | |
568 | ||
569 | fallbackFmt = getFallbackFormat(locale, status); | |
570 | if (U_FAILURE(status)) { | |
571 | goto error_cleanup; | |
572 | } | |
573 | regionFmt = getRegionFormat(locale, status); | |
574 | if (U_FAILURE(status)) { | |
575 | goto error_cleanup; | |
576 | } | |
577 | ||
578 | tzids = TimeZone::createEnumeration(); | |
579 | const char *tzid; | |
580 | while ((tzid = tzids->next(NULL, status))) { | |
581 | if (U_FAILURE(status)) { | |
582 | goto error_cleanup; | |
583 | } | |
584 | // Skip non-canonical IDs | |
585 | UnicodeString utzid(tzid, -1, US_INV); | |
586 | UnicodeString canonicalID; | |
587 | TimeZone::getCanonicalID(utzid, canonicalID, status); | |
588 | if (U_FAILURE(status)) { | |
589 | // Ignore unknown ID - we should not get here, but just in case. | |
590 | status = U_ZERO_ERROR; | |
591 | continue; | |
592 | } | |
593 | if (utzid != canonicalID) { | |
594 | continue; | |
595 | } | |
596 | ||
597 | uprv_strcpy(zidkey, tzid); | |
598 | ||
599 | // Replace '/' with ':' | |
600 | char *pCity = NULL; | |
601 | char *p = zidkey; | |
602 | while (*p) { | |
603 | if (*p == '/') { | |
604 | *p = ':'; | |
605 | pCity = p + 1; | |
606 | } | |
607 | p++; | |
608 | } | |
609 | ||
610 | if (zoneStringsArray != NULL) { | |
611 | zoneItem = ures_getByKeyWithFallback(zoneStringsArray, zidkey, zoneItem, &status); | |
612 | if (U_FAILURE(status)) { | |
613 | // If failed to open the zone item, create only location string | |
614 | ures_close(zoneItem); | |
615 | zoneItem = NULL; | |
616 | status = U_ZERO_ERROR; | |
617 | } | |
618 | } | |
619 | zstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(zoneItem, gLongStandardTag); | |
620 | zstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(zoneItem, gShortStandardTag); | |
621 | zstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gLongDaylightTag); | |
622 | zstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gShortDaylightTag); | |
623 | zstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(zoneItem, gLongGenericTag); | |
624 | zstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(zoneItem, gShortGenericTag); | |
625 | ||
626 | // Compose location format string | |
627 | UnicodeString location; | |
628 | UnicodeString country; | |
629 | UnicodeString city; | |
630 | UnicodeString countryCode; | |
631 | ZoneMeta::getCanonicalCountry(utzid, countryCode); | |
632 | if (countryCode.isEmpty()) { | |
633 | zstrarray[ZSIDX_LOCATION] = NULL; | |
634 | } else { | |
635 | const UChar* tmpCity = getZoneStringFromBundle(zoneItem, gExemplarCityTag); | |
636 | if (tmpCity != NULL) { | |
637 | city.setTo(TRUE, tmpCity, -1); | |
638 | } else { | |
639 | city.setTo(UnicodeString(pCity, -1, US_INV)); | |
640 | // Replace '_' with ' ' | |
641 | for (int32_t i = 0; i < city.length(); i++) { | |
642 | if (city.charAt(i) == (UChar)0x5F /*'_'*/) { | |
643 | city.setCharAt(i, (UChar)0x20 /*' '*/); | |
644 | } | |
645 | } | |
646 | } | |
647 | getLocalizedCountry(countryCode, locale, country); | |
648 | UnicodeString singleCountry; | |
649 | ZoneMeta::getSingleCountry(utzid, singleCountry); | |
650 | FieldPosition fpos; | |
651 | if (singleCountry.isEmpty()) { | |
652 | Formattable params [] = { | |
653 | Formattable(city), | |
654 | Formattable(country) | |
655 | }; | |
656 | fallbackFmt->format(params, 2, location, fpos, status); | |
657 | } else { | |
658 | // If the zone is only one zone in the country, do not add city | |
659 | Formattable params [] = { | |
660 | Formattable(country) | |
661 | }; | |
662 | regionFmt->format(params, 1, location, fpos, status); | |
663 | } | |
664 | if (U_FAILURE(status)) { | |
665 | goto error_cleanup; | |
666 | } | |
667 | ||
668 | // Workaround for reducing UMR warning in Purify. | |
669 | // Append NULL before calling getTerminatedBuffer() | |
670 | int32_t locLen = location.length(); | |
671 | location.append((UChar)0).truncate(locLen); | |
672 | ||
673 | zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer(); | |
674 | } | |
675 | ||
676 | UBool commonlyUsed = isCommonlyUsed(zoneItem); | |
677 | ||
678 | // Resolve metazones used by this zone | |
679 | int32_t mzPartialLocIdx = 0; | |
680 | const UVector *metazoneMappings = ZoneMeta::getMetazoneMappings(utzid); | |
681 | if (metazoneMappings != NULL) { | |
682 | for (int32_t i = 0; i < metazoneMappings->size(); i++) { | |
683 | const OlsonToMetaMappingEntry *mzmap = (const OlsonToMetaMappingEntry*)metazoneMappings->elementAt(i); | |
684 | UnicodeString mzid(mzmap->mzid); | |
685 | const ZoneStrings *mzStrings = (const ZoneStrings*)fMzidToStrings.get(mzid); | |
686 | if (mzStrings == NULL) { | |
687 | // If the metazone strings are not yet processed, do it now. | |
688 | char mzidkey[ZID_KEY_MAX]; | |
689 | uprv_strcpy(mzidkey, gMetazoneIdPrefix); | |
690 | u_UCharsToChars(mzmap->mzid, mzidkey + MZID_PREFIX_LEN, u_strlen(mzmap->mzid) + 1); | |
691 | metazoneItem = ures_getByKeyWithFallback(zoneStringsArray, mzidkey, metazoneItem, &status); | |
692 | if (U_FAILURE(status)) { | |
693 | // No resources available for this metazone | |
694 | // Resource bundle will be cleaned up after end of the loop. | |
695 | status = U_ZERO_ERROR; | |
696 | continue; | |
697 | } | |
698 | UBool mzCommonlyUsed = isCommonlyUsed(metazoneItem); | |
699 | mzstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(metazoneItem, gLongStandardTag); | |
700 | mzstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(metazoneItem, gShortStandardTag); | |
701 | mzstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gLongDaylightTag); | |
702 | mzstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gShortDaylightTag); | |
703 | mzstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(metazoneItem, gLongGenericTag); | |
704 | mzstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(metazoneItem, gShortGenericTag); | |
705 | mzstrarray[ZSIDX_LOCATION] = NULL; | |
706 | ||
707 | int32_t lastNonNullIdx = ZSIDX_COUNT - 1; | |
708 | while (lastNonNullIdx >= 0) { | |
709 | if (mzstrarray[lastNonNullIdx] != NULL) { | |
710 | break; | |
711 | } | |
712 | lastNonNullIdx--; | |
713 | } | |
714 | UnicodeString *strings_mz = NULL; | |
715 | ZoneStrings *tmp_mzStrings = NULL; | |
716 | if (lastNonNullIdx >= 0) { | |
717 | // Create UnicodeString array and put strings to the zone string trie | |
718 | strings_mz = new UnicodeString[lastNonNullIdx + 1]; | |
719 | ||
720 | UnicodeString preferredIdForLocale; | |
721 | ZoneMeta::getZoneIdByMetazone(mzid, region, preferredIdForLocale); | |
722 | ||
723 | for (int32_t typeidx = 0; typeidx <= lastNonNullIdx; typeidx++) { | |
724 | if (mzstrarray[typeidx] != NULL) { | |
725 | strings_mz[typeidx].setTo(TRUE, mzstrarray[typeidx], -1); | |
726 | ||
727 | // Add a metazone string to the zone string trie | |
728 | int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeidx); | |
729 | ZoneStringInfo *zsinfo = new ZoneStringInfo(preferredIdForLocale, strings_mz[typeidx], (TimeZoneTranslationType)type); | |
730 | fZoneStringsTrie.put(strings_mz[typeidx], zsinfo, status); | |
731 | if (U_FAILURE(status)) { | |
732 | delete []strings_mz; | |
733 | goto error_cleanup; | |
734 | } | |
735 | } | |
736 | } | |
737 | tmp_mzStrings = new ZoneStrings(strings_mz, lastNonNullIdx + 1, mzCommonlyUsed, NULL, 0, 0); | |
738 | } else { | |
739 | // Create ZoneStrings with empty contents | |
740 | tmp_mzStrings = new ZoneStrings(NULL, 0, FALSE, NULL, 0, 0); | |
741 | } | |
742 | ||
743 | fMzidToStrings.put(mzid, tmp_mzStrings, status); | |
744 | if (U_FAILURE(status)) { | |
745 | goto error_cleanup; | |
746 | } | |
747 | ||
748 | mzStrings = tmp_mzStrings; | |
749 | } | |
750 | ||
751 | // Compose generic partial location format | |
752 | UnicodeString lg; | |
753 | UnicodeString sg; | |
754 | ||
755 | mzStrings->getString(ZSIDX_LONG_GENERIC, lg); | |
756 | mzStrings->getString(ZSIDX_SHORT_GENERIC, sg); | |
757 | ||
758 | if (!lg.isEmpty() || !sg.isEmpty()) { | |
759 | UBool addMzPartialLocationNames = TRUE; | |
760 | for (int32_t j = 0; j < mzPartialLocIdx; j++) { | |
761 | if (mzPartialLoc[j][0] == mzid) { | |
762 | // already processed | |
763 | addMzPartialLocationNames = FALSE; | |
764 | break; | |
765 | } | |
766 | } | |
767 | if (addMzPartialLocationNames) { | |
768 | UnicodeString *locationPart = NULL; | |
769 | // Check if the zone is the preferred zone for the territory associated with the zone | |
770 | UnicodeString preferredID; | |
771 | ZoneMeta::getZoneIdByMetazone(mzid, countryCode, preferredID); | |
772 | if (utzid == preferredID) { | |
773 | // Use country for the location | |
774 | locationPart = &country; | |
775 | } else { | |
776 | // Use city for the location | |
777 | locationPart = &city; | |
778 | } | |
779 | // Reset the partial location string array | |
780 | mzPartialLoc[mzPartialLocIdx][0].setTo(mzid); | |
781 | mzPartialLoc[mzPartialLocIdx][1].remove(); | |
782 | mzPartialLoc[mzPartialLocIdx][2].remove(); | |
783 | mzPartialLoc[mzPartialLocIdx][3].remove(); | |
784 | ||
785 | if (locationPart != NULL) { | |
786 | FieldPosition fpos; | |
787 | if (!lg.isEmpty()) { | |
788 | Formattable params [] = { | |
789 | Formattable(*locationPart), | |
790 | Formattable(lg) | |
791 | }; | |
792 | fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][1], fpos, status); | |
793 | } | |
794 | if (!sg.isEmpty()) { | |
795 | Formattable params [] = { | |
796 | Formattable(*locationPart), | |
797 | Formattable(sg) | |
798 | }; | |
799 | fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][2], fpos, status); | |
800 | if (mzStrings->isShortFormatCommonlyUsed()) { | |
801 | mzPartialLoc[mzPartialLocIdx][3].setTo(TRUE, gCommonlyUsedTrue, -1); | |
802 | } | |
803 | } | |
804 | if (U_FAILURE(status)) { | |
805 | goto error_cleanup; | |
806 | } | |
807 | } | |
808 | mzPartialLocIdx++; | |
809 | } | |
810 | } | |
811 | } | |
812 | } | |
813 | // Collected names for a zone | |
814 | ||
815 | // Create UnicodeString array for localized zone strings | |
816 | int32_t lastIdx = ZSIDX_COUNT - 1; | |
817 | while (lastIdx >= 0) { | |
818 | if (zstrarray[lastIdx] != NULL) { | |
819 | break; | |
820 | } | |
821 | lastIdx--; | |
822 | } | |
823 | UnicodeString *strings = NULL; | |
824 | int32_t stringsCount = lastIdx + 1; | |
825 | ||
826 | if (stringsCount > 0) { | |
827 | strings = new UnicodeString[stringsCount]; | |
828 | for (int32_t i = 0; i < stringsCount; i++) { | |
829 | if (zstrarray[i] != NULL) { | |
830 | strings[i].setTo(zstrarray[i], -1); | |
831 | ||
832 | // Add names to the trie | |
833 | int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)i); | |
834 | ZoneStringInfo *zsinfo = new ZoneStringInfo(utzid, strings[i], (TimeZoneTranslationType)type); | |
835 | fZoneStringsTrie.put(strings[i], zsinfo, status); | |
836 | if (U_FAILURE(status)) { | |
837 | delete zsinfo; | |
838 | delete[] strings; | |
839 | goto error_cleanup; | |
840 | } | |
841 | } | |
842 | } | |
843 | } | |
844 | ||
845 | // Create UnicodeString array for generic partial location strings | |
846 | UnicodeString **genericPartialLocationNames = NULL; | |
847 | int32_t genericPartialRowCount = mzPartialLocIdx; | |
848 | int32_t genericPartialColCount = 4; | |
849 | ||
850 | if (genericPartialRowCount != 0) { | |
851 | genericPartialLocationNames = (UnicodeString**)uprv_malloc(genericPartialRowCount * sizeof(UnicodeString*)); | |
852 | if (genericPartialLocationNames == NULL) { | |
853 | status = U_MEMORY_ALLOCATION_ERROR; | |
854 | delete[] strings; | |
855 | goto error_cleanup; | |
856 | } | |
857 | for (int32_t i = 0; i < genericPartialRowCount; i++) { | |
858 | genericPartialLocationNames[i] = new UnicodeString[genericPartialColCount]; | |
859 | for (int32_t j = 0; j < genericPartialColCount; j++) { | |
860 | genericPartialLocationNames[i][j].setTo(mzPartialLoc[i][j]); | |
861 | // Add names to the trie | |
862 | if ((j == 1 || j == 2) &&!genericPartialLocationNames[i][j].isEmpty()) { | |
863 | ZoneStringInfo *zsinfo; | |
864 | TimeZoneTranslationType type = (j == 1) ? GENERIC_LONG : GENERIC_SHORT; | |
865 | zsinfo = new ZoneStringInfo(utzid, genericPartialLocationNames[i][j], type); | |
866 | fZoneStringsTrie.put(genericPartialLocationNames[i][j], zsinfo, status); | |
867 | if (U_FAILURE(status)) { | |
868 | delete[] genericPartialLocationNames[i]; | |
869 | uprv_free(genericPartialLocationNames); | |
870 | delete[] strings; | |
871 | goto error_cleanup; | |
872 | } | |
873 | } | |
874 | } | |
875 | } | |
876 | } | |
877 | ||
878 | // Finally, create ZoneStrings instance and put it into the tzidToStinrgs map | |
879 | ZoneStrings *zstrings = new ZoneStrings(strings, stringsCount, commonlyUsed, | |
880 | genericPartialLocationNames, genericPartialRowCount, genericPartialColCount); | |
881 | ||
882 | fTzidToStrings.put(utzid, zstrings, status); | |
883 | if (U_FAILURE(status)) { | |
884 | delete zstrings; | |
885 | goto error_cleanup; | |
886 | } | |
887 | } | |
888 | ||
889 | error_cleanup: | |
890 | if (fallbackFmt != NULL) { | |
891 | delete fallbackFmt; | |
892 | } | |
893 | if (regionFmt != NULL) { | |
894 | delete regionFmt; | |
895 | } | |
896 | if (tzids != NULL) { | |
897 | delete tzids; | |
898 | } | |
899 | ures_close(zoneItem); | |
900 | ures_close(metazoneItem); | |
901 | ures_close(zoneStringsArray); | |
902 | } | |
903 | ||
904 | ZoneStringFormat::~ZoneStringFormat() { | |
905 | } | |
906 | ||
907 | SafeZoneStringFormatPtr* | |
908 | ZoneStringFormat::getZoneStringFormat(const Locale& locale, UErrorCode &status) { | |
909 | umtx_lock(&gZSFCacheLock); | |
910 | if (gZoneStringFormatCache == NULL) { | |
911 | gZoneStringFormatCache = new ZSFCache(10 /* capacity */); | |
912 | ucln_i18n_registerCleanup(UCLN_I18N_ZSFORMAT, zoneStringFormat_cleanup); | |
913 | } | |
914 | umtx_unlock(&gZSFCacheLock); | |
915 | ||
916 | return gZoneStringFormatCache->get(locale, status); | |
917 | } | |
918 | ||
919 | ||
920 | UnicodeString** | |
921 | ZoneStringFormat::createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const { | |
922 | if (U_FAILURE(status)) { | |
923 | return NULL; | |
924 | } | |
925 | UnicodeString **result = NULL; | |
926 | rowCount = 0; | |
927 | colCount = 0; | |
928 | ||
929 | // Collect canonical time zone IDs | |
930 | UVector canonicalIDs(uhash_deleteUnicodeString, uhash_compareUnicodeString, status); | |
931 | if (U_FAILURE(status)) { | |
932 | return NULL; | |
933 | } | |
934 | StringEnumeration *tzids = TimeZone::createEnumeration(); | |
935 | const UChar *tzid; | |
936 | while ((tzid = tzids->unext(NULL, status))) { | |
937 | if (U_FAILURE(status)) { | |
938 | delete tzids; | |
939 | return NULL; | |
940 | } | |
941 | UnicodeString utzid(tzid); | |
942 | UnicodeString canonicalID; | |
943 | TimeZone::getCanonicalID(UnicodeString(tzid), canonicalID, status); | |
944 | if (U_FAILURE(status)) { | |
945 | // Ignore unknown ID - we should not get here, but just in case. | |
946 | status = U_ZERO_ERROR; | |
947 | continue; | |
948 | } | |
949 | if (utzid == canonicalID) { | |
950 | canonicalIDs.addElement(new UnicodeString(utzid), status); | |
951 | if (U_FAILURE(status)) { | |
952 | delete tzids; | |
953 | return NULL; | |
954 | } | |
955 | } | |
956 | } | |
957 | delete tzids; | |
958 | ||
959 | // Allocate array | |
960 | result = (UnicodeString**)uprv_malloc(canonicalIDs.size() * sizeof(UnicodeString*)); | |
961 | if (result == NULL) { | |
962 | status = U_MEMORY_ALLOCATION_ERROR; | |
963 | return NULL; | |
964 | } | |
965 | for (int32_t i = 0; i < canonicalIDs.size(); i++) { | |
966 | result[i] = new UnicodeString[8]; | |
967 | UnicodeString *id = (UnicodeString*)canonicalIDs.elementAt(i); | |
968 | result[i][0].setTo(*id); | |
969 | getLongStandard(*id, date, result[i][1]); | |
970 | getShortStandard(*id, date, FALSE, result[i][2]); | |
971 | getLongDaylight(*id, date, result[i][3]); | |
972 | getShortDaylight(*id, date, FALSE, result[i][4]); | |
973 | getGenericLocation(*id, result[i][5]); | |
974 | getLongGenericNonLocation(*id, date, result[i][6]); | |
975 | getShortGenericNonLocation(*id, date, FALSE, result[i][7]); | |
976 | } | |
977 | ||
978 | rowCount = canonicalIDs.size(); | |
979 | colCount = 8; | |
980 | return result; | |
981 | } | |
982 | ||
983 | UnicodeString& | |
984 | ZoneStringFormat::getSpecificLongString(const Calendar &cal, UnicodeString &result, | |
985 | UErrorCode &status) const { | |
986 | result.remove(); | |
987 | if (U_FAILURE(status)) { | |
988 | return result; | |
989 | } | |
990 | UnicodeString tzid; | |
991 | cal.getTimeZone().getID(tzid); | |
992 | UDate date = cal.getTime(status); | |
993 | if (cal.get(UCAL_DST_OFFSET, status) == 0) { | |
994 | return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /*not used*/, result); | |
995 | } else { | |
996 | return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /*not used*/, result); | |
997 | } | |
998 | } | |
999 | ||
1000 | UnicodeString& | |
1001 | ZoneStringFormat::getSpecificShortString(const Calendar &cal, UBool commonlyUsedOnly, | |
1002 | UnicodeString &result, UErrorCode &status) const { | |
1003 | result.remove(); | |
1004 | if (U_FAILURE(status)) { | |
1005 | return result; | |
1006 | } | |
1007 | UnicodeString tzid; | |
1008 | cal.getTimeZone().getID(tzid); | |
1009 | UDate date = cal.getTime(status); | |
1010 | if (cal.get(UCAL_DST_OFFSET, status) == 0) { | |
1011 | return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result); | |
1012 | } else { | |
1013 | return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result); | |
1014 | } | |
1015 | } | |
1016 | ||
1017 | UnicodeString& | |
1018 | ZoneStringFormat::getGenericLongString(const Calendar &cal, UnicodeString &result, | |
1019 | UErrorCode &status) const { | |
1020 | return getGenericString(cal, FALSE /*long*/, FALSE /* not used */, result, status); | |
1021 | } | |
1022 | ||
1023 | UnicodeString& | |
1024 | ZoneStringFormat::getGenericShortString(const Calendar &cal, UBool commonlyUsedOnly, | |
1025 | UnicodeString &result, UErrorCode &status) const { | |
1026 | return getGenericString(cal, TRUE /*short*/, commonlyUsedOnly, result, status); | |
1027 | } | |
1028 | ||
1029 | UnicodeString& | |
1030 | ZoneStringFormat::getGenericLocationString(const Calendar &cal, UnicodeString &result, | |
1031 | UErrorCode &status) const { | |
1032 | UnicodeString tzid; | |
1033 | cal.getTimeZone().getID(tzid); | |
1034 | UDate date = cal.getTime(status); | |
1035 | return getString(tzid, ZSIDX_LOCATION, date, FALSE /*not used*/, result); | |
1036 | } | |
1037 | ||
1038 | const ZoneStringInfo* | |
1039 | ZoneStringFormat::findSpecificLong(const UnicodeString &text, int32_t start, | |
1040 | int32_t &matchLength, UErrorCode &status) const { | |
1041 | return find(text, start, STANDARD_LONG | DAYLIGHT_LONG, matchLength, status); | |
1042 | } | |
1043 | ||
1044 | const ZoneStringInfo* | |
1045 | ZoneStringFormat::findSpecificShort(const UnicodeString &text, int32_t start, | |
1046 | int32_t &matchLength, UErrorCode &status) const { | |
1047 | return find(text, start, STANDARD_SHORT | DAYLIGHT_SHORT, matchLength, status); | |
1048 | } | |
1049 | ||
1050 | const ZoneStringInfo* | |
1051 | ZoneStringFormat::findGenericLong(const UnicodeString &text, int32_t start, | |
1052 | int32_t &matchLength, UErrorCode &status) const { | |
1053 | return find(text, start, GENERIC_LONG | STANDARD_LONG | LOCATION, matchLength, status); | |
1054 | } | |
1055 | ||
1056 | const ZoneStringInfo* | |
1057 | ZoneStringFormat::findGenericShort(const UnicodeString &text, int32_t start, | |
1058 | int32_t &matchLength, UErrorCode &status) const { | |
1059 | return find(text, start, GENERIC_SHORT | STANDARD_SHORT | LOCATION, matchLength, status); | |
1060 | } | |
1061 | ||
1062 | const ZoneStringInfo* | |
1063 | ZoneStringFormat::findGenericLocation(const UnicodeString &text, int32_t start, | |
1064 | int32_t &matchLength, UErrorCode &status) const { | |
1065 | return find(text, start, LOCATION, matchLength, status); | |
1066 | } | |
1067 | ||
1068 | UnicodeString& | |
1069 | ZoneStringFormat::getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date, | |
1070 | UBool commonlyUsedOnly, UnicodeString& result) const { | |
1071 | result.remove(); | |
1072 | ||
1073 | // ICU's own array does not have entries for aliases | |
1074 | UnicodeString canonicalID; | |
1075 | UErrorCode status = U_ZERO_ERROR; | |
1076 | TimeZone::getCanonicalID(tzid, canonicalID, status); | |
1077 | if (U_FAILURE(status)) { | |
1078 | // Unknown ID, but users might have their own data. | |
1079 | canonicalID.setTo(tzid); | |
1080 | } | |
1081 | ||
1082 | if (fTzidToStrings.count() > 0) { | |
1083 | ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID); | |
1084 | if (zstrings != NULL) { | |
1085 | switch (typeIdx) { | |
1086 | case ZSIDX_LONG_STANDARD: | |
1087 | case ZSIDX_LONG_DAYLIGHT: | |
1088 | case ZSIDX_LONG_GENERIC: | |
1089 | case ZSIDX_LOCATION: | |
1090 | zstrings->getString(typeIdx, result); | |
1091 | break; | |
1092 | case ZSIDX_SHORT_STANDARD: | |
1093 | case ZSIDX_SHORT_DAYLIGHT: | |
1094 | case ZSIDX_COUNT: //added to avoid warning | |
1095 | case ZSIDX_SHORT_GENERIC: | |
1096 | if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) { | |
1097 | zstrings->getString(typeIdx, result); | |
1098 | } | |
1099 | break; | |
1100 | } | |
1101 | } | |
1102 | } | |
1103 | if (result.isEmpty() && fMzidToStrings.count() > 0 && typeIdx != ZSIDX_LOCATION) { | |
1104 | // Try metazone | |
1105 | UnicodeString mzid; | |
1106 | ZoneMeta::getMetazoneID(canonicalID, date, mzid); | |
1107 | if (!mzid.isEmpty()) { | |
1108 | ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings.get(mzid); | |
1109 | if (mzstrings != NULL) { | |
1110 | switch (typeIdx) { | |
1111 | case ZSIDX_LONG_STANDARD: | |
1112 | case ZSIDX_LONG_DAYLIGHT: | |
1113 | case ZSIDX_LONG_GENERIC: | |
1114 | case ZSIDX_LOCATION: | |
1115 | mzstrings->getString(typeIdx, result); | |
1116 | break; | |
1117 | case ZSIDX_SHORT_STANDARD: | |
1118 | case ZSIDX_SHORT_DAYLIGHT: | |
1119 | case ZSIDX_COUNT: //added to avoid warning | |
1120 | case ZSIDX_SHORT_GENERIC: | |
1121 | if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) { | |
1122 | mzstrings->getString(typeIdx, result); | |
1123 | } | |
1124 | break; | |
1125 | } | |
1126 | } | |
1127 | } | |
1128 | } | |
1129 | return result; | |
1130 | } | |
1131 | ||
1132 | UnicodeString& | |
1133 | ZoneStringFormat::getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly, | |
1134 | UnicodeString &result, UErrorCode &status) const { | |
1135 | result.remove(); | |
1136 | UDate time = cal.getTime(status); | |
1137 | if (U_FAILURE(status)) { | |
1138 | return result; | |
1139 | } | |
1140 | const TimeZone &tz = cal.getTimeZone(); | |
1141 | UnicodeString tzid; | |
1142 | tz.getID(tzid); | |
1143 | ||
1144 | // ICU's own array does not have entries for aliases | |
1145 | UnicodeString canonicalID; | |
1146 | TimeZone::getCanonicalID(tzid, canonicalID, status); | |
1147 | if (U_FAILURE(status)) { | |
1148 | // Unknown ID, but users might have their own data. | |
1149 | status = U_ZERO_ERROR; | |
1150 | canonicalID.setTo(tzid); | |
1151 | } | |
1152 | ||
1153 | ZoneStrings *zstrings = NULL; | |
1154 | if (fTzidToStrings.count() > 0) { | |
1155 | zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID); | |
1156 | if (zstrings != NULL) { | |
1157 | if (isShort) { | |
1158 | if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) { | |
1159 | zstrings->getString(ZSIDX_SHORT_GENERIC, result); | |
1160 | } | |
1161 | } else { | |
1162 | zstrings->getString(ZSIDX_LONG_GENERIC, result); | |
1163 | } | |
1164 | } | |
1165 | } | |
1166 | if (result.isEmpty() && fMzidToStrings.count() > 0) { | |
1167 | // try metazone | |
1168 | int32_t raw, sav; | |
1169 | UnicodeString mzid; | |
1170 | ZoneMeta::getMetazoneID(canonicalID, time, mzid); | |
1171 | if (!mzid.isEmpty()) { | |
1172 | UBool useStandard = FALSE; | |
1173 | sav = cal.get(UCAL_DST_OFFSET, status); | |
1174 | if (U_FAILURE(status)) { | |
1175 | return result; | |
1176 | } | |
1177 | if (sav == 0) { | |
1178 | useStandard = TRUE; | |
1179 | // Check if the zone actually uses daylight saving time around the time | |
1180 | TimeZone *tmptz = tz.clone(); | |
1181 | BasicTimeZone *btz = NULL; | |
1182 | if (tmptz->getDynamicClassID() == OlsonTimeZone::getStaticClassID() | |
1183 | || tmptz->getDynamicClassID() == SimpleTimeZone::getStaticClassID() | |
1184 | || tmptz->getDynamicClassID() == RuleBasedTimeZone::getStaticClassID() | |
1185 | || tmptz->getDynamicClassID() == VTimeZone::getStaticClassID()) { | |
1186 | btz = (BasicTimeZone*)tmptz; | |
1187 | } | |
1188 | ||
1189 | if (btz != NULL) { | |
1190 | TimeZoneTransition before; | |
1191 | UBool beforTrs = btz->getPreviousTransition(time, TRUE, before); | |
1192 | if (beforTrs | |
1193 | && (time - before.getTime() < kDstCheckRange) | |
1194 | && before.getFrom()->getDSTSavings() != 0) { | |
1195 | useStandard = FALSE; | |
1196 | } else { | |
1197 | TimeZoneTransition after; | |
1198 | UBool afterTrs = btz->getNextTransition(time, FALSE, after); | |
1199 | if (afterTrs | |
1200 | && (after.getTime() - time < kDstCheckRange) | |
1201 | && after.getTo()->getDSTSavings() != 0) { | |
1202 | useStandard = FALSE; | |
1203 | } | |
1204 | } | |
1205 | } else { | |
1206 | // If not BasicTimeZone... only if the instance is not an ICU's implementation. | |
1207 | // We may get a wrong answer in edge case, but it should practically work OK. | |
1208 | tmptz->getOffset(time - kDstCheckRange, FALSE, raw, sav, status); | |
1209 | if (sav != 0) { | |
1210 | useStandard = FALSE; | |
1211 | } else { | |
1212 | tmptz->getOffset(time + kDstCheckRange, FALSE, raw, sav, status); | |
1213 | if (sav != 0){ | |
1214 | useStandard = FALSE; | |
1215 | } | |
1216 | } | |
1217 | if (U_FAILURE(status)) { | |
1218 | delete tmptz; | |
1219 | result.remove(); | |
1220 | return result; | |
1221 | } | |
1222 | } | |
1223 | delete tmptz; | |
1224 | } | |
1225 | if (useStandard) { | |
1226 | getString(canonicalID, (isShort ? ZSIDX_SHORT_STANDARD : ZSIDX_LONG_STANDARD), | |
1227 | time, commonlyUsedOnly, result); | |
1228 | ||
1229 | // Note: | |
1230 | // In CLDR 1.5.1, a same localization is used for both generic and standard | |
1231 | // for some metazones in some locales. This is actually data bugs and should | |
1232 | // be resolved in later versions of CLDR. For now, we check if the standard | |
1233 | // name is different from its generic name below. | |
1234 | if (!result.isEmpty()) { | |
1235 | UnicodeString genericNonLocation; | |
1236 | getString(canonicalID, (isShort ? ZSIDX_SHORT_GENERIC : ZSIDX_LONG_GENERIC), | |
1237 | time, commonlyUsedOnly, genericNonLocation); | |
1238 | if (!genericNonLocation.isEmpty() && result == genericNonLocation) { | |
1239 | result.remove(); | |
1240 | } | |
1241 | } | |
1242 | } | |
1243 | if (result.isEmpty()) { | |
1244 | ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings.get(mzid); | |
1245 | if (mzstrings != NULL) { | |
1246 | if (isShort) { | |
1247 | if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) { | |
1248 | mzstrings->getString(ZSIDX_SHORT_GENERIC, result); | |
1249 | } | |
1250 | } else { | |
1251 | mzstrings->getString(ZSIDX_LONG_GENERIC, result); | |
1252 | } | |
1253 | } | |
1254 | if (!result.isEmpty()) { | |
1255 | // Check if the offsets at the given time matches the preferred zone's offsets | |
1256 | UnicodeString preferredId; | |
1257 | UnicodeString region; | |
1258 | ZoneMeta::getZoneIdByMetazone(mzid, getRegion(region), preferredId); | |
1259 | if (canonicalID != preferredId) { | |
1260 | // Check if the offsets at the given time are identical with the preferred zone | |
1261 | raw = cal.get(UCAL_ZONE_OFFSET, status); | |
1262 | if (U_FAILURE(status)) { | |
1263 | result.remove(); | |
1264 | return result; | |
1265 | } | |
1266 | TimeZone *preferredZone = TimeZone::createTimeZone(preferredId); | |
1267 | int32_t prfRaw, prfSav; | |
1268 | // Check offset in preferred time zone with wall time. | |
1269 | // With getOffset(time, false, preferredOffsets), | |
1270 | // you may get incorrect results because of time overlap at DST->STD | |
1271 | // transition. | |
1272 | preferredZone->getOffset(time + raw + sav, TRUE, prfRaw, prfSav, status); | |
1273 | delete preferredZone; | |
1274 | ||
1275 | if (U_FAILURE(status)) { | |
1276 | result.remove(); | |
1277 | return result; | |
1278 | } | |
1279 | if ((raw != prfRaw || sav != prfSav) && zstrings != NULL) { | |
1280 | // Use generic partial location string as fallback | |
1281 | zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result); | |
1282 | } | |
1283 | } | |
1284 | } | |
1285 | } | |
1286 | } | |
1287 | } | |
1288 | if (result.isEmpty()) { | |
1289 | // Use location format as the final fallback | |
1290 | getString(canonicalID, ZSIDX_LOCATION, time, FALSE /*not used*/, result); | |
1291 | } | |
1292 | ||
1293 | return result; | |
1294 | } | |
1295 | ||
1296 | UnicodeString& | |
1297 | ZoneStringFormat::getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort, | |
1298 | UDate date, UBool commonlyUsedOnly, UnicodeString &result) const { | |
1299 | result.remove(); | |
1300 | if (fTzidToStrings.count() <= 0) { | |
1301 | return result; | |
1302 | } | |
1303 | ||
1304 | UnicodeString canonicalID; | |
1305 | UErrorCode status = U_ZERO_ERROR; | |
1306 | TimeZone::getCanonicalID(tzid, canonicalID, status); | |
1307 | if (U_FAILURE(status)) { | |
1308 | // Unknown ID, so no corresponding meta data. | |
1309 | return result; | |
1310 | } | |
1311 | ||
1312 | UnicodeString mzid; | |
1313 | ZoneMeta::getMetazoneID(canonicalID, date, mzid); | |
1314 | ||
1315 | if (!mzid.isEmpty()) { | |
1316 | ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings.get(canonicalID); | |
1317 | if (zstrings != NULL) { | |
1318 | zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result); | |
1319 | } | |
1320 | } | |
1321 | return result; | |
1322 | } | |
1323 | ||
1324 | const ZoneStringInfo* | |
1325 | ZoneStringFormat::find(const UnicodeString &text, int32_t start, int32_t types, | |
1326 | int32_t &matchLength, UErrorCode &status) const { | |
1327 | matchLength = 0; | |
1328 | if (U_FAILURE(status)) { | |
1329 | return NULL; | |
1330 | } | |
1331 | if (fZoneStringsTrie.isEmpty()) { | |
1332 | return NULL; | |
1333 | } | |
1334 | const ZoneStringInfo *result = NULL; | |
1335 | const ZoneStringInfo *fallback = NULL; | |
1336 | int32_t fallbackMatchLen = 0; | |
1337 | ||
1338 | ZoneStringSearchResultHandler handler(status); | |
1339 | fZoneStringsTrie.search(text, start, (TextTrieMapSearchResultHandler*)&handler, status); | |
1340 | if (U_SUCCESS(status)) { | |
1341 | int32_t numMatches = handler.countMatches(); | |
1342 | for (int32_t i = 0; i < numMatches; i++) { | |
1343 | int32_t tmpMatchLen; | |
1344 | const ZoneStringInfo *tmp = handler.getMatch(i, tmpMatchLen); | |
1345 | if ((types & tmp->fType) != 0) { | |
1346 | if (result == NULL || matchLength < tmpMatchLen) { | |
1347 | result = tmp; | |
1348 | matchLength = tmpMatchLen; | |
1349 | } else if (matchLength == tmpMatchLen) { | |
1350 | // Tie breaker - there are some examples that a | |
1351 | // long standard name is identical with a location | |
1352 | // name - for example, "Uruguay Time". In this case, | |
1353 | // we interpret it as generic, not specific. | |
1354 | if (tmp->isGeneric() && !result->isGeneric()) { | |
1355 | result = tmp; | |
1356 | } | |
1357 | } | |
1358 | } else if (result == NULL) { | |
1359 | if (fallback == NULL || fallbackMatchLen < tmpMatchLen) { | |
1360 | fallback = tmp; | |
1361 | fallbackMatchLen = tmpMatchLen; | |
1362 | } else if (fallbackMatchLen == tmpMatchLen) { | |
1363 | if (tmp->isGeneric() && !fallback->isGeneric()) { | |
1364 | fallback = tmp; | |
1365 | } | |
1366 | } | |
1367 | } | |
1368 | } | |
1369 | if (result == NULL && fallback != NULL) { | |
1370 | result = fallback; | |
1371 | matchLength = fallbackMatchLen; | |
1372 | } | |
1373 | } | |
1374 | return result; | |
1375 | } | |
1376 | ||
1377 | ||
1378 | UnicodeString& | |
1379 | ZoneStringFormat::getRegion(UnicodeString ®ion) const { | |
1380 | const char* country = fLocale.getCountry(); | |
1381 | // TODO: Utilize addLikelySubtag in Locale to resolve default region | |
1382 | // when the implementation is ready. | |
1383 | region.setTo(UnicodeString(country, -1, US_INV)); | |
1384 | return region; | |
1385 | } | |
1386 | ||
1387 | MessageFormat* | |
1388 | ZoneStringFormat::getFallbackFormat(const Locale &locale, UErrorCode &status) { | |
1389 | if (U_FAILURE(status)) { | |
1390 | return NULL; | |
1391 | } | |
1392 | UnicodeString pattern(TRUE, gDefFallbackPattern, -1); | |
1393 | UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status); | |
1394 | zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status); | |
1395 | int32_t len; | |
1396 | const UChar *flbkfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gFallbackFormatTag, &len, &status); | |
1397 | if (U_SUCCESS(status)) { | |
1398 | pattern.setTo(flbkfmt); | |
1399 | } else { | |
1400 | status = U_ZERO_ERROR; | |
1401 | } | |
1402 | ures_close(zoneStringsArray); | |
1403 | ||
1404 | MessageFormat *fallbackFmt = new MessageFormat(pattern, status); | |
1405 | return fallbackFmt; | |
1406 | } | |
1407 | ||
1408 | MessageFormat* | |
1409 | ZoneStringFormat::getRegionFormat(const Locale& locale, UErrorCode &status) { | |
1410 | if (U_FAILURE(status)) { | |
1411 | return NULL; | |
1412 | } | |
1413 | UnicodeString pattern(TRUE, gDefRegionPattern, -1); | |
1414 | UResourceBundle *zoneStringsArray = ures_open(NULL, locale.getName(), &status); | |
1415 | zoneStringsArray = ures_getByKeyWithFallback(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status); | |
1416 | int32_t len; | |
1417 | const UChar *regionfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gRegionFormatTag, &len, &status); | |
1418 | if (U_SUCCESS(status)) { | |
1419 | pattern.setTo(regionfmt); | |
1420 | } else { | |
1421 | status = U_ZERO_ERROR; | |
1422 | } | |
1423 | ures_close(zoneStringsArray); | |
1424 | ||
1425 | MessageFormat *regionFmt = new MessageFormat(pattern, status); | |
1426 | return regionFmt; | |
1427 | } | |
1428 | ||
1429 | const UChar* | |
1430 | ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key) { | |
1431 | const UChar *str = NULL; | |
1432 | if (zoneitem != NULL) { | |
1433 | UErrorCode status = U_ZERO_ERROR; | |
1434 | int32_t len; | |
1435 | str = ures_getStringByKeyWithFallback(zoneitem, key, &len, &status); | |
1436 | if (U_FAILURE(status)) { | |
1437 | str = NULL; | |
1438 | } | |
1439 | } | |
1440 | return str; | |
1441 | } | |
1442 | ||
1443 | UBool | |
1444 | ZoneStringFormat::isCommonlyUsed(const UResourceBundle *zoneitem) { | |
1445 | if (zoneitem == NULL) { | |
1446 | return TRUE; | |
1447 | } | |
1448 | ||
1449 | UBool commonlyUsed = FALSE; | |
1450 | UErrorCode status = U_ZERO_ERROR; | |
1451 | UResourceBundle *cuRes = ures_getByKey(zoneitem, gCommonlyUsedTag, NULL, &status); | |
1452 | int32_t cuValue = ures_getInt(cuRes, &status); | |
1453 | if (U_SUCCESS(status)) { | |
1454 | if (cuValue == 1) { | |
1455 | commonlyUsed = TRUE; | |
1456 | } | |
1457 | } | |
1458 | ures_close(cuRes); | |
1459 | return commonlyUsed; | |
1460 | } | |
1461 | ||
1462 | UnicodeString& | |
1463 | ZoneStringFormat::getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale, UnicodeString &displayCountry) { | |
1464 | // We do not want to use display country names only from the target language bundle | |
1465 | // Note: we should do this in better way. | |
1466 | displayCountry.remove(); | |
1467 | int32_t ccLen = countryCode.length(); | |
1468 | if (ccLen > 0 && ccLen < ULOC_COUNTRY_CAPACITY) { | |
1469 | UErrorCode status = U_ZERO_ERROR; | |
1470 | UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status); | |
1471 | if (U_SUCCESS(status)) { | |
1472 | const char *bundleLocStr = ures_getLocale(localeBundle, &status); | |
1473 | if (U_SUCCESS(status) && uprv_strlen(bundleLocStr) > 0) { | |
1474 | Locale bundleLoc(bundleLocStr); | |
1475 | if (uprv_strcmp(bundleLocStr, "root") != 0 && uprv_strcmp(bundleLoc.getLanguage(), locale.getLanguage()) == 0) { | |
1476 | // Create a fake locale strings | |
1477 | char tmpLocStr[ULOC_COUNTRY_CAPACITY + 3]; | |
1478 | uprv_strcpy(tmpLocStr, "xx_"); | |
1479 | u_UCharsToChars(countryCode.getBuffer(), &tmpLocStr[3], ccLen); | |
1480 | tmpLocStr[3 + ccLen] = 0; | |
1481 | ||
1482 | Locale tmpLoc(tmpLocStr); | |
1483 | tmpLoc.getDisplayCountry(locale, displayCountry); | |
1484 | } | |
1485 | } | |
1486 | } | |
1487 | ures_close(localeBundle); | |
1488 | } | |
1489 | if (displayCountry.isEmpty()) { | |
1490 | // Use the country code as the fallback | |
1491 | displayCountry.setTo(countryCode); | |
1492 | } | |
1493 | return displayCountry; | |
1494 | } | |
1495 | ||
1496 | // ---------------------------------------------------------------------------- | |
1497 | /* | |
1498 | * This constructor adopts the input UnicodeString arrays. | |
1499 | */ | |
1500 | ZoneStrings::ZoneStrings(UnicodeString *strings, int32_t stringsCount, UBool commonlyUsed, | |
1501 | UnicodeString **genericPartialLocationStrings, int32_t genericRowCount, int32_t genericColCount) | |
1502 | : fStrings(strings), fStringsCount(stringsCount), fIsCommonlyUsed(commonlyUsed), | |
1503 | fGenericPartialLocationStrings(genericPartialLocationStrings), | |
1504 | fGenericPartialLocationRowCount(genericRowCount), fGenericPartialLocationColCount(genericColCount) { | |
1505 | } | |
1506 | ||
1507 | ZoneStrings::~ZoneStrings() { | |
1508 | if (fStrings != NULL) { | |
1509 | delete[] fStrings; | |
1510 | } | |
1511 | if (fGenericPartialLocationStrings != NULL) { | |
1512 | for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) { | |
1513 | delete[] fGenericPartialLocationStrings[i]; | |
1514 | } | |
1515 | uprv_free(fGenericPartialLocationStrings); | |
1516 | } | |
1517 | } | |
1518 | ||
1519 | ||
1520 | UnicodeString& | |
1521 | ZoneStrings::getString(int32_t typeIdx, UnicodeString &result) const { | |
1522 | if (typeIdx >= 0 && typeIdx < fStringsCount) { | |
1523 | result.setTo(fStrings[typeIdx]); | |
1524 | } else { | |
1525 | result.remove(); | |
1526 | } | |
1527 | return result; | |
1528 | } | |
1529 | ||
1530 | UnicodeString& | |
1531 | ZoneStrings::getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort, | |
1532 | UBool commonlyUsedOnly, UnicodeString &result) const { | |
1533 | UBool isSet = FALSE; | |
1534 | if (fGenericPartialLocationColCount >= 2) { | |
1535 | for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) { | |
1536 | if (fGenericPartialLocationStrings[i][0] == mzid) { | |
1537 | if (isShort) { | |
1538 | if (fGenericPartialLocationColCount >= 3) { | |
1539 | if (!commonlyUsedOnly || | |
1540 | fGenericPartialLocationColCount == 3 || fGenericPartialLocationStrings[i][3].length() != 0) { | |
1541 | result.setTo(fGenericPartialLocationStrings[i][2]); | |
1542 | isSet = TRUE; | |
1543 | } | |
1544 | } | |
1545 | } else { | |
1546 | result.setTo(fGenericPartialLocationStrings[i][1]); | |
1547 | isSet = TRUE; | |
1548 | } | |
1549 | break; | |
1550 | } | |
1551 | } | |
1552 | } | |
1553 | if (!isSet) { | |
1554 | result.remove(); | |
1555 | } | |
1556 | return result; | |
1557 | } | |
1558 | ||
1559 | // -------------------------------------------------------------- | |
1560 | SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry) | |
1561 | : fCacheEntry(cacheEntry) { | |
1562 | } | |
1563 | ||
1564 | SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() { | |
1565 | fCacheEntry->delRef(); | |
1566 | } | |
1567 | ||
1568 | const ZoneStringFormat* | |
1569 | SafeZoneStringFormatPtr::get() const { | |
1570 | return fCacheEntry->getZoneStringFormat(); | |
1571 | } | |
1572 | ||
1573 | ZSFCacheEntry::ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next) | |
1574 | : fLocale(locale), fZoneStringFormat(zsf), | |
1575 | fNext(next), fRefCount(1) | |
1576 | { | |
1577 | } | |
1578 | ||
1579 | ZSFCacheEntry::~ZSFCacheEntry () { | |
1580 | delete fZoneStringFormat; | |
1581 | } | |
1582 | ||
1583 | const ZoneStringFormat* | |
1584 | ZSFCacheEntry::getZoneStringFormat(void) { | |
1585 | return (const ZoneStringFormat*)fZoneStringFormat; | |
1586 | } | |
1587 | ||
1588 | void | |
1589 | ZSFCacheEntry::delRef(void) { | |
1590 | umtx_lock(&gZSFCacheLock); | |
1591 | --fRefCount; | |
1592 | umtx_unlock(&gZSFCacheLock); | |
1593 | } | |
1594 | ||
1595 | ZSFCache::ZSFCache(int32_t capacity) | |
1596 | : fCapacity(capacity), fFirst(NULL) { | |
1597 | } | |
1598 | ||
1599 | ZSFCache::~ZSFCache() { | |
1600 | ZSFCacheEntry *entry = fFirst; | |
1601 | while (entry) { | |
1602 | ZSFCacheEntry *next = entry->fNext; | |
1603 | delete entry; | |
1604 | entry = next; | |
1605 | } | |
1606 | } | |
1607 | ||
1608 | SafeZoneStringFormatPtr* | |
1609 | ZSFCache::get(const Locale &locale, UErrorCode &status) { | |
1610 | SafeZoneStringFormatPtr *result = NULL; | |
1611 | ||
1612 | // Search the cache entry list | |
1613 | ZSFCacheEntry *entry = NULL; | |
1614 | ZSFCacheEntry *next, *prev; | |
1615 | ||
1616 | umtx_lock(&gZSFCacheLock); | |
1617 | entry = fFirst; | |
1618 | prev = NULL; | |
1619 | while (entry) { | |
1620 | next = entry->fNext; | |
1621 | if (entry->fLocale == locale) { | |
1622 | // Add reference count | |
1623 | entry->fRefCount++; | |
1624 | ||
1625 | // move the entry to the top | |
1626 | if (entry != fFirst) { | |
1627 | prev->fNext = next; | |
1628 | entry->fNext = fFirst; | |
1629 | fFirst = entry; | |
1630 | } | |
1631 | break; | |
1632 | } | |
1633 | prev = entry; | |
1634 | entry = next; | |
1635 | } | |
1636 | umtx_unlock(&gZSFCacheLock); | |
1637 | ||
1638 | // Create a new ZoneStringFormat | |
1639 | if (entry == NULL) { | |
1640 | ZoneStringFormat *zsf = new ZoneStringFormat(locale, status); | |
1641 | if (U_FAILURE(status)) { | |
1642 | delete zsf; | |
1643 | return NULL; | |
1644 | } | |
1645 | if (zsf == NULL) { | |
1646 | status = U_MEMORY_ALLOCATION_ERROR; | |
1647 | return NULL; | |
1648 | } | |
1649 | // Now add the new entry | |
1650 | umtx_lock(&gZSFCacheLock); | |
1651 | // Make sure no other threads already created the one for the same locale | |
1652 | entry = fFirst; | |
1653 | prev = NULL; | |
1654 | while (entry) { | |
1655 | next = entry->fNext; | |
1656 | if (entry->fLocale == locale) { | |
1657 | // Add reference count | |
1658 | entry->fRefCount++; | |
1659 | ||
1660 | // move the entry to the top | |
1661 | if (entry != fFirst) { | |
1662 | prev->fNext = next; | |
1663 | entry->fNext = fFirst; | |
1664 | fFirst = entry; | |
1665 | } | |
1666 | break; | |
1667 | } | |
1668 | prev = entry; | |
1669 | entry = next; | |
1670 | } | |
1671 | if (entry == NULL) { | |
1672 | // Add the new one to the top | |
1673 | next = fFirst; | |
1674 | entry = new ZSFCacheEntry(locale, zsf, next); | |
1675 | fFirst = entry; | |
1676 | } else { | |
1677 | delete zsf; | |
1678 | } | |
1679 | umtx_unlock(&gZSFCacheLock); | |
1680 | } | |
1681 | ||
1682 | result = new SafeZoneStringFormatPtr(entry); | |
1683 | ||
1684 | // Now, delete unused cache entries beyond the capacity | |
1685 | umtx_lock(&gZSFCacheLock); | |
1686 | entry = fFirst; | |
1687 | prev = NULL; | |
1688 | int32_t idx = 1; | |
1689 | while (entry) { | |
1690 | next = entry->fNext; | |
1691 | if (idx >= fCapacity && entry->fRefCount == 0) { | |
1692 | if (entry == fFirst) { | |
1693 | fFirst = next; | |
1694 | } else { | |
1695 | prev->fNext = next; | |
1696 | } | |
1697 | delete entry; | |
1698 | } else { | |
1699 | prev = entry; | |
1700 | } | |
1701 | entry = next; | |
1702 | idx++; | |
1703 | } | |
1704 | umtx_unlock(&gZSFCacheLock); | |
1705 | ||
1706 | return result; | |
1707 | } | |
1708 | ||
1709 | U_NAMESPACE_END | |
1710 | ||
1711 | #endif /* #if !UCONFIG_NO_FORMATTING */ |