xref: /aosp_15_r20/external/libtextclassifier/native/utils/resources.cc (revision 993b0882672172b81d12fad7a7ac0c3e5c824a12)
1 /*
2  * Copyright (C) 2018 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 #include "utils/resources.h"
18 
19 #include "utils/base/logging.h"
20 #include "utils/zlib/buffer_generated.h"
21 
22 namespace libtextclassifier3 {
23 namespace {
isWildcardMatch(const flatbuffers::String * left,const std::string & right)24 bool isWildcardMatch(const flatbuffers::String* left,
25                      const std::string& right) {
26   return (left == nullptr || right.empty());
27 }
28 
isExactMatch(const flatbuffers::String * left,const std::string & right)29 bool isExactMatch(const flatbuffers::String* left, const std::string& right) {
30   if (left == nullptr) {
31     return right.empty();
32   }
33   return left->str() == right;
34 }
35 
NormalizeLanguageCode(const std::string & language_code)36 std::string NormalizeLanguageCode(const std::string& language_code) {
37   if (language_code == "id") {
38     return "in";
39   } else if (language_code == "iw") {
40     return "he";
41   } else if (language_code == "no") {
42     return "nb";
43   } else if (language_code == "tl") {
44     return "fil";
45   }
46   return language_code;
47 }
48 
49 }  // namespace
50 
LocaleMatch(const Locale & locale,const LanguageTag * entry_locale) const51 int Resources::LocaleMatch(const Locale& locale,
52                            const LanguageTag* entry_locale) const {
53   int match = LOCALE_NO_MATCH;
54   if (isExactMatch(entry_locale->language(),
55                    NormalizeLanguageCode(locale.Language()))) {
56     match |= LOCALE_LANGUAGE_MATCH;
57   } else if (isWildcardMatch(entry_locale->language(), locale.Language())) {
58     match |= LOCALE_LANGUAGE_WILDCARD_MATCH;
59   }
60 
61   if (isExactMatch(entry_locale->script(), locale.Script())) {
62     match |= LOCALE_SCRIPT_MATCH;
63   } else if (isWildcardMatch(entry_locale->script(), locale.Script())) {
64     match |= LOCALE_SCRIPT_WILDCARD_MATCH;
65   }
66 
67   if (isExactMatch(entry_locale->region(), locale.Region())) {
68     match |= LOCALE_REGION_MATCH;
69   } else if (isWildcardMatch(entry_locale->region(), locale.Region())) {
70     match |= LOCALE_REGION_WILDCARD_MATCH;
71   }
72 
73   return match;
74 }
75 
FindResource(const StringPiece resource_name) const76 const ResourceEntry* Resources::FindResource(
77     const StringPiece resource_name) const {
78   if (resources_ == nullptr || resources_->resource_entry() == nullptr) {
79     TC3_LOG(ERROR) << "No resources defined.";
80     return nullptr;
81   }
82   const ResourceEntry* entry =
83       resources_->resource_entry()->LookupByKey(resource_name.data());
84   if (entry == nullptr) {
85     TC3_LOG(ERROR) << "Resource " << resource_name.ToString() << " not found";
86     return nullptr;
87   }
88   return entry;
89 }
90 
BestResourceForLocales(const ResourceEntry * resource,const std::vector<Locale> & locales) const91 int Resources::BestResourceForLocales(
92     const ResourceEntry* resource, const std::vector<Locale>& locales) const {
93   // Find best match based on locale.
94   int resource_id = -1;
95   int locale_match = LOCALE_NO_MATCH;
96   const auto* resources = resource->resource();
97   for (int user_locale = 0; user_locale < locales.size(); user_locale++) {
98     if (!locales[user_locale].IsValid()) {
99       continue;
100     }
101     for (int i = 0; i < resources->size(); i++) {
102       for (const int locale_id : *resources->Get(i)->locale()) {
103         const int candidate_match = LocaleMatch(
104             locales[user_locale], resources_->locale()->Get(locale_id));
105 
106         // Only consider if at least the language matches.
107         if ((candidate_match & LOCALE_LANGUAGE_MATCH) == 0 &&
108             (candidate_match & LOCALE_LANGUAGE_WILDCARD_MATCH) == 0) {
109           continue;
110         }
111 
112         if (candidate_match > locale_match) {
113           locale_match = candidate_match;
114           resource_id = i;
115         }
116       }
117     }
118 
119     // If the language matches exactly, we are already finished.
120     // We found an exact language match.
121     if (locale_match & LOCALE_LANGUAGE_MATCH) {
122       return resource_id;
123     }
124   }
125   return resource_id;
126 }
127 
GetResourceContent(const std::vector<Locale> & locales,const StringPiece resource_name,std::string * result) const128 bool Resources::GetResourceContent(const std::vector<Locale>& locales,
129                                    const StringPiece resource_name,
130                                    std::string* result) const {
131   const ResourceEntry* entry = FindResource(resource_name);
132   if (entry == nullptr || entry->resource() == nullptr) {
133     return false;
134   }
135 
136   int resource_id = BestResourceForLocales(entry, locales);
137   if (resource_id < 0) {
138     return false;
139   }
140   const auto* resource = entry->resource()->Get(resource_id);
141   if (resource->content() != nullptr) {
142     *result = resource->content()->str();
143     return true;
144   }
145   return false;
146 }
147 
148 }  // namespace libtextclassifier3
149