• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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