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