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
32static UHashtable* gGenderInfoCache = NULL;
33static UMutex gGenderMetaLock = U_MUTEX_INITIALIZER;
34static const char* gNeutralStr = "neutral";
35static const char* gMailTaintsStr = "maleTaints";
36static const char* gMixedNeutralStr = "mixedNeutral";
37static icu::GenderInfo* gObjs = NULL;
38static icu::UInitOnce gGenderInitOnce = U_INITONCE_INITIALIZER;
39
40enum GenderStyle {
41  NEUTRAL,
42  MIXED_NEUTRAL,
43  MALE_TAINTS,
44  GENDER_STYLE_LENGTH
45};
46
47U_CDECL_BEGIN
48
49static 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
59U_CDECL_END
60
61U_NAMESPACE_BEGIN
62
63void 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
86GenderInfo::GenderInfo() {
87}
88
89GenderInfo::~GenderInfo() {
90}
91
92const 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
132const 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
174UGender 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
227const GenderInfo* GenderInfo::getNeutralInstance() {
228  return &gObjs[NEUTRAL];
229}
230
231const GenderInfo* GenderInfo::getMixedNeutralInstance() {
232  return &gObjs[MIXED_NEUTRAL];
233}
234
235const GenderInfo* GenderInfo::getMaleTaintsInstance() {
236  return &gObjs[MALE_TAINTS];
237}
238
239U_NAMESPACE_END
240
241U_CAPI const UGenderInfo* U_EXPORT2
242ugender_getInstance(const char* locale, UErrorCode* status) {
243  return (const UGenderInfo*) icu::GenderInfo::getInstance(locale, *status);
244}
245
246U_CAPI UGender U_EXPORT2
247ugender_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