1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "Minikin" 18 19#include "LocaleListCache.h" 20 21#include <unordered_set> 22 23#include <log/log.h> 24#include <unicode/uloc.h> 25 26#include "Locale.h" 27#include "MinikinInternal.h" 28 29namespace minikin { 30 31const uint32_t LocaleListCache::kEmptyListId; 32 33// Returns the text length of output. 34static size_t toLanguageTag(char* output, size_t outSize, const StringPiece& locale) { 35 output[0] = '\0'; 36 if (locale.empty()) { 37 return 0; 38 } 39 40 std::string localeString = locale.toString(); // ICU only understands C-style string. 41 42 size_t outLength = 0; 43 UErrorCode uErr = U_ZERO_ERROR; 44 outLength = uloc_canonicalize(localeString.c_str(), output, outSize, &uErr); 45 if (U_FAILURE(uErr)) { 46 // unable to build a proper locale identifier 47 ALOGD("uloc_canonicalize(\"%s\") failed: %s", localeString.c_str(), u_errorName(uErr)); 48 output[0] = '\0'; 49 return 0; 50 } 51 52 // Preserve "und" and "und-****" since uloc_addLikelySubtags changes "und" to "en-Latn-US". 53 if (strncmp(output, "und", 3) == 0 && 54 (outLength == 3 || (outLength == 8 && output[3] == '_'))) { 55 output[3] = '-'; // to be language tag. 56 return outLength; 57 } 58 59 char likelyChars[ULOC_FULLNAME_CAPACITY]; 60 uErr = U_ZERO_ERROR; 61 uloc_addLikelySubtags(output, likelyChars, ULOC_FULLNAME_CAPACITY, &uErr); 62 if (U_FAILURE(uErr)) { 63 // unable to build a proper locale identifier 64 ALOGD("uloc_addLikelySubtags(\"%s\") failed: %s", output, u_errorName(uErr)); 65 output[0] = '\0'; 66 return 0; 67 } 68 69 uErr = U_ZERO_ERROR; 70 outLength = uloc_toLanguageTag(likelyChars, output, outSize, FALSE, &uErr); 71 if (U_FAILURE(uErr)) { 72 // unable to build a proper locale identifier 73 ALOGD("uloc_toLanguageTag(\"%s\") failed: %s", likelyChars, u_errorName(uErr)); 74 output[0] = '\0'; 75 return 0; 76 } 77 return outLength; 78} 79 80static std::vector<Locale> parseLocaleList(const std::string& input) { 81 std::vector<Locale> result; 82 char langTag[ULOC_FULLNAME_CAPACITY]; 83 std::unordered_set<uint64_t> seen; 84 85 SplitIterator it(input, ','); 86 while (it.hasNext()) { 87 StringPiece localeStr = it.next(); 88 size_t length = toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, localeStr); 89 Locale locale(StringPiece(langTag, length)); 90 if (locale.isUnsupported()) { 91 continue; 92 } 93 const bool isNewLocale = seen.insert(locale.getIdentifier()).second; 94 if (!isNewLocale) { 95 continue; 96 } 97 98 result.push_back(locale); 99 if (result.size() >= FONT_LOCALE_LIMIT) { 100 break; 101 } 102 } 103 return result; 104} 105 106LocaleListCache::LocaleListCache() { 107 // Insert an empty locale list for mapping default locale list to kEmptyListId. 108 // The default locale list has only one Locale and it is the unsupported locale. 109 mLocaleLists.emplace_back(); 110 mLocaleListLookupTable.insert(std::make_pair("", kEmptyListId)); 111} 112 113uint32_t LocaleListCache::getIdInternal(const std::string& locales) { 114 std::lock_guard<std::mutex> lock(mMutex); 115 const auto& it = mLocaleListLookupTable.find(locales); 116 if (it != mLocaleListLookupTable.end()) { 117 return it->second; 118 } 119 120 // Given locale list is not in cache. Insert it and return newly assigned ID. 121 const uint32_t nextId = mLocaleLists.size(); 122 LocaleList fontLocales(parseLocaleList(locales)); 123 if (fontLocales.empty()) { 124 return kEmptyListId; 125 } 126 mLocaleLists.push_back(std::move(fontLocales)); 127 mLocaleListLookupTable.insert(std::make_pair(locales, nextId)); 128 return nextId; 129} 130 131const LocaleList& LocaleListCache::getByIdInternal(uint32_t id) { 132 std::lock_guard<std::mutex> lock(mMutex); 133 MINIKIN_ASSERT(id < mLocaleLists.size(), "Lookup by unknown locale list ID."); 134 return mLocaleLists[id]; 135} 136 137} // namespace minikin 138