11cb0ef41Sopenharmony_ci// © 2016 and later: Unicode, Inc. and others.
21cb0ef41Sopenharmony_ci// License & terms of use: http://www.unicode.org/copyright.html
31cb0ef41Sopenharmony_ci/*
41cb0ef41Sopenharmony_ci*******************************************************************************
51cb0ef41Sopenharmony_ci* Copyright (C) 2008-2013, International Business Machines Corporation and
61cb0ef41Sopenharmony_ci* others. All Rights Reserved.
71cb0ef41Sopenharmony_ci*******************************************************************************
81cb0ef41Sopenharmony_ci*
91cb0ef41Sopenharmony_ci*
101cb0ef41Sopenharmony_ci* File GENDER.CPP
111cb0ef41Sopenharmony_ci*
121cb0ef41Sopenharmony_ci* Modification History:*
131cb0ef41Sopenharmony_ci*   Date        Name        Description
141cb0ef41Sopenharmony_ci*
151cb0ef41Sopenharmony_ci********************************************************************************
161cb0ef41Sopenharmony_ci*/
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ci#include "unicode/utypes.h"
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ci#if !UCONFIG_NO_FORMATTING
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_ci#include <utility>
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ci#include "unicode/gender.h"
251cb0ef41Sopenharmony_ci#include "unicode/ugender.h"
261cb0ef41Sopenharmony_ci#include "unicode/ures.h"
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ci#include "bytesinkutil.h"
291cb0ef41Sopenharmony_ci#include "charstr.h"
301cb0ef41Sopenharmony_ci#include "cmemory.h"
311cb0ef41Sopenharmony_ci#include "cstring.h"
321cb0ef41Sopenharmony_ci#include "mutex.h"
331cb0ef41Sopenharmony_ci#include "uassert.h"
341cb0ef41Sopenharmony_ci#include "ucln_in.h"
351cb0ef41Sopenharmony_ci#include "ulocimp.h"
361cb0ef41Sopenharmony_ci#include "umutex.h"
371cb0ef41Sopenharmony_ci#include "uhash.h"
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_cistatic UHashtable* gGenderInfoCache = nullptr;
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_cistatic const char* gNeutralStr = "neutral";
421cb0ef41Sopenharmony_cistatic const char* gMailTaintsStr = "maleTaints";
431cb0ef41Sopenharmony_cistatic const char* gMixedNeutralStr = "mixedNeutral";
441cb0ef41Sopenharmony_cistatic icu::GenderInfo* gObjs = nullptr;
451cb0ef41Sopenharmony_cistatic icu::UInitOnce gGenderInitOnce {};
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_cienum GenderStyle {
481cb0ef41Sopenharmony_ci  NEUTRAL,
491cb0ef41Sopenharmony_ci  MIXED_NEUTRAL,
501cb0ef41Sopenharmony_ci  MALE_TAINTS,
511cb0ef41Sopenharmony_ci  GENDER_STYLE_LENGTH
521cb0ef41Sopenharmony_ci};
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ciU_CDECL_BEGIN
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_cistatic UBool U_CALLCONV gender_cleanup() {
571cb0ef41Sopenharmony_ci  if (gGenderInfoCache != nullptr) {
581cb0ef41Sopenharmony_ci    uhash_close(gGenderInfoCache);
591cb0ef41Sopenharmony_ci    gGenderInfoCache = nullptr;
601cb0ef41Sopenharmony_ci    delete [] gObjs;
611cb0ef41Sopenharmony_ci  }
621cb0ef41Sopenharmony_ci  gGenderInitOnce.reset();
631cb0ef41Sopenharmony_ci  return true;
641cb0ef41Sopenharmony_ci}
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ciU_CDECL_END
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ciU_NAMESPACE_BEGIN
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_civoid U_CALLCONV GenderInfo_initCache(UErrorCode &status) {
711cb0ef41Sopenharmony_ci  ucln_i18n_registerCleanup(UCLN_I18N_GENDERINFO, gender_cleanup);
721cb0ef41Sopenharmony_ci  U_ASSERT(gGenderInfoCache == nullptr);
731cb0ef41Sopenharmony_ci  if (U_FAILURE(status)) {
741cb0ef41Sopenharmony_ci      return;
751cb0ef41Sopenharmony_ci  }
761cb0ef41Sopenharmony_ci  gObjs = new GenderInfo[GENDER_STYLE_LENGTH];
771cb0ef41Sopenharmony_ci  if (gObjs == nullptr) {
781cb0ef41Sopenharmony_ci    status = U_MEMORY_ALLOCATION_ERROR;
791cb0ef41Sopenharmony_ci    return;
801cb0ef41Sopenharmony_ci  }
811cb0ef41Sopenharmony_ci  for (int i = 0; i < GENDER_STYLE_LENGTH; i++) {
821cb0ef41Sopenharmony_ci    gObjs[i]._style = i;
831cb0ef41Sopenharmony_ci  }
841cb0ef41Sopenharmony_ci  gGenderInfoCache = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status);
851cb0ef41Sopenharmony_ci  if (U_FAILURE(status)) {
861cb0ef41Sopenharmony_ci    delete [] gObjs;
871cb0ef41Sopenharmony_ci    return;
881cb0ef41Sopenharmony_ci  }
891cb0ef41Sopenharmony_ci  uhash_setKeyDeleter(gGenderInfoCache, uprv_free);
901cb0ef41Sopenharmony_ci}
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ciGenderInfo::GenderInfo() {
941cb0ef41Sopenharmony_ci}
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ciGenderInfo::~GenderInfo() {
971cb0ef41Sopenharmony_ci}
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_ciconst GenderInfo* GenderInfo::getInstance(const Locale& locale, UErrorCode& status) {
1001cb0ef41Sopenharmony_ci  // Make sure our cache exists.
1011cb0ef41Sopenharmony_ci  umtx_initOnce(gGenderInitOnce, &GenderInfo_initCache, status);
1021cb0ef41Sopenharmony_ci  if (U_FAILURE(status)) {
1031cb0ef41Sopenharmony_ci    return nullptr;
1041cb0ef41Sopenharmony_ci  }
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci  static UMutex gGenderMetaLock;
1071cb0ef41Sopenharmony_ci  const GenderInfo* result = nullptr;
1081cb0ef41Sopenharmony_ci  const char* key = locale.getName();
1091cb0ef41Sopenharmony_ci  {
1101cb0ef41Sopenharmony_ci    Mutex lock(&gGenderMetaLock);
1111cb0ef41Sopenharmony_ci    result = (const GenderInfo*) uhash_get(gGenderInfoCache, key);
1121cb0ef41Sopenharmony_ci  }
1131cb0ef41Sopenharmony_ci  if (result) {
1141cb0ef41Sopenharmony_ci    return result;
1151cb0ef41Sopenharmony_ci  }
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci  // On cache miss, try to create GenderInfo from CLDR data
1181cb0ef41Sopenharmony_ci  result = loadInstance(locale, status);
1191cb0ef41Sopenharmony_ci  if (U_FAILURE(status)) {
1201cb0ef41Sopenharmony_ci    return nullptr;
1211cb0ef41Sopenharmony_ci  }
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci  // Try to put our GenderInfo object in cache. If there is a race condition,
1241cb0ef41Sopenharmony_ci  // favor the GenderInfo object that is already in the cache.
1251cb0ef41Sopenharmony_ci  {
1261cb0ef41Sopenharmony_ci    Mutex lock(&gGenderMetaLock);
1271cb0ef41Sopenharmony_ci    GenderInfo* temp = (GenderInfo*) uhash_get(gGenderInfoCache, key);
1281cb0ef41Sopenharmony_ci    if (temp) {
1291cb0ef41Sopenharmony_ci      result = temp;
1301cb0ef41Sopenharmony_ci    } else {
1311cb0ef41Sopenharmony_ci      uhash_put(gGenderInfoCache, uprv_strdup(key), (void*) result, &status);
1321cb0ef41Sopenharmony_ci      if (U_FAILURE(status)) {
1331cb0ef41Sopenharmony_ci        return nullptr;
1341cb0ef41Sopenharmony_ci      }
1351cb0ef41Sopenharmony_ci    }
1361cb0ef41Sopenharmony_ci  }
1371cb0ef41Sopenharmony_ci  return result;
1381cb0ef41Sopenharmony_ci}
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ciconst GenderInfo* GenderInfo::loadInstance(const Locale& locale, UErrorCode& status) {
1411cb0ef41Sopenharmony_ci  LocalUResourceBundlePointer rb(
1421cb0ef41Sopenharmony_ci      ures_openDirect(nullptr, "genderList", &status));
1431cb0ef41Sopenharmony_ci  if (U_FAILURE(status)) {
1441cb0ef41Sopenharmony_ci    return nullptr;
1451cb0ef41Sopenharmony_ci  }
1461cb0ef41Sopenharmony_ci  LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), "genderList", nullptr, &status));
1471cb0ef41Sopenharmony_ci  if (U_FAILURE(status)) {
1481cb0ef41Sopenharmony_ci    return nullptr;
1491cb0ef41Sopenharmony_ci  }
1501cb0ef41Sopenharmony_ci  int32_t resLen = 0;
1511cb0ef41Sopenharmony_ci  const char* curLocaleName = locale.getName();
1521cb0ef41Sopenharmony_ci  UErrorCode key_status = U_ZERO_ERROR;
1531cb0ef41Sopenharmony_ci  const char16_t* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &key_status);
1541cb0ef41Sopenharmony_ci  if (s == nullptr) {
1551cb0ef41Sopenharmony_ci    key_status = U_ZERO_ERROR;
1561cb0ef41Sopenharmony_ci    CharString parentLocaleName(curLocaleName, key_status);
1571cb0ef41Sopenharmony_ci    while (s == nullptr) {
1581cb0ef41Sopenharmony_ci      {
1591cb0ef41Sopenharmony_ci        CharString tmp;
1601cb0ef41Sopenharmony_ci        CharStringByteSink sink(&tmp);
1611cb0ef41Sopenharmony_ci        ulocimp_getParent(parentLocaleName.data(), sink, &status);
1621cb0ef41Sopenharmony_ci        if (tmp.isEmpty()) break;
1631cb0ef41Sopenharmony_ci        parentLocaleName = std::move(tmp);
1641cb0ef41Sopenharmony_ci      }
1651cb0ef41Sopenharmony_ci      key_status = U_ZERO_ERROR;
1661cb0ef41Sopenharmony_ci      resLen = 0;
1671cb0ef41Sopenharmony_ci      s = ures_getStringByKey(locRes.getAlias(), parentLocaleName.data(), &resLen, &key_status);
1681cb0ef41Sopenharmony_ci      key_status = U_ZERO_ERROR;
1691cb0ef41Sopenharmony_ci    }
1701cb0ef41Sopenharmony_ci  }
1711cb0ef41Sopenharmony_ci  if (s == nullptr) {
1721cb0ef41Sopenharmony_ci    return &gObjs[NEUTRAL];
1731cb0ef41Sopenharmony_ci  }
1741cb0ef41Sopenharmony_ci  char type_str[256] = "";
1751cb0ef41Sopenharmony_ci  u_UCharsToChars(s, type_str, resLen + 1);
1761cb0ef41Sopenharmony_ci  if (uprv_strcmp(type_str, gNeutralStr) == 0) {
1771cb0ef41Sopenharmony_ci    return &gObjs[NEUTRAL];
1781cb0ef41Sopenharmony_ci  }
1791cb0ef41Sopenharmony_ci  if (uprv_strcmp(type_str, gMixedNeutralStr) == 0) {
1801cb0ef41Sopenharmony_ci    return &gObjs[MIXED_NEUTRAL];
1811cb0ef41Sopenharmony_ci  }
1821cb0ef41Sopenharmony_ci  if (uprv_strcmp(type_str, gMailTaintsStr) == 0) {
1831cb0ef41Sopenharmony_ci    return &gObjs[MALE_TAINTS];
1841cb0ef41Sopenharmony_ci  }
1851cb0ef41Sopenharmony_ci  return &gObjs[NEUTRAL];
1861cb0ef41Sopenharmony_ci}
1871cb0ef41Sopenharmony_ci
1881cb0ef41Sopenharmony_ciUGender GenderInfo::getListGender(const UGender* genders, int32_t length, UErrorCode& status) const {
1891cb0ef41Sopenharmony_ci  if (U_FAILURE(status)) {
1901cb0ef41Sopenharmony_ci    return UGENDER_OTHER;
1911cb0ef41Sopenharmony_ci  }
1921cb0ef41Sopenharmony_ci  if (length == 0) {
1931cb0ef41Sopenharmony_ci    return UGENDER_OTHER;
1941cb0ef41Sopenharmony_ci  }
1951cb0ef41Sopenharmony_ci  if (length == 1) {
1961cb0ef41Sopenharmony_ci    return genders[0];
1971cb0ef41Sopenharmony_ci  }
1981cb0ef41Sopenharmony_ci  UBool has_female = false;
1991cb0ef41Sopenharmony_ci  UBool has_male = false;
2001cb0ef41Sopenharmony_ci  switch (_style) {
2011cb0ef41Sopenharmony_ci    case NEUTRAL:
2021cb0ef41Sopenharmony_ci      return UGENDER_OTHER;
2031cb0ef41Sopenharmony_ci    case MIXED_NEUTRAL:
2041cb0ef41Sopenharmony_ci      for (int32_t i = 0; i < length; ++i) {
2051cb0ef41Sopenharmony_ci        switch (genders[i]) {
2061cb0ef41Sopenharmony_ci          case UGENDER_OTHER:
2071cb0ef41Sopenharmony_ci            return UGENDER_OTHER;
2081cb0ef41Sopenharmony_ci            break;
2091cb0ef41Sopenharmony_ci          case UGENDER_FEMALE:
2101cb0ef41Sopenharmony_ci            if (has_male) {
2111cb0ef41Sopenharmony_ci              return UGENDER_OTHER;
2121cb0ef41Sopenharmony_ci            }
2131cb0ef41Sopenharmony_ci            has_female = true;
2141cb0ef41Sopenharmony_ci            break;
2151cb0ef41Sopenharmony_ci          case UGENDER_MALE:
2161cb0ef41Sopenharmony_ci            if (has_female) {
2171cb0ef41Sopenharmony_ci              return UGENDER_OTHER;
2181cb0ef41Sopenharmony_ci            }
2191cb0ef41Sopenharmony_ci            has_male = true;
2201cb0ef41Sopenharmony_ci            break;
2211cb0ef41Sopenharmony_ci          default:
2221cb0ef41Sopenharmony_ci            break;
2231cb0ef41Sopenharmony_ci        }
2241cb0ef41Sopenharmony_ci      }
2251cb0ef41Sopenharmony_ci      return has_male ? UGENDER_MALE : UGENDER_FEMALE;
2261cb0ef41Sopenharmony_ci      break;
2271cb0ef41Sopenharmony_ci    case MALE_TAINTS:
2281cb0ef41Sopenharmony_ci      for (int32_t i = 0; i < length; ++i) {
2291cb0ef41Sopenharmony_ci        if (genders[i] != UGENDER_FEMALE) {
2301cb0ef41Sopenharmony_ci          return UGENDER_MALE;
2311cb0ef41Sopenharmony_ci        }
2321cb0ef41Sopenharmony_ci      }
2331cb0ef41Sopenharmony_ci      return UGENDER_FEMALE;
2341cb0ef41Sopenharmony_ci      break;
2351cb0ef41Sopenharmony_ci    default:
2361cb0ef41Sopenharmony_ci      return UGENDER_OTHER;
2371cb0ef41Sopenharmony_ci      break;
2381cb0ef41Sopenharmony_ci  }
2391cb0ef41Sopenharmony_ci}
2401cb0ef41Sopenharmony_ci
2411cb0ef41Sopenharmony_ciconst GenderInfo* GenderInfo::getNeutralInstance() {
2421cb0ef41Sopenharmony_ci  return &gObjs[NEUTRAL];
2431cb0ef41Sopenharmony_ci}
2441cb0ef41Sopenharmony_ci
2451cb0ef41Sopenharmony_ciconst GenderInfo* GenderInfo::getMixedNeutralInstance() {
2461cb0ef41Sopenharmony_ci  return &gObjs[MIXED_NEUTRAL];
2471cb0ef41Sopenharmony_ci}
2481cb0ef41Sopenharmony_ci
2491cb0ef41Sopenharmony_ciconst GenderInfo* GenderInfo::getMaleTaintsInstance() {
2501cb0ef41Sopenharmony_ci  return &gObjs[MALE_TAINTS];
2511cb0ef41Sopenharmony_ci}
2521cb0ef41Sopenharmony_ci
2531cb0ef41Sopenharmony_ciU_NAMESPACE_END
2541cb0ef41Sopenharmony_ci
2551cb0ef41Sopenharmony_ciU_CAPI const UGenderInfo* U_EXPORT2
2561cb0ef41Sopenharmony_ciugender_getInstance(const char* locale, UErrorCode* status) {
2571cb0ef41Sopenharmony_ci  return (const UGenderInfo*) icu::GenderInfo::getInstance(locale, *status);
2581cb0ef41Sopenharmony_ci}
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ciU_CAPI UGender U_EXPORT2
2611cb0ef41Sopenharmony_ciugender_getListGender(const UGenderInfo* genderInfo, const UGender* genders, int32_t size, UErrorCode* status) {
2621cb0ef41Sopenharmony_ci  return ((const icu::GenderInfo *)genderInfo)->getListGender(genders, size, *status);
2631cb0ef41Sopenharmony_ci}
2641cb0ef41Sopenharmony_ci
2651cb0ef41Sopenharmony_ci#endif /* #if !UCONFIG_NO_FORMATTING */
266