| 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 |  | 
|---|