Index: source/i18n/decimfmtimpl.h |
diff --git a/source/i18n/decimfmtimpl.h b/source/i18n/decimfmtimpl.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9193d57e8f5169ba7e2559f3003e190864dd6ee3 |
--- /dev/null |
+++ b/source/i18n/decimfmtimpl.h |
@@ -0,0 +1,547 @@ |
+/* |
+******************************************************************************** |
+* 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( |
+ const 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 |