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