// © 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