summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/decimfmtimpl.h
blob: 82f154edbd3557335ad9458a66beb0d716431ed5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
// Copyright (C) 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
********************************************************************************
*   Copyright (C) 2015, International Business Machines
*   Corporation and others.  All Rights Reserved.
********************************************************************************
*
* File decimfmtimpl.h
********************************************************************************
*/

#ifndef DECIMFMTIMPL_H
#define DECIMFMTIMPL_H

#include "unicode/utypes.h"

#if !UCONFIG_NO_FORMATTING

#include "unicode/decimfmt.h"
#include "unicode/uobject.h"
#include "affixpatternparser.h"
#include "digitaffixesandpadding.h"
#include "digitformatter.h"
#include "digitgrouping.h"
#include "precision.h"

U_NAMESPACE_BEGIN

class UnicodeString;
class FieldPosition;
class ValueFormatter;
class FieldPositionHandler;
class FixedDecimal;

/**
 * DecimalFormatImpl is the glue code between the legacy DecimalFormat class
 * and the new decimal formatting classes. DecimalFormat still handles
 * parsing directly. However, DecimalFormat uses attributes of this class
 * for parsing when possible.
 *
 * The public API of this class closely mirrors the legacy API of the
 * legacy DecimalFormat deviating only when the legacy API does not make
 * sense. For example, although DecimalFormat has a
 * getPadCharacterString() method, DecimalFormatImpl has a getPadCharacter()
 * method because formatting uses only a single pad character for padding.
 *
 * Each legacy DecimalFormat instance heap allocates its own instance of
 * this class. Most DecimalFormat methods that deal with formatting simply
 * delegate to the DecimalFormat's DecimalFormatImpl method.
 *
 * Because DecimalFormat extends NumberFormat, Each instance of this class
 * "borrows" a pointer to the NumberFormat part of its enclosing DecimalFormat
 * instance. This way each DecimalFormatImpl instance can read or even modify
 * the NumberFormat portion of its enclosing DecimalFormat instance.
 *
 * Directed acyclic graph (DAG):
 *
 * This class can be represented as a directed acyclic graph (DAG) where each
 * vertex is an attribute, and each directed edge indicates that the value
 * of the destination attribute is calculated from the value of the source
 * attribute. Attributes with setter methods reside at the bottom of the
 * DAG. That is, no edges point to them. We call these independent attributes
 * because their values can be set independently of one another. The rest of
 * the attributes are derived attributes because their values depend on the
 * independent attributes. DecimalFormatImpl often uses the derived
 * attributes, not the independent attributes, when formatting numbers.
 *
 * The independent attributes at the bottom of the DAG correspond to the legacy
 * attributes of DecimalFormat while the attributes at the top of the DAG
 * correspond to the attributes of the new code. The edges of the DAG
 * correspond to the code that handles the complex interaction among all the
 * legacy attributes of the DecimalFormat API.
 *
 * We use a DAG for three reasons.
 *
 * First, the DAG preserves backward compatibility. Clients of the legacy
 * DecimalFormat expect existing getters and setters of each attribute to be
 * consistent. That means if a client sets a particular attribute to a new
 * value, the attribute should retain that value until the client sets it to
 * a new value. The DAG allows these attributes to remain consistent even
 * though the new code may not use them when formatting.
 *
 * Second, the DAG obviates the need to recalculate derived attributes with
 * each format. Instead, the DAG "remembers" the values of all derived
 * attributes. Only setting an independent attribute requires a recalculation.
 * Moreover, setting an independent attribute recalculates only the affected
 * dependent attributes rather than all dependent attributes.
 *
 * Third, the DAG abstracts away the complex interaction among the legacy
 * attributes of the DecimalFormat API.
 *
 * Only the independent attributes of the DAG have setters and getters.
 * Derived attributes have no setters (and often no getters either).
 *
 * Copy and assign:
 *
 * For copy and assign, DecimalFormatImpl copies and assigns every attribute
 * regardless of whether or not it is independent. We do this for simplicity.
 *
 * Implementation of the DAG:
 *
 * The DAG consists of three smaller DAGs:
 * 1. Grouping attributes
 * 2. Precision attributes
 * 3. Formatting attributes.
 *
 * The first two DAGs are simple in that setting any independent attribute
 * in the DAG recalculates all the dependent attributes in that DAG.
 * The updateGrouping() and updatePrecision() perform the respective
 * recalculations. 
 *
 * Because some of the derived formatting attributes are expensive to
 * calculate, the formatting attributes DAG is more complex. The
 * updateFormatting() method is composed of many updateFormattingXXX()
 * methods, each of which recalculates a single derived attribute. The
 * updateFormatting() method accepts a bitfield of recently changed
 * attributes and passes this bitfield by reference to each of the
 * updateFormattingXXX() methods. Each updateFormattingXXX() method checks
 * the bitfield to see if any of the attributes it uses to compute the XXX
 * attribute changed. If none of them changed, it exists immediately. However,
 * if at least one of them changed, it recalculates the XXX attribute and
 * sets the corresponding bit in the bitfield. In this way, each
 * updateFormattingXXX() method encodes the directed edges in the formatting
 * DAG that point to the attribute its calculating.
 *
 * Maintenance of the updateFormatting() method.
 *
 * Use care when changing the updateFormatting() method.
 * The updateFormatting() method must call each updateFormattingXXX() in the
 * same partial order that the formatting DAG prescribes. That is, the
 * attributes near the bottom of the DAG must be calculated before attributes
 * further up. As we mentioned in the prvious paragraph, the directed edges of
 * the formatting DAG are encoded within each updateFormattingXXX() method.
 * Finally, adding new attributes may involve adding to the bitmap that the
 * updateFormatting() method uses. The top most attributes in the DAG,
 * those that do not point to any attributes but only have attributes
 * pointing to it, need not have a slot in the bitmap.
 *
 * Keep in mind that most of the code that makes the legacy DecimalFormat API
 * work the way it always has before can be found in these various updateXXX()
 * methods. For example the updatePrecisionForScientific() method
 * handles the complex interactions amoung the various precision attributes
 * when formatting in scientific notation. Changing the way attributes
 * interract, often means changing one of these updateXXX() methods.
 *
 * Conclusion:
 *
 * The DecimFmtImpl class is the glue code between the legacy and new
 * number formatting code. It uses a direct acyclic graph (DAG) to
 * maintain backward compatibility, to make the code efficient, and to
 * abstract away the complex interraction among legacy attributs.
 */


class DecimalFormatImpl : public UObject {
public:

DecimalFormatImpl(
        NumberFormat *super,
        const Locale &locale,
        const UnicodeString &pattern,
        UErrorCode &status);
DecimalFormatImpl(
        NumberFormat *super,
        const UnicodeString &pattern,
        DecimalFormatSymbols *symbolsToAdopt,
        UParseError &parseError,
        UErrorCode &status);
DecimalFormatImpl(
        NumberFormat *super,
        const DecimalFormatImpl &other,
        UErrorCode &status);
DecimalFormatImpl &assign(
        const DecimalFormatImpl &other, UErrorCode &status);
virtual ~DecimalFormatImpl();
void adoptDecimalFormatSymbols(DecimalFormatSymbols *symbolsToAdopt);
const DecimalFormatSymbols &getDecimalFormatSymbols() const {
    return *fSymbols;
}
UnicodeString &format(
        int32_t number,
        UnicodeString &appendTo,
        FieldPosition &pos,
        UErrorCode &status) const;
UnicodeString &format(
        int32_t number,
        UnicodeString &appendTo,
        FieldPositionIterator *posIter,
        UErrorCode &status) const;
UnicodeString &format(
        int64_t number,
        UnicodeString &appendTo,
        FieldPosition &pos,
        UErrorCode &status) const;
UnicodeString &format(
        double number,
        UnicodeString &appendTo,
        FieldPosition &pos,
        UErrorCode &status) const;
UnicodeString &format(
        const DigitList &number,
        UnicodeString &appendTo,
        FieldPosition &pos,
        UErrorCode &status) const;
UnicodeString &format(
        int64_t number,
        UnicodeString &appendTo,
        FieldPositionIterator *posIter,
        UErrorCode &status) const;
UnicodeString &format(
        double number,
        UnicodeString &appendTo,
        FieldPositionIterator *posIter,
        UErrorCode &status) const;
UnicodeString &format(
        const DigitList &number,
        UnicodeString &appendTo,
        FieldPositionIterator *posIter,
        UErrorCode &status) const;
UnicodeString &format(
        StringPiece number,
        UnicodeString &appendTo,
        FieldPositionIterator *posIter,
        UErrorCode &status) const;
UnicodeString &format(
        const VisibleDigitsWithExponent &digits,
        UnicodeString &appendTo,
        FieldPosition &pos,
        UErrorCode &status) const;
UnicodeString &format(
        const VisibleDigitsWithExponent &digits,
        UnicodeString &appendTo,
        FieldPositionIterator *posIter,
        UErrorCode &status) const;

UBool operator==(const DecimalFormatImpl &) const;

UBool operator!=(const DecimalFormatImpl &other) const {
    return !(*this == other);
}

void setRoundingMode(DecimalFormat::ERoundingMode mode) {
    fRoundingMode = mode;
    fEffPrecision.fMantissa.fExactOnly = (fRoundingMode == DecimalFormat::kRoundUnnecessary);
    fEffPrecision.fMantissa.fRoundingMode = mode;
}
DecimalFormat::ERoundingMode getRoundingMode() const {
    return fRoundingMode;
}
void setFailIfMoreThanMaxDigits(UBool b) {
    fEffPrecision.fMantissa.fFailIfOverMax = b;
}
UBool isFailIfMoreThanMaxDigits() const { return fEffPrecision.fMantissa.fFailIfOverMax; }
void setMinimumSignificantDigits(int32_t newValue);
void setMaximumSignificantDigits(int32_t newValue);
void setMinMaxSignificantDigits(int32_t min, int32_t max);
void setScientificNotation(UBool newValue);
void setSignificantDigitsUsed(UBool newValue);

int32_t getMinimumSignificantDigits() const { 
        return fMinSigDigits; }
int32_t getMaximumSignificantDigits() const { 
        return fMaxSigDigits; }
UBool isScientificNotation() const { return fUseScientific; }
UBool areSignificantDigitsUsed() const { return fUseSigDigits; }
void setGroupingSize(int32_t newValue);
void setSecondaryGroupingSize(int32_t newValue);
void setMinimumGroupingDigits(int32_t newValue);
int32_t getGroupingSize() const { return fGrouping.fGrouping; }
int32_t getSecondaryGroupingSize() const { return fGrouping.fGrouping2; }
int32_t getMinimumGroupingDigits() const { return fGrouping.fMinGrouping; }
void applyPattern(const UnicodeString &pattern, UErrorCode &status);
void applyPatternFavorCurrencyPrecision(
        const UnicodeString &pattern, UErrorCode &status);
void applyPattern(
        const UnicodeString &pattern, UParseError &perror, UErrorCode &status);
void applyLocalizedPattern(const UnicodeString &pattern, UErrorCode &status);
void applyLocalizedPattern(
        const UnicodeString &pattern, UParseError &perror, UErrorCode &status);
void setCurrencyUsage(UCurrencyUsage usage, UErrorCode &status);
UCurrencyUsage getCurrencyUsage() const { return fCurrencyUsage; }
void setRoundingIncrement(double d);
double getRoundingIncrement() const;
int32_t getMultiplier() const;
void setMultiplier(int32_t m);
UChar32 getPadCharacter() const { return fAffixes.fPadChar; }
void setPadCharacter(UChar32 c) { fAffixes.fPadChar = c; }
int32_t getFormatWidth() const { return fAffixes.fWidth; }
void setFormatWidth(int32_t x) { fAffixes.fWidth = x; }
DigitAffixesAndPadding::EPadPosition getPadPosition() const {
    return fAffixes.fPadPosition;
}
void setPadPosition(DigitAffixesAndPadding::EPadPosition x) {
    fAffixes.fPadPosition = x;
}
int32_t getMinimumExponentDigits() const {
    return fEffPrecision.fMinExponentDigits;
}
void setMinimumExponentDigits(int32_t x) {
    fEffPrecision.fMinExponentDigits = x;
}
UBool isExponentSignAlwaysShown() const {
    return fOptions.fExponent.fAlwaysShowSign;
}
void setExponentSignAlwaysShown(UBool x) {
    fOptions.fExponent.fAlwaysShowSign = x;
}
UBool isDecimalSeparatorAlwaysShown() const {
    return fOptions.fMantissa.fAlwaysShowDecimal;
}
void setDecimalSeparatorAlwaysShown(UBool x) {
    fOptions.fMantissa.fAlwaysShowDecimal = x;
}
UnicodeString &getPositivePrefix(UnicodeString &result) const;
UnicodeString &getPositiveSuffix(UnicodeString &result) const;
UnicodeString &getNegativePrefix(UnicodeString &result) const;
UnicodeString &getNegativeSuffix(UnicodeString &result) const;
void setPositivePrefix(const UnicodeString &str);
void setPositiveSuffix(const UnicodeString &str);
void setNegativePrefix(const UnicodeString &str);
void setNegativeSuffix(const UnicodeString &str);
UnicodeString &toPattern(UnicodeString& result) const;
FixedDecimal &getFixedDecimal(double value, FixedDecimal &result, UErrorCode &status) const;
FixedDecimal &getFixedDecimal(DigitList &number, FixedDecimal &result, UErrorCode &status) const;
DigitList &round(DigitList &number, UErrorCode &status) const;

VisibleDigitsWithExponent &
initVisibleDigitsWithExponent(
        int64_t number,
        VisibleDigitsWithExponent &digits,
        UErrorCode &status) const;
VisibleDigitsWithExponent &
initVisibleDigitsWithExponent(
        double number,
        VisibleDigitsWithExponent &digits,
        UErrorCode &status) const;
VisibleDigitsWithExponent &
initVisibleDigitsWithExponent(
        DigitList &number,
        VisibleDigitsWithExponent &digits,
        UErrorCode &status) const;

void updatePrecision();
void updateGrouping();
void updateCurrency(UErrorCode &status);


private:
// Disallow copy and assign
DecimalFormatImpl(const DecimalFormatImpl &other);
DecimalFormatImpl &operator=(const DecimalFormatImpl &other);
NumberFormat *fSuper;
DigitList fMultiplier;
int32_t fScale;

DecimalFormat::ERoundingMode fRoundingMode;

// These fields include what the user can see and set.
// When the user updates these fields, it triggers automatic updates of
// other fields that may be invisible to user

// Updating any of the following fields triggers an update to
// fEffPrecision.fMantissa.fMin,
// fEffPrecision.fMantissa.fMax,
// fEffPrecision.fMantissa.fSignificant fields
// We have this two phase update because of backward compatibility. 
// DecimalFormat has to remember all settings even if those settings are
// invalid or disabled.
int32_t fMinSigDigits;
int32_t fMaxSigDigits;
UBool fUseScientific;
UBool fUseSigDigits;
// In addition to these listed above, changes to min/max int digits and
// min/max frac digits from fSuper also trigger an update.

// Updating any of the following fields triggers an update to
// fEffGrouping field Again we do it this way because original
// grouping settings have to be retained if grouping is turned off.
DigitGrouping fGrouping;
// In addition to these listed above, changes to isGroupingUsed in
// fSuper also triggers an update to fEffGrouping.

// Updating any of the following fields triggers updates on the following:
// fMonetary, fRules, fAffixParser, fCurrencyAffixInfo,
// fFormatter, fAffixes.fPositivePrefiix, fAffixes.fPositiveSuffix,
// fAffixes.fNegativePrefiix, fAffixes.fNegativeSuffix
// We do this two phase update because localizing the affix patterns
// and formatters can be expensive. Better to do it once with the setters
// than each time within format.
AffixPattern fPositivePrefixPattern;
AffixPattern fNegativePrefixPattern;
AffixPattern fPositiveSuffixPattern;
AffixPattern fNegativeSuffixPattern;
DecimalFormatSymbols *fSymbols;
UCurrencyUsage fCurrencyUsage;
// In addition to these listed above, changes to getCurrency() in
// fSuper also triggers an update.

// Optional may be NULL
PluralRules *fRules;

// These fields are totally hidden from user and are used to derive the affixes
// in fAffixes below from the four affix patterns above.
UBool fMonetary;
AffixPatternParser fAffixParser;
CurrencyAffixInfo fCurrencyAffixInfo;

// The actual precision used when formatting
ScientificPrecision fEffPrecision;

// The actual grouping used when formatting
DigitGrouping fEffGrouping;
SciFormatterOptions fOptions;   // Encapsulates fixed precision options
DigitFormatter fFormatter;
DigitAffixesAndPadding fAffixes;

UnicodeString &formatInt32(
        int32_t number,
        UnicodeString &appendTo,
        FieldPositionHandler &handler,
        UErrorCode &status) const;

UnicodeString &formatInt64(
        int64_t number,
        UnicodeString &appendTo,
        FieldPositionHandler &handler,
        UErrorCode &status) const;

UnicodeString &formatDouble(
        double number,
        UnicodeString &appendTo,
        FieldPositionHandler &handler,
        UErrorCode &status) const;

// Scales for precent or permille symbols
UnicodeString &formatDigitList(
        DigitList &number,
        UnicodeString &appendTo,
        FieldPositionHandler &handler,
        UErrorCode &status) const;

// Does not scale for precent or permille symbols
UnicodeString &formatAdjustedDigitList(
        DigitList &number,
        UnicodeString &appendTo,
        FieldPositionHandler &handler,
        UErrorCode &status) const;

UnicodeString &formatVisibleDigitsWithExponent(
        const VisibleDigitsWithExponent &number,
        UnicodeString &appendTo,
        FieldPositionHandler &handler,
        UErrorCode &status) const;

VisibleDigitsWithExponent &
initVisibleDigitsFromAdjusted(
        DigitList &number,
        VisibleDigitsWithExponent &digits,
        UErrorCode &status) const;

template<class T>
UBool maybeFormatWithDigitList(
        T number,
        UnicodeString &appendTo,
        FieldPositionHandler &handler,
        UErrorCode &status) const;

template<class T>
UBool maybeInitVisibleDigitsFromDigitList(
        T number,
        VisibleDigitsWithExponent &digits,
        UErrorCode &status) const;

DigitList &adjustDigitList(DigitList &number, UErrorCode &status) const;

void applyPattern(
        const UnicodeString &pattern,
        UBool localized, UParseError &perror, UErrorCode &status);

ValueFormatter &prepareValueFormatter(ValueFormatter &vf) const;
void setMultiplierScale(int32_t s);
int32_t getPatternScale() const;
void setScale(int32_t s) { fScale = s; }
int32_t getScale() const { return fScale; }

// Updates everything
void updateAll(UErrorCode &status);
void updateAll(
        int32_t formattingFlags,
        UBool updatePrecisionBasedOnCurrency,
        UErrorCode &status);

// Updates from formatting pattern changes
void updateForApplyPattern(UErrorCode &status);
void updateForApplyPatternFavorCurrencyPrecision(UErrorCode &status);

// Updates from changes to third group of attributes
void updateFormatting(int32_t changedFormattingFields, UErrorCode &status);
void updateFormatting(
        int32_t changedFormattingFields,
        UBool updatePrecisionBasedOnCurrency,
        UErrorCode &status);

// Helper functions for updatePrecision
void updatePrecisionForScientific();
void updatePrecisionForFixed();
void extractMinMaxDigits(DigitInterval &min, DigitInterval &max) const;
void extractSigDigits(SignificantDigitInterval &sig) const;

// Helper functions for updateFormatting
void updateFormattingUsesCurrency(int32_t &changedFormattingFields);
void updateFormattingPluralRules(
        int32_t &changedFormattingFields, UErrorCode &status);
void updateFormattingAffixParser(int32_t &changedFormattingFields);
void updateFormattingCurrencyAffixInfo(
        int32_t &changedFormattingFields,
        UBool updatePrecisionBasedOnCurrency,
        UErrorCode &status);
void updateFormattingFixedPointFormatter(
        int32_t &changedFormattingFields);
void updateFormattingLocalizedPositivePrefix(
        int32_t &changedFormattingFields, UErrorCode &status);
void updateFormattingLocalizedPositiveSuffix(
        int32_t &changedFormattingFields, UErrorCode &status);
void updateFormattingLocalizedNegativePrefix(
        int32_t &changedFormattingFields, UErrorCode &status);
void updateFormattingLocalizedNegativeSuffix(
        int32_t &changedFormattingFields, UErrorCode &status);

int32_t computeExponentPatternLength() const;
int32_t countFractionDigitAndDecimalPatternLength(int32_t fracDigitCount) const;
UnicodeString &toNumberPattern(
        UBool hasPadding, int32_t minimumLength, UnicodeString& result) const;

int32_t getOldFormatWidth() const;
const UnicodeString &getConstSymbol(
        DecimalFormatSymbols::ENumberFormatSymbol symbol) const;
UBool isParseFastpath() const;

friend class DecimalFormat;

};


U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif // DECIMFMTIMPL_H
//eof