ICU-20956 Fix monetary symbol getters in DecimalFormat

See #987
diff --git a/icu4c/source/i18n/dcfmtsym.cpp b/icu4c/source/i18n/dcfmtsym.cpp
index d8b1ecd..15418bf 100644
--- a/icu4c/source/i18n/dcfmtsym.cpp
+++ b/icu4c/source/i18n/dcfmtsym.cpp
@@ -167,6 +167,7 @@
         fIsCustomCurrencySymbol = rhs.fIsCustomCurrencySymbol; 
         fIsCustomIntlCurrencySymbol = rhs.fIsCustomIntlCurrencySymbol; 
         fCodePointZero = rhs.fCodePointZero;
+        currPattern = rhs.currPattern;
     }
     return *this;
 }
@@ -453,58 +454,16 @@
     }
     fCodePointZero = tempCodePointZero;
 
-    // Obtain currency data from the currency API.  This is strictly
-    // for backward compatibility; we don't use DecimalFormatSymbols
-    // for currency data anymore.
+    // Get the default currency from the currency API.
     UErrorCode internalStatus = U_ZERO_ERROR; // don't propagate failures out
     UChar curriso[4];
     UnicodeString tempStr;
     int32_t currisoLength = ucurr_forLocale(locStr, curriso, UPRV_LENGTHOF(curriso), &internalStatus);
     if (U_SUCCESS(internalStatus) && currisoLength == 3) {
-        uprv_getStaticCurrencyName(curriso, locStr, tempStr, internalStatus);
-        if (U_SUCCESS(internalStatus)) {
-            fSymbols[kIntlCurrencySymbol].setTo(curriso, currisoLength);
-            fSymbols[kCurrencySymbol] = tempStr;
-        }
+        setCurrency(curriso, status);
+    } else {
+        setCurrency(nullptr, status);
     }
-    /* else use the default values. */
-
-    //load the currency data
-    UChar ucc[4]={0}; //Currency Codes are always 3 chars long
-    int32_t uccLen = 4;
-    const char* locName = loc.getName();
-    UErrorCode localStatus = U_ZERO_ERROR;
-    uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus);
-
-    // TODO: Currency pattern data loading is duplicated in number_formatimpl.cpp
-    if(U_SUCCESS(localStatus) && uccLen > 0) {
-        char cc[4]={0};
-        u_UCharsToChars(ucc, cc, uccLen);
-        /* An explicit currency was requested */
-        LocalUResourceBundlePointer currencyResource(ures_open(U_ICUDATA_CURR, locStr, &localStatus));
-        LocalUResourceBundlePointer currency(
-            ures_getByKeyWithFallback(currencyResource.getAlias(), "Currencies", NULL, &localStatus));
-        ures_getByKeyWithFallback(currency.getAlias(), cc, currency.getAlias(), &localStatus);
-        if(U_SUCCESS(localStatus) && ures_getSize(currency.getAlias())>2) { // the length is 3 if more data is present
-            ures_getByIndex(currency.getAlias(), 2, currency.getAlias(), &localStatus);
-            int32_t currPatternLen = 0;
-            currPattern =
-                ures_getStringByIndex(currency.getAlias(), (int32_t)0, &currPatternLen, &localStatus);
-            UnicodeString decimalSep =
-                ures_getUnicodeStringByIndex(currency.getAlias(), (int32_t)1, &localStatus);
-            UnicodeString groupingSep =
-                ures_getUnicodeStringByIndex(currency.getAlias(), (int32_t)2, &localStatus);
-            if(U_SUCCESS(localStatus)){
-                fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep;
-                fSymbols[kMonetarySeparatorSymbol] = decimalSep;
-                //pattern.setTo(TRUE, currPattern, currPatternLen);
-                status = localStatus;
-            }
-        }
-        /* else An explicit currency was requested and is unknown or locale data is malformed. */
-        /* ucurr_* API will get the correct value later on. */
-    }
-        // else ignore the error if no currency
 
     // Currency Spacing.
     LocalUResourceBundlePointer currencyResource(ures_open(U_ICUDATA_CURR, locStr, &status));
@@ -553,9 +512,54 @@
     fIsCustomIntlCurrencySymbol = FALSE;
     fCodePointZero = 0x30;
     U_ASSERT(fCodePointZero == fSymbols[kZeroDigitSymbol].char32At(0));
+    currPattern = nullptr;
 
 }
 
+void DecimalFormatSymbols::setCurrency(const UChar* currency, UErrorCode& status) {
+    // TODO: If this method is made public:
+    // - Adopt ICU4J behavior of not allowing currency to be null.
+    // - Also verify that the length of currency is 3.
+    if (!currency) {
+        return;
+    }
+
+    UnicodeString tempStr;
+    uprv_getStaticCurrencyName(currency, locale.getName(), tempStr, status);
+    if (U_SUCCESS(status)) {
+        fSymbols[kIntlCurrencySymbol].setTo(currency, 3);
+        fSymbols[kCurrencySymbol] = tempStr;
+    }
+
+    char cc[4]={0};
+    u_UCharsToChars(currency, cc, 3);
+
+    /* An explicit currency was requested */
+    // TODO(ICU-13297): Move this data loading logic into a centralized place
+    UErrorCode localStatus = U_ZERO_ERROR;
+    LocalUResourceBundlePointer rbTop(ures_open(U_ICUDATA_CURR, locale.getName(), &localStatus));
+    LocalUResourceBundlePointer rb(
+        ures_getByKeyWithFallback(rbTop.getAlias(), "Currencies", NULL, &localStatus));
+    ures_getByKeyWithFallback(rb.getAlias(), cc, rb.getAlias(), &localStatus);
+    if(U_SUCCESS(localStatus) && ures_getSize(rb.getAlias())>2) { // the length is 3 if more data is present
+        ures_getByIndex(rb.getAlias(), 2, rb.getAlias(), &localStatus);
+        int32_t currPatternLen = 0;
+        currPattern =
+            ures_getStringByIndex(rb.getAlias(), (int32_t)0, &currPatternLen, &localStatus);
+        UnicodeString decimalSep =
+            ures_getUnicodeStringByIndex(rb.getAlias(), (int32_t)1, &localStatus);
+        UnicodeString groupingSep =
+            ures_getUnicodeStringByIndex(rb.getAlias(), (int32_t)2, &localStatus);
+        if(U_SUCCESS(localStatus)){
+            fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep;
+            fSymbols[kMonetarySeparatorSymbol] = decimalSep;
+            //pattern.setTo(TRUE, currPattern, currPatternLen);
+        }
+    }
+    /* else An explicit currency was requested and is unknown or locale data is malformed. */
+    /* ucurr_* API will get the correct value later on. */
+}
+
 Locale
 DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
     U_LOCALE_BASED(locBased, *this);
diff --git a/icu4c/source/i18n/decimfmt.cpp b/icu4c/source/i18n/decimfmt.cpp
index 0cbaca7..07a41e5 100644
--- a/icu4c/source/i18n/decimfmt.cpp
+++ b/icu4c/source/i18n/decimfmt.cpp
@@ -1502,8 +1502,11 @@
     }
     NumberFormat::setCurrency(theCurrency, ec); // to set field for compatibility
     fields->properties.currency = currencyUnit;
-    // TODO: Set values in fields->symbols, too?
-    touchNoError();
+    // In Java, the DecimalFormatSymbols is mutable. Why not in C++?
+    LocalPointer<DecimalFormatSymbols> newSymbols(new DecimalFormatSymbols(*fields->symbols), ec);
+    newSymbols->setCurrency(currencyUnit.getISOCurrency(), ec);
+    fields->symbols.adoptInsteadAndCheckErrorCode(newSymbols.orphan(), ec);
+    touch(ec);
 }
 
 void DecimalFormat::setCurrency(const char16_t* theCurrency) {
diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp
index 83ee2a8..8042979 100644
--- a/icu4c/source/i18n/number_formatimpl.cpp
+++ b/icu4c/source/i18n/number_formatimpl.cpp
@@ -24,45 +24,6 @@
 using namespace icu::number;
 using namespace icu::number::impl;
 
-namespace {
-
-struct CurrencyFormatInfoResult {
-    bool exists;
-    const char16_t* pattern;
-    const char16_t* decimalSeparator;
-    const char16_t* groupingSeparator;
-};
-
-CurrencyFormatInfoResult
-getCurrencyFormatInfo(const Locale& locale, const char* isoCode, UErrorCode& status) {
-    // TODO: Load this data in a centralized location like ICU4J?
-    // TODO: Move this into the CurrencySymbols class?
-    // TODO: Parts of this same data are loaded in dcfmtsym.cpp; should clean up.
-    CurrencyFormatInfoResult result = {false, nullptr, nullptr, nullptr};
-    if (U_FAILURE(status)) { return result; }
-    CharString key;
-    key.append("Currencies/", status);
-    key.append(isoCode, status);
-    UErrorCode localStatus = status;
-    LocalUResourceBundlePointer bundle(ures_open(U_ICUDATA_CURR, locale.getName(), &localStatus));
-    ures_getByKeyWithFallback(bundle.getAlias(), key.data(), bundle.getAlias(), &localStatus);
-    if (U_SUCCESS(localStatus) &&
-        ures_getSize(bundle.getAlias()) > 2) { // the length is 3 if more data is present
-        ures_getByIndex(bundle.getAlias(), 2, bundle.getAlias(), &localStatus);
-        int32_t dummy;
-        result.exists = true;
-        result.pattern = ures_getStringByIndex(bundle.getAlias(), 0, &dummy, &localStatus);
-        result.decimalSeparator = ures_getStringByIndex(bundle.getAlias(), 1, &dummy, &localStatus);
-        result.groupingSeparator = ures_getStringByIndex(bundle.getAlias(), 2, &dummy, &localStatus);
-        status = localStatus;
-    } else if (localStatus != U_MISSING_RESOURCE_ERROR) {
-        status = localStatus;
-    }
-    return result;
-}
-
-}  // namespace
-
 
 MicroPropsGenerator::~MicroPropsGenerator() = default;
 
@@ -179,14 +140,6 @@
     if (isCurrency) {
         currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit
     }
-    const CurrencySymbols* currencySymbols;
-    if (macros.currencySymbols != nullptr) {
-        // Used by the DecimalFormat code path
-        currencySymbols = macros.currencySymbols;
-    } else {
-        fWarehouse.fCurrencySymbols = {currency, macros.locale, status};
-        currencySymbols = &fWarehouse.fCurrencySymbols;
-    }
     UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT;
     if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) {
         unitWidth = macros.unitWidth;
@@ -213,41 +166,26 @@
     if (macros.symbols.isDecimalFormatSymbols()) {
         fMicros.symbols = macros.symbols.getDecimalFormatSymbols();
     } else {
-        auto newSymbols = new DecimalFormatSymbols(macros.locale, *ns, status);
-        if (newSymbols == nullptr) {
-            status = U_MEMORY_ALLOCATION_ERROR;
+        LocalPointer<DecimalFormatSymbols> newSymbols(
+            new DecimalFormatSymbols(macros.locale, *ns, status), status);
+        if (U_FAILURE(status)) {
             return nullptr;
         }
-        fMicros.symbols = newSymbols;
-        // Give ownership to the NumberFormatterImpl.
-        fSymbols.adoptInstead(fMicros.symbols);
+        if (isCurrency) {
+            newSymbols->setCurrency(currency.getISOCurrency(), status);
+            if (U_FAILURE(status)) {
+                return nullptr;
+            }
+        }
+        fMicros.symbols = newSymbols.getAlias();
+        fSymbols.adoptInstead(newSymbols.orphan());
     }
 
     // Load and parse the pattern string. It is used for grouping sizes and affixes only.
     // If we are formatting currency, check for a currency-specific pattern.
     const char16_t* pattern = nullptr;
-    if (isCurrency) {
-        CurrencyFormatInfoResult info = getCurrencyFormatInfo(
-                macros.locale, currency.getSubtype(), status);
-        if (info.exists) {
-            pattern = info.pattern;
-            // It's clunky to clone an object here, but this code is not frequently executed.
-            auto symbols = new DecimalFormatSymbols(*fMicros.symbols);
-            if (symbols == nullptr) {
-                status = U_MEMORY_ALLOCATION_ERROR;
-                return nullptr;
-            }
-            fMicros.symbols = symbols;
-            fSymbols.adoptInstead(symbols);
-            symbols->setSymbol(
-                    DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol,
-                    UnicodeString(info.decimalSeparator),
-                    FALSE);
-            symbols->setSymbol(
-                    DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol,
-                    UnicodeString(info.groupingSeparator),
-                    FALSE);
-        }
+    if (isCurrency && fMicros.symbols->getCurrencyPattern() != nullptr) {
+        pattern = fMicros.symbols->getCurrencyPattern();
     }
     if (pattern == nullptr) {
         CldrPatternStyle patternStyle;
@@ -375,11 +313,12 @@
     if (patternModifier->needsPlurals()) {
         patternModifier->setSymbols(
                 fMicros.symbols,
-                currencySymbols,
+                currency,
                 unitWidth,
-                resolvePluralRules(macros.rules, macros.locale, status));
+                resolvePluralRules(macros.rules, macros.locale, status),
+                status);
     } else {
-        patternModifier->setSymbols(fMicros.symbols, currencySymbols, unitWidth, nullptr);
+        patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status);
     }
     if (safe) {
         fImmutablePatternModifier.adoptInstead(patternModifier->createImmutable(status));
diff --git a/icu4c/source/i18n/number_mapper.cpp b/icu4c/source/i18n/number_mapper.cpp
index b730887..ec61743 100644
--- a/icu4c/source/i18n/number_mapper.cpp
+++ b/icu4c/source/i18n/number_mapper.cpp
@@ -13,7 +13,6 @@
 #include "number_patternstring.h"
 #include "unicode/errorcode.h"
 #include "number_utils.h"
-#include "number_currencysymbols.h"
 
 using namespace icu;
 using namespace icu::number;
@@ -81,8 +80,6 @@
         // NOTE: Slicing is OK.
         macros.unit = currency; // NOLINT
     }
-    warehouse.currencySymbols = {currency, locale, symbols, status};
-    macros.currencySymbols = &warehouse.currencySymbols;
 
     ///////////////////////
     // ROUNDING STRATEGY //
diff --git a/icu4c/source/i18n/number_mapper.h b/icu4c/source/i18n/number_mapper.h
index 88e0cb7..d18b8b3 100644
--- a/icu4c/source/i18n/number_mapper.h
+++ b/icu4c/source/i18n/number_mapper.h
@@ -155,7 +155,7 @@
  */
 struct DecimalFormatWarehouse {
     AutoAffixPatternProvider affixProvider;
-    CurrencySymbols currencySymbols;
+
 };
 
 
diff --git a/icu4c/source/i18n/number_patternmodifier.cpp b/icu4c/source/i18n/number_patternmodifier.cpp
index e37dde8..4560294 100644
--- a/icu4c/source/i18n/number_patternmodifier.cpp
+++ b/icu4c/source/i18n/number_patternmodifier.cpp
@@ -34,11 +34,13 @@
 }
 
 void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols,
-                                        const CurrencySymbols* currencySymbols,
-                                        const UNumberUnitWidth unitWidth, const PluralRules* rules) {
+                                        const CurrencyUnit& currency,
+                                        const UNumberUnitWidth unitWidth,
+                                        const PluralRules* rules,
+                                        UErrorCode& status) {
     U_ASSERT((rules != nullptr) == needsPlurals());
     fSymbols = symbols;
-    fCurrencySymbols = currencySymbols;
+    fCurrencySymbols = {currency, symbols->getLocale(), *symbols, status};
     fUnitWidth = unitWidth;
     fRules = rules;
 }
@@ -294,23 +296,23 @@
         case AffixPatternType::TYPE_CURRENCY_SINGLE: {
             // UnitWidth ISO and HIDDEN overrides the singular currency symbol.
             if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) {
-                return fCurrencySymbols->getIntlCurrencySymbol(localStatus);
+                return fCurrencySymbols.getIntlCurrencySymbol(localStatus);
             } else if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) {
                 return UnicodeString();
             } else if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) {
-                return fCurrencySymbols->getNarrowCurrencySymbol(localStatus);
+                return fCurrencySymbols.getNarrowCurrencySymbol(localStatus);
             } else {
-                return fCurrencySymbols->getCurrencySymbol(localStatus);
+                return fCurrencySymbols.getCurrencySymbol(localStatus);
             }
         }
         case AffixPatternType::TYPE_CURRENCY_DOUBLE:
-            return fCurrencySymbols->getIntlCurrencySymbol(localStatus);
+            return fCurrencySymbols.getIntlCurrencySymbol(localStatus);
         case AffixPatternType::TYPE_CURRENCY_TRIPLE:
             // NOTE: This is the code path only for patterns containing "¤¤¤".
             // Plural currencies set via the API are formatted in LongNameHandler.
             // This code path is used by DecimalFormat via CurrencyPluralInfo.
             U_ASSERT(fPlural != StandardPlural::Form::COUNT);
-            return fCurrencySymbols->getPluralName(fPlural, localStatus);
+            return fCurrencySymbols.getPluralName(fPlural, localStatus);
         case AffixPatternType::TYPE_CURRENCY_QUAD:
             return UnicodeString(u"\uFFFD");
         case AffixPatternType::TYPE_CURRENCY_QUINT:
diff --git a/icu4c/source/i18n/number_patternmodifier.h b/icu4c/source/i18n/number_patternmodifier.h
index 2597afe..5ba842d 100644
--- a/icu4c/source/i18n/number_patternmodifier.h
+++ b/icu4c/source/i18n/number_patternmodifier.h
@@ -124,16 +124,18 @@
      *
      * @param symbols
      *            The desired instance of DecimalFormatSymbols.
-     * @param currencySymbols
-     *            The currency symbols to be used when substituting currency values into the affixes.
+     * @param currency
+     *            The currency to be used when substituting currency values into the affixes.
      * @param unitWidth
      *            The width used to render currencies.
      * @param rules
      *            Required if the triple currency sign, "¤¤¤", appears in the pattern, which can be determined from the
      *            convenience method {@link #needsPlurals()}.
+     * @param status
+     *            Set if an error occurs while loading currency data.
      */
-    void setSymbols(const DecimalFormatSymbols* symbols, const CurrencySymbols* currencySymbols,
-                    UNumberUnitWidth unitWidth, const PluralRules* rules);
+    void setSymbols(const DecimalFormatSymbols* symbols, const CurrencyUnit& currency,
+                    UNumberUnitWidth unitWidth, const PluralRules* rules, UErrorCode& status);
 
     /**
      * Sets attributes of the current number being processed.
@@ -206,7 +208,7 @@
     // Symbol details (initialized in setSymbols)
     const DecimalFormatSymbols *fSymbols;
     UNumberUnitWidth fUnitWidth;
-    const CurrencySymbols *fCurrencySymbols;
+    CurrencySymbols fCurrencySymbols;
     const PluralRules *fRules;
 
     // Number details (initialized in setNumberProperties)
diff --git a/icu4c/source/i18n/number_skeletons.cpp b/icu4c/source/i18n/number_skeletons.cpp
index fe96279..e186a56 100644
--- a/icu4c/source/i18n/number_skeletons.cpp
+++ b/icu4c/source/i18n/number_skeletons.cpp
@@ -885,10 +885,6 @@
         status = U_UNSUPPORTED_ERROR;
         return;
     }
-    if (macros.currencySymbols != nullptr) {
-        status = U_UNSUPPORTED_ERROR;
-        return;
-    }
 
     // Remove the trailing space
     if (sb.length() > 0) {
diff --git a/icu4c/source/i18n/unicode/dcfmtsym.h b/icu4c/source/i18n/unicode/dcfmtsym.h
index e1e0ab6..582e753 100644
--- a/icu4c/source/i18n/unicode/dcfmtsym.h
+++ b/icu4c/source/i18n/unicode/dcfmtsym.h
@@ -291,6 +291,17 @@
      */
     void setSymbol(ENumberFormatSymbol symbol, const UnicodeString &value, const UBool propogateDigits);
 
+#ifndef U_HIDE_INTERNAL_API
+    /**
+     * Loads symbols for the specified currency into this instance.
+     *
+     * This method is internal. If you think it should be public, file a ticket.
+     *
+     * @internal
+     */
+    void setCurrency(const UChar* currency, UErrorCode& status);
+#endif  // U_HIDE_INTERNAL_API
+
     /**
      * Returns the locale for which this object was constructed.
      * @stable ICU 2.6
@@ -374,8 +385,6 @@
      */
     void initialize();
 
-    void setCurrencyForSymbols();
-
 public:
 
 #ifndef U_HIDE_INTERNAL_API
diff --git a/icu4c/source/i18n/unicode/numberformatter.h b/icu4c/source/i18n/unicode/numberformatter.h
index 58b13c3..112a285 100644
--- a/icu4c/source/i18n/unicode/numberformatter.h
+++ b/icu4c/source/i18n/unicode/numberformatter.h
@@ -1416,9 +1416,6 @@
     const PluralRules* rules = nullptr;  // no ownership
 
     /** @internal */
-    const CurrencySymbols* currencySymbols = nullptr;  // no ownership
-
-    /** @internal */
     int32_t threshold = kInternalDefaultThreshold;
 
     /** @internal */
diff --git a/icu4c/source/test/intltest/numbertest_patternmodifier.cpp b/icu4c/source/test/intltest/numbertest_patternmodifier.cpp
index 42d3a6f..650c526 100644
--- a/icu4c/source/test/intltest/numbertest_patternmodifier.cpp
+++ b/icu4c/source/test/intltest/numbertest_patternmodifier.cpp
@@ -29,11 +29,10 @@
     mod.setPatternInfo(&patternInfo, kUndefinedField);
     mod.setPatternAttributes(UNUM_SIGN_AUTO, false);
     DecimalFormatSymbols symbols(Locale::getEnglish(), status);
-    CurrencySymbols currencySymbols({u"USD", status}, "en", status);
+    mod.setSymbols(&symbols, {u"USD", status}, UNUM_UNIT_WIDTH_SHORT, nullptr, status);
     if (!assertSuccess("Spot 2", status, true)) {
         return;
     }
-    mod.setSymbols(&symbols, &currencySymbols, UNUM_UNIT_WIDTH_SHORT, nullptr);
 
     mod.setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT);
     assertEquals("Pattern a0b", u"a", getPrefix(mod, status));
@@ -96,11 +95,10 @@
     mod.setPatternInfo(&patternInfo, kUndefinedField);
     mod.setPatternAttributes(UNUM_SIGN_AUTO, false);
     DecimalFormatSymbols symbols(Locale::getEnglish(), status);
-    CurrencySymbols currencySymbols({u"USD", status}, "en", status);
+    mod.setSymbols(&symbols, {u"USD", status}, UNUM_UNIT_WIDTH_SHORT, nullptr, status);
     if (!assertSuccess("Spot 2", status, true)) {
         return;
     }
-    mod.setSymbols(&symbols, &currencySymbols, UNUM_UNIT_WIDTH_SHORT, nullptr);
     mod.setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT);
 
     // Unsafe Code Path
@@ -139,10 +137,9 @@
     mod.setPatternInfo(&patternInfo, kUndefinedField);
     mod.setPatternAttributes(UNUM_SIGN_AUTO, false);
     DecimalFormatSymbols symbols(Locale::getEnglish(), status);
-    CurrencySymbols currencySymbols({u"USD", status}, "en", status);
+    mod.setSymbols(&symbols, {u"USD", status}, UNUM_UNIT_WIDTH_SHORT, nullptr, status);
     assertSuccess("Spot 2", status);
     if (U_FAILURE(status)) { return; }
-    mod.setSymbols(&symbols, &currencySymbols, UNUM_UNIT_WIDTH_SHORT, nullptr);
     DecimalQuantity fq;
     fq.setToInt(1);
 
diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp
index b4fc3a0..9ebd26e 100644
--- a/icu4c/source/test/intltest/numfmtst.cpp
+++ b/icu4c/source/test/intltest/numfmtst.cpp
@@ -238,6 +238,7 @@
   TESTCASE_AUTO(Test13840_ParseLongStringCrash);
   TESTCASE_AUTO(Test13850_EmptyStringCurrency);
   TESTCASE_AUTO(Test20348_CurrencyPrefixOverride);
+  TESTCASE_AUTO(Test20956_MonetarySymbolGetters);
   TESTCASE_AUTO(Test20358_GroupingInPattern);
   TESTCASE_AUTO(Test13731_DefaultCurrency);
   TESTCASE_AUTO(Test20499_CurrencyVisibleDigitsPlural);
@@ -9568,12 +9569,12 @@
         const char16_t* currencyArg;
         UErrorCode expectedError;
     } cases[] = {
-        {u"", U_ZERO_ERROR},
+        {u"", U_USING_FALLBACK_WARNING},
         {u"U", U_ILLEGAL_ARGUMENT_ERROR},
         {u"Us", U_ILLEGAL_ARGUMENT_ERROR},
-        {nullptr, U_ZERO_ERROR},
+        {nullptr, U_USING_FALLBACK_WARNING},
         {u"U$D", U_INVARIANT_CONVERSION_ERROR},
-        {u"Xxx", U_ZERO_ERROR}
+        {u"Xxx", U_USING_FALLBACK_WARNING}
     };
     for (const auto& cas : cases) {
         UnicodeString message(u"with currency arg: ");
@@ -9636,6 +9637,35 @@
         u"$100.00", fmt->format(100, result.remove(), NULL, status));
 }
 
+void NumberFormatTest::Test20956_MonetarySymbolGetters() {
+    IcuTestErrorCode status(*this, "Test20956_MonetarySymbolGetters");
+    LocalPointer<DecimalFormat> decimalFormat(static_cast<DecimalFormat*>(
+        NumberFormat::createCurrencyInstance("et", status)));
+
+    decimalFormat->setCurrency(u"EEK");
+
+    const DecimalFormatSymbols* decimalFormatSymbols = decimalFormat->getDecimalFormatSymbols();
+    assertEquals("MONETARY DECIMAL SEPARATOR",
+        u".",
+        decimalFormatSymbols->getSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol));
+    assertEquals("DECIMAL SEPARATOR",
+        u",",
+        decimalFormatSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol));
+    assertEquals("MONETARY GROUPING SEPARATOR",
+        u" ",
+        decimalFormatSymbols->getSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol));
+    assertEquals("GROUPING SEPARATOR",
+        u" ",
+        decimalFormatSymbols->getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol));
+    assertEquals("CURRENCY SYMBOL",
+        u"kr",
+        decimalFormatSymbols->getSymbol(DecimalFormatSymbols::kCurrencySymbol));
+
+    UnicodeString sb;
+    decimalFormat->format(12345.12, sb, status);
+    assertEquals("OUTPUT", u"12 345.12 kr", sb);
+}
+
 void NumberFormatTest::Test20358_GroupingInPattern() {
     IcuTestErrorCode status(*this, "Test20358_GroupingInPattern");
     LocalPointer<DecimalFormat> fmt(static_cast<DecimalFormat*>(
diff --git a/icu4c/source/test/intltest/numfmtst.h b/icu4c/source/test/intltest/numfmtst.h
index 71b1a77..068ad58 100644
--- a/icu4c/source/test/intltest/numfmtst.h
+++ b/icu4c/source/test/intltest/numfmtst.h
@@ -294,6 +294,7 @@
     void Test13840_ParseLongStringCrash();
     void Test13850_EmptyStringCurrency();
     void Test20348_CurrencyPrefixOverride();
+    void Test20956_MonetarySymbolGetters();
     void Test20358_GroupingInPattern();
     void Test13731_DefaultCurrency();
     void Test20499_CurrencyVisibleDigitsPlural();
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
index a849685..e0233f2 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
@@ -2,8 +2,6 @@
 // License & terms of use: http://www.unicode.org/copyright.html#License
 package com.ibm.icu.number;
 
-import com.ibm.icu.impl.CurrencyData;
-import com.ibm.icu.impl.CurrencyData.CurrencyFormatInfo;
 import com.ibm.icu.impl.FormattedStringBuilder;
 import com.ibm.icu.impl.StandardPlural;
 import com.ibm.icu.impl.number.CompactData.CompactType;
@@ -213,21 +211,16 @@
             micros.symbols = (DecimalFormatSymbols) macros.symbols;
         } else {
             micros.symbols = DecimalFormatSymbols.forNumberingSystem(macros.loc, ns);
+            if (isCurrency) {
+                micros.symbols.setCurrency(currency);
+            }
         }
 
         // Load and parse the pattern string. It is used for grouping sizes and affixes only.
         // If we are formatting currency, check for a currency-specific pattern.
         String pattern = null;
-        if (isCurrency) {
-            CurrencyFormatInfo info = CurrencyData.provider.getInstance(macros.loc, true)
-                    .getFormatInfo(currency.getCurrencyCode());
-            if (info != null) {
-                pattern = info.currencyPattern;
-                // It's clunky to clone an object here, but this code is not frequently executed.
-                micros.symbols = (DecimalFormatSymbols) micros.symbols.clone();
-                micros.symbols.setMonetaryDecimalSeparatorString(info.monetaryDecimalSeparator);
-                micros.symbols.setMonetaryGroupingSeparatorString(info.monetaryGroupingSeparator);
-            }
+        if (isCurrency && micros.symbols.getCurrencyPattern() != null) {
+            pattern = micros.symbols.getCurrencyPattern();
         }
         if (pattern == null) {
             int patternStyle;
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
index 1bfeff4..a83bd1b 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
@@ -787,7 +787,15 @@
    */
   @Override
   public StringBuffer format(CurrencyAmount currAmt, StringBuffer result, FieldPosition fieldPosition) {
-    FormattedNumber output = formatter.format(currAmt);
+    // We need to make localSymbols in order for monetary symbols to be initialized.
+    // Also, bypass the CurrencyAmount override of LocalizedNumberFormatter#format,
+    // because its caching mechanism will not provide any benefit here.
+    DecimalFormatSymbols localSymbols = (DecimalFormatSymbols) symbols.clone();
+    localSymbols.setCurrency(currAmt.getCurrency());
+    FormattedNumber output = formatter
+            .symbols(localSymbols)
+            .unit(currAmt.getCurrency())
+            .format(currAmt.getNumber());
     fieldPositionHelper(output, fieldPosition, result.length());
     output.appendTo(result);
     return result;
@@ -2043,11 +2051,8 @@
   @Override
   public synchronized void setCurrency(Currency currency) {
     properties.setCurrency(currency);
-    // Backwards compatibility: also set the currency in the DecimalFormatSymbols
     if (currency != null) {
       symbols.setCurrency(currency);
-      String symbol = currency.getName(symbols.getULocale(), Currency.SYMBOL_NAME, null);
-      symbols.setCurrencySymbol(symbol);
     }
     refreshFormatter();
   }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java
index 8ad220f..749971b 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java
@@ -888,9 +888,32 @@
         if (currency == null) {
             throw new NullPointerException();
         }
+        if (currency.equals(this.currency)) {
+            return;
+        }
+        CurrencyDisplayInfo displayInfo = CurrencyData.provider.getInstance(ulocale, true);
+        setCurrencyOrNull(currency, displayInfo);
+    }
+
+    private void setCurrencyOrNull(Currency currency, CurrencyDisplayInfo displayInfo) {
         this.currency = currency;
+
+        if (currency == null) {
+            intlCurrencySymbol = "XXX";
+            currencySymbol = "\u00A4"; // 'OX' currency symbol
+            currencyPattern = null;
+            return;
+        }
+
         intlCurrencySymbol = currency.getCurrencyCode();
-        currencySymbol = currency.getSymbol(requestedLocale);
+        currencySymbol = currency.getSymbol(ulocale);
+
+        CurrencyFormatInfo formatInfo = displayInfo.getFormatInfo(currency.getCurrencyCode());
+        if (formatInfo != null) {
+            setMonetaryDecimalSeparatorString(formatInfo.monetaryDecimalSeparator);
+            setMonetaryGroupingSeparatorString(formatInfo.monetaryGroupingSeparator);
+            currencyPattern = formatInfo.currencyPattern;
+        }
     }
 
     /**
@@ -1004,11 +1027,12 @@
     }
 
     /**
-    }
      * Internal API for NumberFormat
      * @return String currency pattern string
+     * @deprecated This API is for ICU internal use only
      */
-    String getCurrencyPattern() {
+    @Deprecated
+    public String getCurrencyPattern() {
         return currencyPattern;
     }
 
@@ -1396,30 +1420,10 @@
         padEscape = '*';
         sigDigit  = '@';
 
+        CurrencyDisplayInfo displayInfo = CurrencyData.provider.getInstance(ulocale, true);
+        initSpacingInfo(displayInfo.getSpacingInfo());
 
-        CurrencyDisplayInfo info = CurrencyData.provider.getInstance(locale, true);
-
-        // Obtain currency data from the currency API.  This is strictly
-        // for backward compatibility; we don't use DecimalFormatSymbols
-        // for currency data anymore.
-        currency = Currency.getInstance(locale);
-        if (currency != null) {
-            intlCurrencySymbol = currency.getCurrencyCode();
-            currencySymbol = currency.getName(locale, Currency.SYMBOL_NAME, null);
-            CurrencyFormatInfo fmtInfo = info.getFormatInfo(intlCurrencySymbol);
-            if (fmtInfo != null) {
-                currencyPattern = fmtInfo.currencyPattern;
-                setMonetaryDecimalSeparatorString(fmtInfo.monetaryDecimalSeparator);
-                setMonetaryGroupingSeparatorString(fmtInfo.monetaryGroupingSeparator);
-            }
-        } else {
-            intlCurrencySymbol = "XXX";
-            currencySymbol = "\u00A4"; // 'OX' currency symbol
-        }
-
-
-        // Get currency spacing data.
-        initSpacingInfo(info.getSpacingInfo());
+        setCurrencyOrNull(Currency.getInstance(ulocale), displayInfo);
     }
 
     private static CacheData loadData(ULocale locale) {
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
index 42034df..5d35226 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
@@ -43,6 +43,7 @@
 import com.ibm.icu.dev.test.TestFmwk;
 import com.ibm.icu.dev.test.TestUtil;
 import com.ibm.icu.dev.test.format.IntlTestDecimalFormatAPIC.FieldContainer;
+import com.ibm.icu.impl.DontCareFieldPosition;
 import com.ibm.icu.impl.ICUConfig;
 import com.ibm.icu.impl.LocaleUtility;
 import com.ibm.icu.impl.data.ResourceReader;
@@ -6670,6 +6671,26 @@
     }
 
     @Test
+    public void test20956_MonetarySymbolGetters() {
+        Locale locale = new Locale.Builder().setLocale(Locale.forLanguageTag("et")).build();
+        DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
+        Currency currency = Currency.getInstance("EEK");
+
+        decimalFormat.setCurrency(currency);
+
+        DecimalFormatSymbols decimalFormatSymbols = decimalFormat.getDecimalFormatSymbols();
+        assertEquals("MONETARY DECIMAL SEPARATOR", ".", decimalFormatSymbols.getMonetaryDecimalSeparatorString());
+        assertEquals("DECIMAL SEPARATOR", ",", decimalFormatSymbols.getDecimalSeparatorString());
+        assertEquals("MONETARY GROUPING SEPARATOR", " ", decimalFormatSymbols.getMonetaryGroupingSeparatorString());
+        assertEquals("GROUPING SEPARATOR", " ", decimalFormatSymbols.getGroupingSeparatorString());
+        assertEquals("CURRENCY SYMBOL", "kr", decimalFormatSymbols.getCurrencySymbol());
+
+        StringBuffer sb = new StringBuffer();
+        decimalFormat.format(new BigDecimal(12345.12), sb, DontCareFieldPosition.INSTANCE);
+        assertEquals("OUTPUT", "12 345.12 kr", sb.toString());
+    }
+
+    @Test
     public void test20358_GroupingInPattern() {
         DecimalFormat fmt = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH);
         assertEquals("Initial pattern",