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