intl/icu/source/i18n/tznames_impl.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial