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/uobject.h"
16 #include "cstring.h"
17 #include "localeprioritylist.h"
18 #include "loclikelysubtags.h"
19 #include "locdistance.h"
20 #include "lsr.h"
21 #include "uassert.h"
22 #include "uhash.h"
23 #include "uvector.h"
24
25 #define UND_LSR LSR("und", "", "")
26
27 /**
28 * Indicator for the lifetime of desired-locale objects passed into the LocaleMatcher.
29 *
30 * @draft ICU 65
31 */
32 enum ULocMatchLifetime {
33 /**
34 * Locale objects are temporary.
35 * The matcher will make a copy of a locale that will be used beyond one function call.
36 *
37 * @draft ICU 65
38 */
39 ULOCMATCH_TEMPORARY_LOCALES,
40 /**
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,
43 * avoiding a copy.
44 *
45 * @draft ICU 65
46 */
47 ULOCMATCH_STORED_LOCALES // TODO: permanent? cached? clone?
48 };
49 #ifndef U_IN_DOXYGEN
50 typedef enum ULocMatchLifetime ULocMatchLifetime;
51 #endif
52
53 U_NAMESPACE_BEGIN
54
Result(LocaleMatcher::Result && src)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) {
61 if (desiredIsOwned) {
62 src.desiredLocale = nullptr;
63 src.desiredIndex = -1;
64 src.desiredIsOwned = FALSE;
65 }
66 }
67
~Result()68 LocaleMatcher::Result::~Result() {
69 if (desiredIsOwned) {
70 delete desiredLocale;
71 }
72 }
73
operator =(LocaleMatcher::Result && src)74 LocaleMatcher::Result &LocaleMatcher::Result::operator=(LocaleMatcher::Result &&src) U_NOEXCEPT {
75 this->~Result();
76
77 desiredLocale = src.desiredLocale;
78 supportedLocale = src.supportedLocale;
79 desiredIndex = src.desiredIndex;
80 supportedIndex = src.supportedIndex;
81 desiredIsOwned = src.desiredIsOwned;
82
83 if (desiredIsOwned) {
84 src.desiredLocale = nullptr;
85 src.desiredIndex = -1;
86 src.desiredIsOwned = FALSE;
87 }
88 return *this;
89 }
90
makeResolvedLocale(UErrorCode & errorCode) const91 Locale LocaleMatcher::Result::makeResolvedLocale(UErrorCode &errorCode) const {
92 if (U_FAILURE(errorCode) || supportedLocale == nullptr) {
93 return Locale::getRoot();
94 }
95 const Locale *bestDesired = getDesiredLocale();
96 if (bestDesired == nullptr || *supportedLocale == *bestDesired) {
97 return *supportedLocale;
98 }
99 LocaleBuilder b;
100 b.setLocale(*supportedLocale);
101
102 // Copy the region from bestDesired, if there is one.
103 const char *region = bestDesired->getCountry();
104 if (*region != 0) {
105 b.setRegion(region);
106 }
107
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);
114 }
115
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);
124 }
125
Builder(LocaleMatcher::Builder && src)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_),
132 favor_(src.favor_) {
133 src.supportedLocales_ = nullptr;
134 src.defaultLocale_ = nullptr;
135 }
136
~Builder()137 LocaleMatcher::Builder::~Builder() {
138 delete supportedLocales_;
139 delete defaultLocale_;
140 }
141
operator =(LocaleMatcher::Builder && src)142 LocaleMatcher::Builder &LocaleMatcher::Builder::operator=(LocaleMatcher::Builder &&src) U_NOEXCEPT {
143 this->~Builder();
144
145 errorCode_ = src.errorCode_;
146 supportedLocales_ = src.supportedLocales_;
147 thresholdDistance_ = src.thresholdDistance_;
148 demotion_ = src.demotion_;
149 defaultLocale_ = src.defaultLocale_;
150 favor_ = src.favor_;
151
152 src.supportedLocales_ = nullptr;
153 src.defaultLocale_ = nullptr;
154 return *this;
155 }
156
clearSupportedLocales()157 void LocaleMatcher::Builder::clearSupportedLocales() {
158 if (supportedLocales_ != nullptr) {
159 supportedLocales_->removeAllElements();
160 }
161 }
162
ensureSupportedLocaleVector()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;
170 return false;
171 }
172 return true;
173 }
174
setSupportedLocalesFromListString(StringPiece locales)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_)) {
187 delete locale;
188 break;
189 }
190 }
191 return *this;
192 }
193
setSupportedLocales(Locale::Iterator & locales)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;
203 break;
204 }
205 supportedLocales_->addElement(clone, errorCode_);
206 if (U_FAILURE(errorCode_)) {
207 delete clone;
208 break;
209 }
210 }
211 return *this;
212 }
213
addSupportedLocale(const Locale & locale)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;
219 return *this;
220 }
221 supportedLocales_->addElement(clone, errorCode_);
222 if (U_FAILURE(errorCode_)) {
223 delete clone;
224 }
225 return *this;
226 }
227
setDefaultLocale(const Locale * defaultLocale)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;
235 return *this;
236 }
237 }
238 delete defaultLocale_;
239 defaultLocale_ = clone;
240 return *this;
241 }
242
setFavorSubtag(ULocMatchFavorSubtag subtag)243 LocaleMatcher::Builder &LocaleMatcher::Builder::setFavorSubtag(ULocMatchFavorSubtag subtag) {
244 if (U_FAILURE(errorCode_)) { return *this; }
245 favor_ = subtag;
246 return *this;
247 }
248
setDemotionPerDesiredLocale(ULocMatchDemotion demotion)249 LocaleMatcher::Builder &LocaleMatcher::Builder::setDemotionPerDesiredLocale(ULocMatchDemotion demotion) {
250 if (U_FAILURE(errorCode_)) { return *this; }
251 demotion_ = demotion;
252 return *this;
253 }
254
255 #if 0
256 /**
257 * <i>Internal only!</i>
258 *
259 * @param thresholdDistance the thresholdDistance to set, with -1 = default
260 * @return this Builder object
261 * @internal
262 * @deprecated This API is ICU internal only.
263 */
264 @Deprecated
265 LocaleMatcher::Builder &LocaleMatcher::Builder::internalSetThresholdDistance(int32_t thresholdDistance) {
266 if (U_FAILURE(errorCode_)) { return *this; }
267 if (thresholdDistance > 100) {
268 thresholdDistance = 100;
269 }
270 thresholdDistance_ = thresholdDistance;
271 return *this;
272 }
273 #endif
274
copyErrorTo(UErrorCode & outErrorCode) const275 UBool LocaleMatcher::Builder::copyErrorTo(UErrorCode &outErrorCode) const {
276 if (U_FAILURE(outErrorCode)) { return TRUE; }
277 if (U_SUCCESS(errorCode_)) { return FALSE; }
278 outErrorCode = errorCode_;
279 return TRUE;
280 }
281
build(UErrorCode & errorCode) const282 LocaleMatcher LocaleMatcher::Builder::build(UErrorCode &errorCode) const {
283 if (U_SUCCESS(errorCode) && U_FAILURE(errorCode_)) {
284 errorCode = errorCode_;
285 }
286 return LocaleMatcher(*this, errorCode);
287 }
288
289 namespace {
290
getMaximalLsrOrUnd(const XLikelySubtags & likelySubtags,const Locale & locale,UErrorCode & errorCode)291 LSR getMaximalLsrOrUnd(const XLikelySubtags &likelySubtags, const Locale &locale,
292 UErrorCode &errorCode) {
293 if (U_FAILURE(errorCode) || locale.isBogus() || *locale.getName() == 0 /* "und" */) {
294 return UND_LSR;
295 } else {
296 return likelySubtags.makeMaximizedLsrFrom(locale, errorCode);
297 }
298 }
299
hashLSR(const UHashTok token)300 int32_t hashLSR(const UHashTok token) {
301 const LSR *lsr = static_cast<const LSR *>(token.pointer);
302 return lsr->hashCode;
303 }
304
compareLSRs(const UHashTok t1,const UHashTok t2)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;
309 }
310
putIfAbsent(UHashtable * lsrToIndex,const LSR & lsr,int32_t i,UErrorCode & errorCode)311 bool putIfAbsent(UHashtable *lsrToIndex, const LSR &lsr, int32_t i, UErrorCode &errorCode) {
312 if (U_FAILURE(errorCode)) { return false; }
313 U_ASSERT(i > 0);
314 int32_t index = uhash_geti(lsrToIndex, &lsr);
315 if (index != 0) {
316 return false;
317 } else {
318 uhash_puti(lsrToIndex, const_cast<LSR *>(&lsr), i, &errorCode);
319 return U_SUCCESS(errorCode);
320 }
321 }
322
323 } // namespace
324
LocaleMatcher(const Builder & builder,UErrorCode & 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();
338 }
339 supportedLocalesLength = builder.supportedLocales_ != nullptr ?
340 builder.supportedLocales_->size() : 0;
341 const Locale *def = builder.defaultLocale_;
342 int32_t idef = -1;
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;
355 return;
356 }
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;
367 }
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;
373 return;
374 }
375 const Locale &supportedLocale = *supportedLocales[i];
376 LSR &lsr = lsrs[i] = getMaximalLsrOrUnd(likelySubtags, supportedLocale, errorCode);
377 lsr.setHashCode();
378 if (U_FAILURE(errorCode)) { return; }
379 if (idef < 0 && defLSR != nullptr && lsr == *defLSR) {
380 idef = i;
381 defLSR = &lsr; // owned pointer to put into supportedLsrToIndex
382 if (*def == supportedLocale) {
383 def = &supportedLocale; // owned pointer to keep
384 }
385 }
386 }
387
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;
412 return;
413 }
414 int32_t paradigmIndex = 0;
415 int32_t otherIndex = paradigmLimit;
416 if (idef >= 0) {
417 uhash_puti(supportedLsrToIndex, const_cast<LSR *>(defLSR), idef + 1, &errorCode);
418 supportedLSRs[0] = defLSR;
419 supportedIndexes[0] = idef;
420 paradigmIndex = 1;
421 }
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) {
427 U_ASSERT(i == 0);
428 def = &locale;
429 defLSR = &lsr;
430 idef = 0;
431 uhash_puti(supportedLsrToIndex, const_cast<LSR *>(&lsr), 0 + 1, &errorCode);
432 supportedLSRs[0] = &lsr;
433 supportedIndexes[0] = 0;
434 paradigmIndex = 1;
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.
443 } else {
444 if (putIfAbsent(supportedLsrToIndex, lsr, i + 1, errorCode)) {
445 if (localeDistance.isParadigmLSR(lsr)) {
446 supportedLSRs[paradigmIndex] = &lsr;
447 supportedIndexes[paradigmIndex++] = i;
448 } else {
449 supportedLSRs[otherIndex] = &lsr;
450 supportedIndexes[otherIndex++] = i;
451 }
452 }
453 }
454 if (U_FAILURE(errorCode)) { return; }
455 }
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));
462 }
463 supportedLSRsLength = otherIndex - (paradigmLimit - paradigmIndex);
464 }
465
466 if (def != nullptr && (idef < 0 || def != supportedLocales[idef])) {
467 ownedDefaultLocale = def->clone();
468 if (ownedDefaultLocale == nullptr) {
469 errorCode = U_MEMORY_ALLOCATION_ERROR;
470 return;
471 }
472 def = ownedDefaultLocale;
473 }
474 defaultLocale = def;
475 defaultLocaleIndex = idef;
476
477 if (builder.demotion_ == ULOCMATCH_DEMOTION_REGION) {
478 demotionPerDesiredLocale = localeDistance.getDefaultDemotionPerDesiredLocale();
479 }
480 }
481
LocaleMatcher(LocaleMatcher && src)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;
497 src.lsrs = 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;
506 }
507
~LocaleMatcher()508 LocaleMatcher::~LocaleMatcher() {
509 for (int32_t i = 0; i < supportedLocalesLength; ++i) {
510 delete supportedLocales[i];
511 }
512 uprv_free(supportedLocales);
513 delete[] lsrs;
514 uhash_close(supportedLsrToIndex);
515 uprv_free(supportedLSRs);
516 uprv_free(supportedIndexes);
517 delete ownedDefaultLocale;
518 }
519
operator =(LocaleMatcher && src)520 LocaleMatcher &LocaleMatcher::operator=(LocaleMatcher &&src) U_NOEXCEPT {
521 this->~LocaleMatcher();
522
523 thresholdDistance = src.thresholdDistance;
524 demotionPerDesiredLocale = src.demotionPerDesiredLocale;
525 favorSubtag = src.favorSubtag;
526 supportedLocales = src.supportedLocales;
527 lsrs = src.lsrs;
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;
536
537 src.supportedLocales = nullptr;
538 src.lsrs = 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;
547 return *this;
548 }
549
550 class LocaleLsrIterator {
551 public:
LocaleLsrIterator(const XLikelySubtags & likelySubtags,Locale::Iterator & locales,ULocMatchLifetime lifetime)552 LocaleLsrIterator(const XLikelySubtags &likelySubtags, Locale::Iterator &locales,
553 ULocMatchLifetime lifetime) :
554 likelySubtags(likelySubtags), locales(locales), lifetime(lifetime) {}
555
~LocaleLsrIterator()556 ~LocaleLsrIterator() {
557 if (lifetime == ULOCMATCH_TEMPORARY_LOCALES) {
558 delete remembered;
559 }
560 }
561
hasNext() const562 bool hasNext() const {
563 return locales.hasNext();
564 }
565
next(UErrorCode & errorCode)566 LSR next(UErrorCode &errorCode) {
567 current = &locales.next();
568 return getMaximalLsrOrUnd(likelySubtags, *current, errorCode);
569 }
570
rememberCurrent(int32_t desiredIndex,UErrorCode & 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;
576 } else {
577 // ULOCMATCH_TEMPORARY_LOCALES
578 delete remembered;
579 remembered = new Locale(*current);
580 if (remembered == nullptr) {
581 errorCode = U_MEMORY_ALLOCATION_ERROR;
582 }
583 }
584 }
585
orphanRemembered()586 const Locale *orphanRemembered() {
587 const Locale *rem = remembered;
588 remembered = nullptr;
589 return rem;
590 }
591
getBestDesiredIndex() const592 int32_t getBestDesiredIndex() const {
593 return bestDesiredIndex;
594 }
595
596 private:
597 const XLikelySubtags &likelySubtags;
598 Locale::Iterator &locales;
599 ULocMatchLifetime lifetime;
600 const Locale *current = nullptr, *remembered = nullptr;
601 int32_t bestDesiredIndex = -1;
602 };
603
getBestMatch(const Locale & desiredLocale,UErrorCode & errorCode) const604 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),
608 nullptr, errorCode);
609 return U_SUCCESS(errorCode) && suppIndex >= 0 ? supportedLocales[suppIndex] : defaultLocale;
610 }
611
getBestMatch(Locale::Iterator & desiredLocales,UErrorCode & errorCode) const612 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;
617 }
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;
621 }
622
getBestMatchForListString(StringPiece desiredLocaleList,UErrorCode & errorCode) const623 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);
628 }
629
getBestMatchResult(const Locale & desiredLocale,UErrorCode & errorCode) const630 LocaleMatcher::Result LocaleMatcher::getBestMatchResult(
631 const Locale &desiredLocale, UErrorCode &errorCode) const {
632 if (U_FAILURE(errorCode)) {
633 return Result(nullptr, defaultLocale, -1, defaultLocaleIndex, FALSE);
634 }
635 int32_t suppIndex = getBestSuppIndex(
636 getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),
637 nullptr, errorCode);
638 if (U_FAILURE(errorCode) || suppIndex < 0) {
639 return Result(nullptr, defaultLocale, -1, defaultLocaleIndex, FALSE);
640 } else {
641 return Result(&desiredLocale, supportedLocales[suppIndex], 0, suppIndex, FALSE);
642 }
643 }
644
getBestMatchResult(Locale::Iterator & desiredLocales,UErrorCode & errorCode) const645 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);
649 }
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);
654 } else {
655 return Result(lsrIter.orphanRemembered(), supportedLocales[suppIndex],
656 lsrIter.getBestDesiredIndex(), suppIndex, TRUE);
657 }
658 }
659
getBestSuppIndex(LSR desiredLSR,LocaleLsrIterator * remainingIter,UErrorCode & errorCode) const660 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);
671 if (index != 0) {
672 int32_t suppIndex = index - 1;
673 if (remainingIter != nullptr) {
674 remainingIter->rememberCurrent(desiredIndex, errorCode);
675 }
676 return suppIndex;
677 }
678 }
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; }
686 }
687 bestSupportedLsrIndex = bestIndexAndDistance >= 0 ? bestIndexAndDistance >> 8 : -1;
688 }
689 if ((bestDistance -= demotionPerDesiredLocale) <= 0) {
690 break;
691 }
692 if (remainingIter == nullptr || !remainingIter->hasNext()) {
693 break;
694 }
695 desiredLSR = remainingIter->next(errorCode);
696 if (U_FAILURE(errorCode)) { return -1; }
697 ++desiredIndex;
698 }
699 if (bestSupportedLsrIndex < 0) {
700 // no good match
701 return -1;
702 }
703 return supportedIndexes[bestSupportedLsrIndex];
704 }
705
internalMatch(const Locale & desired,const Locale & supported,UErrorCode & errorCode) const706 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),
713 &pSuppLSR, 1,
714 thresholdDistance, favorSubtag) & 0xff;
715 return (100 - distance) / 100.0;
716 }
717
718 U_NAMESPACE_END
719
720 #endif // __LOCMATCHER_H__
721