Home | History | Annotate | Download | only in i18n
      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