/* {{{ intl_stringFromChar */ int intl_stringFromChar(UnicodeString &ret, char *str, size_t str_len, UErrorCode *status) { if(str_len > INT32_MAX) { *status = U_BUFFER_OVERFLOW_ERROR; ret.setToBogus(); return FAILURE; } //the number of UTF-16 code units is not larger than that of UTF-8 code //units, + 1 for the terminator int32_t capacity = (int32_t)str_len + 1; //no check necessary -- if NULL will fail ahead UChar *utf16 = ret.getBuffer(capacity); int32_t utf16_len = 0; *status = U_ZERO_ERROR; u_strFromUTF8WithSub(utf16, ret.getCapacity(), &utf16_len, str, str_len, U_SENTINEL /* no substitution */, NULL, status); ret.releaseBuffer(utf16_len); if (U_FAILURE(*status)) { ret.setToBogus(); return FAILURE; } return SUCCESS; }
UnicodeString RuleBasedNumberFormat::getRuleSetDisplayName(int32_t index, const Locale& localeParam) { if (localizations && index >= 0 && index < localizations->getNumberOfRuleSets()) { UnicodeString localeName(localeParam.getBaseName(), -1, UnicodeString::kInvariant); int32_t len = localeName.length(); UChar* localeStr = localeName.getBuffer(len + 1); while (len >= 0) { localeStr[len] = 0; int32_t ix = localizations->indexForLocale(localeStr); if (ix >= 0) { UnicodeString name(TRUE, localizations->getDisplayName(ix, index), -1); return name; } // trim trailing portion, skipping over ommitted sections do { --len;} while (len > 0 && localeStr[len] != 0x005f); // underscore while (len > 0 && localeStr[len-1] == 0x005F) --len; } UnicodeString name(TRUE, localizations->getRuleSetName(index), -1); return name; } UnicodeString bogus; bogus.setToBogus(); return bogus; }
void U_EXPORT2 Normalizer::normalize(const UnicodeString& source, UNormalizationMode mode, int32_t options, UnicodeString& result, UErrorCode &status) { if(source.isBogus() || U_FAILURE(status)) { result.setToBogus(); if(U_SUCCESS(status)) { status=U_ILLEGAL_ARGUMENT_ERROR; } } else { UnicodeString localDest; UnicodeString *dest; if(&source!=&result) { dest=&result; } else { // the source and result strings are the same object, use a temporary one dest=&localDest; } const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, status); if(U_SUCCESS(status)) { if(options&UNORM_UNICODE_3_2) { FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(status)). normalize(source, *dest, status); } else { n2->normalize(source, *dest, status); } } if(dest==&localDest && U_SUCCESS(status)) { result=*dest; } } }
UnicodeString& U_EXPORT2 ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString ®ion, UnicodeString &result) { UErrorCode status = U_ZERO_ERROR; const UChar *tzid = NULL; int32_t tzidLen = 0; char keyBuf[ZID_KEY_MAX + 1]; int32_t keyLen = 0; if (mzid.isBogus() || mzid.length() > ZID_KEY_MAX) { result.setToBogus(); return result; } keyLen = mzid.extract(0, mzid.length(), keyBuf, ZID_KEY_MAX + 1, US_INV); keyBuf[keyLen] = 0; UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status); ures_getByKey(rb, gMapTimezonesTag, rb, &status); ures_getByKey(rb, keyBuf, rb, &status); if (U_SUCCESS(status)) { // check region mapping if (region.length() == 2 || region.length() == 3) { keyLen = region.extract(0, region.length(), keyBuf, ZID_KEY_MAX + 1, US_INV); keyBuf[keyLen] = 0; tzid = ures_getStringByKey(rb, keyBuf, &tzidLen, &status); if (status == U_MISSING_RESOURCE_ERROR) { status = U_ZERO_ERROR; } } if (U_SUCCESS(status) && tzid == NULL) { // try "001" tzid = ures_getStringByKey(rb, gWorldTag, &tzidLen, &status); } } ures_close(rb); if (tzid == NULL) { result.setToBogus(); } else { result.setTo(tzid, tzidLen); } return result; }
UnicodeString& U_EXPORT2 TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) { if (tzID.isEmpty() || tzID.startsWith(gEtcPrefix, gEtcPrefixLen) || tzID.startsWith(gSystemVPrefix, gSystemVPrefixLen) || tzID.indexOf(gRiyadh8, gRiyadh8Len, 0) > 0) { name.setToBogus(); return name; } int32_t sep = tzID.lastIndexOf((UChar)0x2F /* '/' */); if (sep > 0 && sep + 1 < tzID.length()) { name.setTo(tzID, sep + 1); name.findAndReplace(UnicodeString((UChar)0x5f /* _ */), UnicodeString((UChar)0x20 /* space */)); } else { name.setToBogus(); } return name; }
UnicodeString& U_EXPORT2 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) { const UChar *region = TimeZone::getRegion(tzid); if (region != NULL && u_strcmp(gWorld, region) != 0) { canonicalCountry.setTo(region, -1); } else { canonicalCountry.setToBogus(); } return canonicalCountry; }
UnicodeString RuleBasedNumberFormat::getDefaultRuleSetName() const { UnicodeString result; if (defaultRuleSet && defaultRuleSet->isPublic()) { defaultRuleSet->getName(result); } else { result.setToBogus(); } return result; }
UnicodeString& U_EXPORT2 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) { const UChar *canonicalID = getCanonicalCLDRID(tzid, status); if (U_FAILURE(status) || canonicalID == NULL) { systemID.setToBogus(); return systemID; } systemID.setTo(TRUE, canonicalID, -1); return systemID; }
UnicodeString RuleBasedNumberFormat::getRuleSetDisplayName(const UnicodeString& ruleSetName, const Locale& localeParam) { if (localizations) { UnicodeString rsn(ruleSetName); int32_t ix = localizations->indexForRuleSet(rsn.getTerminatedBuffer()); return getRuleSetDisplayName(ix, localeParam); } UnicodeString bogus; bogus.setToBogus(); return bogus; }
UnicodeString & U_EXPORT2 Normalizer::concatenate(UnicodeString & left, UnicodeString & right, UnicodeString & result, UNormalizationMode mode, int32_t options, UErrorCode & errorCode) { if (left.isBogus() || right.isBogus() || U_FAILURE(errorCode)) { result.setToBogus(); if (U_SUCCESS(errorCode)) { errorCode = U_ILLEGAL_ARGUMENT_ERROR; } } else { UnicodeString localDest; UnicodeString * dest; if (&right != &result) { dest = &result; } else { // the right and result strings are the same object, use a temporary one dest = &localDest; } *dest = left; const Normalizer2 * n2 = Normalizer2Factory::getInstance(mode, errorCode); if (U_SUCCESS(errorCode)) { if (options & UNORM_UNICODE_3_2) { FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(errorCode)). append(*dest, right, errorCode); } else { n2->append(*dest, right, errorCode); } } if (dest == &localDest && U_SUCCESS(errorCode)) { result = *dest; } } return result; }
UnicodeString MeasureFormat::getUnitDisplayName(const MeasureUnit& unit, UErrorCode& /*status*/) const { UMeasureFormatWidth width = getRegularWidth(fWidth); const UChar* const* styleToDnam = cache->dnams[unit.getIndex()]; const UChar* dnam = styleToDnam[width]; if (dnam == NULL) { int32_t fallbackWidth = cache->widthFallback[width]; dnam = styleToDnam[fallbackWidth]; } UnicodeString result; if (dnam == NULL) { result.setToBogus(); } else { result.setTo(dnam, -1); } return result; }
UnicodeString& U_EXPORT2 ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) { UBool isSet = FALSE; const UVector *mappings = getMetazoneMappings(tzid); if (mappings != NULL) { for (int32_t i = 0; i < mappings->size(); i++) { OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i); if (mzm->from <= date && mzm->to > date) { result.setTo(mzm->mzid, -1); isSet = TRUE; break; } } } if (!isSet) { result.setToBogus(); } return result; }
UnicodeString MessageFormat::autoQuoteApostrophe(const UnicodeString& pattern, UErrorCode& status) { UnicodeString result; if (U_SUCCESS(status)) { int32_t plen = pattern.length(); const UChar* pat = pattern.getBuffer(); int32_t blen = plen * 2 + 1; // space for null termination, convenience UChar* buf = result.getBuffer(blen); if (buf == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } else { int32_t len = umsg_autoQuoteApostrophe(pat, plen, buf, blen, &status); result.releaseBuffer(U_SUCCESS(status) ? len : 0); } } if (U_FAILURE(status)) { result.setToBogus(); } return result; }
UnicodeString& U_EXPORT2 ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) { // Get canonical country for the zone const UChar *region = TimeZone::getRegion(tzid); if (region == NULL || u_strcmp(gWorld, region) == 0) { // special case - unknown or "001" country.setToBogus(); return country; } // Checking the cached results UErrorCode status = U_ZERO_ERROR; UBool initialized; UMTX_CHECK(&gZoneMetaLock, gCountryInfoVectorsInitialized, initialized); if (!initialized) { // Create empty vectors umtx_lock(&gZoneMetaLock); { if (!gCountryInfoVectorsInitialized) { // No deleters for these UVectors, it's a reference to a resource bundle string. gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status); if (gSingleZoneCountries == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status); if (gMultiZonesCountries == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } if (U_SUCCESS(status)) { gCountryInfoVectorsInitialized = TRUE; } else { delete gSingleZoneCountries; delete gMultiZonesCountries; } } } umtx_unlock(&gZoneMetaLock); if (U_FAILURE(status)) { country.setToBogus(); return country; } U_ASSERT(gSingleZoneCountries != NULL); U_ASSERT(gMultiZonesCountries != NULL); } // Check if it was already cached UBool cached = FALSE; UBool multiZones = FALSE; umtx_lock(&gZoneMetaLock); { multiZones = cached = gMultiZonesCountries->contains((void*)region); if (!multiZones) { cached = gSingleZoneCountries->contains((void*)region); } } umtx_unlock(&gZoneMetaLock); if (!cached) { // We need to go through all zones associated with the region. // This is relatively heavy operation. U_ASSERT(u_strlen(region) == 2); char buf[] = {0, 0, 0}; u_UCharsToChars(region, buf, 2); StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, buf, NULL, status); int32_t idsLen = ids->count(status); if (U_SUCCESS(status) && idsLen > 1) { // multiple canonical zones are available for the region multiZones = TRUE; } if (U_FAILURE(status)) { // no single country by default for any error cases multiZones = TRUE; } delete ids; // Cache the result umtx_lock(&gZoneMetaLock); { UErrorCode ec = U_ZERO_ERROR; if (multiZones) { if (!gMultiZonesCountries->contains((void*)region)) { gMultiZonesCountries->addElement((void*)region, ec); } } else { if (!gSingleZoneCountries->contains((void*)region)) { gSingleZoneCountries->addElement((void*)region, ec); } } } umtx_unlock(&gZoneMetaLock); } if (multiZones) { country.setToBogus(); } else { country.setTo(region, -1); } return country; }
inline CDFUnit() : prefix(), suffix() { prefix.setToBogus(); }
bool DecimalMatcher::match(StringSegment& segment, ParsedNumber& result, int8_t exponentSign, UErrorCode&) const { if (result.seenNumber() && exponentSign == 0) { // A number has already been consumed. return false; } else if (exponentSign != 0) { // scientific notation always comes after the number U_ASSERT(!result.quantity.bogus); } // Initial offset before any character consumption. int32_t initialOffset = segment.getOffset(); // Return value: whether to ask for more characters. bool maybeMore = false; // All digits consumed so far. number::impl::DecimalQuantity digitsConsumed; digitsConsumed.bogus = true; // The total number of digits after the decimal place, used for scaling the result. int32_t digitsAfterDecimalPlace = 0; // The actual grouping and decimal separators used in the string. // If non-null, we have seen that token. UnicodeString actualGroupingString; UnicodeString actualDecimalString; actualGroupingString.setToBogus(); actualDecimalString.setToBogus(); // Information for two groups: the previous group and the current group. // // Each group has three pieces of information: // // Offset: the string position of the beginning of the group, including a leading separator // if there was a leading separator. This is needed in case we need to rewind the parse to // that position. // // Separator type: // 0 => beginning of string // 1 => lead separator is a grouping separator // 2 => lead separator is a decimal separator // // Count: the number of digits in the group. If -1, the group has been validated. int32_t currGroupOffset = 0; int32_t currGroupSepType = 0; int32_t currGroupCount = 0; int32_t prevGroupOffset = -1; int32_t prevGroupSepType = -1; int32_t prevGroupCount = -1; while (segment.length() > 0) { maybeMore = false; // Attempt to match a digit. int8_t digit = -1; // Try by code point digit value. UChar32 cp = segment.getCodePoint(); if (u_isdigit(cp)) { segment.adjustOffset(U16_LENGTH(cp)); digit = static_cast<int8_t>(u_digit(cp, 10)); } // Try by digit string. if (digit == -1 && !fLocalDigitStrings.isNull()) { for (int32_t i = 0; i < 10; i++) { const UnicodeString& str = fLocalDigitStrings[i]; if (str.isEmpty()) { continue; } int32_t overlap = segment.getCommonPrefixLength(str); if (overlap == str.length()) { segment.adjustOffset(overlap); digit = static_cast<int8_t>(i); break; } maybeMore = maybeMore || (overlap == segment.length()); } } if (digit >= 0) { // Digit was found. if (digitsConsumed.bogus) { digitsConsumed.bogus = false; digitsConsumed.clear(); } digitsConsumed.appendDigit(digit, 0, true); currGroupCount++; if (!actualDecimalString.isBogus()) { digitsAfterDecimalPlace++; } continue; } // Attempt to match a literal grouping or decimal separator. bool isDecimal = false; bool isGrouping = false; // 1) Attempt the decimal separator string literal. // if (we have not seen a decimal separator yet) { ... } if (actualDecimalString.isBogus() && !decimalSeparator.isEmpty()) { int32_t overlap = segment.getCommonPrefixLength(decimalSeparator); maybeMore = maybeMore || (overlap == segment.length()); if (overlap == decimalSeparator.length()) { isDecimal = true; actualDecimalString = decimalSeparator; } } // 2) Attempt to match the actual grouping string literal. if (!actualGroupingString.isBogus()) { int32_t overlap = segment.getCommonPrefixLength(actualGroupingString); maybeMore = maybeMore || (overlap == segment.length()); if (overlap == actualGroupingString.length()) { isGrouping = true; } } // 2.5) Attempt to match a new the grouping separator string literal. // if (we have not seen a grouping or decimal separator yet) { ... } if (!groupingDisabled && actualGroupingString.isBogus() && actualDecimalString.isBogus() && !groupingSeparator.isEmpty()) { int32_t overlap = segment.getCommonPrefixLength(groupingSeparator); maybeMore = maybeMore || (overlap == segment.length()); if (overlap == groupingSeparator.length()) { isGrouping = true; actualGroupingString = groupingSeparator; } } // 3) Attempt to match a decimal separator from the equivalence set. // if (we have not seen a decimal separator yet) { ... } // The !isGrouping is to confirm that we haven't yet matched the current character. if (!isGrouping && actualDecimalString.isBogus()) { if (decimalUniSet->contains(cp)) { isDecimal = true; actualDecimalString = UnicodeString(cp); } } // 4) Attempt to match a grouping separator from the equivalence set. // if (we have not seen a grouping or decimal separator yet) { ... } if (!groupingDisabled && actualGroupingString.isBogus() && actualDecimalString.isBogus()) { if (groupingUniSet->contains(cp)) { isGrouping = true; actualGroupingString = UnicodeString(cp); } } // Leave if we failed to match this as a separator. if (!isDecimal && !isGrouping) { break; } // Check for conditions when we don't want to accept the separator. if (isDecimal && integerOnly) { break; } else if (currGroupSepType == 2 && isGrouping) { // Fraction grouping break; } // Validate intermediate grouping sizes. bool prevValidSecondary = validateGroup(prevGroupSepType, prevGroupCount, false); bool currValidPrimary = validateGroup(currGroupSepType, currGroupCount, true); if (!prevValidSecondary || (isDecimal && !currValidPrimary)) { // Invalid grouping sizes. if (isGrouping && currGroupCount == 0) { // Trailing grouping separators: these are taken care of below U_ASSERT(currGroupSepType == 1); } else if (requireGroupingMatch) { // Strict mode: reject the parse digitsConsumed.clear(); digitsConsumed.bogus = true; } break; } else if (requireGroupingMatch && currGroupCount == 0 && currGroupSepType == 1) { break; } else { // Grouping sizes OK so far. prevGroupOffset = currGroupOffset; prevGroupCount = currGroupCount; if (isDecimal) { // Do not validate this group any more. prevGroupSepType = -1; } else { prevGroupSepType = currGroupSepType; } } // OK to accept the separator. // Special case: don't update currGroup if it is empty; this allows two grouping // separators in a row in lenient mode. if (currGroupCount != 0) { currGroupOffset = segment.getOffset(); } currGroupSepType = isGrouping ? 1 : 2; currGroupCount = 0; if (isGrouping) { segment.adjustOffset(actualGroupingString.length()); } else { segment.adjustOffset(actualDecimalString.length()); } } // End of main loop. // Back up if there was a trailing grouping separator. // Shift prev -> curr so we can check it as a final group. if (currGroupSepType != 2 && currGroupCount == 0) { maybeMore = true; segment.setOffset(currGroupOffset); currGroupOffset = prevGroupOffset; currGroupSepType = prevGroupSepType; currGroupCount = prevGroupCount; prevGroupOffset = -1; prevGroupSepType = 0; prevGroupCount = 1; } // Validate final grouping sizes. bool prevValidSecondary = validateGroup(prevGroupSepType, prevGroupCount, false); bool currValidPrimary = validateGroup(currGroupSepType, currGroupCount, true); if (!requireGroupingMatch) { // The cases we need to handle here are lone digits. // Examples: "1,1" "1,1," "1,1,1" "1,1,1," ",1" (all parse as 1) // See more examples in numberformattestspecification.txt int32_t digitsToRemove = 0; if (!prevValidSecondary) { segment.setOffset(prevGroupOffset); digitsToRemove += prevGroupCount; digitsToRemove += currGroupCount; } else if (!currValidPrimary && (prevGroupSepType != 0 || prevGroupCount != 0)) { maybeMore = true; segment.setOffset(currGroupOffset); digitsToRemove += currGroupCount; } if (digitsToRemove != 0) { digitsConsumed.adjustMagnitude(-digitsToRemove); digitsConsumed.truncate(); } prevValidSecondary = true; currValidPrimary = true; } if (currGroupSepType != 2 && (!prevValidSecondary || !currValidPrimary)) { // Grouping failure. digitsConsumed.bogus = true; } // Strings that start with a separator but have no digits, // or strings that failed a grouping size check. if (digitsConsumed.bogus) { maybeMore = maybeMore || (segment.length() == 0); segment.setOffset(initialOffset); return maybeMore; } // We passed all inspections. Start post-processing. // Adjust for fraction part. digitsConsumed.adjustMagnitude(-digitsAfterDecimalPlace); // Set the digits, either normal or exponent. if (exponentSign != 0 && segment.getOffset() != initialOffset) { bool overflow = false; if (digitsConsumed.fitsInLong()) { int64_t exponentLong = digitsConsumed.toLong(false); U_ASSERT(exponentLong >= 0); if (exponentLong <= INT32_MAX) { auto exponentInt = static_cast<int32_t>(exponentLong); if (result.quantity.adjustMagnitude(exponentSign * exponentInt)) { overflow = true; } } else { overflow = true; } } else { overflow = true; } if (overflow) { if (exponentSign == -1) { // Set to zero result.quantity.clear(); } else { // Set to infinity result.quantity.bogus = true; result.flags |= FLAG_INFINITY; } } } else { result.quantity = digitsConsumed; } // Set other information into the result and return. if (!actualDecimalString.isBogus()) { result.flags |= FLAG_HAS_DECIMAL_SEPARATOR; } result.setCharsConsumed(segment); return segment.length() == 0 || maybeMore; }
int32_t TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types, UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const { timeType = UTZFMT_TIME_TYPE_UNKNOWN; tzID.setToBogus(); if (U_FAILURE(status)) { return 0; } // Find matches in the TimeZoneNames first TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status); if (U_FAILURE(status)) { return 0; } int32_t bestMatchLen = 0; UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; UnicodeString bestMatchTzID; // UBool isLongStandard = FALSE; // workaround - see the comments below UBool isStandard = FALSE; // TODO: Temporary hack (on hack) for short standard name/location name conflict (found in zh_Hant), should be removed after CLDR 21m1 integration if (tznamesMatches != NULL) { UnicodeString mzID; for (int32_t i = 0; i < tznamesMatches->size(); i++) { int32_t len = tznamesMatches->getMatchLengthAt(i); if (len > bestMatchLen) { bestMatchLen = len; if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) { // name for a meta zone if (tznamesMatches->getMetaZoneIDAt(i, mzID)) { fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID); } } UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i); if (U_FAILURE(status)) { break; } switch (nameType) { case UTZNM_LONG_STANDARD: // isLongStandard = TRUE; case UTZNM_SHORT_STANDARD: // this one is never used for generic, but just in case isStandard = TRUE; // TODO: Remove this later, see the comments above. bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD; break; case UTZNM_LONG_DAYLIGHT: case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT; break; default: bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; } } } delete tznamesMatches; if (U_FAILURE(status)) { return 0; } if (bestMatchLen == (text.length() - start)) { // Full match //tzID.setTo(bestMatchTzID); //timeType = bestMatchTimeType; //return bestMatchLen; // TODO Some time zone uses a same name for the long standard name // and the location name. When the match is a long standard name, // then we need to check if the name is same with the location name. // This is probably a data error or a design bug. /* if (!isLongStandard) { tzID.setTo(bestMatchTzID); timeType = bestMatchTimeType; return bestMatchLen; } */ // TODO The deprecation of commonlyUsed flag introduced the name // conflict not only for long standard names, but short standard names too. // These short names (found in zh_Hant) should be gone once we clean // up CLDR time zone display name data. Once the short name conflict // problem (with location name) is resolved, we should change the condition // below back to the original one above. -Yoshito (2011-09-14) if (!isStandard) { tzID.setTo(bestMatchTzID); timeType = bestMatchTimeType; return bestMatchLen; } } } // Find matches in the local trie TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status); if (U_FAILURE(status)) { return 0; } if (localMatches != NULL) { for (int32_t i = 0; i < localMatches->size(); i++) { int32_t len = localMatches->getMatchLength(i); // TODO See the above TODO. We use len >= bestMatchLen // because of the long standard/location name collision // problem. If it is also a location name, carrying // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a // problem in SimpleDateFormat if (len >= bestMatchLen) { bestMatchLen = localMatches->getMatchLength(i); bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; // because generic localMatches->getTimeZoneID(i, bestMatchTzID); } } delete localMatches; } if (bestMatchLen > 0) { timeType = bestMatchTimeType; tzID.setTo(bestMatchTzID); } return bestMatchLen; }
UnicodeString& U_EXPORT2 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country, UBool *isPrimary /* = NULL */) { if (isPrimary != NULL) { *isPrimary = FALSE; } const UChar *region = TimeZone::getRegion(tzid); if (region != NULL && u_strcmp(gWorld, region) != 0) { country.setTo(region, -1); } else { country.setToBogus(); return country; } if (isPrimary != NULL) { char regionBuf[] = {0, 0, 0}; // Checking the cached results UErrorCode status = U_ZERO_ERROR; umtx_initOnce(gCountryInfoVectorsInitOnce, &countryInfoVectorsInit, status); if (U_FAILURE(status)) { return country; } // Check if it was already cached UBool cached = FALSE; UBool singleZone = FALSE; umtx_lock(gZoneMetaLock()); { singleZone = cached = gSingleZoneCountries->contains((void*)region); if (!cached) { cached = gMultiZonesCountries->contains((void*)region); } } umtx_unlock(gZoneMetaLock()); if (!cached) { // We need to go through all zones associated with the region. // This is relatively heavy operation. U_ASSERT(u_strlen(region) == 2); u_UCharsToChars(region, regionBuf, 2); StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, regionBuf, NULL, status); int32_t idsLen = ids->count(status); if (U_SUCCESS(status) && idsLen == 1) { // only the single zone is available for the region singleZone = TRUE; } delete ids; // Cache the result umtx_lock(gZoneMetaLock()); { UErrorCode ec = U_ZERO_ERROR; if (singleZone) { if (!gSingleZoneCountries->contains((void*)region)) { gSingleZoneCountries->addElement((void*)region, ec); } } else { if (!gMultiZonesCountries->contains((void*)region)) { gMultiZonesCountries->addElement((void*)region, ec); } } } umtx_unlock(gZoneMetaLock()); } if (singleZone) { *isPrimary = TRUE; } else { // Note: We may cache the primary zone map in future. // Even a country has multiple zones, one of them might be // dominant and treated as a primary zone int32_t idLen = 0; if (regionBuf[0] == 0) { u_UCharsToChars(region, regionBuf, 2); } UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status); ures_getByKey(rb, gPrimaryZonesTag, rb, &status); const UChar *primaryZone = ures_getStringByKey(rb, regionBuf, &idLen, &status); if (U_SUCCESS(status)) { if (tzid.compare(primaryZone, idLen) == 0) { *isPrimary = TRUE; } else { // The given ID might not be a canonical ID UnicodeString canonicalID; TimeZone::getCanonicalID(tzid, canonicalID, status); if (U_SUCCESS(status) && canonicalID.compare(primaryZone, idLen) == 0) { *isPrimary = TRUE; } } } ures_close(rb); } } return country; }