numsys.cpp (11412B)
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 char16_t *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] = static_cast<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 {}; 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 numsysNames->adoptElement(newElem.orphan(), status); 317 } 318 319 ures_close(numberingSystemsInfo); 320 if (U_SUCCESS(status)) { 321 gNumsysNames = numsysNames.orphan(); 322 } 323 } 324 325 } // end anonymous namespace 326 327 StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) { 328 umtx_initOnce(gNumSysInitOnce, &initNumsysNames, status); 329 LocalPointer<StringEnumeration> result(new NumsysNameEnumeration(status), status); 330 return result.orphan(); 331 } 332 333 NumsysNameEnumeration::NumsysNameEnumeration(UErrorCode& status) : pos(0) { 334 (void)status; 335 } 336 337 const UnicodeString* 338 NumsysNameEnumeration::snext(UErrorCode& status) { 339 if (U_SUCCESS(status) && (gNumsysNames != nullptr) && (pos < gNumsysNames->size())) { 340 return static_cast<const UnicodeString*>(gNumsysNames->elementAt(pos++)); 341 } 342 return nullptr; 343 } 344 345 void 346 NumsysNameEnumeration::reset(UErrorCode& /*status*/) { 347 pos=0; 348 } 349 350 int32_t 351 NumsysNameEnumeration::count(UErrorCode& /*status*/) const { 352 return (gNumsysNames==nullptr) ? 0 : gNumsysNames->size(); 353 } 354 355 NumsysNameEnumeration::~NumsysNameEnumeration() { 356 } 357 U_NAMESPACE_END 358 359 #endif /* #if !UCONFIG_NO_FORMATTING */ 360 361 //eof