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  */
34 enum 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
52 typedef enum ULocMatchLifetime ULocMatchLifetime;
53 #endif
54 
55 U_NAMESPACE_BEGIN
56 
Result(LocaleMatcher::Result && src)57 LocaleMatcher::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 
~Result()70 LocaleMatcher::Result::~Result() {
71     if (desiredIsOwned) {
72         delete desiredLocale;
73     }
74 }
75 
operator =(LocaleMatcher::Result && src)76 LocaleMatcher::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 
makeResolvedLocale(UErrorCode & errorCode) const93 Locale 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 
Builder(LocaleMatcher::Builder && src)128 LocaleMatcher::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 
~Builder()140 LocaleMatcher::Builder::~Builder() {
141     delete supportedLocales_;
142     delete defaultLocale_;
143 }
144 
operator =(LocaleMatcher::Builder && src)145 LocaleMatcher::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 
clearSupportedLocales()161 void LocaleMatcher::Builder::clearSupportedLocales() {
162     if (supportedLocales_ != nullptr) {
163         supportedLocales_->removeAllElements();
164     }
165 }
166 
ensureSupportedLocaleVector()167 bool 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 
setSupportedLocalesFromListString(StringPiece locales)179 LocaleMatcher::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 
setSupportedLocales(Locale::Iterator & locales)198 LocaleMatcher::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 
addSupportedLocale(const Locale & locale)218 LocaleMatcher::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 
setDefaultLocale(const Locale * defaultLocale)232 LocaleMatcher::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 
setFavorSubtag(ULocMatchFavorSubtag subtag)247 LocaleMatcher::Builder &LocaleMatcher::Builder::setFavorSubtag(ULocMatchFavorSubtag subtag) {
248     if (U_FAILURE(errorCode_)) { return *this; }
249     favor_ = subtag;
250     return *this;
251 }
252 
setDemotionPerDesiredLocale(ULocMatchDemotion demotion)253 LocaleMatcher::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
269 LocaleMatcher::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 
copyErrorTo(UErrorCode & outErrorCode) const279 UBool 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 
build(UErrorCode & errorCode) const286 LocaleMatcher 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 
293 namespace {
294 
getMaximalLsrOrUnd(const XLikelySubtags & likelySubtags,const Locale & locale,UErrorCode & errorCode)295 LSR 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 
hashLSR(const UHashTok token)304 int32_t hashLSR(const UHashTok token) {
305     const LSR *lsr = static_cast<const LSR *>(token.pointer);
306     return lsr->hashCode;
307 }
308 
compareLSRs(const UHashTok t1,const UHashTok t2)309 UBool 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 
putIfAbsent(const LSR & lsr,int32_t i,int32_t suppLength,UErrorCode & errorCode)317 int32_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 
LocaleMatcher(const Builder & builder,UErrorCode & errorCode)331 LocaleMatcher::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 
LocaleMatcher(LocaleMatcher && src)463 LocaleMatcher::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 
~LocaleMatcher()488 LocaleMatcher::~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 
operator =(LocaleMatcher && src)500 LocaleMatcher &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 
529 class LocaleLsrIterator {
530 public:
LocaleLsrIterator(const XLikelySubtags & likelySubtags,Locale::Iterator & locales,ULocMatchLifetime lifetime)531     LocaleLsrIterator(const XLikelySubtags &likelySubtags, Locale::Iterator &locales,
532                       ULocMatchLifetime lifetime) :
533             likelySubtags(likelySubtags), locales(locales), lifetime(lifetime) {}
534 
~LocaleLsrIterator()535     ~LocaleLsrIterator() {
536         if (lifetime == ULOCMATCH_TEMPORARY_LOCALES) {
537             delete remembered;
538         }
539     }
540 
hasNext() const541     bool hasNext() const {
542         return locales.hasNext();
543     }
544 
next(UErrorCode & errorCode)545     LSR next(UErrorCode &errorCode) {
546         current = &locales.next();
547         return getMaximalLsrOrUnd(likelySubtags, *current, errorCode);
548     }
549 
rememberCurrent(int32_t desiredIndex,UErrorCode & errorCode)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 
orphanRemembered()565     const Locale *orphanRemembered() {
566         const Locale *rem = remembered;
567         remembered = nullptr;
568         return rem;
569     }
570 
getBestDesiredIndex() const571     int32_t getBestDesiredIndex() const {
572         return bestDesiredIndex;
573     }
574 
575 private:
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 
getBestMatch(const Locale & desiredLocale,UErrorCode & errorCode) const583 const 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 
getBestMatch(Locale::Iterator & desiredLocales,UErrorCode & errorCode) const591 const 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 
getBestMatchForListString(StringPiece desiredLocaleList,UErrorCode & errorCode) const602 const 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 
getBestMatchResult(const Locale & desiredLocale,UErrorCode & errorCode) const609 LocaleMatcher::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 
getBestMatchResult(Locale::Iterator & desiredLocales,UErrorCode & errorCode) const624 LocaleMatcher::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 
getBestSuppIndex(LSR desiredLSR,LocaleLsrIterator * remainingIter,UErrorCode & errorCode) const639 int32_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 
internalMatch(const Locale & desired,const Locale & supported,UErrorCode & errorCode) const686 double 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 
699 U_NAMESPACE_END
700 
701 // uloc_acceptLanguage() --------------------------------------------------- ***
702 
703 U_NAMESPACE_USE
704 
705 namespace {
706 
707 class LocaleFromTag {
708 public:
LocaleFromTag()709     LocaleFromTag() : locale(Locale::getRoot()) {}
operator ()(const char * tag)710     const Locale &operator()(const char *tag) { return locale = Locale(tag); }
711 
712 private:
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 
acceptLanguage(UEnumeration & supportedLocales,Locale::Iterator & desiredLocales,char * dest,int32_t capacity,UAcceptResult * acceptResult,UErrorCode & errorCode)718 int32_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 
756 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)757 uloc_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 
776 U_CAPI int32_t U_EXPORT2
uloc_acceptLanguageFromHTTP(char * result,int32_t resultAvailable,UAcceptResult * outResult,const char * httpAcceptLanguage,UEnumeration * availableLocales,UErrorCode * status)777 uloc_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