Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/icu/source/i18n/rbnf.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
*******************************************************************************
5
* Copyright (C) 1997-2015, International Business Machines Corporation
6
* and others. All Rights Reserved.
7
*******************************************************************************
8
*/
9
10
#include "unicode/utypes.h"
11
#include "utypeinfo.h"  // for 'typeid' to work
12
13
#include "unicode/rbnf.h"
14
15
#if U_HAVE_RBNF
16
17
#include "unicode/normlzr.h"
18
#include "unicode/plurfmt.h"
19
#include "unicode/tblcoll.h"
20
#include "unicode/uchar.h"
21
#include "unicode/ucol.h"
22
#include "unicode/uloc.h"
23
#include "unicode/unum.h"
24
#include "unicode/ures.h"
25
#include "unicode/ustring.h"
26
#include "unicode/utf16.h"
27
#include "unicode/udata.h"
28
#include "unicode/udisplaycontext.h"
29
#include "unicode/brkiter.h"
30
#include "unicode/ucasemap.h"
31
32
#include "cmemory.h"
33
#include "cstring.h"
34
#include "patternprops.h"
35
#include "uresimp.h"
36
#include "nfrs.h"
37
#include "number_decimalquantity.h"
38
39
// debugging
40
// #define RBNF_DEBUG
41
42
#ifdef RBNF_DEBUG
43
#include <stdio.h>
44
#endif
45
46
0
#define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf"
47
48
static const UChar gPercentPercent[] =
49
{
50
    0x25, 0x25, 0
51
}; /* "%%" */
52
53
// All urbnf objects are created through openRules, so we init all of the
54
// Unicode string constants required by rbnf, nfrs, or nfr here.
55
static const UChar gLenientParse[] =
56
{
57
    0x25, 0x25, 0x6C, 0x65, 0x6E, 0x69, 0x65, 0x6E, 0x74, 0x2D, 0x70, 0x61, 0x72, 0x73, 0x65, 0x3A, 0
58
}; /* "%%lenient-parse:" */
59
static const UChar gSemiColon = 0x003B;
60
static const UChar gSemiPercent[] =
61
{
62
    0x3B, 0x25, 0
63
}; /* ";%" */
64
65
0
#define kSomeNumberOfBitsDiv2 22
66
0
#define kHalfMaxDouble (double)(1 << kSomeNumberOfBitsDiv2)
67
0
#define kMaxDouble (kHalfMaxDouble * kHalfMaxDouble)
68
69
U_NAMESPACE_BEGIN
70
71
using number::impl::DecimalQuantity;
72
73
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat)
74
75
/*
76
This is a utility class. It does not use ICU's RTTI.
77
If ICU's RTTI is needed again, you can uncomment the RTTI code and derive from UObject.
78
Please make sure that intltest passes on Windows in Release mode,
79
since the string pooling per compilation unit will mess up how RTTI works.
80
The RTTI code was also removed due to lack of code coverage.
81
*/
82
class LocalizationInfo : public UMemory {
83
protected:
84
    virtual ~LocalizationInfo();
85
    uint32_t refcount;
86
    
87
public:
88
0
    LocalizationInfo() : refcount(0) {}
89
    
90
0
    LocalizationInfo* ref(void) {
91
0
        ++refcount;
92
0
        return this;
93
0
    }
94
    
95
0
    LocalizationInfo* unref(void) {
96
0
        if (refcount && --refcount == 0) {
97
0
            delete this;
98
0
        }
99
0
        return NULL;
100
0
    }
101
    
102
    virtual UBool operator==(const LocalizationInfo* rhs) const;
103
0
    inline  UBool operator!=(const LocalizationInfo* rhs) const { return !operator==(rhs); }
104
    
105
    virtual int32_t getNumberOfRuleSets(void) const = 0;
106
    virtual const UChar* getRuleSetName(int32_t index) const = 0;
107
    virtual int32_t getNumberOfDisplayLocales(void) const = 0;
108
    virtual const UChar* getLocaleName(int32_t index) const = 0;
109
    virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const = 0;
110
    
111
    virtual int32_t indexForLocale(const UChar* locale) const;
112
    virtual int32_t indexForRuleSet(const UChar* ruleset) const;
113
    
114
//    virtual UClassID getDynamicClassID() const = 0;
115
//    static UClassID getStaticClassID(void);
116
};
117
118
0
LocalizationInfo::~LocalizationInfo() {}
119
120
//UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
121
122
// if both strings are NULL, this returns TRUE
123
static UBool 
124
0
streq(const UChar* lhs, const UChar* rhs) {
125
0
    if (rhs == lhs) {
126
0
        return TRUE;
127
0
    }
128
0
    if (lhs && rhs) {
129
0
        return u_strcmp(lhs, rhs) == 0;
130
0
    }
131
0
    return FALSE;
132
0
}
133
134
UBool
135
0
LocalizationInfo::operator==(const LocalizationInfo* rhs) const {
136
0
    if (rhs) {
137
0
        if (this == rhs) {
138
0
            return TRUE;
139
0
        }
140
0
        
141
0
        int32_t rsc = getNumberOfRuleSets();
142
0
        if (rsc == rhs->getNumberOfRuleSets()) {
143
0
            for (int i = 0; i < rsc; ++i) {
144
0
                if (!streq(getRuleSetName(i), rhs->getRuleSetName(i))) {
145
0
                    return FALSE;
146
0
                }
147
0
            }
148
0
            int32_t dlc = getNumberOfDisplayLocales();
149
0
            if (dlc == rhs->getNumberOfDisplayLocales()) {
150
0
                for (int i = 0; i < dlc; ++i) {
151
0
                    const UChar* locale = getLocaleName(i);
152
0
                    int32_t ix = rhs->indexForLocale(locale);
153
0
                    // if no locale, ix is -1, getLocaleName returns null, so streq returns false
154
0
                    if (!streq(locale, rhs->getLocaleName(ix))) {
155
0
                        return FALSE;
156
0
                    }
157
0
                    for (int j = 0; j < rsc; ++j) {
158
0
                        if (!streq(getDisplayName(i, j), rhs->getDisplayName(ix, j))) {
159
0
                            return FALSE;
160
0
                        }
161
0
                    }
162
0
                }
163
0
                return TRUE;
164
0
            }
165
0
        }
166
0
    }
167
0
    return FALSE;
168
0
}
169
170
int32_t
171
0
LocalizationInfo::indexForLocale(const UChar* locale) const {
172
0
    for (int i = 0; i < getNumberOfDisplayLocales(); ++i) {
173
0
        if (streq(locale, getLocaleName(i))) {
174
0
            return i;
175
0
        }
176
0
    }
177
0
    return -1;
178
0
}
179
180
int32_t
181
0
LocalizationInfo::indexForRuleSet(const UChar* ruleset) const {
182
0
    if (ruleset) {
183
0
        for (int i = 0; i < getNumberOfRuleSets(); ++i) {
184
0
            if (streq(ruleset, getRuleSetName(i))) {
185
0
                return i;
186
0
            }
187
0
        }
188
0
    }
189
0
    return -1;
190
0
}
191
192
193
typedef void (*Fn_Deleter)(void*);
194
195
class VArray {
196
    void** buf;
197
    int32_t cap;
198
    int32_t size;
199
    Fn_Deleter deleter;
200
public:
201
0
    VArray() : buf(NULL), cap(0), size(0), deleter(NULL) {}
202
    
203
0
    VArray(Fn_Deleter del) : buf(NULL), cap(0), size(0), deleter(del) {}
204
    
205
0
    ~VArray() {
206
0
        if (deleter) {
207
0
            for (int i = 0; i < size; ++i) {
208
0
                (*deleter)(buf[i]);
209
0
            }
210
0
        }
211
0
        uprv_free(buf); 
212
0
    }
213
    
214
0
    int32_t length() {
215
0
        return size;
216
0
    }
217
    
218
0
    void add(void* elem, UErrorCode& status) {
219
0
        if (U_SUCCESS(status)) {
220
0
            if (size == cap) {
221
0
                if (cap == 0) {
222
0
                    cap = 1;
223
0
                } else if (cap < 256) {
224
0
                    cap *= 2;
225
0
                } else {
226
0
                    cap += 256;
227
0
                }
228
0
                if (buf == NULL) {
229
0
                    buf = (void**)uprv_malloc(cap * sizeof(void*));
230
0
                } else {
231
0
                    buf = (void**)uprv_realloc(buf, cap * sizeof(void*));
232
0
                }
233
0
                if (buf == NULL) {
234
0
                    // if we couldn't realloc, we leak the memory we've already allocated, but we're in deep trouble anyway
235
0
                    status = U_MEMORY_ALLOCATION_ERROR;
236
0
                    return;
237
0
                }
238
0
                void* start = &buf[size];
239
0
                size_t count = (cap - size) * sizeof(void*);
240
0
                uprv_memset(start, 0, count); // fill with nulls, just because
241
0
            }
242
0
            buf[size++] = elem;
243
0
        }
244
0
    }
245
    
246
0
    void** release(void) {
247
0
        void** result = buf;
248
0
        buf = NULL;
249
0
        cap = 0;
250
0
        size = 0;
251
0
        return result;
252
0
    }
253
};
254
255
class LocDataParser;
256
257
class StringLocalizationInfo : public LocalizationInfo {
258
    UChar* info;
259
    UChar*** data;
260
    int32_t numRuleSets;
261
    int32_t numLocales;
262
263
friend class LocDataParser;
264
265
    StringLocalizationInfo(UChar* i, UChar*** d, int32_t numRS, int32_t numLocs)
266
        : info(i), data(d), numRuleSets(numRS), numLocales(numLocs)
267
0
    {
268
0
    }
269
    
270
public:
271
    static StringLocalizationInfo* create(const UnicodeString& info, UParseError& perror, UErrorCode& status);
272
    
273
    virtual ~StringLocalizationInfo();
274
0
    virtual int32_t getNumberOfRuleSets(void) const { return numRuleSets; }
275
    virtual const UChar* getRuleSetName(int32_t index) const;
276
0
    virtual int32_t getNumberOfDisplayLocales(void) const { return numLocales; }
277
    virtual const UChar* getLocaleName(int32_t index) const;
278
    virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const;
279
    
280
//    virtual UClassID getDynamicClassID() const;
281
//    static UClassID getStaticClassID(void);
282
    
283
private:
284
    void init(UErrorCode& status) const;
285
};
286
287
288
enum {
289
    OPEN_ANGLE = 0x003c, /* '<' */
290
    CLOSE_ANGLE = 0x003e, /* '>' */
291
    COMMA = 0x002c,
292
    TICK = 0x0027,
293
    QUOTE = 0x0022,
294
    SPACE = 0x0020
295
};
296
297
/**
298
 * Utility for parsing a localization string and returning a StringLocalizationInfo*.
299
 */
300
class LocDataParser {
301
    UChar* data;
302
    const UChar* e;
303
    UChar* p;
304
    UChar ch;
305
    UParseError& pe;
306
    UErrorCode& ec;
307
    
308
public:
309
    LocDataParser(UParseError& parseError, UErrorCode& status) 
310
0
        : data(NULL), e(NULL), p(NULL), ch(0xffff), pe(parseError), ec(status) {}
311
0
    ~LocDataParser() {}
312
    
313
    /*
314
    * On a successful parse, return a StringLocalizationInfo*, otherwise delete locData, set perror and status,
315
    * and return NULL.  The StringLocalizationInfo will adopt locData if it is created.
316
    */
317
    StringLocalizationInfo* parse(UChar* data, int32_t len);
318
    
319
private:
320
    
321
0
    inline void inc(void) {
322
0
        ++p;
323
0
        ch = 0xffff;
324
0
    }
325
0
    inline UBool checkInc(UChar c) {
326
0
        if (p < e && (ch == c || *p == c)) {
327
0
            inc();
328
0
            return TRUE;
329
0
        }
330
0
        return FALSE;
331
0
    }
332
0
    inline UBool check(UChar c) {
333
0
        return p < e && (ch == c || *p == c);
334
0
    }
335
0
    inline void skipWhitespace(void) {
336
0
        while (p < e && PatternProps::isWhiteSpace(ch != 0xffff ? ch : *p)) {
337
0
            inc();
338
0
        }
339
0
    }
340
0
    inline UBool inList(UChar c, const UChar* list) const {
341
0
        if (*list == SPACE && PatternProps::isWhiteSpace(c)) {
342
0
            return TRUE;
343
0
        }
344
0
        while (*list && *list != c) {
345
0
            ++list;
346
0
        }
347
0
        return *list == c;
348
0
    }
349
    void parseError(const char* msg);
350
    
351
    StringLocalizationInfo* doParse(void);
352
        
353
    UChar** nextArray(int32_t& requiredLength);
354
    UChar*  nextString(void);
355
};
356
357
#ifdef RBNF_DEBUG
358
#define ERROR(msg) parseError(msg); return NULL;
359
#define EXPLANATION_ARG explanationArg
360
#else
361
0
#define ERROR(msg) parseError(NULL); return NULL;
362
#define EXPLANATION_ARG
363
#endif
364
        
365
366
static const UChar DQUOTE_STOPLIST[] = { 
367
    QUOTE, 0
368
};
369
370
static const UChar SQUOTE_STOPLIST[] = { 
371
    TICK, 0
372
};
373
374
static const UChar NOQUOTE_STOPLIST[] = { 
375
    SPACE, COMMA, CLOSE_ANGLE, OPEN_ANGLE, TICK, QUOTE, 0
376
};
377
378
static void
379
0
DeleteFn(void* p) {
380
0
  uprv_free(p);
381
0
}
382
383
StringLocalizationInfo*
384
0
LocDataParser::parse(UChar* _data, int32_t len) {
385
0
    if (U_FAILURE(ec)) {
386
0
        if (_data) uprv_free(_data);
387
0
        return NULL;
388
0
    }
389
0
390
0
    pe.line = 0;
391
0
    pe.offset = -1;
392
0
    pe.postContext[0] = 0;
393
0
    pe.preContext[0] = 0;
394
0
395
0
    if (_data == NULL) {
396
0
        ec = U_ILLEGAL_ARGUMENT_ERROR;
397
0
        return NULL;
398
0
    }
399
0
400
0
    if (len <= 0) {
401
0
        ec = U_ILLEGAL_ARGUMENT_ERROR;
402
0
        uprv_free(_data);
403
0
        return NULL;
404
0
    }
405
0
406
0
    data = _data;
407
0
    e = data + len;
408
0
    p = _data;
409
0
    ch = 0xffff;
410
0
411
0
    return doParse();
412
0
}
413
414
415
StringLocalizationInfo*
416
0
LocDataParser::doParse(void) {
417
0
    skipWhitespace();
418
0
    if (!checkInc(OPEN_ANGLE)) {
419
0
        ERROR("Missing open angle");
420
0
    } else {
421
0
        VArray array(DeleteFn);
422
0
        UBool mightHaveNext = TRUE;
423
0
        int32_t requiredLength = -1;
424
0
        while (mightHaveNext) {
425
0
            mightHaveNext = FALSE;
426
0
            UChar** elem = nextArray(requiredLength);
427
0
            skipWhitespace();
428
0
            UBool haveComma = check(COMMA);
429
0
            if (elem) {
430
0
                array.add(elem, ec);
431
0
                if (haveComma) {
432
0
                    inc();
433
0
                    mightHaveNext = TRUE;
434
0
                }
435
0
            } else if (haveComma) {
436
0
                ERROR("Unexpected character");
437
0
            }
438
0
        }
439
0
440
0
        skipWhitespace();
441
0
        if (!checkInc(CLOSE_ANGLE)) {
442
0
            if (check(OPEN_ANGLE)) {
443
0
                ERROR("Missing comma in outer array");
444
0
            } else {
445
0
                ERROR("Missing close angle bracket in outer array");
446
0
            }
447
0
        }
448
0
449
0
        skipWhitespace();
450
0
        if (p != e) {
451
0
            ERROR("Extra text after close of localization data");
452
0
        }
453
0
454
0
        array.add(NULL, ec);
455
0
        if (U_SUCCESS(ec)) {
456
0
            int32_t numLocs = array.length() - 2; // subtract first, NULL
457
0
            UChar*** result = (UChar***)array.release();
458
0
            
459
0
            return new StringLocalizationInfo(data, result, requiredLength-2, numLocs); // subtract first, NULL
460
0
        }
461
0
    }
462
0
  
463
0
    ERROR("Unknown error");
464
0
}
465
466
UChar**
467
0
LocDataParser::nextArray(int32_t& requiredLength) {
468
0
    if (U_FAILURE(ec)) {
469
0
        return NULL;
470
0
    }
471
0
    
472
0
    skipWhitespace();
473
0
    if (!checkInc(OPEN_ANGLE)) {
474
0
        ERROR("Missing open angle");
475
0
    }
476
0
477
0
    VArray array;
478
0
    UBool mightHaveNext = TRUE;
479
0
    while (mightHaveNext) {
480
0
        mightHaveNext = FALSE;
481
0
        UChar* elem = nextString();
482
0
        skipWhitespace();
483
0
        UBool haveComma = check(COMMA);
484
0
        if (elem) {
485
0
            array.add(elem, ec);
486
0
            if (haveComma) {
487
0
                inc();
488
0
                mightHaveNext = TRUE;
489
0
            }
490
0
        } else if (haveComma) {
491
0
            ERROR("Unexpected comma");
492
0
        }
493
0
    }
494
0
    skipWhitespace();
495
0
    if (!checkInc(CLOSE_ANGLE)) {
496
0
        if (check(OPEN_ANGLE)) {
497
0
            ERROR("Missing close angle bracket in inner array");
498
0
        } else {
499
0
            ERROR("Missing comma in inner array");
500
0
        }
501
0
    }
502
0
503
0
    array.add(NULL, ec);
504
0
    if (U_SUCCESS(ec)) {
505
0
        if (requiredLength == -1) {
506
0
            requiredLength = array.length() + 1;
507
0
        } else if (array.length() != requiredLength) {
508
0
            ec = U_ILLEGAL_ARGUMENT_ERROR;
509
0
            ERROR("Array not of required length");
510
0
        }
511
0
        
512
0
        return (UChar**)array.release();
513
0
    }
514
0
    ERROR("Unknown Error");
515
0
}
516
517
UChar*
518
0
LocDataParser::nextString() {
519
0
    UChar* result = NULL;
520
0
    
521
0
    skipWhitespace();
522
0
    if (p < e) {
523
0
        const UChar* terminators;
524
0
        UChar c = *p;
525
0
        UBool haveQuote = c == QUOTE || c == TICK;
526
0
        if (haveQuote) {
527
0
            inc();
528
0
            terminators = c == QUOTE ? DQUOTE_STOPLIST : SQUOTE_STOPLIST;
529
0
        } else {
530
0
            terminators = NOQUOTE_STOPLIST;
531
0
        }
532
0
        UChar* start = p;
533
0
        while (p < e && !inList(*p, terminators)) ++p;
534
0
        if (p == e) {
535
0
            ERROR("Unexpected end of data");
536
0
        }
537
0
        
538
0
        UChar x = *p;
539
0
        if (p > start) {
540
0
            ch = x;
541
0
            *p = 0x0; // terminate by writing to data
542
0
            result = start; // just point into data
543
0
        }
544
0
        if (haveQuote) {
545
0
            if (x != c) {
546
0
                ERROR("Missing matching quote");
547
0
            } else if (p == start) {
548
0
                ERROR("Empty string");
549
0
            }
550
0
            inc();
551
0
        } else if (x == OPEN_ANGLE || x == TICK || x == QUOTE) {
552
0
            ERROR("Unexpected character in string");
553
0
        }
554
0
    }
555
0
556
0
    // ok for there to be no next string
557
0
    return result;
558
0
}
559
560
void LocDataParser::parseError(const char* EXPLANATION_ARG)
561
0
{
562
0
    if (!data) {
563
0
        return;
564
0
    }
565
0
566
0
    const UChar* start = p - U_PARSE_CONTEXT_LEN - 1;
567
0
    if (start < data) {
568
0
        start = data;
569
0
    }
570
0
    for (UChar* x = p; --x >= start;) {
571
0
        if (!*x) {
572
0
            start = x+1;
573
0
            break;
574
0
        }
575
0
    }
576
0
    const UChar* limit = p + U_PARSE_CONTEXT_LEN - 1;
577
0
    if (limit > e) {
578
0
        limit = e;
579
0
    }
580
0
    u_strncpy(pe.preContext, start, (int32_t)(p-start));
581
0
    pe.preContext[p-start] = 0;
582
0
    u_strncpy(pe.postContext, p, (int32_t)(limit-p));
583
0
    pe.postContext[limit-p] = 0;
584
0
    pe.offset = (int32_t)(p - data);
585
0
    
586
#ifdef RBNF_DEBUG
587
    fprintf(stderr, "%s at or near character %ld: ", EXPLANATION_ARG, p-data);
588
589
    UnicodeString msg;
590
    msg.append(start, p - start);
591
    msg.append((UChar)0x002f); /* SOLIDUS/SLASH */
592
    msg.append(p, limit-p);
593
    msg.append(UNICODE_STRING_SIMPLE("'"));
594
    
595
    char buf[128];
596
    int32_t len = msg.extract(0, msg.length(), buf, 128);
597
    if (len >= 128) {
598
        buf[127] = 0;
599
    } else {
600
        buf[len] = 0;
601
    }
602
    fprintf(stderr, "%s\n", buf);
603
    fflush(stderr);
604
#endif
605
    
606
0
    uprv_free(data);
607
0
    data = NULL;
608
0
    p = NULL;
609
0
    e = NULL;
610
0
    
611
0
    if (U_SUCCESS(ec)) {
612
0
        ec = U_PARSE_ERROR;
613
0
    }
614
0
}
615
616
//UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)
617
618
StringLocalizationInfo* 
619
0
StringLocalizationInfo::create(const UnicodeString& info, UParseError& perror, UErrorCode& status) {
620
0
    if (U_FAILURE(status)) {
621
0
        return NULL;
622
0
    }
623
0
    
624
0
    int32_t len = info.length();
625
0
    if (len == 0) {
626
0
        return NULL; // no error;
627
0
    }
628
0
    
629
0
    UChar* p = (UChar*)uprv_malloc(len * sizeof(UChar));
630
0
    if (!p) {
631
0
        status = U_MEMORY_ALLOCATION_ERROR;
632
0
        return NULL;
633
0
    }
634
0
    info.extract(p, len, status);
635
0
    if (!U_FAILURE(status)) {
636
0
        status = U_ZERO_ERROR; // clear warning about non-termination
637
0
    }
638
0
    
639
0
    LocDataParser parser(perror, status);
640
0
    return parser.parse(p, len);
641
0
}
642
643
0
StringLocalizationInfo::~StringLocalizationInfo() {
644
0
    for (UChar*** p = (UChar***)data; *p; ++p) {
645
0
        // remaining data is simply pointer into our unicode string data.
646
0
        if (*p) uprv_free(*p);
647
0
    }
648
0
    if (data) uprv_free(data);
649
0
    if (info) uprv_free(info);
650
0
}
651
652
653
const UChar*
654
0
StringLocalizationInfo::getRuleSetName(int32_t index) const {
655
0
    if (index >= 0 && index < getNumberOfRuleSets()) {
656
0
        return data[0][index];
657
0
    }
658
0
    return NULL;
659
0
}
660
661
const UChar*
662
0
StringLocalizationInfo::getLocaleName(int32_t index) const {
663
0
    if (index >= 0 && index < getNumberOfDisplayLocales()) {
664
0
        return data[index+1][0];
665
0
    }
666
0
    return NULL;
667
0
}
668
669
const UChar*
670
0
StringLocalizationInfo::getDisplayName(int32_t localeIndex, int32_t ruleIndex) const {
671
0
    if (localeIndex >= 0 && localeIndex < getNumberOfDisplayLocales() &&
672
0
        ruleIndex >= 0 && ruleIndex < getNumberOfRuleSets()) {
673
0
        return data[localeIndex+1][ruleIndex+1];
674
0
    }
675
0
    return NULL;
676
0
}
677
678
// ----------
679
680
RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, 
681
                                             const UnicodeString& locs,
682
                                             const Locale& alocale, UParseError& perror, UErrorCode& status)
683
  : ruleSets(NULL)
684
  , ruleSetDescriptions(NULL)
685
  , numRuleSets(0)
686
  , defaultRuleSet(NULL)
687
  , locale(alocale)
688
  , collator(NULL)
689
  , decimalFormatSymbols(NULL)
690
  , defaultInfinityRule(NULL)
691
  , defaultNaNRule(NULL)
692
  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
693
  , lenient(FALSE)
694
  , lenientParseRules(NULL)
695
  , localizations(NULL)
696
  , capitalizationInfoSet(FALSE)
697
  , capitalizationForUIListMenu(FALSE)
698
  , capitalizationForStandAlone(FALSE)
699
  , capitalizationBrkIter(NULL)
700
0
{
701
0
  LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status);
702
0
  init(description, locinfo, perror, status);
703
0
}
704
705
RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, 
706
                                             const UnicodeString& locs,
707
                                             UParseError& perror, UErrorCode& status)
708
  : ruleSets(NULL)
709
  , ruleSetDescriptions(NULL)
710
  , numRuleSets(0)
711
  , defaultRuleSet(NULL)
712
  , locale(Locale::getDefault())
713
  , collator(NULL)
714
  , decimalFormatSymbols(NULL)
715
  , defaultInfinityRule(NULL)
716
  , defaultNaNRule(NULL)
717
  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
718
  , lenient(FALSE)
719
  , lenientParseRules(NULL)
720
  , localizations(NULL)
721
  , capitalizationInfoSet(FALSE)
722
  , capitalizationForUIListMenu(FALSE)
723
  , capitalizationForStandAlone(FALSE)
724
  , capitalizationBrkIter(NULL)
725
0
{
726
0
  LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status);
727
0
  init(description, locinfo, perror, status);
728
0
}
729
730
RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, 
731
                                             LocalizationInfo* info,
732
                                             const Locale& alocale, UParseError& perror, UErrorCode& status)
733
  : ruleSets(NULL)
734
  , ruleSetDescriptions(NULL)
735
  , numRuleSets(0)
736
  , defaultRuleSet(NULL)
737
  , locale(alocale)
738
  , collator(NULL)
739
  , decimalFormatSymbols(NULL)
740
  , defaultInfinityRule(NULL)
741
  , defaultNaNRule(NULL)
742
  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
743
  , lenient(FALSE)
744
  , lenientParseRules(NULL)
745
  , localizations(NULL)
746
  , capitalizationInfoSet(FALSE)
747
  , capitalizationForUIListMenu(FALSE)
748
  , capitalizationForStandAlone(FALSE)
749
  , capitalizationBrkIter(NULL)
750
0
{
751
0
  init(description, info, perror, status);
752
0
}
753
754
RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, 
755
                         UParseError& perror, 
756
                         UErrorCode& status) 
757
  : ruleSets(NULL)
758
  , ruleSetDescriptions(NULL)
759
  , numRuleSets(0)
760
  , defaultRuleSet(NULL)
761
  , locale(Locale::getDefault())
762
  , collator(NULL)
763
  , decimalFormatSymbols(NULL)
764
  , defaultInfinityRule(NULL)
765
  , defaultNaNRule(NULL)
766
  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
767
  , lenient(FALSE)
768
  , lenientParseRules(NULL)
769
  , localizations(NULL)
770
  , capitalizationInfoSet(FALSE)
771
  , capitalizationForUIListMenu(FALSE)
772
  , capitalizationForStandAlone(FALSE)
773
  , capitalizationBrkIter(NULL)
774
0
{
775
0
    init(description, NULL, perror, status);
776
0
}
777
778
RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, 
779
                         const Locale& aLocale,
780
                         UParseError& perror, 
781
                         UErrorCode& status) 
782
  : ruleSets(NULL)
783
  , ruleSetDescriptions(NULL)
784
  , numRuleSets(0)
785
  , defaultRuleSet(NULL)
786
  , locale(aLocale)
787
  , collator(NULL)
788
  , decimalFormatSymbols(NULL)
789
  , defaultInfinityRule(NULL)
790
  , defaultNaNRule(NULL)
791
  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
792
  , lenient(FALSE)
793
  , lenientParseRules(NULL)
794
  , localizations(NULL)
795
  , capitalizationInfoSet(FALSE)
796
  , capitalizationForUIListMenu(FALSE)
797
  , capitalizationForStandAlone(FALSE)
798
  , capitalizationBrkIter(NULL)
799
0
{
800
0
    init(description, NULL, perror, status);
801
0
}
802
803
RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status)
804
  : ruleSets(NULL)
805
  , ruleSetDescriptions(NULL)
806
  , numRuleSets(0)
807
  , defaultRuleSet(NULL)
808
  , locale(alocale)
809
  , collator(NULL)
810
  , decimalFormatSymbols(NULL)
811
  , defaultInfinityRule(NULL)
812
  , defaultNaNRule(NULL)
813
  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
814
  , lenient(FALSE)
815
  , lenientParseRules(NULL)
816
  , localizations(NULL)
817
  , capitalizationInfoSet(FALSE)
818
  , capitalizationForUIListMenu(FALSE)
819
  , capitalizationForStandAlone(FALSE)
820
  , capitalizationBrkIter(NULL)
821
0
{
822
0
    if (U_FAILURE(status)) {
823
0
        return;
824
0
    }
825
0
826
0
    const char* rules_tag = "RBNFRules";
827
0
    const char* fmt_tag = "";
828
0
    switch (tag) {
829
0
    case URBNF_SPELLOUT: fmt_tag = "SpelloutRules"; break;
830
0
    case URBNF_ORDINAL: fmt_tag = "OrdinalRules"; break;
831
0
    case URBNF_DURATION: fmt_tag = "DurationRules"; break;
832
0
    case URBNF_NUMBERING_SYSTEM: fmt_tag = "NumberingSystemRules"; break;
833
0
    default: status = U_ILLEGAL_ARGUMENT_ERROR; return;
834
0
    }
835
0
836
0
    // TODO: read localization info from resource
837
0
    LocalizationInfo* locinfo = NULL;
838
0
839
0
    UResourceBundle* nfrb = ures_open(U_ICUDATA_RBNF, locale.getName(), &status);
840
0
    if (U_SUCCESS(status)) {
841
0
        setLocaleIDs(ures_getLocaleByType(nfrb, ULOC_VALID_LOCALE, &status),
842
0
                     ures_getLocaleByType(nfrb, ULOC_ACTUAL_LOCALE, &status));
843
0
844
0
        UResourceBundle* rbnfRules = ures_getByKeyWithFallback(nfrb, rules_tag, NULL, &status);
845
0
        if (U_FAILURE(status)) {
846
0
            ures_close(nfrb);
847
0
        }
848
0
        UResourceBundle* ruleSets = ures_getByKeyWithFallback(rbnfRules, fmt_tag, NULL, &status);
849
0
        if (U_FAILURE(status)) {
850
0
            ures_close(rbnfRules);
851
0
            ures_close(nfrb);
852
0
            return;
853
0
        }
854
0
855
0
        UnicodeString desc;
856
0
        while (ures_hasNext(ruleSets)) {
857
0
           desc.append(ures_getNextUnicodeString(ruleSets,NULL,&status));
858
0
        }
859
0
        UParseError perror;
860
0
861
0
        init(desc, locinfo, perror, status);
862
0
863
0
        ures_close(ruleSets);
864
0
        ures_close(rbnfRules);
865
0
    }
866
0
    ures_close(nfrb);
867
0
}
868
869
RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs)
870
  : NumberFormat(rhs)
871
  , ruleSets(NULL)
872
  , ruleSetDescriptions(NULL)
873
  , numRuleSets(0)
874
  , defaultRuleSet(NULL)
875
  , locale(rhs.locale)
876
  , collator(NULL)
877
  , decimalFormatSymbols(NULL)
878
  , defaultInfinityRule(NULL)
879
  , defaultNaNRule(NULL)
880
  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
881
  , lenient(FALSE)
882
  , lenientParseRules(NULL)
883
  , localizations(NULL)
884
  , capitalizationInfoSet(FALSE)
885
  , capitalizationForUIListMenu(FALSE)
886
  , capitalizationForStandAlone(FALSE)
887
  , capitalizationBrkIter(NULL)
888
0
{
889
0
    this->operator=(rhs);
890
0
}
891
892
// --------
893
894
RuleBasedNumberFormat&
895
RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat& rhs)
896
0
{
897
0
    if (this == &rhs) {
898
0
        return *this;
899
0
    }
900
0
    NumberFormat::operator=(rhs);
901
0
    UErrorCode status = U_ZERO_ERROR;
902
0
    dispose();
903
0
    locale = rhs.locale;
904
0
    lenient = rhs.lenient;
905
0
906
0
    UParseError perror;
907
0
    setDecimalFormatSymbols(*rhs.getDecimalFormatSymbols());
908
0
    init(rhs.originalDescription, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status);
909
0
    setDefaultRuleSet(rhs.getDefaultRuleSetName(), status);
910
0
    setRoundingMode(rhs.getRoundingMode());
911
0
912
0
    capitalizationInfoSet = rhs.capitalizationInfoSet;
913
0
    capitalizationForUIListMenu = rhs.capitalizationForUIListMenu;
914
0
    capitalizationForStandAlone = rhs.capitalizationForStandAlone;
915
0
#if !UCONFIG_NO_BREAK_ITERATION
916
0
    capitalizationBrkIter = (rhs.capitalizationBrkIter!=NULL)? rhs.capitalizationBrkIter->clone(): NULL;
917
0
#endif
918
0
919
0
    return *this;
920
0
}
921
922
RuleBasedNumberFormat::~RuleBasedNumberFormat()
923
0
{
924
0
    dispose();
925
0
}
926
927
Format*
928
RuleBasedNumberFormat::clone(void) const
929
0
{
930
0
    return new RuleBasedNumberFormat(*this);
931
0
}
932
933
UBool
934
RuleBasedNumberFormat::operator==(const Format& other) const
935
0
{
936
0
    if (this == &other) {
937
0
        return TRUE;
938
0
    }
939
0
940
0
    if (typeid(*this) == typeid(other)) {
941
0
        const RuleBasedNumberFormat& rhs = (const RuleBasedNumberFormat&)other;
942
0
        // test for capitalization info equality is adequately handled
943
0
        // by the NumberFormat test for fCapitalizationContext equality;
944
0
        // the info here is just derived from that.
945
0
        if (locale == rhs.locale &&
946
0
            lenient == rhs.lenient &&
947
0
            (localizations == NULL 
948
0
                ? rhs.localizations == NULL 
949
0
                : (rhs.localizations == NULL 
950
0
                    ? FALSE
951
0
                    : *localizations == rhs.localizations))) {
952
0
953
0
            NFRuleSet** p = ruleSets;
954
0
            NFRuleSet** q = rhs.ruleSets;
955
0
            if (p == NULL) {
956
0
                return q == NULL;
957
0
            } else if (q == NULL) {
958
0
                return FALSE;
959
0
            }
960
0
            while (*p && *q && (**p == **q)) {
961
0
                ++p;
962
0
                ++q;
963
0
            }
964
0
            return *q == NULL && *p == NULL;
965
0
        }
966
0
    }
967
0
968
0
    return FALSE;
969
0
}
970
971
UnicodeString
972
RuleBasedNumberFormat::getRules() const
973
0
{
974
0
    UnicodeString result;
975
0
    if (ruleSets != NULL) {
976
0
        for (NFRuleSet** p = ruleSets; *p; ++p) {
977
0
            (*p)->appendRules(result);
978
0
        }
979
0
    }
980
0
    return result;
981
0
}
982
983
UnicodeString
984
RuleBasedNumberFormat::getRuleSetName(int32_t index) const
985
0
{
986
0
    if (localizations) {
987
0
        UnicodeString string(TRUE, localizations->getRuleSetName(index), (int32_t)-1);
988
0
        return string;
989
0
    }
990
0
    else if (ruleSets) {
991
0
        UnicodeString result;
992
0
        for (NFRuleSet** p = ruleSets; *p; ++p) {
993
0
            NFRuleSet* rs = *p;
994
0
            if (rs->isPublic()) {
995
0
                if (--index == -1) {
996
0
                    rs->getName(result);
997
0
                    return result;
998
0
                }
999
0
            }
1000
0
        }
1001
0
    }
1002
0
    UnicodeString empty;
1003
0
    return empty;
1004
0
}
1005
1006
int32_t
1007
RuleBasedNumberFormat::getNumberOfRuleSetNames() const
1008
0
{
1009
0
    int32_t result = 0;
1010
0
    if (localizations) {
1011
0
        result = localizations->getNumberOfRuleSets();
1012
0
    }
1013
0
    else if (ruleSets) {
1014
0
        for (NFRuleSet** p = ruleSets; *p; ++p) {
1015
0
            if ((**p).isPublic()) {
1016
0
                ++result;
1017
0
            }
1018
0
        }
1019
0
    }
1020
0
    return result;
1021
0
}
1022
1023
int32_t 
1024
0
RuleBasedNumberFormat::getNumberOfRuleSetDisplayNameLocales(void) const {
1025
0
    if (localizations) {
1026
0
        return localizations->getNumberOfDisplayLocales();
1027
0
    }
1028
0
    return 0;
1029
0
}
1030
1031
Locale 
1032
0
RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index, UErrorCode& status) const {
1033
0
    if (U_FAILURE(status)) {
1034
0
        return Locale("");
1035
0
    }
1036
0
    if (localizations && index >= 0 && index < localizations->getNumberOfDisplayLocales()) {
1037
0
        UnicodeString name(TRUE, localizations->getLocaleName(index), -1);
1038
0
        char buffer[64];
1039
0
        int32_t cap = name.length() + 1;
1040
0
        char* bp = buffer;
1041
0
        if (cap > 64) {
1042
0
            bp = (char *)uprv_malloc(cap);
1043
0
            if (bp == NULL) {
1044
0
                status = U_MEMORY_ALLOCATION_ERROR;
1045
0
                return Locale("");
1046
0
            }
1047
0
        }
1048
0
        name.extract(0, name.length(), bp, cap, UnicodeString::kInvariant);
1049
0
        Locale retLocale(bp);
1050
0
        if (bp != buffer) {
1051
0
            uprv_free(bp);
1052
0
        }
1053
0
        return retLocale;
1054
0
    }
1055
0
    status = U_ILLEGAL_ARGUMENT_ERROR;
1056
0
    Locale retLocale;
1057
0
    return retLocale;
1058
0
}
1059
1060
UnicodeString 
1061
0
RuleBasedNumberFormat::getRuleSetDisplayName(int32_t index, const Locale& localeParam) {
1062
0
    if (localizations && index >= 0 && index < localizations->getNumberOfRuleSets()) {
1063
0
        UnicodeString localeName(localeParam.getBaseName(), -1, UnicodeString::kInvariant); 
1064
0
        int32_t len = localeName.length();
1065
0
        UChar* localeStr = localeName.getBuffer(len + 1);
1066
0
        while (len >= 0) {
1067
0
            localeStr[len] = 0;
1068
0
            int32_t ix = localizations->indexForLocale(localeStr);
1069
0
            if (ix >= 0) {
1070
0
                UnicodeString name(TRUE, localizations->getDisplayName(ix, index), -1);
1071
0
                return name;
1072
0
            }
1073
0
            
1074
0
            // trim trailing portion, skipping over ommitted sections
1075
0
            do { --len;} while (len > 0 && localeStr[len] != 0x005f); // underscore
1076
0
            while (len > 0 && localeStr[len-1] == 0x005F) --len;
1077
0
        }
1078
0
        UnicodeString name(TRUE, localizations->getRuleSetName(index), -1);
1079
0
        return name;
1080
0
    }
1081
0
    UnicodeString bogus;
1082
0
    bogus.setToBogus();
1083
0
    return bogus;
1084
0
}
1085
1086
UnicodeString 
1087
0
RuleBasedNumberFormat::getRuleSetDisplayName(const UnicodeString& ruleSetName, const Locale& localeParam) {
1088
0
    if (localizations) {
1089
0
        UnicodeString rsn(ruleSetName);
1090
0
        int32_t ix = localizations->indexForRuleSet(rsn.getTerminatedBuffer());
1091
0
        return getRuleSetDisplayName(ix, localeParam);
1092
0
    }
1093
0
    UnicodeString bogus;
1094
0
    bogus.setToBogus();
1095
0
    return bogus;
1096
0
}
1097
1098
NFRuleSet*
1099
RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status) const
1100
0
{
1101
0
    if (U_SUCCESS(status) && ruleSets) {
1102
0
        for (NFRuleSet** p = ruleSets; *p; ++p) {
1103
0
            NFRuleSet* rs = *p;
1104
0
            if (rs->isNamed(name)) {
1105
0
                return rs;
1106
0
            }
1107
0
        }
1108
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1109
0
    }
1110
0
    return NULL;
1111
0
}
1112
1113
UnicodeString&
1114
RuleBasedNumberFormat::format(const DecimalQuantity &number,
1115
                      UnicodeString &appendTo,
1116
                      FieldPositionIterator *posIter,
1117
0
                      UErrorCode &status) const {
1118
0
    if (U_FAILURE(status)) {
1119
0
        return appendTo;
1120
0
    }
1121
0
    DecimalQuantity copy(number);
1122
0
    if (copy.fitsInLong()) {
1123
0
        format(number.toLong(), appendTo, posIter, status);
1124
0
    }
1125
0
    else {
1126
0
        copy.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN, status);
1127
0
        if (copy.fitsInLong()) {
1128
0
            format(number.toDouble(), appendTo, posIter, status);
1129
0
        }
1130
0
        else {
1131
0
            // We're outside of our normal range that this framework can handle.
1132
0
            // The DecimalFormat will provide more accurate results.
1133
0
1134
0
            // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
1135
0
            NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status);
1136
0
            Formattable f;
1137
0
            f.adoptDecimalQuantity(new DecimalQuantity(number));
1138
0
            decimalFormat->format(f, appendTo, posIter, status);
1139
0
            delete decimalFormat;
1140
0
        }
1141
0
    }
1142
0
    return appendTo;
1143
0
}
1144
1145
1146
UnicodeString&
1147
RuleBasedNumberFormat::format(const DecimalQuantity &number,
1148
                     UnicodeString& appendTo,
1149
                     FieldPosition& pos,
1150
0
                     UErrorCode &status) const {
1151
0
    if (U_FAILURE(status)) {
1152
0
        return appendTo;
1153
0
    }
1154
0
    DecimalQuantity copy(number);
1155
0
    if (copy.fitsInLong()) {
1156
0
        format(number.toLong(), appendTo, pos, status);
1157
0
    }
1158
0
    else {
1159
0
        copy.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN, status);
1160
0
        if (copy.fitsInLong()) {
1161
0
            format(number.toDouble(), appendTo, pos, status);
1162
0
        }
1163
0
        else {
1164
0
            // We're outside of our normal range that this framework can handle.
1165
0
            // The DecimalFormat will provide more accurate results.
1166
0
1167
0
            // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
1168
0
            NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status);
1169
0
            Formattable f;
1170
0
            f.adoptDecimalQuantity(new DecimalQuantity(number));
1171
0
            decimalFormat->format(f, appendTo, pos, status);
1172
0
            delete decimalFormat;
1173
0
        }
1174
0
    }
1175
0
    return appendTo;
1176
0
}
1177
1178
UnicodeString&
1179
RuleBasedNumberFormat::format(int32_t number,
1180
                              UnicodeString& toAppendTo,
1181
                              FieldPosition& pos) const
1182
0
{
1183
0
    return format((int64_t)number, toAppendTo, pos);
1184
0
}
1185
1186
1187
UnicodeString&
1188
RuleBasedNumberFormat::format(int64_t number,
1189
                              UnicodeString& toAppendTo,
1190
                              FieldPosition& /* pos */) const
1191
0
{
1192
0
    if (defaultRuleSet) {
1193
0
        UErrorCode status = U_ZERO_ERROR;
1194
0
        format(number, defaultRuleSet, toAppendTo, status);
1195
0
    }
1196
0
    return toAppendTo;
1197
0
}
1198
1199
1200
UnicodeString&
1201
RuleBasedNumberFormat::format(double number,
1202
                              UnicodeString& toAppendTo,
1203
                              FieldPosition& /* pos */) const
1204
0
{
1205
0
    UErrorCode status = U_ZERO_ERROR;
1206
0
    if (defaultRuleSet) {
1207
0
        format(number, *defaultRuleSet, toAppendTo, status);
1208
0
    }
1209
0
    return toAppendTo;
1210
0
}
1211
1212
1213
UnicodeString&
1214
RuleBasedNumberFormat::format(int32_t number,
1215
                              const UnicodeString& ruleSetName,
1216
                              UnicodeString& toAppendTo,
1217
                              FieldPosition& pos,
1218
                              UErrorCode& status) const
1219
0
{
1220
0
    return format((int64_t)number, ruleSetName, toAppendTo, pos, status);
1221
0
}
1222
1223
1224
UnicodeString&
1225
RuleBasedNumberFormat::format(int64_t number,
1226
                              const UnicodeString& ruleSetName,
1227
                              UnicodeString& toAppendTo,
1228
                              FieldPosition& /* pos */,
1229
                              UErrorCode& status) const
1230
0
{
1231
0
    if (U_SUCCESS(status)) {
1232
0
        if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) {
1233
0
            // throw new IllegalArgumentException("Can't use internal rule set");
1234
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
1235
0
        } else {
1236
0
            NFRuleSet *rs = findRuleSet(ruleSetName, status);
1237
0
            if (rs) {
1238
0
                format(number, rs, toAppendTo, status);
1239
0
            }
1240
0
        }
1241
0
    }
1242
0
    return toAppendTo;
1243
0
}
1244
1245
1246
UnicodeString&
1247
RuleBasedNumberFormat::format(double number,
1248
                              const UnicodeString& ruleSetName,
1249
                              UnicodeString& toAppendTo,
1250
                              FieldPosition& /* pos */,
1251
                              UErrorCode& status) const
1252
0
{
1253
0
    if (U_SUCCESS(status)) {
1254
0
        if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) {
1255
0
            // throw new IllegalArgumentException("Can't use internal rule set");
1256
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
1257
0
        } else {
1258
0
            NFRuleSet *rs = findRuleSet(ruleSetName, status);
1259
0
            if (rs) {
1260
0
                format(number, *rs, toAppendTo, status);
1261
0
            }
1262
0
        }
1263
0
    }
1264
0
    return toAppendTo;
1265
0
}
1266
1267
void
1268
RuleBasedNumberFormat::format(double number,
1269
                              NFRuleSet& rs,
1270
                              UnicodeString& toAppendTo,
1271
                              UErrorCode& status) const
1272
0
{
1273
0
    int32_t startPos = toAppendTo.length();
1274
0
    if (getRoundingMode() != DecimalFormat::ERoundingMode::kRoundUnnecessary && !uprv_isNaN(number) && !uprv_isInfinite(number)) {
1275
0
        DecimalQuantity digitList;
1276
0
        digitList.setToDouble(number);
1277
0
        digitList.roundToMagnitude(
1278
0
                -getMaximumFractionDigits(),
1279
0
                static_cast<UNumberFormatRoundingMode>(getRoundingMode()),
1280
0
                status);
1281
0
        number = digitList.toDouble();
1282
0
    }
1283
0
    rs.format(number, toAppendTo, toAppendTo.length(), 0, status);
1284
0
    adjustForCapitalizationContext(startPos, toAppendTo, status);
1285
0
}
1286
1287
/**
1288
 * Bottleneck through which all the public format() methods
1289
 * that take a long pass. By the time we get here, we know
1290
 * which rule set we're using to do the formatting.
1291
 * @param number The number to format
1292
 * @param ruleSet The rule set to use to format the number
1293
 * @return The text that resulted from formatting the number
1294
 */
1295
UnicodeString&
1296
RuleBasedNumberFormat::format(int64_t number, NFRuleSet *ruleSet, UnicodeString& toAppendTo, UErrorCode& status) const
1297
0
{
1298
0
    // all API format() routines that take a double vector through
1299
0
    // here.  We have these two identical functions-- one taking a
1300
0
    // double and one taking a long-- the couple digits of precision
1301
0
    // that long has but double doesn't (both types are 8 bytes long,
1302
0
    // but double has to borrow some of the mantissa bits to hold
1303
0
    // the exponent).
1304
0
    // Create an empty string buffer where the result will
1305
0
    // be built, and pass it to the rule set (along with an insertion
1306
0
    // position of 0 and the number being formatted) to the rule set
1307
0
    // for formatting
1308
0
1309
0
    if (U_SUCCESS(status)) {
1310
0
        if (number == U_INT64_MIN) {
1311
0
            // We can't handle this value right now. Provide an accurate default value.
1312
0
1313
0
            // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
1314
0
            NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status);
1315
0
            Formattable f;
1316
0
            FieldPosition pos(FieldPosition::DONT_CARE);
1317
0
            DecimalQuantity *digitList = new DecimalQuantity();
1318
0
            digitList->setToLong(number);
1319
0
            f.adoptDecimalQuantity(digitList);
1320
0
            decimalFormat->format(f, toAppendTo, pos, status);
1321
0
            delete decimalFormat;
1322
0
        }
1323
0
        else {
1324
0
            int32_t startPos = toAppendTo.length();
1325
0
            ruleSet->format(number, toAppendTo, toAppendTo.length(), 0, status);
1326
0
            adjustForCapitalizationContext(startPos, toAppendTo, status);
1327
0
        }
1328
0
    }
1329
0
    return toAppendTo;
1330
0
}
1331
1332
UnicodeString&
1333
RuleBasedNumberFormat::adjustForCapitalizationContext(int32_t startPos,
1334
                                                      UnicodeString& currentResult,
1335
                                                      UErrorCode& status) const
1336
0
{
1337
0
#if !UCONFIG_NO_BREAK_ITERATION
1338
0
    UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
1339
0
    if (capitalizationContext != UDISPCTX_CAPITALIZATION_NONE && startPos == 0 && currentResult.length() > 0) {
1340
0
        // capitalize currentResult according to context
1341
0
        UChar32 ch = currentResult.char32At(0);
1342
0
        if (u_islower(ch) && U_SUCCESS(status) && capitalizationBrkIter != NULL &&
1343
0
              ( capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
1344
0
                (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) ||
1345
0
                (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) {
1346
0
            // titlecase first word of currentResult, here use sentence iterator unlike current implementations
1347
0
            // in LocaleDisplayNamesImpl::adjustForUsageAndContext and RelativeDateFormat::format
1348
0
            currentResult.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
1349
0
        }
1350
0
    }
1351
0
#endif
1352
0
    return currentResult;
1353
0
}
1354
1355
1356
void
1357
RuleBasedNumberFormat::parse(const UnicodeString& text,
1358
                             Formattable& result,
1359
                             ParsePosition& parsePosition) const
1360
0
{
1361
0
    if (!ruleSets) {
1362
0
        parsePosition.setErrorIndex(0);
1363
0
        return;
1364
0
    }
1365
0
1366
0
    UnicodeString workingText(text, parsePosition.getIndex());
1367
0
    ParsePosition workingPos(0);
1368
0
1369
0
    ParsePosition high_pp(0);
1370
0
    Formattable high_result;
1371
0
1372
0
    for (NFRuleSet** p = ruleSets; *p; ++p) {
1373
0
        NFRuleSet *rp = *p;
1374
0
        if (rp->isPublic() && rp->isParseable()) {
1375
0
            ParsePosition working_pp(0);
1376
0
            Formattable working_result;
1377
0
1378
0
            rp->parse(workingText, working_pp, kMaxDouble, 0, working_result);
1379
0
            if (working_pp.getIndex() > high_pp.getIndex()) {
1380
0
                high_pp = working_pp;
1381
0
                high_result = working_result;
1382
0
1383
0
                if (high_pp.getIndex() == workingText.length()) {
1384
0
                    break;
1385
0
                }
1386
0
            }
1387
0
        }
1388
0
    }
1389
0
1390
0
    int32_t startIndex = parsePosition.getIndex();
1391
0
    parsePosition.setIndex(startIndex + high_pp.getIndex());
1392
0
    if (high_pp.getIndex() > 0) {
1393
0
        parsePosition.setErrorIndex(-1);
1394
0
    } else {
1395
0
        int32_t errorIndex = (high_pp.getErrorIndex()>0)? high_pp.getErrorIndex(): 0;
1396
0
        parsePosition.setErrorIndex(startIndex + errorIndex);
1397
0
    }
1398
0
    result = high_result;
1399
0
    if (result.getType() == Formattable::kDouble) {
1400
0
        double d = result.getDouble();
1401
0
        if (!uprv_isNaN(d) && d == uprv_trunc(d) && INT32_MIN <= d && d <= INT32_MAX) {
1402
0
            // Note: casting a double to an int when the double is too large or small
1403
0
            //       to fit the destination is undefined behavior. The explicit range checks,
1404
0
            //       above, are required. Just casting and checking the result value is undefined.
1405
0
            result.setLong(static_cast<int32_t>(d));
1406
0
        }
1407
0
    }
1408
0
}
1409
1410
#if !UCONFIG_NO_COLLATION
1411
1412
void
1413
RuleBasedNumberFormat::setLenient(UBool enabled)
1414
0
{
1415
0
    lenient = enabled;
1416
0
    if (!enabled && collator) {
1417
0
        delete collator;
1418
0
        collator = NULL;
1419
0
    }
1420
0
}
1421
1422
#endif
1423
1424
void 
1425
0
RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString& ruleSetName, UErrorCode& status) {
1426
0
    if (U_SUCCESS(status)) {
1427
0
        if (ruleSetName.isEmpty()) {
1428
0
          if (localizations) {
1429
0
              UnicodeString name(TRUE, localizations->getRuleSetName(0), -1);
1430
0
              defaultRuleSet = findRuleSet(name, status);
1431
0
          } else {
1432
0
            initDefaultRuleSet();
1433
0
          }
1434
0
        } else if (ruleSetName.startsWith(UNICODE_STRING_SIMPLE("%%"))) {
1435
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
1436
0
        } else {
1437
0
            NFRuleSet* result = findRuleSet(ruleSetName, status);
1438
0
            if (result != NULL) {
1439
0
                defaultRuleSet = result;
1440
0
            }
1441
0
        }
1442
0
    }
1443
0
}
1444
1445
UnicodeString
1446
0
RuleBasedNumberFormat::getDefaultRuleSetName() const {
1447
0
    UnicodeString result;
1448
0
    if (defaultRuleSet && defaultRuleSet->isPublic()) {
1449
0
        defaultRuleSet->getName(result);
1450
0
    } else {
1451
0
        result.setToBogus();
1452
0
    }
1453
0
    return result;
1454
0
}
1455
1456
void 
1457
RuleBasedNumberFormat::initDefaultRuleSet()
1458
0
{
1459
0
    defaultRuleSet = NULL;
1460
0
    if (!ruleSets) {
1461
0
        return;
1462
0
    }
1463
0
1464
0
    const UnicodeString spellout(UNICODE_STRING_SIMPLE("%spellout-numbering"));
1465
0
    const UnicodeString ordinal(UNICODE_STRING_SIMPLE("%digits-ordinal"));
1466
0
    const UnicodeString duration(UNICODE_STRING_SIMPLE("%duration"));
1467
0
1468
0
    NFRuleSet**p = &ruleSets[0];
1469
0
    while (*p) {
1470
0
        if ((*p)->isNamed(spellout) || (*p)->isNamed(ordinal) || (*p)->isNamed(duration)) {
1471
0
            defaultRuleSet = *p;
1472
0
            return;
1473
0
        } else {
1474
0
            ++p;
1475
0
        }
1476
0
    }
1477
0
1478
0
    defaultRuleSet = *--p;
1479
0
    if (!defaultRuleSet->isPublic()) {
1480
0
        while (p != ruleSets) {
1481
0
            if ((*--p)->isPublic()) {
1482
0
                defaultRuleSet = *p;
1483
0
                break;
1484
0
            }
1485
0
        }
1486
0
    }
1487
0
}
1488
1489
1490
void
1491
RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* localizationInfos,
1492
                            UParseError& pErr, UErrorCode& status)
1493
0
{
1494
0
    // TODO: implement UParseError
1495
0
    uprv_memset(&pErr, 0, sizeof(UParseError));
1496
0
    // Note: this can leave ruleSets == NULL, so remaining code should check
1497
0
    if (U_FAILURE(status)) {
1498
0
        return;
1499
0
    }
1500
0
1501
0
    initializeDecimalFormatSymbols(status);
1502
0
    initializeDefaultInfinityRule(status);
1503
0
    initializeDefaultNaNRule(status);
1504
0
    if (U_FAILURE(status)) {
1505
0
        return;
1506
0
    }
1507
0
1508
0
    this->localizations = localizationInfos == NULL ? NULL : localizationInfos->ref();
1509
0
1510
0
    UnicodeString description(rules);
1511
0
    if (!description.length()) {
1512
0
        status = U_MEMORY_ALLOCATION_ERROR;
1513
0
        return;
1514
0
    }
1515
0
1516
0
    // start by stripping the trailing whitespace from all the rules
1517
0
    // (this is all the whitespace follwing each semicolon in the
1518
0
    // description).  This allows us to look for rule-set boundaries
1519
0
    // by searching for ";%" without having to worry about whitespace
1520
0
    // between the ; and the %
1521
0
    stripWhitespace(description);
1522
0
1523
0
    // check to see if there's a set of lenient-parse rules.  If there
1524
0
    // is, pull them out into our temporary holding place for them,
1525
0
    // and delete them from the description before the real desciption-
1526
0
    // parsing code sees them
1527
0
    int32_t lp = description.indexOf(gLenientParse, -1, 0);
1528
0
    if (lp != -1) {
1529
0
        // we've got to make sure we're not in the middle of a rule
1530
0
        // (where "%%lenient-parse" would actually get treated as
1531
0
        // rule text)
1532
0
        if (lp == 0 || description.charAt(lp - 1) == gSemiColon) {
1533
0
            // locate the beginning and end of the actual collation
1534
0
            // rules (there may be whitespace between the name and
1535
0
            // the first token in the description)
1536
0
            int lpEnd = description.indexOf(gSemiPercent, 2, lp);
1537
0
1538
0
            if (lpEnd == -1) {
1539
0
                lpEnd = description.length() - 1;
1540
0
            }
1541
0
            int lpStart = lp + u_strlen(gLenientParse);
1542
0
            while (PatternProps::isWhiteSpace(description.charAt(lpStart))) {
1543
0
                ++lpStart;
1544
0
            }
1545
0
1546
0
            // copy out the lenient-parse rules and delete them
1547
0
            // from the description
1548
0
            lenientParseRules = new UnicodeString();
1549
0
            /* test for NULL */
1550
0
            if (lenientParseRules == 0) {
1551
0
                status = U_MEMORY_ALLOCATION_ERROR;
1552
0
                return;
1553
0
            }
1554
0
            lenientParseRules->setTo(description, lpStart, lpEnd - lpStart);
1555
0
1556
0
            description.remove(lp, lpEnd + 1 - lp);
1557
0
        }
1558
0
    }
1559
0
1560
0
    // pre-flight parsing the description and count the number of
1561
0
    // rule sets (";%" marks the end of one rule set and the beginning
1562
0
    // of the next)
1563
0
    numRuleSets = 0;
1564
0
    for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, p)) {
1565
0
        ++numRuleSets;
1566
0
        ++p;
1567
0
    }
1568
0
    ++numRuleSets;
1569
0
1570
0
    // our rule list is an array of the appropriate size
1571
0
    ruleSets = (NFRuleSet **)uprv_malloc((numRuleSets + 1) * sizeof(NFRuleSet *));
1572
0
    /* test for NULL */
1573
0
    if (ruleSets == 0) {
1574
0
        status = U_MEMORY_ALLOCATION_ERROR;
1575
0
        return;
1576
0
    }
1577
0
1578
0
    for (int i = 0; i <= numRuleSets; ++i) {
1579
0
        ruleSets[i] = NULL;
1580
0
    }
1581
0
1582
0
    // divide up the descriptions into individual rule-set descriptions
1583
0
    // and store them in a temporary array.  At each step, we also
1584
0
    // new up a rule set, but all this does is initialize its name
1585
0
    // and remove it from its description.  We can't actually parse
1586
0
    // the rest of the descriptions and finish initializing everything
1587
0
    // because we have to know the names and locations of all the rule
1588
0
    // sets before we can actually set everything up
1589
0
    if(!numRuleSets) {
1590
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1591
0
        return;
1592
0
    }
1593
0
1594
0
    ruleSetDescriptions = new UnicodeString[numRuleSets];
1595
0
    if (ruleSetDescriptions == 0) {
1596
0
        status = U_MEMORY_ALLOCATION_ERROR;
1597
0
        return;
1598
0
    }
1599
0
1600
0
    {
1601
0
        int curRuleSet = 0;
1602
0
        int32_t start = 0;
1603
0
        for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, start)) {
1604
0
            ruleSetDescriptions[curRuleSet].setTo(description, start, p + 1 - start);
1605
0
            ruleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status);
1606
0
            if (ruleSets[curRuleSet] == 0) {
1607
0
                status = U_MEMORY_ALLOCATION_ERROR;
1608
0
                return;
1609
0
            }
1610
0
            ++curRuleSet;
1611
0
            start = p + 1;
1612
0
        }
1613
0
        ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start);
1614
0
        ruleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status);
1615
0
        if (ruleSets[curRuleSet] == 0) {
1616
0
            status = U_MEMORY_ALLOCATION_ERROR;
1617
0
            return;
1618
0
        }
1619
0
    }
1620
0
1621
0
    // now we can take note of the formatter's default rule set, which
1622
0
    // is the last public rule set in the description (it's the last
1623
0
    // rather than the first so that a user can create a new formatter
1624
0
    // from an existing formatter and change its default behavior just
1625
0
    // by appending more rule sets to the end)
1626
0
1627
0
    // {dlf} Initialization of a fraction rule set requires the default rule
1628
0
    // set to be known.  For purposes of initialization, this is always the 
1629
0
    // last public rule set, no matter what the localization data says.
1630
0
    initDefaultRuleSet();
1631
0
1632
0
    // finally, we can go back through the temporary descriptions
1633
0
    // list and finish seting up the substructure (and we throw
1634
0
    // away the temporary descriptions as we go)
1635
0
    {
1636
0
        for (int i = 0; i < numRuleSets; i++) {
1637
0
            ruleSets[i]->parseRules(ruleSetDescriptions[i], status);
1638
0
        }
1639
0
    }
1640
0
1641
0
    // Now that the rules are initialized, the 'real' default rule
1642
0
    // set can be adjusted by the localization data.
1643
0
1644
0
    // The C code keeps the localization array as is, rather than building
1645
0
    // a separate array of the public rule set names, so we have less work
1646
0
    // to do here-- but we still need to check the names.
1647
0
    
1648
0
    if (localizationInfos) {
1649
0
        // confirm the names, if any aren't in the rules, that's an error
1650
0
        // it is ok if the rules contain public rule sets that are not in this list
1651
0
        for (int32_t i = 0; i < localizationInfos->getNumberOfRuleSets(); ++i) {
1652
0
            UnicodeString name(TRUE, localizationInfos->getRuleSetName(i), -1);
1653
0
            NFRuleSet* rs = findRuleSet(name, status);
1654
0
            if (rs == NULL) {
1655
0
                break; // error
1656
0
            }
1657
0
            if (i == 0) {
1658
0
                defaultRuleSet = rs;
1659
0
            }
1660
0
        }
1661
0
    } else {
1662
0
        defaultRuleSet = getDefaultRuleSet();
1663
0
    }
1664
0
    originalDescription = rules;
1665
0
}
1666
1667
// override the NumberFormat implementation in order to
1668
// lazily initialize relevant items
1669
void
1670
RuleBasedNumberFormat::setContext(UDisplayContext value, UErrorCode& status)
1671
0
{
1672
0
    NumberFormat::setContext(value, status);
1673
0
    if (U_SUCCESS(status)) {
1674
0
      if (!capitalizationInfoSet &&
1675
0
              (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) {
1676
0
          initCapitalizationContextInfo(locale);
1677
0
          capitalizationInfoSet = TRUE;
1678
0
        }
1679
0
#if !UCONFIG_NO_BREAK_ITERATION
1680
0
        if ( capitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
1681
0
                (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) ||
1682
0
                (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) {
1683
0
            UErrorCode status = U_ZERO_ERROR;
1684
0
            capitalizationBrkIter = BreakIterator::createSentenceInstance(locale, status);
1685
0
            if (U_FAILURE(status)) {
1686
0
                delete capitalizationBrkIter;
1687
0
                capitalizationBrkIter = NULL;
1688
0
            }
1689
0
        }
1690
0
#endif
1691
0
    }
1692
0
}
1693
1694
void
1695
RuleBasedNumberFormat::initCapitalizationContextInfo(const Locale& thelocale)
1696
0
{
1697
0
#if !UCONFIG_NO_BREAK_ITERATION
1698
0
    const char * localeID = (thelocale != NULL)? thelocale.getBaseName(): NULL;
1699
0
    UErrorCode status = U_ZERO_ERROR;
1700
0
    UResourceBundle *rb = ures_open(NULL, localeID, &status);
1701
0
    rb = ures_getByKeyWithFallback(rb, "contextTransforms", rb, &status);
1702
0
    rb = ures_getByKeyWithFallback(rb, "number-spellout", rb, &status);
1703
0
    if (U_SUCCESS(status) && rb != NULL) {
1704
0
        int32_t len = 0;
1705
0
        const int32_t * intVector = ures_getIntVector(rb, &len, &status);
1706
0
        if (U_SUCCESS(status) && intVector != NULL && len >= 2) {
1707
0
            capitalizationForUIListMenu = intVector[0];
1708
0
            capitalizationForStandAlone = intVector[1];
1709
0
        }
1710
0
    }
1711
0
    ures_close(rb);
1712
0
#endif
1713
0
}
1714
1715
void
1716
RuleBasedNumberFormat::stripWhitespace(UnicodeString& description)
1717
0
{
1718
0
    // iterate through the characters...
1719
0
    UnicodeString result;
1720
0
1721
0
    int start = 0;
1722
0
    while (start != -1 && start < description.length()) {
1723
0
        // seek to the first non-whitespace character...
1724
0
        while (start < description.length()
1725
0
            && PatternProps::isWhiteSpace(description.charAt(start))) {
1726
0
            ++start;
1727
0
        }
1728
0
1729
0
        // locate the next semicolon in the text and copy the text from
1730
0
        // our current position up to that semicolon into the result
1731
0
        int32_t p = description.indexOf(gSemiColon, start);
1732
0
        if (p == -1) {
1733
0
            // or if we don't find a semicolon, just copy the rest of
1734
0
            // the string into the result
1735
0
            result.append(description, start, description.length() - start);
1736
0
            start = -1;
1737
0
        }
1738
0
        else if (p < description.length()) {
1739
0
            result.append(description, start, p + 1 - start);
1740
0
            start = p + 1;
1741
0
        }
1742
0
1743
0
        // when we get here, we've seeked off the end of the sring, and
1744
0
        // we terminate the loop (we continue until *start* is -1 rather
1745
0
        // than until *p* is -1, because otherwise we'd miss the last
1746
0
        // rule in the description)
1747
0
        else {
1748
0
            start = -1;
1749
0
        }
1750
0
    }
1751
0
1752
0
    description.setTo(result);
1753
0
}
1754
1755
1756
void
1757
RuleBasedNumberFormat::dispose()
1758
0
{
1759
0
    if (ruleSets) {
1760
0
        for (NFRuleSet** p = ruleSets; *p; ++p) {
1761
0
            delete *p;
1762
0
        }
1763
0
        uprv_free(ruleSets);
1764
0
        ruleSets = NULL;
1765
0
    }
1766
0
1767
0
    if (ruleSetDescriptions) {
1768
0
        delete [] ruleSetDescriptions;
1769
0
        ruleSetDescriptions = NULL;
1770
0
    }
1771
0
1772
0
#if !UCONFIG_NO_COLLATION
1773
0
    delete collator;
1774
0
#endif
1775
0
    collator = NULL;
1776
0
1777
0
    delete decimalFormatSymbols;
1778
0
    decimalFormatSymbols = NULL;
1779
0
1780
0
    delete defaultInfinityRule;
1781
0
    defaultInfinityRule = NULL;
1782
0
1783
0
    delete defaultNaNRule;
1784
0
    defaultNaNRule = NULL;
1785
0
1786
0
    delete lenientParseRules;
1787
0
    lenientParseRules = NULL;
1788
0
1789
0
#if !UCONFIG_NO_BREAK_ITERATION
1790
0
    delete capitalizationBrkIter;
1791
0
    capitalizationBrkIter = NULL;
1792
0
#endif
1793
0
1794
0
    if (localizations) {
1795
0
        localizations = localizations->unref();
1796
0
    }
1797
0
}
1798
1799
1800
//-----------------------------------------------------------------------
1801
// package-internal API
1802
//-----------------------------------------------------------------------
1803
1804
/**
1805
 * Returns the collator to use for lenient parsing.  The collator is lazily created:
1806
 * this function creates it the first time it's called.
1807
 * @return The collator to use for lenient parsing, or null if lenient parsing
1808
 * is turned off.
1809
*/
1810
const RuleBasedCollator*
1811
RuleBasedNumberFormat::getCollator() const
1812
0
{
1813
0
#if !UCONFIG_NO_COLLATION
1814
0
    if (!ruleSets) {
1815
0
        return NULL;
1816
0
    }
1817
0
1818
0
    // lazy-evaluate the collator
1819
0
    if (collator == NULL && lenient) {
1820
0
        // create a default collator based on the formatter's locale,
1821
0
        // then pull out that collator's rules, append any additional
1822
0
        // rules specified in the description, and create a _new_
1823
0
        // collator based on the combinaiton of those rules
1824
0
1825
0
        UErrorCode status = U_ZERO_ERROR;
1826
0
1827
0
        Collator* temp = Collator::createInstance(locale, status);
1828
0
        RuleBasedCollator* newCollator;
1829
0
        if (U_SUCCESS(status) && (newCollator = dynamic_cast<RuleBasedCollator*>(temp)) != NULL) {
1830
0
            if (lenientParseRules) {
1831
0
                UnicodeString rules(newCollator->getRules());
1832
0
                rules.append(*lenientParseRules);
1833
0
1834
0
                newCollator = new RuleBasedCollator(rules, status);
1835
0
                // Exit if newCollator could not be created.
1836
0
                if (newCollator == NULL) {
1837
0
                    return NULL;
1838
0
                }
1839
0
            } else {
1840
0
                temp = NULL;
1841
0
            }
1842
0
            if (U_SUCCESS(status)) {
1843
0
                newCollator->setAttribute(UCOL_DECOMPOSITION_MODE, UCOL_ON, status);
1844
0
                // cast away const
1845
0
                ((RuleBasedNumberFormat*)this)->collator = newCollator;
1846
0
            } else {
1847
0
                delete newCollator;
1848
0
            }
1849
0
        }
1850
0
        delete temp;
1851
0
    }
1852
0
#endif
1853
0
1854
0
    // if lenient-parse mode is off, this will be null
1855
0
    // (see setLenientParseMode())
1856
0
    return collator;
1857
0
}
1858
1859
1860
DecimalFormatSymbols*
1861
RuleBasedNumberFormat::initializeDecimalFormatSymbols(UErrorCode &status)
1862
0
{
1863
0
    // lazy-evaluate the DecimalFormatSymbols object.  This object
1864
0
    // is shared by all DecimalFormat instances belonging to this
1865
0
    // formatter
1866
0
    if (decimalFormatSymbols == NULL) {
1867
0
        DecimalFormatSymbols* temp = new DecimalFormatSymbols(locale, status);
1868
0
        if (U_SUCCESS(status)) {
1869
0
            decimalFormatSymbols = temp;
1870
0
        }
1871
0
        else {
1872
0
            delete temp;
1873
0
        }
1874
0
    }
1875
0
    return decimalFormatSymbols;
1876
0
}
1877
1878
/**
1879
 * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat
1880
 * instances owned by this formatter.
1881
*/
1882
const DecimalFormatSymbols*
1883
RuleBasedNumberFormat::getDecimalFormatSymbols() const
1884
0
{
1885
0
    return decimalFormatSymbols;
1886
0
}
1887
1888
NFRule*
1889
RuleBasedNumberFormat::initializeDefaultInfinityRule(UErrorCode &status)
1890
0
{
1891
0
    if (U_FAILURE(status)) {
1892
0
        return NULL;
1893
0
    }
1894
0
    if (defaultInfinityRule == NULL) {
1895
0
        UnicodeString rule(UNICODE_STRING_SIMPLE("Inf: "));
1896
0
        rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kInfinitySymbol));
1897
0
        NFRule* temp = new NFRule(this, rule, status);
1898
0
        if (U_SUCCESS(status)) {
1899
0
            defaultInfinityRule = temp;
1900
0
        }
1901
0
        else {
1902
0
            delete temp;
1903
0
        }
1904
0
    }
1905
0
    return defaultInfinityRule;
1906
0
}
1907
1908
const NFRule*
1909
RuleBasedNumberFormat::getDefaultInfinityRule() const
1910
0
{
1911
0
    return defaultInfinityRule;
1912
0
}
1913
1914
NFRule*
1915
RuleBasedNumberFormat::initializeDefaultNaNRule(UErrorCode &status)
1916
0
{
1917
0
    if (U_FAILURE(status)) {
1918
0
        return NULL;
1919
0
    }
1920
0
    if (defaultNaNRule == NULL) {
1921
0
        UnicodeString rule(UNICODE_STRING_SIMPLE("NaN: "));
1922
0
        rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kNaNSymbol));
1923
0
        NFRule* temp = new NFRule(this, rule, status);
1924
0
        if (U_SUCCESS(status)) {
1925
0
            defaultNaNRule = temp;
1926
0
        }
1927
0
        else {
1928
0
            delete temp;
1929
0
        }
1930
0
    }
1931
0
    return defaultNaNRule;
1932
0
}
1933
1934
const NFRule*
1935
RuleBasedNumberFormat::getDefaultNaNRule() const
1936
0
{
1937
0
    return defaultNaNRule;
1938
0
}
1939
1940
// De-owning the current localized symbols and adopt the new symbols.
1941
void
1942
RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt)
1943
0
{
1944
0
    if (symbolsToAdopt == NULL) {
1945
0
        return; // do not allow caller to set decimalFormatSymbols to NULL
1946
0
    }
1947
0
1948
0
    if (decimalFormatSymbols != NULL) {
1949
0
        delete decimalFormatSymbols;
1950
0
    }
1951
0
1952
0
    decimalFormatSymbols = symbolsToAdopt;
1953
0
1954
0
    {
1955
0
        // Apply the new decimalFormatSymbols by reparsing the rulesets
1956
0
        UErrorCode status = U_ZERO_ERROR;
1957
0
1958
0
        delete defaultInfinityRule;
1959
0
        defaultInfinityRule = NULL;
1960
0
        initializeDefaultInfinityRule(status); // Reset with the new DecimalFormatSymbols
1961
0
1962
0
        delete defaultNaNRule;
1963
0
        defaultNaNRule = NULL;
1964
0
        initializeDefaultNaNRule(status); // Reset with the new DecimalFormatSymbols
1965
0
1966
0
        if (ruleSets) {
1967
0
            for (int32_t i = 0; i < numRuleSets; i++) {
1968
0
                ruleSets[i]->setDecimalFormatSymbols(*symbolsToAdopt, status);
1969
0
            }
1970
0
        }
1971
0
    }
1972
0
}
1973
1974
// Setting the symbols is equlivalent to adopting a newly created localized symbols.
1975
void
1976
RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols)
1977
0
{
1978
0
    adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols));
1979
0
}
1980
1981
PluralFormat *
1982
RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType,
1983
                                          const UnicodeString &pattern,
1984
                                          UErrorCode& status) const
1985
0
{
1986
0
    return new PluralFormat(locale, pluralType, pattern, status);
1987
0
}
1988
1989
/**
1990
 * Get the rounding mode.
1991
 * @return A rounding mode
1992
 */
1993
0
DecimalFormat::ERoundingMode RuleBasedNumberFormat::getRoundingMode() const {
1994
0
    return roundingMode;
1995
0
}
1996
1997
/**
1998
 * Set the rounding mode.  This has no effect unless the rounding
1999
 * increment is greater than zero.
2000
 * @param roundingMode A rounding mode
2001
 */
2002
0
void RuleBasedNumberFormat::setRoundingMode(DecimalFormat::ERoundingMode roundingMode) {
2003
0
    this->roundingMode = roundingMode;
2004
0
}
2005
2006
U_NAMESPACE_END
2007
2008
/* U_HAVE_RBNF */
2009
#endif