diff options
Diffstat (limited to 'intl/icu/source/i18n/decimfmtimpl.h')
-rw-r--r-- | intl/icu/source/i18n/decimfmtimpl.h | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/decimfmtimpl.h b/intl/icu/source/i18n/decimfmtimpl.h new file mode 100644 index 000000000..82f154edb --- /dev/null +++ b/intl/icu/source/i18n/decimfmtimpl.h @@ -0,0 +1,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 |