1// © 2019 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3
4// localematcher.cpp
5// created: 2019may08 Markus W. Scherer
6
7#ifndef __LOCMATCHER_H__
8#define __LOCMATCHER_H__
9
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/uloc.h"
16#include "unicode/uobject.h"
17#include "cstring.h"
18#include "localeprioritylist.h"
19#include "loclikelysubtags.h"
20#include "locdistance.h"
21#include "lsr.h"
22#include "uassert.h"
23#include "uhash.h"
24#include "ustr_imp.h"
25#include "uvector.h"
26
27#define UND_LSR LSR("und", "", "", LSR::EXPLICIT_LSR)
28
29/**
30 * Indicator for the lifetime of desired-locale objects passed into the LocaleMatcher.
31 *
32 * @draft ICU 65
33 */
34enum ULocMatchLifetime {
35 /**
36 * Locale objects are temporary.
37 * The matcher will make a copy of a locale that will be used beyond one function call.
38 *
39 * @draft ICU 65
40 */
41 ULOCMATCH_TEMPORARY_LOCALES,
42 /**
43 * Locale objects are stored at least as long as the matcher is used.
44 * The matcher will keep only a pointer to a locale that will be used beyond one function call,
45 * avoiding a copy.
46 *
47 * @draft ICU 65
48 */
49 ULOCMATCH_STORED_LOCALES // TODO: permanent? cached? clone?
50};
51#ifndef U_IN_DOXYGEN
52typedef enum ULocMatchLifetime ULocMatchLifetime;
53#endif
54
55U_NAMESPACE_BEGIN
56
57LocaleMatcher::Result::Result(LocaleMatcher::Result &&src) U_NOEXCEPT :
58 desiredLocale(src.desiredLocale),
59 supportedLocale(src.supportedLocale),
60 desiredIndex(src.desiredIndex),
61 supportedIndex(src.supportedIndex),
62 desiredIsOwned(src.desiredIsOwned) {
63 if (desiredIsOwned) {
64 src.desiredLocale = nullptr;
65 src.desiredIndex = -1;
66 src.desiredIsOwned = FALSE;
67 }
68}
69
70LocaleMatcher::Result::~Result() {
71 if (desiredIsOwned) {
72 delete desiredLocale;
73 }
74}
75
76LocaleMatcher::Result &LocaleMatcher::Result::operator=(LocaleMatcher::Result &&src) U_NOEXCEPT {
77 this->~Result();
78
79 desiredLocale = src.desiredLocale;
80 supportedLocale = src.supportedLocale;
81 desiredIndex = src.desiredIndex;
82 supportedIndex = src.supportedIndex;
83 desiredIsOwned = src.desiredIsOwned;
84
85 if (desiredIsOwned) {
86 src.desiredLocale = nullptr;
87 src.desiredIndex = -1;
88 src.desiredIsOwned = FALSE;
89 }
90 return *this;
91}
92
93Locale LocaleMatcher::Result::makeResolvedLocale(UErrorCode &errorCode) const {
94 if (U_FAILURE(errorCode) || supportedLocale == nullptr) {
95 return Locale::getRoot();
96 }
97 const Locale *bestDesired = getDesiredLocale();
98 if (bestDesired == nullptr || *supportedLocale == *bestDesired) {
99 return *supportedLocale;
100 }
101 LocaleBuilder b;
102 b.setLocale(*supportedLocale);
103
104 // Copy the region from bestDesired, if there is one.
105 const char *region = bestDesired->getCountry();
106 if (*region != 0) {
107 b.setRegion(region);
108 }
109
110 // Copy the variants from bestDesired, if there are any.
111 // Note that this will override any supportedLocale variants.
112 // For example, "sco-ulster-fonipa" + "...-fonupa" => "sco-fonupa" (replacing ulster).
113 const char *variants = bestDesired->getVariant();
114 if (*variants != 0) {
115 b.setVariant(variants);
116 }
117
118 // Copy the extensions from bestDesired, if there are any.
119 // C++ note: The following note, copied from Java, may not be true,
120 // as long as C++ copies by legacy ICU keyword, not by extension singleton.
121 // Note that this will override any supportedLocale extensions.
122 // For example, "th-u-nu-latn-ca-buddhist" + "...-u-nu-native" => "th-u-nu-native"
123 // (replacing calendar).
124 b.copyExtensionsFrom(*bestDesired, errorCode);
125 return b.build(errorCode);
126}
127
128LocaleMatcher::Builder::Builder(LocaleMatcher::Builder &&src) U_NOEXCEPT :
129 errorCode_(src.errorCode_),
130 supportedLocales_(src.supportedLocales_),
131 thresholdDistance_(src.thresholdDistance_),
132 demotion_(src.demotion_),
133 defaultLocale_(src.defaultLocale_),
134 favor_(src.favor_),
135 direction_(src.direction_) {
136 src.supportedLocales_ = nullptr;
137 src.defaultLocale_ = nullptr;
138}
139
140LocaleMatcher::Builder::~Builder() {
141 delete supportedLocales_;
142 delete defaultLocale_;
143}
144
145LocaleMatcher::Builder &LocaleMatcher::Builder::operator=(LocaleMatcher::Builder &&src) U_NOEXCEPT {
146 this->~Builder();
147
148 errorCode_ = src.errorCode_;
149 supportedLocales_ = src.supportedLocales_;
150 thresholdDistance_ = src.thresholdDistance_;
151 demotion_ = src.demotion_;
152 defaultLocale_ = src.defaultLocale_;
153 favor_ = src.favor_;
154 direction_ = src.direction_;
155
156 src.supportedLocales_ = nullptr;
157 src.defaultLocale_ = nullptr;
158 return *this;
159}
160
161void LocaleMatcher::Builder::clearSupportedLocales() {
162 if (supportedLocales_ != nullptr) {
163 supportedLocales_->removeAllElements();
164 }
165}
166
167bool LocaleMatcher::Builder::ensureSupportedLocaleVector() {
168 if (U_FAILURE(errorCode_)) { return false; }
169 if (supportedLocales_ != nullptr) { return true; }
170 supportedLocales_ = new UVector(uprv_deleteUObject, nullptr, errorCode_);
171 if (U_FAILURE(errorCode_)) { return false; }
172 if (supportedLocales_ == nullptr) {
173 errorCode_ = U_MEMORY_ALLOCATION_ERROR;
174 return false;
175 }
176 return true;
177}
178
179LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocalesFromListString(
180 StringPiece locales) {
181 LocalePriorityList list(locales, errorCode_);
182 if (U_FAILURE(errorCode_)) { return *this; }
183 clearSupportedLocales();
184 if (!ensureSupportedLocaleVector()) { return *this; }
185 int32_t length = list.getLengthIncludingRemoved();
186 for (int32_t i = 0; i < length; ++i) {
187 Locale *locale = list.orphanLocaleAt(i);
188 if (locale == nullptr) { continue; }
189 supportedLocales_->addElement(locale, errorCode_);
190 if (U_FAILURE(errorCode_)) {
191 delete locale;
192 break;
193 }
194 }
195 return *this;
196}
197
198LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocales(Locale::Iterator &locales) {
199 if (U_FAILURE(errorCode_)) { return *this; }
200 clearSupportedLocales();
201 if (!ensureSupportedLocaleVector()) { return *this; }
202 while (locales.hasNext()) {
203 const Locale &locale = locales.next();
204 Locale *clone = locale.clone();
205 if (clone == nullptr) {
206 errorCode_ = U_MEMORY_ALLOCATION_ERROR;
207 break;
208 }
209 supportedLocales_->addElement(clone, errorCode_);
210 if (U_FAILURE(errorCode_)) {
211 delete clone;
212 break;
213 }
214 }
215 return *this;
216}
217
218LocaleMatcher::Builder &LocaleMatcher::Builder::addSupportedLocale(const Locale &locale) {
219 if (!ensureSupportedLocaleVector()) { return *this; }
220 Locale *clone = locale.clone();
221 if (clone == nullptr) {
222 errorCode_ = U_MEMORY_ALLOCATION_ERROR;
223 return *this;
224 }
225 supportedLocales_->addElement(clone, errorCode_);
226 if (U_FAILURE(errorCode_)) {
227 delete clone;
228 }
229 return *this;
230}
231
232LocaleMatcher::Builder &LocaleMatcher::Builder::setDefaultLocale(const Locale *defaultLocale) {
233 if (U_FAILURE(errorCode_)) { return *this; }
234 Locale *clone = nullptr;
235 if (defaultLocale != nullptr) {
236 clone = defaultLocale->clone();
237 if (clone == nullptr) {
238 errorCode_ = U_MEMORY_ALLOCATION_ERROR;
239 return *this;
240 }
241 }
242 delete defaultLocale_;
243 defaultLocale_ = clone;
244 return *this;
245}
246
247LocaleMatcher::Builder &LocaleMatcher::Builder::setFavorSubtag(ULocMatchFavorSubtag subtag) {
248 if (U_FAILURE(errorCode_)) { return *this; }
249 favor_ = subtag;
250 return *this;
251}
252
253LocaleMatcher::Builder &LocaleMatcher::Builder::setDemotionPerDesiredLocale(ULocMatchDemotion demotion) {
254 if (U_FAILURE(errorCode_)) { return *this; }
255 demotion_ = demotion;
256 return *this;
257}
258
259#if 0
260/**
261 * <i>Internal only!</i>
262 *
263 * @param thresholdDistance the thresholdDistance to set, with -1 = default
264 * @return this Builder object
265 * @internal
266 * @deprecated This API is ICU internal only.
267 */
268@Deprecated
269LocaleMatcher::Builder &LocaleMatcher::Builder::internalSetThresholdDistance(int32_t thresholdDistance) {
270 if (U_FAILURE(errorCode_)) { return *this; }
271 if (thresholdDistance > 100) {
272 thresholdDistance = 100;
273 }
274 thresholdDistance_ = thresholdDistance;
275 return *this;
276}
277#endif
278
279UBool LocaleMatcher::Builder::copyErrorTo(UErrorCode &outErrorCode) const {
280 if (U_FAILURE(outErrorCode)) { return TRUE; }
281 if (U_SUCCESS(errorCode_)) { return FALSE; }
282 outErrorCode = errorCode_;
283 return TRUE;
284}
285
286LocaleMatcher LocaleMatcher::Builder::build(UErrorCode &errorCode) const {
287 if (U_SUCCESS(errorCode) && U_FAILURE(errorCode_)) {
288 errorCode = errorCode_;
289 }
290 return LocaleMatcher(*this, errorCode);
291}
292
293namespace {
294
295LSR getMaximalLsrOrUnd(const XLikelySubtags &likelySubtags, const Locale &locale,
296 UErrorCode &errorCode) {
297 if (U_FAILURE(errorCode) || locale.isBogus() || *locale.getName() == 0 /* "und" */) {
298 return UND_LSR;
299 } else {
300 return likelySubtags.makeMaximizedLsrFrom(locale, errorCode);
301 }
302}
303
304int32_t hashLSR(const UHashTok token) {
305 const LSR *lsr = static_cast<const LSR *>(token.pointer);
306 return lsr->hashCode;
307}
308
309UBool compareLSRs(const UHashTok t1, const UHashTok t2) {
310 const LSR *lsr1 = static_cast<const LSR *>(t1.pointer);
311 const LSR *lsr2 = static_cast<const LSR *>(t2.pointer);
312 return *lsr1 == *lsr2;
313}
314
315} // namespace
316
317int32_t LocaleMatcher::putIfAbsent(const LSR &lsr, int32_t i, int32_t suppLength,
318 UErrorCode &errorCode) {
319 if (U_FAILURE(errorCode)) { return suppLength; }
320 int32_t index = uhash_geti(supportedLsrToIndex, &lsr);
321 if (index == 0) {
322 uhash_puti(supportedLsrToIndex, const_cast<LSR *>(&lsr), i + 1, &errorCode);
323 if (U_SUCCESS(errorCode)) {
324 supportedLSRs[suppLength] = &lsr;
325 supportedIndexes[suppLength++] = i;
326 }
327 }
328 return suppLength;
329}
330
331LocaleMatcher::LocaleMatcher(const Builder &builder, UErrorCode &errorCode) :
332 likelySubtags(*XLikelySubtags::getSingleton(errorCode)),
333 localeDistance(*LocaleDistance::getSingleton(errorCode)),
334 thresholdDistance(builder.thresholdDistance_),
335 demotionPerDesiredLocale(0),
336 favorSubtag(builder.favor_),
337 direction(builder.direction_),
338 supportedLocales(nullptr), lsrs(nullptr), supportedLocalesLength(0),
339 supportedLsrToIndex(nullptr),
340 supportedLSRs(nullptr), supportedIndexes(nullptr), supportedLSRsLength(0),
341 ownedDefaultLocale(nullptr), defaultLocale(nullptr) {
342 if (U_FAILURE(errorCode)) { return; }
343 if (thresholdDistance < 0) {
344 thresholdDistance = localeDistance.getDefaultScriptDistance();
345 }
346 const Locale *def = builder.defaultLocale_;
347 LSR builderDefaultLSR;
348 const LSR *defLSR = nullptr;
349 if (def != nullptr) {
350 ownedDefaultLocale = def->clone();
351 if (ownedDefaultLocale == nullptr) {
352 errorCode = U_MEMORY_ALLOCATION_ERROR;
353 return;
354 }
355 def = ownedDefaultLocale;
356 builderDefaultLSR = getMaximalLsrOrUnd(likelySubtags, *def, errorCode);
357 if (U_FAILURE(errorCode)) { return; }
358 defLSR = &builderDefaultLSR;
359 }
360 supportedLocalesLength = builder.supportedLocales_ != nullptr ?
361 builder.supportedLocales_->size() : 0;
362 if (supportedLocalesLength > 0) {
363 // Store the supported locales in input order,
364 // so that when different types are used (e.g., language tag strings)
365 // we can return those by parallel index.
366 supportedLocales = static_cast<const Locale **>(
367 uprv_malloc(supportedLocalesLength * sizeof(const Locale *)));
368 // Supported LRSs in input order.
369 // In C++, we store these permanently to simplify ownership management
370 // in the hash tables. Duplicate LSRs (if any) are unused overhead.
371 lsrs = new LSR[supportedLocalesLength];
372 if (supportedLocales == nullptr || lsrs == nullptr) {
373 errorCode = U_MEMORY_ALLOCATION_ERROR;
374 return;
375 }
376 // If the constructor fails partway, we need null pointers for destructibility.
377 uprv_memset(supportedLocales, 0, supportedLocalesLength * sizeof(const Locale *));
378 for (int32_t i = 0; i < supportedLocalesLength; ++i) {
379 const Locale &locale = *static_cast<Locale *>(builder.supportedLocales_->elementAt(i));
380 supportedLocales[i] = locale.clone();
381 if (supportedLocales[i] == nullptr) {
382 errorCode = U_MEMORY_ALLOCATION_ERROR;
383 return;
384 }
385 const Locale &supportedLocale = *supportedLocales[i];
386 LSR &lsr = lsrs[i] = getMaximalLsrOrUnd(likelySubtags, supportedLocale, errorCode);
387 lsr.setHashCode();
388 if (U_FAILURE(errorCode)) { return; }
389 }
390
391 // We need an unordered map from LSR to first supported locale with that LSR,
392 // and an ordered list of (LSR, supported index) for
393 // the supported locales in the following order:
394 // 1. Default locale, if it is supported.
395 // 2. Priority locales (aka "paradigm locales") in builder order.
396 // 3. Remaining locales in builder order.
397 supportedLsrToIndex = uhash_openSize(hashLSR, compareLSRs, uhash_compareLong,
398 supportedLocalesLength, &errorCode);
399 if (U_FAILURE(errorCode)) { return; }
400 supportedLSRs = static_cast<const LSR **>(
401 uprv_malloc(supportedLocalesLength * sizeof(const LSR *)));
402 supportedIndexes = static_cast<int32_t *>(
403 uprv_malloc(supportedLocalesLength * sizeof(int32_t)));
404 if (supportedLSRs == nullptr || supportedIndexes == nullptr) {
405 errorCode = U_MEMORY_ALLOCATION_ERROR;
406 return;
407 }
408 int32_t suppLength = 0;
409 // Determine insertion order.
410 // Add locales immediately that are equivalent to the default.
411 MaybeStackArray<int8_t, 100> order(supportedLocalesLength);
412 if (order.getAlias() == nullptr) {
413 errorCode = U_MEMORY_ALLOCATION_ERROR;
414 return;
415 }
416 int32_t numParadigms = 0;
417 for (int32_t i = 0; i < supportedLocalesLength; ++i) {
418 const Locale &locale = *supportedLocales[i];
419 const LSR &lsr = lsrs[i];
420 if (defLSR == nullptr) {
421 U_ASSERT(i == 0);
422 def = &locale;
423 defLSR = &lsr;
424 order[i] = 1;
425 suppLength = putIfAbsent(lsr, 0, suppLength, errorCode);
426 } else if (lsr.isEquivalentTo(*defLSR)) {
427 order[i] = 1;
428 suppLength = putIfAbsent(lsr, i, suppLength, errorCode);
429 } else if (localeDistance.isParadigmLSR(lsr)) {
430 order[i] = 2;
431 ++numParadigms;
432 } else {
433 order[i] = 3;
434 }
435 if (U_FAILURE(errorCode)) { return; }
436 }
437 // Add supported paradigm locales.
438 int32_t paradigmLimit = suppLength + numParadigms;
439 for (int32_t i = 0; i < supportedLocalesLength && suppLength < paradigmLimit; ++i) {
440 if (order[i] == 2) {
441 suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode);
442 }
443 }
444 // Add remaining supported locales.
445 for (int32_t i = 0; i < supportedLocalesLength; ++i) {
446 if (order[i] == 3) {
447 suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode);
448 }
449 }
450 supportedLSRsLength = suppLength;
451 // If supportedLSRsLength < supportedLocalesLength then
452 // we waste as many array slots as there are duplicate supported LSRs,
453 // but the amount of wasted space is small as long as there are few duplicates.
454 }
455
456 defaultLocale = def;
457
458 if (builder.demotion_ == ULOCMATCH_DEMOTION_REGION) {
459 demotionPerDesiredLocale = localeDistance.getDefaultDemotionPerDesiredLocale();
460 }
461}
462
463LocaleMatcher::LocaleMatcher(LocaleMatcher &&src) U_NOEXCEPT :
464 likelySubtags(src.likelySubtags),
465 localeDistance(src.localeDistance),
466 thresholdDistance(src.thresholdDistance),
467 demotionPerDesiredLocale(src.demotionPerDesiredLocale),
468 favorSubtag(src.favorSubtag),
469 direction(src.direction),
470 supportedLocales(src.supportedLocales), lsrs(src.lsrs),
471 supportedLocalesLength(src.supportedLocalesLength),
472 supportedLsrToIndex(src.supportedLsrToIndex),
473 supportedLSRs(src.supportedLSRs),
474 supportedIndexes(src.supportedIndexes),
475 supportedLSRsLength(src.supportedLSRsLength),
476 ownedDefaultLocale(src.ownedDefaultLocale), defaultLocale(src.defaultLocale) {
477 src.supportedLocales = nullptr;
478 src.lsrs = nullptr;
479 src.supportedLocalesLength = 0;
480 src.supportedLsrToIndex = nullptr;
481 src.supportedLSRs = nullptr;
482 src.supportedIndexes = nullptr;
483 src.supportedLSRsLength = 0;
484 src.ownedDefaultLocale = nullptr;
485 src.defaultLocale = nullptr;
486}
487
488LocaleMatcher::~LocaleMatcher() {
489 for (int32_t i = 0; i < supportedLocalesLength; ++i) {
490 delete supportedLocales[i];
491 }
492 uprv_free(supportedLocales);
493 delete[] lsrs;
494 uhash_close(supportedLsrToIndex);
495 uprv_free(supportedLSRs);
496 uprv_free(supportedIndexes);
497 delete ownedDefaultLocale;
498}
499
500LocaleMatcher &LocaleMatcher::operator=(LocaleMatcher &&src) U_NOEXCEPT {
501 this->~LocaleMatcher();
502
503 thresholdDistance = src.thresholdDistance;
504 demotionPerDesiredLocale = src.demotionPerDesiredLocale;
505 favorSubtag = src.favorSubtag;
506 direction = src.direction;
507 supportedLocales = src.supportedLocales;
508 lsrs = src.lsrs;
509 supportedLocalesLength = src.supportedLocalesLength;
510 supportedLsrToIndex = src.supportedLsrToIndex;
511 supportedLSRs = src.supportedLSRs;
512 supportedIndexes = src.supportedIndexes;
513 supportedLSRsLength = src.supportedLSRsLength;
514 ownedDefaultLocale = src.ownedDefaultLocale;
515 defaultLocale = src.defaultLocale;
516
517 src.supportedLocales = nullptr;
518 src.lsrs = nullptr;
519 src.supportedLocalesLength = 0;
520 src.supportedLsrToIndex = nullptr;
521 src.supportedLSRs = nullptr;
522 src.supportedIndexes = nullptr;
523 src.supportedLSRsLength = 0;
524 src.ownedDefaultLocale = nullptr;
525 src.defaultLocale = nullptr;
526 return *this;
527}
528
529class LocaleLsrIterator {
530public:
531 LocaleLsrIterator(const XLikelySubtags &likelySubtags, Locale::Iterator &locales,
532 ULocMatchLifetime lifetime) :
533 likelySubtags(likelySubtags), locales(locales), lifetime(lifetime) {}
534
535 ~LocaleLsrIterator() {
536 if (lifetime == ULOCMATCH_TEMPORARY_LOCALES) {
537 delete remembered;
538 }
539 }
540
541 bool hasNext() const {
542 return locales.hasNext();
543 }
544
545 LSR next(UErrorCode &errorCode) {
546 current = &locales.next();
547 return getMaximalLsrOrUnd(likelySubtags, *current, errorCode);
548 }
549
550 void rememberCurrent(int32_t desiredIndex, UErrorCode &errorCode) {
551 if (U_FAILURE(errorCode)) { return; }
552 bestDesiredIndex = desiredIndex;
553 if (lifetime == ULOCMATCH_STORED_LOCALES) {
554 remembered = current;
555 } else {
556 // ULOCMATCH_TEMPORARY_LOCALES
557 delete remembered;
558 remembered = new Locale(*current);
559 if (remembered == nullptr) {
560 errorCode = U_MEMORY_ALLOCATION_ERROR;
561 }
562 }
563 }
564
565 const Locale *orphanRemembered() {
566 const Locale *rem = remembered;
567 remembered = nullptr;
568 return rem;
569 }
570
571 int32_t getBestDesiredIndex() const {
572 return bestDesiredIndex;
573 }
574
575private:
576 const XLikelySubtags &likelySubtags;
577 Locale::Iterator &locales;
578 ULocMatchLifetime lifetime;
579 const Locale *current = nullptr, *remembered = nullptr;
580 int32_t bestDesiredIndex = -1;
581};
582
583const Locale *LocaleMatcher::getBestMatch(const Locale &desiredLocale, UErrorCode &errorCode) const {
584 if (U_FAILURE(errorCode)) { return nullptr; }
585 int32_t suppIndex = getBestSuppIndex(
586 getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),
587 nullptr, errorCode);
588 return U_SUCCESS(errorCode) && suppIndex >= 0 ? supportedLocales[suppIndex] : defaultLocale;
589}
590
591const Locale *LocaleMatcher::getBestMatch(Locale::Iterator &desiredLocales,
592 UErrorCode &errorCode) const {
593 if (U_FAILURE(errorCode)) { return nullptr; }
594 if (!desiredLocales.hasNext()) {
595 return defaultLocale;
596 }
597 LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES);
598 int32_t suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode);
599 return U_SUCCESS(errorCode) && suppIndex >= 0 ? supportedLocales[suppIndex] : defaultLocale;
600}
601
602const Locale *LocaleMatcher::getBestMatchForListString(
603 StringPiece desiredLocaleList, UErrorCode &errorCode) const {
604 LocalePriorityList list(desiredLocaleList, errorCode);
605 LocalePriorityList::Iterator iter = list.iterator();
606 return getBestMatch(iter, errorCode);
607}
608
609LocaleMatcher::Result LocaleMatcher::getBestMatchResult(
610 const Locale &desiredLocale, UErrorCode &errorCode) const {
611 if (U_FAILURE(errorCode)) {
612 return Result(nullptr, defaultLocale, -1, -1, FALSE);
613 }
614 int32_t suppIndex = getBestSuppIndex(
615 getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),
616 nullptr, errorCode);
617 if (U_FAILURE(errorCode) || suppIndex < 0) {
618 return Result(nullptr, defaultLocale, -1, -1, FALSE);
619 } else {
620 return Result(&desiredLocale, supportedLocales[suppIndex], 0, suppIndex, FALSE);
621 }
622}
623
624LocaleMatcher::Result LocaleMatcher::getBestMatchResult(
625 Locale::Iterator &desiredLocales, UErrorCode &errorCode) const {
626 if (U_FAILURE(errorCode) || !desiredLocales.hasNext()) {
627 return Result(nullptr, defaultLocale, -1, -1, FALSE);
628 }
629 LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES);
630 int32_t suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode);
631 if (U_FAILURE(errorCode) || suppIndex < 0) {
632 return Result(nullptr, defaultLocale, -1, -1, FALSE);
633 } else {
634 return Result(lsrIter.orphanRemembered(), supportedLocales[suppIndex],
635 lsrIter.getBestDesiredIndex(), suppIndex, TRUE);
636 }
637}
638
639int32_t LocaleMatcher::getBestSuppIndex(LSR desiredLSR, LocaleLsrIterator *remainingIter,
640 UErrorCode &errorCode) const {
641 if (U_FAILURE(errorCode)) { return -1; }
642 int32_t desiredIndex = 0;
643 int32_t bestSupportedLsrIndex = -1;
644 for (int32_t bestShiftedDistance = LocaleDistance::shiftDistance(thresholdDistance);;) {
645 // Quick check for exact maximized LSR.
646 // Returns suppIndex+1 where 0 means not found.
647 if (supportedLsrToIndex != nullptr) {
648 desiredLSR.setHashCode();
649 int32_t index = uhash_geti(supportedLsrToIndex, &desiredLSR);
650 if (index != 0) {
651 int32_t suppIndex = index - 1;
652 if (remainingIter != nullptr) {
653 remainingIter->rememberCurrent(desiredIndex, errorCode);
654 }
655 return suppIndex;
656 }
657 }
658 int32_t bestIndexAndDistance = localeDistance.getBestIndexAndDistance(
659 desiredLSR, supportedLSRs, supportedLSRsLength,
660 bestShiftedDistance, favorSubtag, direction);
661 if (bestIndexAndDistance >= 0) {
662 bestShiftedDistance = LocaleDistance::getShiftedDistance(bestIndexAndDistance);
663 if (remainingIter != nullptr) {
664 remainingIter->rememberCurrent(desiredIndex, errorCode);
665 if (U_FAILURE(errorCode)) { return -1; }
666 }
667 bestSupportedLsrIndex = LocaleDistance::getIndex(bestIndexAndDistance);
668 }
669 if ((bestShiftedDistance -= LocaleDistance::shiftDistance(demotionPerDesiredLocale)) <= 0) {
670 break;
671 }
672 if (remainingIter == nullptr || !remainingIter->hasNext()) {
673 break;
674 }
675 desiredLSR = remainingIter->next(errorCode);
676 if (U_FAILURE(errorCode)) { return -1; }
677 ++desiredIndex;
678 }
679 if (bestSupportedLsrIndex < 0) {
680 // no good match
681 return -1;
682 }
683 return supportedIndexes[bestSupportedLsrIndex];
684}
685
686double LocaleMatcher::internalMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const {
687 // Returns the inverse of the distance: That is, 1-distance(desired, supported).
688 LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode);
689 if (U_FAILURE(errorCode)) { return 0; }
690 const LSR *pSuppLSR = &suppLSR;
691 int32_t indexAndDistance = localeDistance.getBestIndexAndDistance(
692 getMaximalLsrOrUnd(likelySubtags, desired, errorCode),
693 &pSuppLSR, 1,
694 LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction);
695 double distance = LocaleDistance::getDistanceDouble(indexAndDistance);
696 return (100.0 - distance) / 100.0;
697}
698
699U_NAMESPACE_END
700
701// uloc_acceptLanguage() --------------------------------------------------- ***
702
703U_NAMESPACE_USE
704
705namespace {
706
707class LocaleFromTag {
708public:
709 LocaleFromTag() : locale(Locale::getRoot()) {}
710 const Locale &operator()(const char *tag) { return locale = Locale(tag); }
711
712private:
713 // Store the locale in the converter, rather than return a reference to a temporary,
714 // or a value which could go out of scope with the caller's reference to it.
715 Locale locale;
716};
717
718int32_t acceptLanguage(UEnumeration &supportedLocales, Locale::Iterator &desiredLocales,
719 char *dest, int32_t capacity, UAcceptResult *acceptResult,
720 UErrorCode &errorCode) {
721 if (U_FAILURE(errorCode)) { return 0; }
722 LocaleMatcher::Builder builder;
723 const char *locString;
724 while ((locString = uenum_next(&supportedLocales, nullptr, &errorCode)) != nullptr) {
725 Locale loc(locString);
726 if (loc.isBogus()) {
727 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
728 return 0;
729 }
730 builder.addSupportedLocale(loc);
731 }
732 LocaleMatcher matcher = builder.build(errorCode);
733 LocaleMatcher::Result result = matcher.getBestMatchResult(desiredLocales, errorCode);
734 if (U_FAILURE(errorCode)) { return 0; }
735 if (result.getDesiredIndex() >= 0) {
736 if (acceptResult != nullptr) {
737 *acceptResult = *result.getDesiredLocale() == *result.getSupportedLocale() ?
738 ULOC_ACCEPT_VALID : ULOC_ACCEPT_FALLBACK;
739 }
740 const char *bestStr = result.getSupportedLocale()->getName();
741 int32_t bestLength = (int32_t)uprv_strlen(bestStr);
742 if (bestLength <= capacity) {
743 uprv_memcpy(dest, bestStr, bestLength);
744 }
745 return u_terminateChars(dest, capacity, bestLength, &errorCode);
746 } else {
747 if (acceptResult != nullptr) {
748 *acceptResult = ULOC_ACCEPT_FAILED;
749 }
750 return u_terminateChars(dest, capacity, 0, &errorCode);
751 }
752}
753
754} // namespace
755
756U_CAPI int32_t U_EXPORT2
757uloc_acceptLanguage(char *result, int32_t resultAvailable,
758 UAcceptResult *outResult,
759 const char **acceptList, int32_t acceptListCount,
760 UEnumeration *availableLocales,
761 UErrorCode *status) {
762 if (U_FAILURE(*status)) { return 0; }
763 if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) ||
764 (acceptList == nullptr ? acceptListCount != 0 : acceptListCount < 0) ||
765 availableLocales == nullptr) {
766 *status = U_ILLEGAL_ARGUMENT_ERROR;
767 return 0;
768 }
769 LocaleFromTag converter;
770 Locale::ConvertingIterator<const char **, LocaleFromTag> desiredLocales(
771 acceptList, acceptList + acceptListCount, converter);
772 return acceptLanguage(*availableLocales, desiredLocales,
773 result, resultAvailable, outResult, *status);
774}
775
776U_CAPI int32_t U_EXPORT2
777uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable,
778 UAcceptResult *outResult,
779 const char *httpAcceptLanguage,
780 UEnumeration *availableLocales,
781 UErrorCode *status) {
782 if (U_FAILURE(*status)) { return 0; }
783 if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) ||
784 httpAcceptLanguage == nullptr || availableLocales == nullptr) {
785 *status = U_ILLEGAL_ARGUMENT_ERROR;
786 return 0;
787 }
788 LocalePriorityList list(httpAcceptLanguage, *status);
789 LocalePriorityList::Iterator desiredLocales = list.iterator();
790 return acceptLanguage(*availableLocales, desiredLocales,
791 result, resultAvailable, outResult, *status);
792}
793
794#endif // __LOCMATCHER_H__
795