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