1 // © 2019 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
5 // created: 2019may08 Markus W. Scherer
7 #ifndef __LOCMATCHER_H__
8 #define __LOCMATCHER_H__
10 #include "unicode/utypes.h"
11 #include "unicode/localebuilder.h"
12 #include "unicode/localematcher.h"
13 #include "unicode/locid.h"
14 #include "unicode/stringpiece.h"
15 #include "unicode/uobject.h"
17 #include "localeprioritylist.h"
18 #include "loclikelysubtags.h"
19 #include "locdistance.h"
25 #define UND_LSR LSR("und", "", "")
28 * Indicator for the lifetime of desired-locale objects passed into the LocaleMatcher.
32 enum ULocMatchLifetime
{
34 * Locale objects are temporary.
35 * The matcher will make a copy of a locale that will be used beyond one function call.
39 ULOCMATCH_TEMPORARY_LOCALES
,
41 * Locale objects are stored at least as long as the matcher is used.
42 * The matcher will keep only a pointer to a locale that will be used beyond one function call,
47 ULOCMATCH_STORED_LOCALES
// TODO: permanent? cached? clone?
50 typedef enum ULocMatchLifetime ULocMatchLifetime
;
55 LocaleMatcher::Result::Result(LocaleMatcher::Result
&&src
) U_NOEXCEPT
:
56 desiredLocale(src
.desiredLocale
),
57 supportedLocale(src
.supportedLocale
),
58 desiredIndex(src
.desiredIndex
),
59 supportedIndex(src
.supportedIndex
),
60 desiredIsOwned(src
.desiredIsOwned
) {
62 src
.desiredLocale
= nullptr;
63 src
.desiredIndex
= -1;
64 src
.desiredIsOwned
= FALSE
;
68 LocaleMatcher::Result::~Result() {
74 LocaleMatcher::Result
&LocaleMatcher::Result::operator=(LocaleMatcher::Result
&&src
) U_NOEXCEPT
{
77 desiredLocale
= src
.desiredLocale
;
78 supportedLocale
= src
.supportedLocale
;
79 desiredIndex
= src
.desiredIndex
;
80 supportedIndex
= src
.supportedIndex
;
81 desiredIsOwned
= src
.desiredIsOwned
;
84 src
.desiredLocale
= nullptr;
85 src
.desiredIndex
= -1;
86 src
.desiredIsOwned
= FALSE
;
91 Locale
LocaleMatcher::Result::makeResolvedLocale(UErrorCode
&errorCode
) const {
92 if (U_FAILURE(errorCode
) || supportedLocale
== nullptr) {
93 return Locale::getRoot();
95 const Locale
*bestDesired
= getDesiredLocale();
96 if (bestDesired
== nullptr || *supportedLocale
== *bestDesired
) {
97 return *supportedLocale
;
100 b
.setLocale(*supportedLocale
);
102 // Copy the region from bestDesired, if there is one.
103 const char *region
= bestDesired
->getCountry();
108 // Copy the variants from bestDesired, if there are any.
109 // Note that this will override any supportedLocale variants.
110 // For example, "sco-ulster-fonipa" + "...-fonupa" => "sco-fonupa" (replacing ulster).
111 const char *variants
= bestDesired
->getVariant();
112 if (*variants
!= 0) {
113 b
.setVariant(variants
);
116 // Copy the extensions from bestDesired, if there are any.
117 // C++ note: The following note, copied from Java, may not be true,
118 // as long as C++ copies by legacy ICU keyword, not by extension singleton.
119 // Note that this will override any supportedLocale extensions.
120 // For example, "th-u-nu-latn-ca-buddhist" + "...-u-nu-native" => "th-u-nu-native"
121 // (replacing calendar).
122 b
.copyExtensionsFrom(*bestDesired
, errorCode
);
123 return b
.build(errorCode
);
126 LocaleMatcher::Builder::Builder(LocaleMatcher::Builder
&&src
) U_NOEXCEPT
:
127 errorCode_(src
.errorCode_
),
128 supportedLocales_(src
.supportedLocales_
),
129 thresholdDistance_(src
.thresholdDistance_
),
130 demotion_(src
.demotion_
),
131 defaultLocale_(src
.defaultLocale_
),
133 src
.supportedLocales_
= nullptr;
134 src
.defaultLocale_
= nullptr;
137 LocaleMatcher::Builder::~Builder() {
138 delete supportedLocales_
;
139 delete defaultLocale_
;
142 LocaleMatcher::Builder
&LocaleMatcher::Builder::operator=(LocaleMatcher::Builder
&&src
) U_NOEXCEPT
{
145 errorCode_
= src
.errorCode_
;
146 supportedLocales_
= src
.supportedLocales_
;
147 thresholdDistance_
= src
.thresholdDistance_
;
148 demotion_
= src
.demotion_
;
149 defaultLocale_
= src
.defaultLocale_
;
152 src
.supportedLocales_
= nullptr;
153 src
.defaultLocale_
= nullptr;
157 void LocaleMatcher::Builder::clearSupportedLocales() {
158 if (supportedLocales_
!= nullptr) {
159 supportedLocales_
->removeAllElements();
163 bool LocaleMatcher::Builder::ensureSupportedLocaleVector() {
164 if (U_FAILURE(errorCode_
)) { return false; }
165 if (supportedLocales_
!= nullptr) { return true; }
166 supportedLocales_
= new UVector(uprv_deleteUObject
, nullptr, errorCode_
);
167 if (U_FAILURE(errorCode_
)) { return false; }
168 if (supportedLocales_
== nullptr) {
169 errorCode_
= U_MEMORY_ALLOCATION_ERROR
;
175 LocaleMatcher::Builder
&LocaleMatcher::Builder::setSupportedLocalesFromListString(
176 StringPiece locales
) {
177 LocalePriorityList
list(locales
, errorCode_
);
178 if (U_FAILURE(errorCode_
)) { return *this; }
179 clearSupportedLocales();
180 if (!ensureSupportedLocaleVector()) { return *this; }
181 int32_t length
= list
.getLengthIncludingRemoved();
182 for (int32_t i
= 0; i
< length
; ++i
) {
183 Locale
*locale
= list
.orphanLocaleAt(i
);
184 if (locale
== nullptr) { continue; }
185 supportedLocales_
->addElement(locale
, errorCode_
);
186 if (U_FAILURE(errorCode_
)) {
194 LocaleMatcher::Builder
&LocaleMatcher::Builder::setSupportedLocales(Locale::Iterator
&locales
) {
195 if (U_FAILURE(errorCode_
)) { return *this; }
196 clearSupportedLocales();
197 if (!ensureSupportedLocaleVector()) { return *this; }
198 while (locales
.hasNext()) {
199 const Locale
&locale
= locales
.next();
200 Locale
*clone
= locale
.clone();
201 if (clone
== nullptr) {
202 errorCode_
= U_MEMORY_ALLOCATION_ERROR
;
205 supportedLocales_
->addElement(clone
, errorCode_
);
206 if (U_FAILURE(errorCode_
)) {
214 LocaleMatcher::Builder
&LocaleMatcher::Builder::addSupportedLocale(const Locale
&locale
) {
215 if (!ensureSupportedLocaleVector()) { return *this; }
216 Locale
*clone
= locale
.clone();
217 if (clone
== nullptr) {
218 errorCode_
= U_MEMORY_ALLOCATION_ERROR
;
221 supportedLocales_
->addElement(clone
, errorCode_
);
222 if (U_FAILURE(errorCode_
)) {
228 LocaleMatcher::Builder
&LocaleMatcher::Builder::setDefaultLocale(const Locale
*defaultLocale
) {
229 if (U_FAILURE(errorCode_
)) { return *this; }
230 Locale
*clone
= nullptr;
231 if (defaultLocale
!= nullptr) {
232 clone
= defaultLocale
->clone();
233 if (clone
== nullptr) {
234 errorCode_
= U_MEMORY_ALLOCATION_ERROR
;
238 delete defaultLocale_
;
239 defaultLocale_
= clone
;
243 LocaleMatcher::Builder
&LocaleMatcher::Builder::setFavorSubtag(ULocMatchFavorSubtag subtag
) {
244 if (U_FAILURE(errorCode_
)) { return *this; }
249 LocaleMatcher::Builder
&LocaleMatcher::Builder::setDemotionPerDesiredLocale(ULocMatchDemotion demotion
) {
250 if (U_FAILURE(errorCode_
)) { return *this; }
251 demotion_
= demotion
;
257 * <i>Internal only!</i>
259 * @param thresholdDistance the thresholdDistance to set, with -1 = default
260 * @return this Builder object
262 * @deprecated This API is ICU internal only.
265 LocaleMatcher::Builder
&LocaleMatcher::Builder::internalSetThresholdDistance(int32_t thresholdDistance
) {
266 if (U_FAILURE(errorCode_
)) { return *this; }
267 if (thresholdDistance
> 100) {
268 thresholdDistance
= 100;
270 thresholdDistance_
= thresholdDistance
;
275 UBool
LocaleMatcher::Builder::copyErrorTo(UErrorCode
&outErrorCode
) const {
276 if (U_FAILURE(outErrorCode
)) { return TRUE
; }
277 if (U_SUCCESS(errorCode_
)) { return FALSE
; }
278 outErrorCode
= errorCode_
;
282 LocaleMatcher
LocaleMatcher::Builder::build(UErrorCode
&errorCode
) const {
283 if (U_SUCCESS(errorCode
) && U_FAILURE(errorCode_
)) {
284 errorCode
= errorCode_
;
286 return LocaleMatcher(*this, errorCode
);
291 LSR
getMaximalLsrOrUnd(const XLikelySubtags
&likelySubtags
, const Locale
&locale
,
292 UErrorCode
&errorCode
) {
293 if (U_FAILURE(errorCode
) || locale
.isBogus() || *locale
.getName() == 0 /* "und" */) {
296 return likelySubtags
.makeMaximizedLsrFrom(locale
, errorCode
);
300 int32_t hashLSR(const UHashTok token
) {
301 const LSR
*lsr
= static_cast<const LSR
*>(token
.pointer
);
302 return lsr
->hashCode
;
305 UBool
compareLSRs(const UHashTok t1
, const UHashTok t2
) {
306 const LSR
*lsr1
= static_cast<const LSR
*>(t1
.pointer
);
307 const LSR
*lsr2
= static_cast<const LSR
*>(t2
.pointer
);
308 return *lsr1
== *lsr2
;
311 bool putIfAbsent(UHashtable
*lsrToIndex
, const LSR
&lsr
, int32_t i
, UErrorCode
&errorCode
) {
312 if (U_FAILURE(errorCode
)) { return false; }
314 int32_t index
= uhash_geti(lsrToIndex
, &lsr
);
318 uhash_puti(lsrToIndex
, const_cast<LSR
*>(&lsr
), i
, &errorCode
);
319 return U_SUCCESS(errorCode
);
325 LocaleMatcher::LocaleMatcher(const Builder
&builder
, UErrorCode
&errorCode
) :
326 likelySubtags(*XLikelySubtags::getSingleton(errorCode
)),
327 localeDistance(*LocaleDistance::getSingleton(errorCode
)),
328 thresholdDistance(builder
.thresholdDistance_
),
329 demotionPerDesiredLocale(0),
330 favorSubtag(builder
.favor_
),
331 supportedLocales(nullptr), lsrs(nullptr), supportedLocalesLength(0),
332 supportedLsrToIndex(nullptr),
333 supportedLSRs(nullptr), supportedIndexes(nullptr), supportedLSRsLength(0),
334 ownedDefaultLocale(nullptr), defaultLocale(nullptr), defaultLocaleIndex(-1) {
335 if (U_FAILURE(errorCode
)) { return; }
336 if (thresholdDistance
< 0) {
337 thresholdDistance
= localeDistance
.getDefaultScriptDistance();
339 supportedLocalesLength
= builder
.supportedLocales_
!= nullptr ?
340 builder
.supportedLocales_
->size() : 0;
341 const Locale
*def
= builder
.defaultLocale_
;
343 if (supportedLocalesLength
> 0) {
344 // Store the supported locales in input order,
345 // so that when different types are used (e.g., language tag strings)
346 // we can return those by parallel index.
347 supportedLocales
= static_cast<const Locale
**>(
348 uprv_malloc(supportedLocalesLength
* sizeof(const Locale
*)));
349 // Supported LRSs in input order.
350 // In C++, we store these permanently to simplify ownership management
351 // in the hash tables. Duplicate LSRs (if any) are unused overhead.
352 lsrs
= new LSR
[supportedLocalesLength
];
353 if (supportedLocales
== nullptr || lsrs
== nullptr) {
354 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
357 // If the constructor fails partway, we need null pointers for destructibility.
358 uprv_memset(supportedLocales
, 0, supportedLocalesLength
* sizeof(const Locale
*));
359 // Also find the first supported locale whose LSR is
360 // the same as that for the default locale.
361 LSR builderDefaultLSR
;
362 const LSR
*defLSR
= nullptr;
363 if (def
!= nullptr) {
364 builderDefaultLSR
= getMaximalLsrOrUnd(likelySubtags
, *def
, errorCode
);
365 if (U_FAILURE(errorCode
)) { return; }
366 defLSR
= &builderDefaultLSR
;
368 for (int32_t i
= 0; i
< supportedLocalesLength
; ++i
) {
369 const Locale
&locale
= *static_cast<Locale
*>(builder
.supportedLocales_
->elementAt(i
));
370 supportedLocales
[i
] = locale
.clone();
371 if (supportedLocales
[i
] == nullptr) {
372 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
375 const Locale
&supportedLocale
= *supportedLocales
[i
];
376 LSR
&lsr
= lsrs
[i
] = getMaximalLsrOrUnd(likelySubtags
, supportedLocale
, errorCode
);
378 if (U_FAILURE(errorCode
)) { return; }
379 if (idef
< 0 && defLSR
!= nullptr && lsr
== *defLSR
) {
381 defLSR
= &lsr
; // owned pointer to put into supportedLsrToIndex
382 if (*def
== supportedLocale
) {
383 def
= &supportedLocale
; // owned pointer to keep
388 // We need an unordered map from LSR to first supported locale with that LSR,
389 // and an ordered list of (LSR, supported index).
390 // We insert the supported locales in the following order:
391 // 1. Default locale, if it is supported.
392 // 2. Priority locales (aka "paradigm locales") in builder order.
393 // 3. Remaining locales in builder order.
394 // In Java, we use a LinkedHashMap for both map & ordered lists.
395 // In C++, we use separate structures.
396 // We over-allocate arrays of LSRs and indexes for simplicity.
397 // We reserve slots at the array starts for the default and paradigm locales,
398 // plus enough for all supported locales.
399 // If there are few paradigm locales and few duplicate supported LSRs,
400 // then the amount of wasted space is small.
401 supportedLsrToIndex
= uhash_openSize(hashLSR
, compareLSRs
, uhash_compareLong
,
402 supportedLocalesLength
, &errorCode
);
403 if (U_FAILURE(errorCode
)) { return; }
404 int32_t paradigmLimit
= 1 + localeDistance
.getParadigmLSRsLength();
405 int32_t suppLSRsCapacity
= paradigmLimit
+ supportedLocalesLength
;
406 supportedLSRs
= static_cast<const LSR
**>(
407 uprv_malloc(suppLSRsCapacity
* sizeof(const LSR
*)));
408 supportedIndexes
= static_cast<int32_t *>(
409 uprv_malloc(suppLSRsCapacity
* sizeof(int32_t)));
410 if (supportedLSRs
== nullptr || supportedIndexes
== nullptr) {
411 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
414 int32_t paradigmIndex
= 0;
415 int32_t otherIndex
= paradigmLimit
;
417 uhash_puti(supportedLsrToIndex
, const_cast<LSR
*>(defLSR
), idef
+ 1, &errorCode
);
418 supportedLSRs
[0] = defLSR
;
419 supportedIndexes
[0] = idef
;
422 for (int32_t i
= 0; i
< supportedLocalesLength
; ++i
) {
423 if (i
== idef
) { continue; }
424 const Locale
&locale
= *supportedLocales
[i
];
425 const LSR
&lsr
= lsrs
[i
];
426 if (defLSR
== nullptr) {
431 uhash_puti(supportedLsrToIndex
, const_cast<LSR
*>(&lsr
), 0 + 1, &errorCode
);
432 supportedLSRs
[0] = &lsr
;
433 supportedIndexes
[0] = 0;
435 } else if (idef
>= 0 && lsr
== *defLSR
) {
436 // lsr == *defLSR means that this supported locale is
437 // a duplicate of the default locale.
438 // Either an explicit default locale is supported, and we added it before the loop,
439 // or there is no explicit default locale, and this is
440 // a duplicate of the first supported locale.
441 // In both cases, idef >= 0 now, so otherwise we can skip the comparison.
442 // For a duplicate, putIfAbsent() is a no-op, so nothing to do.
444 if (putIfAbsent(supportedLsrToIndex
, lsr
, i
+ 1, errorCode
)) {
445 if (localeDistance
.isParadigmLSR(lsr
)) {
446 supportedLSRs
[paradigmIndex
] = &lsr
;
447 supportedIndexes
[paradigmIndex
++] = i
;
449 supportedLSRs
[otherIndex
] = &lsr
;
450 supportedIndexes
[otherIndex
++] = i
;
454 if (U_FAILURE(errorCode
)) { return; }
456 // Squeeze out unused array slots.
457 if (paradigmIndex
< paradigmLimit
&& paradigmLimit
< otherIndex
) {
458 uprv_memmove(supportedLSRs
+ paradigmIndex
, supportedLSRs
+ paradigmLimit
,
459 (otherIndex
- paradigmLimit
) * sizeof(const LSR
*));
460 uprv_memmove(supportedIndexes
+ paradigmIndex
, supportedIndexes
+ paradigmLimit
,
461 (otherIndex
- paradigmLimit
) * sizeof(int32_t));
463 supportedLSRsLength
= otherIndex
- (paradigmLimit
- paradigmIndex
);
466 if (def
!= nullptr && (idef
< 0 || def
!= supportedLocales
[idef
])) {
467 ownedDefaultLocale
= def
->clone();
468 if (ownedDefaultLocale
== nullptr) {
469 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
472 def
= ownedDefaultLocale
;
475 defaultLocaleIndex
= idef
;
477 if (builder
.demotion_
== ULOCMATCH_DEMOTION_REGION
) {
478 demotionPerDesiredLocale
= localeDistance
.getDefaultDemotionPerDesiredLocale();
482 LocaleMatcher::LocaleMatcher(LocaleMatcher
&&src
) U_NOEXCEPT
:
483 likelySubtags(src
.likelySubtags
),
484 localeDistance(src
.localeDistance
),
485 thresholdDistance(src
.thresholdDistance
),
486 demotionPerDesiredLocale(src
.demotionPerDesiredLocale
),
487 favorSubtag(src
.favorSubtag
),
488 supportedLocales(src
.supportedLocales
), lsrs(src
.lsrs
),
489 supportedLocalesLength(src
.supportedLocalesLength
),
490 supportedLsrToIndex(src
.supportedLsrToIndex
),
491 supportedLSRs(src
.supportedLSRs
),
492 supportedIndexes(src
.supportedIndexes
),
493 supportedLSRsLength(src
.supportedLSRsLength
),
494 ownedDefaultLocale(src
.ownedDefaultLocale
), defaultLocale(src
.defaultLocale
),
495 defaultLocaleIndex(src
.defaultLocaleIndex
) {
496 src
.supportedLocales
= nullptr;
498 src
.supportedLocalesLength
= 0;
499 src
.supportedLsrToIndex
= nullptr;
500 src
.supportedLSRs
= nullptr;
501 src
.supportedIndexes
= nullptr;
502 src
.supportedLSRsLength
= 0;
503 src
.ownedDefaultLocale
= nullptr;
504 src
.defaultLocale
= nullptr;
505 src
.defaultLocaleIndex
= -1;
508 LocaleMatcher::~LocaleMatcher() {
509 for (int32_t i
= 0; i
< supportedLocalesLength
; ++i
) {
510 delete supportedLocales
[i
];
512 uprv_free(supportedLocales
);
514 uhash_close(supportedLsrToIndex
);
515 uprv_free(supportedLSRs
);
516 uprv_free(supportedIndexes
);
517 delete ownedDefaultLocale
;
520 LocaleMatcher
&LocaleMatcher::operator=(LocaleMatcher
&&src
) U_NOEXCEPT
{
521 this->~LocaleMatcher();
523 thresholdDistance
= src
.thresholdDistance
;
524 demotionPerDesiredLocale
= src
.demotionPerDesiredLocale
;
525 favorSubtag
= src
.favorSubtag
;
526 supportedLocales
= src
.supportedLocales
;
528 supportedLocalesLength
= src
.supportedLocalesLength
;
529 supportedLsrToIndex
= src
.supportedLsrToIndex
;
530 supportedLSRs
= src
.supportedLSRs
;
531 supportedIndexes
= src
.supportedIndexes
;
532 supportedLSRsLength
= src
.supportedLSRsLength
;
533 ownedDefaultLocale
= src
.ownedDefaultLocale
;
534 defaultLocale
= src
.defaultLocale
;
535 defaultLocaleIndex
= src
.defaultLocaleIndex
;
537 src
.supportedLocales
= nullptr;
539 src
.supportedLocalesLength
= 0;
540 src
.supportedLsrToIndex
= nullptr;
541 src
.supportedLSRs
= nullptr;
542 src
.supportedIndexes
= nullptr;
543 src
.supportedLSRsLength
= 0;
544 src
.ownedDefaultLocale
= nullptr;
545 src
.defaultLocale
= nullptr;
546 src
.defaultLocaleIndex
= -1;
550 class LocaleLsrIterator
{
552 LocaleLsrIterator(const XLikelySubtags
&likelySubtags
, Locale::Iterator
&locales
,
553 ULocMatchLifetime lifetime
) :
554 likelySubtags(likelySubtags
), locales(locales
), lifetime(lifetime
) {}
556 ~LocaleLsrIterator() {
557 if (lifetime
== ULOCMATCH_TEMPORARY_LOCALES
) {
562 bool hasNext() const {
563 return locales
.hasNext();
566 LSR
next(UErrorCode
&errorCode
) {
567 current
= &locales
.next();
568 return getMaximalLsrOrUnd(likelySubtags
, *current
, errorCode
);
571 void rememberCurrent(int32_t desiredIndex
, UErrorCode
&errorCode
) {
572 if (U_FAILURE(errorCode
)) { return; }
573 bestDesiredIndex
= desiredIndex
;
574 if (lifetime
== ULOCMATCH_STORED_LOCALES
) {
575 remembered
= current
;
577 // ULOCMATCH_TEMPORARY_LOCALES
579 remembered
= new Locale(*current
);
580 if (remembered
== nullptr) {
581 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
586 const Locale
*orphanRemembered() {
587 const Locale
*rem
= remembered
;
588 remembered
= nullptr;
592 int32_t getBestDesiredIndex() const {
593 return bestDesiredIndex
;
597 const XLikelySubtags
&likelySubtags
;
598 Locale::Iterator
&locales
;
599 ULocMatchLifetime lifetime
;
600 const Locale
*current
= nullptr, *remembered
= nullptr;
601 int32_t bestDesiredIndex
= -1;
604 const Locale
*LocaleMatcher::getBestMatch(const Locale
&desiredLocale
, UErrorCode
&errorCode
) const {
605 if (U_FAILURE(errorCode
)) { return nullptr; }
606 int32_t suppIndex
= getBestSuppIndex(
607 getMaximalLsrOrUnd(likelySubtags
, desiredLocale
, errorCode
),
609 return U_SUCCESS(errorCode
) && suppIndex
>= 0 ? supportedLocales
[suppIndex
] : defaultLocale
;
612 const Locale
*LocaleMatcher::getBestMatch(Locale::Iterator
&desiredLocales
,
613 UErrorCode
&errorCode
) const {
614 if (U_FAILURE(errorCode
)) { return nullptr; }
615 if (!desiredLocales
.hasNext()) {
616 return defaultLocale
;
618 LocaleLsrIterator
lsrIter(likelySubtags
, desiredLocales
, ULOCMATCH_TEMPORARY_LOCALES
);
619 int32_t suppIndex
= getBestSuppIndex(lsrIter
.next(errorCode
), &lsrIter
, errorCode
);
620 return U_SUCCESS(errorCode
) && suppIndex
>= 0 ? supportedLocales
[suppIndex
] : defaultLocale
;
623 const Locale
*LocaleMatcher::getBestMatchForListString(
624 StringPiece desiredLocaleList
, UErrorCode
&errorCode
) const {
625 LocalePriorityList
list(desiredLocaleList
, errorCode
);
626 LocalePriorityList::Iterator iter
= list
.iterator();
627 return getBestMatch(iter
, errorCode
);
630 LocaleMatcher::Result
LocaleMatcher::getBestMatchResult(
631 const Locale
&desiredLocale
, UErrorCode
&errorCode
) const {
632 if (U_FAILURE(errorCode
)) {
633 return Result(nullptr, defaultLocale
, -1, defaultLocaleIndex
, FALSE
);
635 int32_t suppIndex
= getBestSuppIndex(
636 getMaximalLsrOrUnd(likelySubtags
, desiredLocale
, errorCode
),
638 if (U_FAILURE(errorCode
) || suppIndex
< 0) {
639 return Result(nullptr, defaultLocale
, -1, defaultLocaleIndex
, FALSE
);
641 return Result(&desiredLocale
, supportedLocales
[suppIndex
], 0, suppIndex
, FALSE
);
645 LocaleMatcher::Result
LocaleMatcher::getBestMatchResult(
646 Locale::Iterator
&desiredLocales
, UErrorCode
&errorCode
) const {
647 if (U_FAILURE(errorCode
) || !desiredLocales
.hasNext()) {
648 return Result(nullptr, defaultLocale
, -1, defaultLocaleIndex
, FALSE
);
650 LocaleLsrIterator
lsrIter(likelySubtags
, desiredLocales
, ULOCMATCH_TEMPORARY_LOCALES
);
651 int32_t suppIndex
= getBestSuppIndex(lsrIter
.next(errorCode
), &lsrIter
, errorCode
);
652 if (U_FAILURE(errorCode
) || suppIndex
< 0) {
653 return Result(nullptr, defaultLocale
, -1, defaultLocaleIndex
, FALSE
);
655 return Result(lsrIter
.orphanRemembered(), supportedLocales
[suppIndex
],
656 lsrIter
.getBestDesiredIndex(), suppIndex
, TRUE
);
660 int32_t LocaleMatcher::getBestSuppIndex(LSR desiredLSR
, LocaleLsrIterator
*remainingIter
,
661 UErrorCode
&errorCode
) const {
662 if (U_FAILURE(errorCode
)) { return -1; }
663 int32_t desiredIndex
= 0;
664 int32_t bestSupportedLsrIndex
= -1;
665 for (int32_t bestDistance
= thresholdDistance
;;) {
666 // Quick check for exact maximized LSR.
667 // Returns suppIndex+1 where 0 means not found.
668 if (supportedLsrToIndex
!= nullptr) {
669 desiredLSR
.setHashCode();
670 int32_t index
= uhash_geti(supportedLsrToIndex
, &desiredLSR
);
672 int32_t suppIndex
= index
- 1;
673 if (remainingIter
!= nullptr) {
674 remainingIter
->rememberCurrent(desiredIndex
, errorCode
);
679 int32_t bestIndexAndDistance
= localeDistance
.getBestIndexAndDistance(
680 desiredLSR
, supportedLSRs
, supportedLSRsLength
, bestDistance
, favorSubtag
);
681 if (bestIndexAndDistance
>= 0) {
682 bestDistance
= bestIndexAndDistance
& 0xff;
683 if (remainingIter
!= nullptr) {
684 remainingIter
->rememberCurrent(desiredIndex
, errorCode
);
685 if (U_FAILURE(errorCode
)) { return -1; }
687 bestSupportedLsrIndex
= bestIndexAndDistance
>= 0 ? bestIndexAndDistance
>> 8 : -1;
689 if ((bestDistance
-= demotionPerDesiredLocale
) <= 0) {
692 if (remainingIter
== nullptr || !remainingIter
->hasNext()) {
695 desiredLSR
= remainingIter
->next(errorCode
);
696 if (U_FAILURE(errorCode
)) { return -1; }
699 if (bestSupportedLsrIndex
< 0) {
703 return supportedIndexes
[bestSupportedLsrIndex
];
706 double LocaleMatcher::internalMatch(const Locale
&desired
, const Locale
&supported
, UErrorCode
&errorCode
) const {
707 // Returns the inverse of the distance: That is, 1-distance(desired, supported).
708 LSR suppLSR
= getMaximalLsrOrUnd(likelySubtags
, supported
, errorCode
);
709 if (U_FAILURE(errorCode
)) { return 0; }
710 const LSR
*pSuppLSR
= &suppLSR
;
711 int32_t distance
= localeDistance
.getBestIndexAndDistance(
712 getMaximalLsrOrUnd(likelySubtags
, desired
, errorCode
),
714 thresholdDistance
, favorSubtag
) & 0xff;
715 return (100 - distance
) / 100.0;
720 #endif // __LOCMATCHER_H__