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