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