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