| 1 | // © 2016 and later: Unicode, Inc. and others. |
| 2 | // License & terms of use: http://www.unicode.org/copyright.html |
| 3 | /* |
| 4 | ******************************************************************************* |
| 5 | * Copyright (C) 2010-2015, International Business Machines Corporation and |
| 6 | * others. All Rights Reserved. |
| 7 | ******************************************************************************* |
| 8 | * |
| 9 | * |
| 10 | * File NUMSYS.CPP |
| 11 | * |
| 12 | * Modification History:* |
| 13 | * Date Name Description |
| 14 | * |
| 15 | ******************************************************************************** |
| 16 | */ |
| 17 | |
| 18 | #include "unicode/utypes.h" |
| 19 | #include "unicode/localpointer.h" |
| 20 | #include "unicode/uchar.h" |
| 21 | #include "unicode/unistr.h" |
| 22 | #include "unicode/ures.h" |
| 23 | #include "unicode/ustring.h" |
| 24 | #include "unicode/uloc.h" |
| 25 | #include "unicode/schriter.h" |
| 26 | #include "unicode/numsys.h" |
| 27 | #include "cstring.h" |
| 28 | #include "uassert.h" |
| 29 | #include "ucln_in.h" |
| 30 | #include "umutex.h" |
| 31 | #include "uresimp.h" |
| 32 | #include "numsys_impl.h" |
| 33 | |
| 34 | #if !UCONFIG_NO_FORMATTING |
| 35 | |
| 36 | U_NAMESPACE_BEGIN |
| 37 | |
| 38 | // Useful constants |
| 39 | |
| 40 | #define DEFAULT_DIGITS UNICODE_STRING_SIMPLE("0123456789") |
| 41 | static const char gNumberingSystems[] = "numberingSystems" ; |
| 42 | static const char gNumberElements[] = "NumberElements" ; |
| 43 | static const char gDefault[] = "default" ; |
| 44 | static const char gNative[] = "native" ; |
| 45 | static const char gTraditional[] = "traditional" ; |
| 46 | static const char gFinance[] = "finance" ; |
| 47 | static const char gDesc[] = "desc" ; |
| 48 | static const char gRadix[] = "radix" ; |
| 49 | static const char gAlgorithmic[] = "algorithmic" ; |
| 50 | static const char gLatn[] = "latn" ; |
| 51 | |
| 52 | |
| 53 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumberingSystem) |
| 54 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumsysNameEnumeration) |
| 55 | |
| 56 | /** |
| 57 | * Default Constructor. |
| 58 | * |
| 59 | * @draft ICU 4.2 |
| 60 | */ |
| 61 | |
| 62 | NumberingSystem::NumberingSystem() { |
| 63 | radix = 10; |
| 64 | algorithmic = FALSE; |
| 65 | UnicodeString defaultDigits = DEFAULT_DIGITS; |
| 66 | desc.setTo(defaultDigits); |
| 67 | uprv_strcpy(name,gLatn); |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * Copy constructor. |
| 72 | * @draft ICU 4.2 |
| 73 | */ |
| 74 | |
| 75 | NumberingSystem::NumberingSystem(const NumberingSystem& other) |
| 76 | : UObject(other) { |
| 77 | *this=other; |
| 78 | } |
| 79 | |
| 80 | NumberingSystem* U_EXPORT2 |
| 81 | NumberingSystem::createInstance(int32_t radix_in, UBool isAlgorithmic_in, const UnicodeString & desc_in, UErrorCode &status) { |
| 82 | |
| 83 | if (U_FAILURE(status)) { |
| 84 | return nullptr; |
| 85 | } |
| 86 | |
| 87 | if ( radix_in < 2 ) { |
| 88 | status = U_ILLEGAL_ARGUMENT_ERROR; |
| 89 | return nullptr; |
| 90 | } |
| 91 | |
| 92 | if ( !isAlgorithmic_in ) { |
| 93 | if ( desc_in.countChar32() != radix_in ) { |
| 94 | status = U_ILLEGAL_ARGUMENT_ERROR; |
| 95 | return nullptr; |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | LocalPointer<NumberingSystem> ns(new NumberingSystem(), status); |
| 100 | if (U_FAILURE(status)) { |
| 101 | return nullptr; |
| 102 | } |
| 103 | |
| 104 | ns->setRadix(radix_in); |
| 105 | ns->setDesc(desc_in); |
| 106 | ns->setAlgorithmic(isAlgorithmic_in); |
| 107 | ns->setName(nullptr); |
| 108 | |
| 109 | return ns.orphan(); |
| 110 | } |
| 111 | |
| 112 | NumberingSystem* U_EXPORT2 |
| 113 | NumberingSystem::createInstance(const Locale & inLocale, UErrorCode& status) { |
| 114 | |
| 115 | if (U_FAILURE(status)) { |
| 116 | return nullptr; |
| 117 | } |
| 118 | |
| 119 | UBool nsResolved = TRUE; |
| 120 | UBool usingFallback = FALSE; |
| 121 | char buffer[ULOC_KEYWORDS_CAPACITY]; |
| 122 | int32_t count = inLocale.getKeywordValue("numbers" , buffer, sizeof(buffer), status); |
| 123 | if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { |
| 124 | // the "numbers" keyword exceeds ULOC_KEYWORDS_CAPACITY; ignore and use default. |
| 125 | count = 0; |
| 126 | status = U_ZERO_ERROR; |
| 127 | } |
| 128 | if ( count > 0 ) { // @numbers keyword was specified in the locale |
| 129 | U_ASSERT(count < ULOC_KEYWORDS_CAPACITY); |
| 130 | buffer[count] = '\0'; // Make sure it is null terminated. |
| 131 | if ( !uprv_strcmp(buffer,gDefault) || !uprv_strcmp(buffer,gNative) || |
| 132 | !uprv_strcmp(buffer,gTraditional) || !uprv_strcmp(buffer,gFinance)) { |
| 133 | nsResolved = FALSE; |
| 134 | } |
| 135 | } else { |
| 136 | uprv_strcpy(buffer, gDefault); |
| 137 | nsResolved = FALSE; |
| 138 | } |
| 139 | |
| 140 | if (!nsResolved) { // Resolve the numbering system ( default, native, traditional or finance ) into a "real" numbering system |
| 141 | UErrorCode localStatus = U_ZERO_ERROR; |
| 142 | LocalUResourceBundlePointer resource(ures_open(nullptr, inLocale.getName(), &localStatus)); |
| 143 | LocalUResourceBundlePointer numberElementsRes(ures_getByKey(resource.getAlias(), gNumberElements, nullptr, &localStatus)); |
| 144 | // Don't stomp on the catastrophic failure of OOM. |
| 145 | if (localStatus == U_MEMORY_ALLOCATION_ERROR) { |
| 146 | status = U_MEMORY_ALLOCATION_ERROR; |
| 147 | return nullptr; |
| 148 | } |
| 149 | while (!nsResolved) { |
| 150 | localStatus = U_ZERO_ERROR; |
| 151 | count = 0; |
| 152 | const UChar *nsName = ures_getStringByKeyWithFallback(numberElementsRes.getAlias(), buffer, &count, &localStatus); |
| 153 | // Don't stomp on the catastrophic failure of OOM. |
| 154 | if (localStatus == U_MEMORY_ALLOCATION_ERROR) { |
| 155 | status = U_MEMORY_ALLOCATION_ERROR; |
| 156 | return nullptr; |
| 157 | } |
| 158 | if ( count > 0 && count < ULOC_KEYWORDS_CAPACITY ) { // numbering system found |
| 159 | u_UCharsToChars(nsName, buffer, count); |
| 160 | buffer[count] = '\0'; // Make sure it is null terminated. |
| 161 | nsResolved = TRUE; |
| 162 | } |
| 163 | |
| 164 | if (!nsResolved) { // Fallback behavior per TR35 - traditional falls back to native, finance and native fall back to default |
| 165 | if (!uprv_strcmp(buffer,gNative) || !uprv_strcmp(buffer,gFinance)) { |
| 166 | uprv_strcpy(buffer,gDefault); |
| 167 | } else if (!uprv_strcmp(buffer,gTraditional)) { |
| 168 | uprv_strcpy(buffer,gNative); |
| 169 | } else { // If we get here we couldn't find even the default numbering system |
| 170 | usingFallback = TRUE; |
| 171 | nsResolved = TRUE; |
| 172 | } |
| 173 | } |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | if (usingFallback) { |
| 178 | status = U_USING_FALLBACK_WARNING; |
| 179 | NumberingSystem *ns = new NumberingSystem(); |
| 180 | if (ns == nullptr) { |
| 181 | status = U_MEMORY_ALLOCATION_ERROR; |
| 182 | } |
| 183 | return ns; |
| 184 | } else { |
| 185 | return NumberingSystem::createInstanceByName(buffer, status); |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | NumberingSystem* U_EXPORT2 |
| 190 | NumberingSystem::createInstance(UErrorCode& status) { |
| 191 | return NumberingSystem::createInstance(Locale::getDefault(), status); |
| 192 | } |
| 193 | |
| 194 | NumberingSystem* U_EXPORT2 |
| 195 | NumberingSystem::createInstanceByName(const char *name, UErrorCode& status) { |
| 196 | int32_t radix = 10; |
| 197 | int32_t algorithmic = 0; |
| 198 | |
| 199 | LocalUResourceBundlePointer numberingSystemsInfo(ures_openDirect(nullptr, gNumberingSystems, &status)); |
| 200 | LocalUResourceBundlePointer nsCurrent(ures_getByKey(numberingSystemsInfo.getAlias(), gNumberingSystems, nullptr, &status)); |
| 201 | LocalUResourceBundlePointer nsTop(ures_getByKey(nsCurrent.getAlias(), name, nullptr, &status)); |
| 202 | |
| 203 | UnicodeString nsd = ures_getUnicodeStringByKey(nsTop.getAlias(), gDesc, &status); |
| 204 | |
| 205 | ures_getByKey(nsTop.getAlias(), gRadix, nsCurrent.getAlias(), &status); |
| 206 | radix = ures_getInt(nsCurrent.getAlias(), &status); |
| 207 | |
| 208 | ures_getByKey(nsTop.getAlias(), gAlgorithmic, nsCurrent.getAlias(), &status); |
| 209 | algorithmic = ures_getInt(nsCurrent.getAlias(), &status); |
| 210 | |
| 211 | UBool isAlgorithmic = ( algorithmic == 1 ); |
| 212 | |
| 213 | if (U_FAILURE(status)) { |
| 214 | // Don't stomp on the catastrophic failure of OOM. |
| 215 | if (status != U_MEMORY_ALLOCATION_ERROR) { |
| 216 | status = U_UNSUPPORTED_ERROR; |
| 217 | } |
| 218 | return nullptr; |
| 219 | } |
| 220 | |
| 221 | LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(radix, isAlgorithmic, nsd, status), status); |
| 222 | if (U_FAILURE(status)) { |
| 223 | return nullptr; |
| 224 | } |
| 225 | ns->setName(name); |
| 226 | return ns.orphan(); |
| 227 | } |
| 228 | |
| 229 | /** |
| 230 | * Destructor. |
| 231 | * @draft ICU 4.2 |
| 232 | */ |
| 233 | NumberingSystem::~NumberingSystem() { |
| 234 | } |
| 235 | |
| 236 | int32_t NumberingSystem::getRadix() const { |
| 237 | return radix; |
| 238 | } |
| 239 | |
| 240 | UnicodeString NumberingSystem::getDescription() const { |
| 241 | return desc; |
| 242 | } |
| 243 | |
| 244 | const char * NumberingSystem::getName() const { |
| 245 | return name; |
| 246 | } |
| 247 | |
| 248 | void NumberingSystem::setRadix(int32_t r) { |
| 249 | radix = r; |
| 250 | } |
| 251 | |
| 252 | void NumberingSystem::setAlgorithmic(UBool c) { |
| 253 | algorithmic = c; |
| 254 | } |
| 255 | |
| 256 | void NumberingSystem::setDesc(const UnicodeString &d) { |
| 257 | desc.setTo(d); |
| 258 | } |
| 259 | void NumberingSystem::setName(const char *n) { |
| 260 | if ( n == nullptr ) { |
| 261 | name[0] = (char) 0; |
| 262 | } else { |
| 263 | uprv_strncpy(name,n,kInternalNumSysNameCapacity); |
| 264 | name[kInternalNumSysNameCapacity] = '\0'; // Make sure it is null terminated. |
| 265 | } |
| 266 | } |
| 267 | UBool NumberingSystem::isAlgorithmic() const { |
| 268 | return ( algorithmic ); |
| 269 | } |
| 270 | |
| 271 | namespace { |
| 272 | |
| 273 | UVector* gNumsysNames = nullptr; |
| 274 | UInitOnce gNumSysInitOnce = U_INITONCE_INITIALIZER; |
| 275 | |
| 276 | U_CFUNC UBool U_CALLCONV numSysCleanup() { |
| 277 | delete gNumsysNames; |
| 278 | gNumsysNames = nullptr; |
| 279 | gNumSysInitOnce.reset(); |
| 280 | return true; |
| 281 | } |
| 282 | |
| 283 | U_CFUNC void initNumsysNames(UErrorCode &status) { |
| 284 | U_ASSERT(gNumsysNames == nullptr); |
| 285 | ucln_i18n_registerCleanup(UCLN_I18N_NUMSYS, numSysCleanup); |
| 286 | |
| 287 | // TODO: Simple array of UnicodeString objects, based on length of table resource? |
| 288 | LocalPointer<UVector> numsysNames(new UVector(uprv_deleteUObject, nullptr, status), status); |
| 289 | if (U_FAILURE(status)) { |
| 290 | return; |
| 291 | } |
| 292 | |
| 293 | UErrorCode rbstatus = U_ZERO_ERROR; |
| 294 | UResourceBundle *numberingSystemsInfo = ures_openDirect(nullptr, "numberingSystems" , &rbstatus); |
| 295 | numberingSystemsInfo = |
| 296 | ures_getByKey(numberingSystemsInfo, "numberingSystems" , numberingSystemsInfo, &rbstatus); |
| 297 | if (U_FAILURE(rbstatus)) { |
| 298 | // Don't stomp on the catastrophic failure of OOM. |
| 299 | if (rbstatus == U_MEMORY_ALLOCATION_ERROR) { |
| 300 | status = rbstatus; |
| 301 | } else { |
| 302 | status = U_MISSING_RESOURCE_ERROR; |
| 303 | } |
| 304 | ures_close(numberingSystemsInfo); |
| 305 | return; |
| 306 | } |
| 307 | |
| 308 | while ( ures_hasNext(numberingSystemsInfo) && U_SUCCESS(status) ) { |
| 309 | LocalUResourceBundlePointer nsCurrent(ures_getNextResource(numberingSystemsInfo, nullptr, &rbstatus)); |
| 310 | if (rbstatus == U_MEMORY_ALLOCATION_ERROR) { |
| 311 | status = rbstatus; // we want to report OOM failure back to the caller. |
| 312 | break; |
| 313 | } |
| 314 | const char *nsName = ures_getKey(nsCurrent.getAlias()); |
| 315 | LocalPointer<UnicodeString> newElem(new UnicodeString(nsName, -1, US_INV), status); |
| 316 | if (U_SUCCESS(status)) { |
| 317 | numsysNames->addElement(newElem.getAlias(), status); |
| 318 | if (U_SUCCESS(status)) { |
| 319 | newElem.orphan(); // on success, the numsysNames vector owns newElem. |
| 320 | } |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | ures_close(numberingSystemsInfo); |
| 325 | if (U_SUCCESS(status)) { |
| 326 | gNumsysNames = numsysNames.orphan(); |
| 327 | } |
| 328 | return; |
| 329 | } |
| 330 | |
| 331 | } // end anonymous namespace |
| 332 | |
| 333 | StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) { |
| 334 | umtx_initOnce(gNumSysInitOnce, &initNumsysNames, status); |
| 335 | LocalPointer<StringEnumeration> result(new NumsysNameEnumeration(status), status); |
| 336 | return result.orphan(); |
| 337 | } |
| 338 | |
| 339 | NumsysNameEnumeration::NumsysNameEnumeration(UErrorCode& status) : pos(0) { |
| 340 | (void)status; |
| 341 | } |
| 342 | |
| 343 | const UnicodeString* |
| 344 | NumsysNameEnumeration::snext(UErrorCode& status) { |
| 345 | if (U_SUCCESS(status) && (gNumsysNames != nullptr) && (pos < gNumsysNames->size())) { |
| 346 | return (const UnicodeString*)gNumsysNames->elementAt(pos++); |
| 347 | } |
| 348 | return nullptr; |
| 349 | } |
| 350 | |
| 351 | void |
| 352 | NumsysNameEnumeration::reset(UErrorCode& /*status*/) { |
| 353 | pos=0; |
| 354 | } |
| 355 | |
| 356 | int32_t |
| 357 | NumsysNameEnumeration::count(UErrorCode& /*status*/) const { |
| 358 | return (gNumsysNames==nullptr) ? 0 : gNumsysNames->size(); |
| 359 | } |
| 360 | |
| 361 | NumsysNameEnumeration::~NumsysNameEnumeration() { |
| 362 | } |
| 363 | U_NAMESPACE_END |
| 364 | |
| 365 | #endif /* #if !UCONFIG_NO_FORMATTING */ |
| 366 | |
| 367 | //eof |
| 368 | |