Example #1
0
/* {{{ 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;
}
Example #2
0
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;
}
Example #3
0
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;
        }
    }
}
Example #4
0
UnicodeString& U_EXPORT2
ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, 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;
}
Example #5
0
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;
}
Example #6
0
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;
}
Example #7
0
UnicodeString
RuleBasedNumberFormat::getDefaultRuleSetName() const {
  UnicodeString result;
  if (defaultRuleSet && defaultRuleSet->isPublic()) {
    defaultRuleSet->getName(result);
  } else {
    result.setToBogus();
  }
  return result;
}
Example #8
0
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;
}
Example #9
0
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;
}
Example #10
0
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;
}
Example #11
0
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;
}
Example #12
0
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;
}
Example #13
0
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;
}
Example #14
0
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();
 }
Example #16
0
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;
}
Example #17
0
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;
}
Example #18
0
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;
}