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