xref: /aosp_15_r20/external/cronet/third_party/icu/source/i18n/gender.cpp (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 * Copyright (C) 2008-2013, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
8 *
9 *
10 * File GENDER.CPP
11 *
12 * Modification History:*
13 *   Date        Name        Description
14 *
15 ********************************************************************************
16 */
17 
18 #include "unicode/utypes.h"
19 
20 #if !UCONFIG_NO_FORMATTING
21 
22 #include <utility>
23 
24 #include "unicode/gender.h"
25 #include "unicode/ugender.h"
26 #include "unicode/ures.h"
27 
28 #include "bytesinkutil.h"
29 #include "charstr.h"
30 #include "cmemory.h"
31 #include "cstring.h"
32 #include "mutex.h"
33 #include "uassert.h"
34 #include "ucln_in.h"
35 #include "ulocimp.h"
36 #include "umutex.h"
37 #include "uhash.h"
38 
39 static UHashtable* gGenderInfoCache = nullptr;
40 
41 static const char* gNeutralStr = "neutral";
42 static const char* gMailTaintsStr = "maleTaints";
43 static const char* gMixedNeutralStr = "mixedNeutral";
44 static icu::GenderInfo* gObjs = nullptr;
45 static icu::UInitOnce gGenderInitOnce {};
46 
47 enum GenderStyle {
48   NEUTRAL,
49   MIXED_NEUTRAL,
50   MALE_TAINTS,
51   GENDER_STYLE_LENGTH
52 };
53 
54 U_CDECL_BEGIN
55 
gender_cleanup()56 static UBool U_CALLCONV gender_cleanup() {
57   if (gGenderInfoCache != nullptr) {
58     uhash_close(gGenderInfoCache);
59     gGenderInfoCache = nullptr;
60     delete [] gObjs;
61   }
62   gGenderInitOnce.reset();
63   return true;
64 }
65 
66 U_CDECL_END
67 
68 U_NAMESPACE_BEGIN
69 
GenderInfo_initCache(UErrorCode & status)70 void U_CALLCONV GenderInfo_initCache(UErrorCode &status) {
71   ucln_i18n_registerCleanup(UCLN_I18N_GENDERINFO, gender_cleanup);
72   U_ASSERT(gGenderInfoCache == nullptr);
73   if (U_FAILURE(status)) {
74       return;
75   }
76   gObjs = new GenderInfo[GENDER_STYLE_LENGTH];
77   if (gObjs == nullptr) {
78     status = U_MEMORY_ALLOCATION_ERROR;
79     return;
80   }
81   for (int i = 0; i < GENDER_STYLE_LENGTH; i++) {
82     gObjs[i]._style = i;
83   }
84   gGenderInfoCache = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status);
85   if (U_FAILURE(status)) {
86     delete [] gObjs;
87     return;
88   }
89   uhash_setKeyDeleter(gGenderInfoCache, uprv_free);
90 }
91 
92 
GenderInfo()93 GenderInfo::GenderInfo() {
94 }
95 
~GenderInfo()96 GenderInfo::~GenderInfo() {
97 }
98 
getInstance(const Locale & locale,UErrorCode & status)99 const GenderInfo* GenderInfo::getInstance(const Locale& locale, UErrorCode& status) {
100   // Make sure our cache exists.
101   umtx_initOnce(gGenderInitOnce, &GenderInfo_initCache, status);
102   if (U_FAILURE(status)) {
103     return nullptr;
104   }
105 
106   static UMutex gGenderMetaLock;
107   const GenderInfo* result = nullptr;
108   const char* key = locale.getName();
109   {
110     Mutex lock(&gGenderMetaLock);
111     result = (const GenderInfo*) uhash_get(gGenderInfoCache, key);
112   }
113   if (result) {
114     return result;
115   }
116 
117   // On cache miss, try to create GenderInfo from CLDR data
118   result = loadInstance(locale, status);
119   if (U_FAILURE(status)) {
120     return nullptr;
121   }
122 
123   // Try to put our GenderInfo object in cache. If there is a race condition,
124   // favor the GenderInfo object that is already in the cache.
125   {
126     Mutex lock(&gGenderMetaLock);
127     GenderInfo* temp = (GenderInfo*) uhash_get(gGenderInfoCache, key);
128     if (temp) {
129       result = temp;
130     } else {
131       uhash_put(gGenderInfoCache, uprv_strdup(key), (void*) result, &status);
132       if (U_FAILURE(status)) {
133         return nullptr;
134       }
135     }
136   }
137   return result;
138 }
139 
loadInstance(const Locale & locale,UErrorCode & status)140 const GenderInfo* GenderInfo::loadInstance(const Locale& locale, UErrorCode& status) {
141   LocalUResourceBundlePointer rb(
142       ures_openDirect(nullptr, "genderList", &status));
143   if (U_FAILURE(status)) {
144     return nullptr;
145   }
146   LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), "genderList", nullptr, &status));
147   if (U_FAILURE(status)) {
148     return nullptr;
149   }
150   int32_t resLen = 0;
151   const char* curLocaleName = locale.getName();
152   UErrorCode key_status = U_ZERO_ERROR;
153   const char16_t* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &key_status);
154   if (s == nullptr) {
155     key_status = U_ZERO_ERROR;
156     CharString parentLocaleName(curLocaleName, key_status);
157     while (s == nullptr) {
158       {
159         CharString tmp;
160         CharStringByteSink sink(&tmp);
161         ulocimp_getParent(parentLocaleName.data(), sink, &status);
162         if (tmp.isEmpty()) break;
163         parentLocaleName = std::move(tmp);
164       }
165       key_status = U_ZERO_ERROR;
166       resLen = 0;
167       s = ures_getStringByKey(locRes.getAlias(), parentLocaleName.data(), &resLen, &key_status);
168       key_status = U_ZERO_ERROR;
169     }
170   }
171   if (s == nullptr) {
172     return &gObjs[NEUTRAL];
173   }
174   char type_str[256] = "";
175   u_UCharsToChars(s, type_str, resLen + 1);
176   if (uprv_strcmp(type_str, gNeutralStr) == 0) {
177     return &gObjs[NEUTRAL];
178   }
179   if (uprv_strcmp(type_str, gMixedNeutralStr) == 0) {
180     return &gObjs[MIXED_NEUTRAL];
181   }
182   if (uprv_strcmp(type_str, gMailTaintsStr) == 0) {
183     return &gObjs[MALE_TAINTS];
184   }
185   return &gObjs[NEUTRAL];
186 }
187 
getListGender(const UGender * genders,int32_t length,UErrorCode & status) const188 UGender GenderInfo::getListGender(const UGender* genders, int32_t length, UErrorCode& status) const {
189   if (U_FAILURE(status)) {
190     return UGENDER_OTHER;
191   }
192   if (length == 0) {
193     return UGENDER_OTHER;
194   }
195   if (length == 1) {
196     return genders[0];
197   }
198   UBool has_female = false;
199   UBool has_male = false;
200   switch (_style) {
201     case NEUTRAL:
202       return UGENDER_OTHER;
203     case MIXED_NEUTRAL:
204       for (int32_t i = 0; i < length; ++i) {
205         switch (genders[i]) {
206           case UGENDER_OTHER:
207             return UGENDER_OTHER;
208             break;
209           case UGENDER_FEMALE:
210             if (has_male) {
211               return UGENDER_OTHER;
212             }
213             has_female = true;
214             break;
215           case UGENDER_MALE:
216             if (has_female) {
217               return UGENDER_OTHER;
218             }
219             has_male = true;
220             break;
221           default:
222             break;
223         }
224       }
225       return has_male ? UGENDER_MALE : UGENDER_FEMALE;
226       break;
227     case MALE_TAINTS:
228       for (int32_t i = 0; i < length; ++i) {
229         if (genders[i] != UGENDER_FEMALE) {
230           return UGENDER_MALE;
231         }
232       }
233       return UGENDER_FEMALE;
234       break;
235     default:
236       return UGENDER_OTHER;
237       break;
238   }
239 }
240 
getNeutralInstance()241 const GenderInfo* GenderInfo::getNeutralInstance() {
242   return &gObjs[NEUTRAL];
243 }
244 
getMixedNeutralInstance()245 const GenderInfo* GenderInfo::getMixedNeutralInstance() {
246   return &gObjs[MIXED_NEUTRAL];
247 }
248 
getMaleTaintsInstance()249 const GenderInfo* GenderInfo::getMaleTaintsInstance() {
250   return &gObjs[MALE_TAINTS];
251 }
252 
253 U_NAMESPACE_END
254 
255 U_CAPI const UGenderInfo* U_EXPORT2
ugender_getInstance(const char * locale,UErrorCode * status)256 ugender_getInstance(const char* locale, UErrorCode* status) {
257   return (const UGenderInfo*) icu::GenderInfo::getInstance(locale, *status);
258 }
259 
260 U_CAPI UGender U_EXPORT2
ugender_getListGender(const UGenderInfo * genderInfo,const UGender * genders,int32_t size,UErrorCode * status)261 ugender_getListGender(const UGenderInfo* genderInfo, const UGender* genders, int32_t size, UErrorCode* status) {
262   return ((const icu::GenderInfo *)genderInfo)->getListGender(genders, size, *status);
263 }
264 
265 #endif /* #if !UCONFIG_NO_FORMATTING */
266