123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834 |
- // © 2019 and later: Unicode, Inc. and others.
- // License & terms of use: http://www.unicode.org/copyright.html
- // localematcher.cpp
- // created: 2019may08 Markus W. Scherer
- #include <optional>
- #include "unicode/utypes.h"
- #include "unicode/localebuilder.h"
- #include "unicode/localematcher.h"
- #include "unicode/locid.h"
- #include "unicode/stringpiece.h"
- #include "unicode/uloc.h"
- #include "unicode/uobject.h"
- #include "cstring.h"
- #include "localeprioritylist.h"
- #include "loclikelysubtags.h"
- #include "locdistance.h"
- #include "lsr.h"
- #include "uassert.h"
- #include "uhash.h"
- #include "ustr_imp.h"
- #include "uvector.h"
- #define UND_LSR LSR("und", "", "", LSR::EXPLICIT_LSR)
- /**
- * Indicator for the lifetime of desired-locale objects passed into the LocaleMatcher.
- *
- * @draft ICU 65
- */
- enum ULocMatchLifetime {
- /**
- * Locale objects are temporary.
- * The matcher will make a copy of a locale that will be used beyond one function call.
- *
- * @draft ICU 65
- */
- ULOCMATCH_TEMPORARY_LOCALES,
- /**
- * Locale objects are stored at least as long as the matcher is used.
- * The matcher will keep only a pointer to a locale that will be used beyond one function call,
- * avoiding a copy.
- *
- * @draft ICU 65
- */
- ULOCMATCH_STORED_LOCALES // TODO: permanent? cached? clone?
- };
- #ifndef U_IN_DOXYGEN
- typedef enum ULocMatchLifetime ULocMatchLifetime;
- #endif
- U_NAMESPACE_BEGIN
- LocaleMatcher::Result::Result(LocaleMatcher::Result &&src) noexcept :
- desiredLocale(src.desiredLocale),
- supportedLocale(src.supportedLocale),
- desiredIndex(src.desiredIndex),
- supportedIndex(src.supportedIndex),
- desiredIsOwned(src.desiredIsOwned) {
- if (desiredIsOwned) {
- src.desiredLocale = nullptr;
- src.desiredIndex = -1;
- src.desiredIsOwned = false;
- }
- }
- LocaleMatcher::Result::~Result() {
- if (desiredIsOwned) {
- delete desiredLocale;
- }
- }
- LocaleMatcher::Result &LocaleMatcher::Result::operator=(LocaleMatcher::Result &&src) noexcept {
- this->~Result();
- desiredLocale = src.desiredLocale;
- supportedLocale = src.supportedLocale;
- desiredIndex = src.desiredIndex;
- supportedIndex = src.supportedIndex;
- desiredIsOwned = src.desiredIsOwned;
- if (desiredIsOwned) {
- src.desiredLocale = nullptr;
- src.desiredIndex = -1;
- src.desiredIsOwned = false;
- }
- return *this;
- }
- Locale LocaleMatcher::Result::makeResolvedLocale(UErrorCode &errorCode) const {
- if (U_FAILURE(errorCode) || supportedLocale == nullptr) {
- return Locale::getRoot();
- }
- const Locale *bestDesired = getDesiredLocale();
- if (bestDesired == nullptr || *supportedLocale == *bestDesired) {
- return *supportedLocale;
- }
- LocaleBuilder b;
- b.setLocale(*supportedLocale);
- // Copy the region from bestDesired, if there is one.
- const char *region = bestDesired->getCountry();
- if (*region != 0) {
- b.setRegion(region);
- }
- // Copy the variants from bestDesired, if there are any.
- // Note that this will override any supportedLocale variants.
- // For example, "sco-ulster-fonipa" + "...-fonupa" => "sco-fonupa" (replacing ulster).
- const char *variants = bestDesired->getVariant();
- if (*variants != 0) {
- b.setVariant(variants);
- }
- // Copy the extensions from bestDesired, if there are any.
- // C++ note: The following note, copied from Java, may not be true,
- // as long as C++ copies by legacy ICU keyword, not by extension singleton.
- // Note that this will override any supportedLocale extensions.
- // For example, "th-u-nu-latn-ca-buddhist" + "...-u-nu-native" => "th-u-nu-native"
- // (replacing calendar).
- b.copyExtensionsFrom(*bestDesired, errorCode);
- return b.build(errorCode);
- }
- LocaleMatcher::Builder::Builder(LocaleMatcher::Builder &&src) noexcept :
- errorCode_(src.errorCode_),
- supportedLocales_(src.supportedLocales_),
- thresholdDistance_(src.thresholdDistance_),
- demotion_(src.demotion_),
- defaultLocale_(src.defaultLocale_),
- withDefault_(src.withDefault_),
- favor_(src.favor_),
- direction_(src.direction_) {
- src.supportedLocales_ = nullptr;
- src.defaultLocale_ = nullptr;
- }
- LocaleMatcher::Builder::~Builder() {
- delete supportedLocales_;
- delete defaultLocale_;
- delete maxDistanceDesired_;
- delete maxDistanceSupported_;
- }
- LocaleMatcher::Builder &LocaleMatcher::Builder::operator=(LocaleMatcher::Builder &&src) noexcept {
- this->~Builder();
- errorCode_ = src.errorCode_;
- supportedLocales_ = src.supportedLocales_;
- thresholdDistance_ = src.thresholdDistance_;
- demotion_ = src.demotion_;
- defaultLocale_ = src.defaultLocale_;
- withDefault_ = src.withDefault_,
- favor_ = src.favor_;
- direction_ = src.direction_;
- src.supportedLocales_ = nullptr;
- src.defaultLocale_ = nullptr;
- return *this;
- }
- void LocaleMatcher::Builder::clearSupportedLocales() {
- if (supportedLocales_ != nullptr) {
- supportedLocales_->removeAllElements();
- }
- }
- bool LocaleMatcher::Builder::ensureSupportedLocaleVector() {
- if (U_FAILURE(errorCode_)) { return false; }
- if (supportedLocales_ != nullptr) { return true; }
- LocalPointer<UVector> lpSupportedLocales(new UVector(uprv_deleteUObject, nullptr, errorCode_), errorCode_);
- if (U_FAILURE(errorCode_)) { return false; }
- supportedLocales_ = lpSupportedLocales.orphan();
- return true;
- }
- LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocalesFromListString(
- StringPiece locales) {
- LocalePriorityList list(locales, errorCode_);
- if (U_FAILURE(errorCode_)) { return *this; }
- clearSupportedLocales();
- if (!ensureSupportedLocaleVector()) { return *this; }
- int32_t length = list.getLengthIncludingRemoved();
- for (int32_t i = 0; i < length; ++i) {
- Locale *locale = list.orphanLocaleAt(i);
- if (locale == nullptr) { continue; }
- supportedLocales_->adoptElement(locale, errorCode_);
- if (U_FAILURE(errorCode_)) {
- break;
- }
- }
- return *this;
- }
- LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocales(Locale::Iterator &locales) {
- if (ensureSupportedLocaleVector()) {
- clearSupportedLocales();
- while (locales.hasNext() && U_SUCCESS(errorCode_)) {
- const Locale &locale = locales.next();
- LocalPointer<Locale> clone (locale.clone(), errorCode_);
- supportedLocales_->adoptElement(clone.orphan(), errorCode_);
- }
- }
- return *this;
- }
- LocaleMatcher::Builder &LocaleMatcher::Builder::addSupportedLocale(const Locale &locale) {
- if (ensureSupportedLocaleVector()) {
- LocalPointer<Locale> clone(locale.clone(), errorCode_);
- supportedLocales_->adoptElement(clone.orphan(), errorCode_);
- }
- return *this;
- }
- LocaleMatcher::Builder &LocaleMatcher::Builder::setNoDefaultLocale() {
- if (U_FAILURE(errorCode_)) { return *this; }
- delete defaultLocale_;
- defaultLocale_ = nullptr;
- withDefault_ = false;
- return *this;
- }
- LocaleMatcher::Builder &LocaleMatcher::Builder::setDefaultLocale(const Locale *defaultLocale) {
- if (U_FAILURE(errorCode_)) { return *this; }
- Locale *clone = nullptr;
- if (defaultLocale != nullptr) {
- clone = defaultLocale->clone();
- if (clone == nullptr) {
- errorCode_ = U_MEMORY_ALLOCATION_ERROR;
- return *this;
- }
- }
- delete defaultLocale_;
- defaultLocale_ = clone;
- withDefault_ = true;
- return *this;
- }
- LocaleMatcher::Builder &LocaleMatcher::Builder::setFavorSubtag(ULocMatchFavorSubtag subtag) {
- if (U_FAILURE(errorCode_)) { return *this; }
- favor_ = subtag;
- return *this;
- }
- LocaleMatcher::Builder &LocaleMatcher::Builder::setDemotionPerDesiredLocale(ULocMatchDemotion demotion) {
- if (U_FAILURE(errorCode_)) { return *this; }
- demotion_ = demotion;
- return *this;
- }
- LocaleMatcher::Builder &LocaleMatcher::Builder::setMaxDistance(const Locale &desired,
- const Locale &supported) {
- if (U_FAILURE(errorCode_)) { return *this; }
- Locale *desiredClone = desired.clone();
- Locale *supportedClone = supported.clone();
- if (desiredClone == nullptr || supportedClone == nullptr) {
- delete desiredClone; // in case only one could not be allocated
- delete supportedClone;
- errorCode_ = U_MEMORY_ALLOCATION_ERROR;
- return *this;
- }
- delete maxDistanceDesired_;
- delete maxDistanceSupported_;
- maxDistanceDesired_ = desiredClone;
- maxDistanceSupported_ = supportedClone;
- return *this;
- }
- #if 0
- /**
- * <i>Internal only!</i>
- *
- * @param thresholdDistance the thresholdDistance to set, with -1 = default
- * @return this Builder object
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @Deprecated
- LocaleMatcher::Builder &LocaleMatcher::Builder::internalSetThresholdDistance(int32_t thresholdDistance) {
- if (U_FAILURE(errorCode_)) { return *this; }
- if (thresholdDistance > 100) {
- thresholdDistance = 100;
- }
- thresholdDistance_ = thresholdDistance;
- return *this;
- }
- #endif
- UBool LocaleMatcher::Builder::copyErrorTo(UErrorCode &outErrorCode) const {
- if (U_FAILURE(outErrorCode)) { return true; }
- if (U_SUCCESS(errorCode_)) { return false; }
- outErrorCode = errorCode_;
- return true;
- }
- LocaleMatcher LocaleMatcher::Builder::build(UErrorCode &errorCode) const {
- if (U_SUCCESS(errorCode) && U_FAILURE(errorCode_)) {
- errorCode = errorCode_;
- }
- return LocaleMatcher(*this, errorCode);
- }
- namespace {
- LSR getMaximalLsrOrUnd(const LikelySubtags &likelySubtags, const Locale &locale,
- UErrorCode &errorCode) {
- if (U_FAILURE(errorCode) || locale.isBogus() || *locale.getName() == 0 /* "und" */) {
- return UND_LSR;
- } else {
- return likelySubtags.makeMaximizedLsrFrom(locale, false, errorCode);
- }
- }
- int32_t hashLSR(const UHashTok token) {
- const LSR *lsr = static_cast<const LSR *>(token.pointer);
- return lsr->hashCode;
- }
- UBool compareLSRs(const UHashTok t1, const UHashTok t2) {
- const LSR *lsr1 = static_cast<const LSR *>(t1.pointer);
- const LSR *lsr2 = static_cast<const LSR *>(t2.pointer);
- return *lsr1 == *lsr2;
- }
- } // namespace
- int32_t LocaleMatcher::putIfAbsent(const LSR &lsr, int32_t i, int32_t suppLength,
- UErrorCode &errorCode) {
- if (U_FAILURE(errorCode)) { return suppLength; }
- if (!uhash_containsKey(supportedLsrToIndex, &lsr)) {
- uhash_putiAllowZero(supportedLsrToIndex, const_cast<LSR *>(&lsr), i, &errorCode);
- if (U_SUCCESS(errorCode)) {
- supportedLSRs[suppLength] = &lsr;
- supportedIndexes[suppLength++] = i;
- }
- }
- return suppLength;
- }
- LocaleMatcher::LocaleMatcher(const Builder &builder, UErrorCode &errorCode) :
- likelySubtags(*LikelySubtags::getSingleton(errorCode)),
- localeDistance(*LocaleDistance::getSingleton(errorCode)),
- thresholdDistance(builder.thresholdDistance_),
- demotionPerDesiredLocale(0),
- favorSubtag(builder.favor_),
- direction(builder.direction_),
- supportedLocales(nullptr), lsrs(nullptr), supportedLocalesLength(0),
- supportedLsrToIndex(nullptr),
- supportedLSRs(nullptr), supportedIndexes(nullptr), supportedLSRsLength(0),
- ownedDefaultLocale(nullptr), defaultLocale(nullptr) {
- if (U_FAILURE(errorCode)) { return; }
- const Locale *def = builder.defaultLocale_;
- LSR builderDefaultLSR;
- const LSR *defLSR = nullptr;
- if (def != nullptr) {
- ownedDefaultLocale = def->clone();
- if (ownedDefaultLocale == nullptr) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- def = ownedDefaultLocale;
- builderDefaultLSR = getMaximalLsrOrUnd(likelySubtags, *def, errorCode);
- if (U_FAILURE(errorCode)) { return; }
- defLSR = &builderDefaultLSR;
- }
- supportedLocalesLength = builder.supportedLocales_ != nullptr ?
- builder.supportedLocales_->size() : 0;
- if (supportedLocalesLength > 0) {
- // Store the supported locales in input order,
- // so that when different types are used (e.g., language tag strings)
- // we can return those by parallel index.
- supportedLocales = static_cast<const Locale **>(
- uprv_malloc(supportedLocalesLength * sizeof(const Locale *)));
- // Supported LRSs in input order.
- // In C++, we store these permanently to simplify ownership management
- // in the hash tables. Duplicate LSRs (if any) are unused overhead.
- lsrs = new LSR[supportedLocalesLength];
- if (supportedLocales == nullptr || lsrs == nullptr) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- // If the constructor fails partway, we need null pointers for destructibility.
- uprv_memset(supportedLocales, 0, supportedLocalesLength * sizeof(const Locale *));
- for (int32_t i = 0; i < supportedLocalesLength; ++i) {
- const Locale &locale = *static_cast<Locale *>(builder.supportedLocales_->elementAt(i));
- supportedLocales[i] = locale.clone();
- if (supportedLocales[i] == nullptr) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- const Locale &supportedLocale = *supportedLocales[i];
- LSR &lsr = lsrs[i] = getMaximalLsrOrUnd(likelySubtags, supportedLocale, errorCode);
- lsr.setHashCode();
- if (U_FAILURE(errorCode)) { return; }
- }
- // We need an unordered map from LSR to first supported locale with that LSR,
- // and an ordered list of (LSR, supported index) for
- // the supported locales in the following order:
- // 1. Default locale, if it is supported.
- // 2. Priority locales (aka "paradigm locales") in builder order.
- // 3. Remaining locales in builder order.
- supportedLsrToIndex = uhash_openSize(hashLSR, compareLSRs, uhash_compareLong,
- supportedLocalesLength, &errorCode);
- if (U_FAILURE(errorCode)) { return; }
- supportedLSRs = static_cast<const LSR **>(
- uprv_malloc(supportedLocalesLength * sizeof(const LSR *)));
- supportedIndexes = static_cast<int32_t *>(
- uprv_malloc(supportedLocalesLength * sizeof(int32_t)));
- if (supportedLSRs == nullptr || supportedIndexes == nullptr) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- int32_t suppLength = 0;
- // Determine insertion order.
- // Add locales immediately that are equivalent to the default.
- MaybeStackArray<int8_t, 100> order(supportedLocalesLength, errorCode);
- if (U_FAILURE(errorCode)) { return; }
- int32_t numParadigms = 0;
- for (int32_t i = 0; i < supportedLocalesLength; ++i) {
- const Locale &locale = *supportedLocales[i];
- const LSR &lsr = lsrs[i];
- if (defLSR == nullptr && builder.withDefault_) {
- // Implicit default locale = first supported locale, if not turned off.
- U_ASSERT(i == 0);
- def = &locale;
- defLSR = &lsr;
- order[i] = 1;
- suppLength = putIfAbsent(lsr, 0, suppLength, errorCode);
- } else if (defLSR != nullptr && lsr.isEquivalentTo(*defLSR)) {
- order[i] = 1;
- suppLength = putIfAbsent(lsr, i, suppLength, errorCode);
- } else if (localeDistance.isParadigmLSR(lsr)) {
- order[i] = 2;
- ++numParadigms;
- } else {
- order[i] = 3;
- }
- if (U_FAILURE(errorCode)) { return; }
- }
- // Add supported paradigm locales.
- int32_t paradigmLimit = suppLength + numParadigms;
- for (int32_t i = 0; i < supportedLocalesLength && suppLength < paradigmLimit; ++i) {
- if (order[i] == 2) {
- suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode);
- }
- }
- // Add remaining supported locales.
- for (int32_t i = 0; i < supportedLocalesLength; ++i) {
- if (order[i] == 3) {
- suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode);
- }
- }
- supportedLSRsLength = suppLength;
- // If supportedLSRsLength < supportedLocalesLength then
- // we waste as many array slots as there are duplicate supported LSRs,
- // but the amount of wasted space is small as long as there are few duplicates.
- }
- defaultLocale = def;
- if (builder.demotion_ == ULOCMATCH_DEMOTION_REGION) {
- demotionPerDesiredLocale = localeDistance.getDefaultDemotionPerDesiredLocale();
- }
- if (thresholdDistance >= 0) {
- // already copied
- } else if (builder.maxDistanceDesired_ != nullptr) {
- LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, *builder.maxDistanceSupported_, errorCode);
- const LSR *pSuppLSR = &suppLSR;
- int32_t indexAndDistance = localeDistance.getBestIndexAndDistance(
- getMaximalLsrOrUnd(likelySubtags, *builder.maxDistanceDesired_, errorCode),
- &pSuppLSR, 1,
- LocaleDistance::shiftDistance(100), favorSubtag, direction);
- if (U_SUCCESS(errorCode)) {
- // +1 for an exclusive threshold from an inclusive max.
- thresholdDistance = LocaleDistance::getDistanceFloor(indexAndDistance) + 1;
- } else {
- thresholdDistance = 0;
- }
- } else {
- thresholdDistance = localeDistance.getDefaultScriptDistance();
- }
- }
- LocaleMatcher::LocaleMatcher(LocaleMatcher &&src) noexcept :
- likelySubtags(src.likelySubtags),
- localeDistance(src.localeDistance),
- thresholdDistance(src.thresholdDistance),
- demotionPerDesiredLocale(src.demotionPerDesiredLocale),
- favorSubtag(src.favorSubtag),
- direction(src.direction),
- supportedLocales(src.supportedLocales), lsrs(src.lsrs),
- supportedLocalesLength(src.supportedLocalesLength),
- supportedLsrToIndex(src.supportedLsrToIndex),
- supportedLSRs(src.supportedLSRs),
- supportedIndexes(src.supportedIndexes),
- supportedLSRsLength(src.supportedLSRsLength),
- ownedDefaultLocale(src.ownedDefaultLocale), defaultLocale(src.defaultLocale) {
- src.supportedLocales = nullptr;
- src.lsrs = nullptr;
- src.supportedLocalesLength = 0;
- src.supportedLsrToIndex = nullptr;
- src.supportedLSRs = nullptr;
- src.supportedIndexes = nullptr;
- src.supportedLSRsLength = 0;
- src.ownedDefaultLocale = nullptr;
- src.defaultLocale = nullptr;
- }
- LocaleMatcher::~LocaleMatcher() {
- for (int32_t i = 0; i < supportedLocalesLength; ++i) {
- delete supportedLocales[i];
- }
- uprv_free(supportedLocales);
- delete[] lsrs;
- uhash_close(supportedLsrToIndex);
- uprv_free(supportedLSRs);
- uprv_free(supportedIndexes);
- delete ownedDefaultLocale;
- }
- LocaleMatcher &LocaleMatcher::operator=(LocaleMatcher &&src) noexcept {
- this->~LocaleMatcher();
- thresholdDistance = src.thresholdDistance;
- demotionPerDesiredLocale = src.demotionPerDesiredLocale;
- favorSubtag = src.favorSubtag;
- direction = src.direction;
- supportedLocales = src.supportedLocales;
- lsrs = src.lsrs;
- supportedLocalesLength = src.supportedLocalesLength;
- supportedLsrToIndex = src.supportedLsrToIndex;
- supportedLSRs = src.supportedLSRs;
- supportedIndexes = src.supportedIndexes;
- supportedLSRsLength = src.supportedLSRsLength;
- ownedDefaultLocale = src.ownedDefaultLocale;
- defaultLocale = src.defaultLocale;
- src.supportedLocales = nullptr;
- src.lsrs = nullptr;
- src.supportedLocalesLength = 0;
- src.supportedLsrToIndex = nullptr;
- src.supportedLSRs = nullptr;
- src.supportedIndexes = nullptr;
- src.supportedLSRsLength = 0;
- src.ownedDefaultLocale = nullptr;
- src.defaultLocale = nullptr;
- return *this;
- }
- class LocaleLsrIterator {
- public:
- LocaleLsrIterator(const LikelySubtags &likelySubtags, Locale::Iterator &locales,
- ULocMatchLifetime lifetime) :
- likelySubtags(likelySubtags), locales(locales), lifetime(lifetime) {}
- ~LocaleLsrIterator() {
- if (lifetime == ULOCMATCH_TEMPORARY_LOCALES) {
- delete remembered;
- }
- }
- bool hasNext() const {
- return locales.hasNext();
- }
- LSR next(UErrorCode &errorCode) {
- current = &locales.next();
- return getMaximalLsrOrUnd(likelySubtags, *current, errorCode);
- }
- void rememberCurrent(int32_t desiredIndex, UErrorCode &errorCode) {
- if (U_FAILURE(errorCode)) { return; }
- bestDesiredIndex = desiredIndex;
- if (lifetime == ULOCMATCH_STORED_LOCALES) {
- remembered = current;
- } else {
- // ULOCMATCH_TEMPORARY_LOCALES
- delete remembered;
- remembered = new Locale(*current);
- if (remembered == nullptr) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- }
- }
- }
- const Locale *orphanRemembered() {
- const Locale *rem = remembered;
- remembered = nullptr;
- return rem;
- }
- int32_t getBestDesiredIndex() const {
- return bestDesiredIndex;
- }
- private:
- const LikelySubtags &likelySubtags;
- Locale::Iterator &locales;
- ULocMatchLifetime lifetime;
- const Locale *current = nullptr, *remembered = nullptr;
- int32_t bestDesiredIndex = -1;
- };
- const Locale *LocaleMatcher::getBestMatch(const Locale &desiredLocale, UErrorCode &errorCode) const {
- if (U_FAILURE(errorCode)) { return nullptr; }
- std::optional<int32_t> suppIndex = getBestSuppIndex(
- getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),
- nullptr, errorCode);
- return U_SUCCESS(errorCode) && suppIndex.has_value() ? supportedLocales[*suppIndex]
- : defaultLocale;
- }
- const Locale *LocaleMatcher::getBestMatch(Locale::Iterator &desiredLocales,
- UErrorCode &errorCode) const {
- if (U_FAILURE(errorCode)) { return nullptr; }
- if (!desiredLocales.hasNext()) {
- return defaultLocale;
- }
- LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES);
- std::optional<int32_t> suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode);
- return U_SUCCESS(errorCode) && suppIndex.has_value() ? supportedLocales[*suppIndex]
- : defaultLocale;
- }
- const Locale *LocaleMatcher::getBestMatchForListString(
- StringPiece desiredLocaleList, UErrorCode &errorCode) const {
- if (U_FAILURE(errorCode)) { return nullptr; }
- LocalePriorityList list(desiredLocaleList, errorCode);
- LocalePriorityList::Iterator iter = list.iterator();
- return getBestMatch(iter, errorCode);
- }
- LocaleMatcher::Result LocaleMatcher::getBestMatchResult(
- const Locale &desiredLocale, UErrorCode &errorCode) const {
- if (U_FAILURE(errorCode)) {
- return Result(nullptr, defaultLocale, -1, -1, false);
- }
- std::optional<int32_t> suppIndex = getBestSuppIndex(
- getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),
- nullptr, errorCode);
- if (U_FAILURE(errorCode) || !suppIndex.has_value()) {
- return Result(nullptr, defaultLocale, -1, -1, false);
- } else {
- return Result(&desiredLocale, supportedLocales[*suppIndex], 0, *suppIndex, false);
- }
- }
- LocaleMatcher::Result LocaleMatcher::getBestMatchResult(
- Locale::Iterator &desiredLocales, UErrorCode &errorCode) const {
- if (U_FAILURE(errorCode) || !desiredLocales.hasNext()) {
- return Result(nullptr, defaultLocale, -1, -1, false);
- }
- LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES);
- std::optional<int32_t> suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode);
- if (U_FAILURE(errorCode) || !suppIndex.has_value()) {
- return Result(nullptr, defaultLocale, -1, -1, false);
- } else {
- return Result(lsrIter.orphanRemembered(), supportedLocales[*suppIndex],
- lsrIter.getBestDesiredIndex(), *suppIndex, true);
- }
- }
- std::optional<int32_t> LocaleMatcher::getBestSuppIndex(LSR desiredLSR,
- LocaleLsrIterator *remainingIter,
- UErrorCode &errorCode) const {
- if (U_FAILURE(errorCode)) { return std::nullopt; }
- int32_t desiredIndex = 0;
- int32_t bestSupportedLsrIndex = -1;
- for (int32_t bestShiftedDistance = LocaleDistance::shiftDistance(thresholdDistance);;) {
- // Quick check for exact maximized LSR.
- if (supportedLsrToIndex != nullptr) {
- desiredLSR.setHashCode();
- UBool found = false;
- int32_t suppIndex = uhash_getiAndFound(supportedLsrToIndex, &desiredLSR, &found);
- if (found) {
- if (remainingIter != nullptr) {
- remainingIter->rememberCurrent(desiredIndex, errorCode);
- }
- return suppIndex;
- }
- }
- int32_t bestIndexAndDistance = localeDistance.getBestIndexAndDistance(
- desiredLSR, supportedLSRs, supportedLSRsLength,
- bestShiftedDistance, favorSubtag, direction);
- if (bestIndexAndDistance >= 0) {
- bestShiftedDistance = LocaleDistance::getShiftedDistance(bestIndexAndDistance);
- if (remainingIter != nullptr) {
- remainingIter->rememberCurrent(desiredIndex, errorCode);
- if (U_FAILURE(errorCode)) { return std::nullopt; }
- }
- bestSupportedLsrIndex = LocaleDistance::getIndex(bestIndexAndDistance);
- }
- if ((bestShiftedDistance -= LocaleDistance::shiftDistance(demotionPerDesiredLocale)) <= 0) {
- break;
- }
- if (remainingIter == nullptr || !remainingIter->hasNext()) {
- break;
- }
- desiredLSR = remainingIter->next(errorCode);
- if (U_FAILURE(errorCode)) { return std::nullopt; }
- ++desiredIndex;
- }
- if (bestSupportedLsrIndex < 0) {
- // no good match
- return std::nullopt;
- }
- return supportedIndexes[bestSupportedLsrIndex];
- }
- UBool LocaleMatcher::isMatch(const Locale &desired, const Locale &supported,
- UErrorCode &errorCode) const {
- if (U_FAILURE(errorCode)) { return false; }
- LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode);
- if (U_FAILURE(errorCode)) { return false; }
- const LSR *pSuppLSR = &suppLSR;
- int32_t indexAndDistance = localeDistance.getBestIndexAndDistance(
- getMaximalLsrOrUnd(likelySubtags, desired, errorCode),
- &pSuppLSR, 1,
- LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction);
- return indexAndDistance >= 0;
- }
- double LocaleMatcher::internalMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const {
- if (U_FAILURE(errorCode)) { return 0.; }
- // Returns the inverse of the distance: That is, 1-distance(desired, supported).
- LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode);
- if (U_FAILURE(errorCode)) { return 0.; }
- const LSR *pSuppLSR = &suppLSR;
- int32_t indexAndDistance = localeDistance.getBestIndexAndDistance(
- getMaximalLsrOrUnd(likelySubtags, desired, errorCode),
- &pSuppLSR, 1,
- LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction);
- double distance = LocaleDistance::getDistanceDouble(indexAndDistance);
- return (100.0 - distance) / 100.0;
- }
- U_NAMESPACE_END
- // uloc_acceptLanguage() --------------------------------------------------- ***
- U_NAMESPACE_USE
- namespace {
- class LocaleFromTag {
- public:
- LocaleFromTag() : locale(Locale::getRoot()) {}
- const Locale &operator()(const char *tag) { return locale = Locale(tag); }
- private:
- // Store the locale in the converter, rather than return a reference to a temporary,
- // or a value which could go out of scope with the caller's reference to it.
- Locale locale;
- };
- int32_t acceptLanguage(UEnumeration &supportedLocales, Locale::Iterator &desiredLocales,
- char *dest, int32_t capacity, UAcceptResult *acceptResult,
- UErrorCode &errorCode) {
- if (U_FAILURE(errorCode)) { return 0; }
- LocaleMatcher::Builder builder;
- const char *locString;
- while ((locString = uenum_next(&supportedLocales, nullptr, &errorCode)) != nullptr) {
- Locale loc(locString);
- if (loc.isBogus()) {
- errorCode = U_ILLEGAL_ARGUMENT_ERROR;
- return 0;
- }
- builder.addSupportedLocale(loc);
- }
- LocaleMatcher matcher = builder.build(errorCode);
- LocaleMatcher::Result result = matcher.getBestMatchResult(desiredLocales, errorCode);
- if (U_FAILURE(errorCode)) { return 0; }
- if (result.getDesiredIndex() >= 0) {
- if (acceptResult != nullptr) {
- *acceptResult = *result.getDesiredLocale() == *result.getSupportedLocale() ?
- ULOC_ACCEPT_VALID : ULOC_ACCEPT_FALLBACK;
- }
- const char *bestStr = result.getSupportedLocale()->getName();
- int32_t bestLength = static_cast<int32_t>(uprv_strlen(bestStr));
- if (bestLength <= capacity) {
- uprv_memcpy(dest, bestStr, bestLength);
- }
- return u_terminateChars(dest, capacity, bestLength, &errorCode);
- } else {
- if (acceptResult != nullptr) {
- *acceptResult = ULOC_ACCEPT_FAILED;
- }
- return u_terminateChars(dest, capacity, 0, &errorCode);
- }
- }
- } // namespace
- U_CAPI int32_t U_EXPORT2
- uloc_acceptLanguage(char *result, int32_t resultAvailable,
- UAcceptResult *outResult,
- const char **acceptList, int32_t acceptListCount,
- UEnumeration *availableLocales,
- UErrorCode *status) {
- if (U_FAILURE(*status)) { return 0; }
- if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) ||
- (acceptList == nullptr ? acceptListCount != 0 : acceptListCount < 0) ||
- availableLocales == nullptr) {
- *status = U_ILLEGAL_ARGUMENT_ERROR;
- return 0;
- }
- LocaleFromTag converter;
- Locale::ConvertingIterator<const char **, LocaleFromTag> desiredLocales(
- acceptList, acceptList + acceptListCount, converter);
- return acceptLanguage(*availableLocales, desiredLocales,
- result, resultAvailable, outResult, *status);
- }
- U_CAPI int32_t U_EXPORT2
- uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable,
- UAcceptResult *outResult,
- const char *httpAcceptLanguage,
- UEnumeration *availableLocales,
- UErrorCode *status) {
- if (U_FAILURE(*status)) { return 0; }
- if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) ||
- httpAcceptLanguage == nullptr || availableLocales == nullptr) {
- *status = U_ILLEGAL_ARGUMENT_ERROR;
- return 0;
- }
- LocalePriorityList list(httpAcceptLanguage, *status);
- LocalePriorityList::Iterator desiredLocales = list.iterator();
- return acceptLanguage(*availableLocales, desiredLocales,
- result, resultAvailable, outResult, *status);
- }
|