ICU-21349 Add extra UnitsConverter constructor that takes only CLDR unit identifiers

See #1578
diff --git a/icu4c/source/i18n/units_converter.cpp b/icu4c/source/i18n/units_converter.cpp
index 710363e..4858cbd 100644
--- a/icu4c/source/i18n/units_converter.cpp
+++ b/icu4c/source/i18n/units_converter.cpp
@@ -490,15 +490,36 @@
 }
 
 UnitsConverter::UnitsConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target,
-                             const ConversionRates &ratesInfo, UErrorCode &status)
+                               const ConversionRates &ratesInfo, UErrorCode &status)
     : conversionRate_(source.copy(status), target.copy(status)) {
-    if (source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
-        target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
+    this->init(ratesInfo, status);
+}
+
+UnitsConverter::UnitsConverter(StringPiece sourceIdentifier, StringPiece targetIdentifier,
+                               UErrorCode &status)
+    : conversionRate_(MeasureUnitImpl::forIdentifier(sourceIdentifier, status),
+                      MeasureUnitImpl::forIdentifier(targetIdentifier, status)) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    ConversionRates ratesInfo(status);
+    this->init(ratesInfo, status);
+}
+
+void UnitsConverter::init(const ConversionRates &ratesInfo, UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    if (this->conversionRate_.source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
+        this->conversionRate_.target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
         status = U_INTERNAL_PROGRAM_ERROR;
         return;
     }
 
-    Convertibility unitsState = extractConvertibility(source, target, ratesInfo, status);
+    Convertibility unitsState = extractConvertibility(this->conversionRate_.source,
+                                                      this->conversionRate_.target, ratesInfo, status);
     if (U_FAILURE(status)) return;
     if (unitsState == Convertibility::UNCONVERTIBLE) {
         status = U_INTERNAL_PROGRAM_ERROR;
@@ -507,11 +528,12 @@
 
     loadConversionRate(conversionRate_, conversionRate_.source, conversionRate_.target, unitsState,
                        ratesInfo, status);
+                          
 }
 
 int32_t UnitsConverter::compareTwoUnits(const MeasureUnitImpl &firstUnit,
-                                       const MeasureUnitImpl &secondUnit,
-                                       const ConversionRates &ratesInfo, UErrorCode &status) {
+                                        const MeasureUnitImpl &secondUnit,
+                                        const ConversionRates &ratesInfo, UErrorCode &status) {
     if (U_FAILURE(status)) {
         return 0;
     }
@@ -532,8 +554,9 @@
         return 0;
     }
 
-    // Represents the conversion factor from the firstUnit to the base unit that specified in the
-    // conversion data which is considered as the root of the firstUnit and the secondUnit.
+    // Represents the conversion factor from the firstUnit to the base
+    // unit that specified in the conversion data which is considered as
+    // the root of the firstUnit and the secondUnit.
     Factor firstUnitToBase = loadCompoundFactor(firstUnit, ratesInfo, status);
     Factor secondUnitToBase = loadCompoundFactor(secondUnit, ratesInfo, status);
 
diff --git a/icu4c/source/i18n/units_converter.h b/icu4c/source/i18n/units_converter.h
index e8a0cae..1d5a3f8 100644
--- a/icu4c/source/i18n/units_converter.h
+++ b/icu4c/source/i18n/units_converter.h
@@ -150,6 +150,20 @@
      * NOTE:
      *   - source and target must be under the same category
      *      - e.g. meter to mile --> both of them are length units.
+     * NOTE:
+     *    This constructor creates an instance of `ConversionRates` internally.
+     *
+     * @param sourceIdentifier represents the source unit identifier.
+     * @param targetIdentifier represents the target unit identifier.
+     * @param status
+     */
+    UnitsConverter(StringPiece sourceIdentifier, StringPiece targetIdentifier, UErrorCode &status);
+
+    /**
+     * Constructor of `UnitConverter`.
+     * NOTE:
+     *   - source and target must be under the same category
+     *      - e.g. meter to mile --> both of them are length units.
      *
      * @param source represents the source unit.
      * @param target represents the target unit.
@@ -191,6 +205,11 @@
 
   private:
     ConversionRate conversionRate_;
+
+    /**
+     * Initialises the object.
+     */ 
+    void init(const ConversionRates &ratesInfo, UErrorCode &status);
 };
 
 } // namespace units
diff --git a/icu4c/source/test/intltest/units_test.cpp b/icu4c/source/test/intltest/units_test.cpp
index a18e751..8e9bfbc 100644
--- a/icu4c/source/test/intltest/units_test.cpp
+++ b/icu4c/source/test/intltest/units_test.cpp
@@ -363,6 +363,30 @@
         assertEqualsNear(
             UnicodeString("testConverter inverse: ") + testCase.target + " back to " + testCase.source,
             testCase.inputValue, converter.convertInverse(testCase.expectedValue), maxDelta);
+
+
+        // TODO: Test UnitsConverter created using CLDR separately.
+        // Test UnitsConverter created by CLDR unit identifiers
+        UnitsConverter converter2(testCase.source, testCase.target, status);
+        if (status.errIfFailureAndReset("UnitsConverter(<%s>, <%s>, ...)", testCase.source,
+                                        testCase.target)) {
+            continue;
+        }
+
+        maxDelta = 1e-6 * uprv_fabs(testCase.expectedValue);
+        if (testCase.expectedValue == 0) {
+            maxDelta = 1e-12;
+        }
+        assertEqualsNear(UnicodeString("testConverter2: ") + testCase.source + " to " + testCase.target,
+                         testCase.expectedValue, converter2.convert(testCase.inputValue), maxDelta);
+
+        maxDelta = 1e-6 * uprv_fabs(testCase.inputValue);
+        if (testCase.inputValue == 0) {
+            maxDelta = 1e-12;
+        }
+        assertEqualsNear(
+            UnicodeString("testConverter2 inverse: ") + testCase.target + " back to " + testCase.source,
+            testCase.inputValue, converter2.convertInverse(testCase.expectedValue), maxDelta);
     }
 }
 
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsConverter.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsConverter.java
index f732c2f..9f80f43 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsConverter.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsConverter.java
@@ -21,6 +21,26 @@
      * NOTE:
      * - source and target must be under the same category
      * - e.g. meter to mile --> both of them are length units.
+     * <p>
+     * NOTE:
+     * This constructor creates an instance of <code>UnitsConverter</code> internally.
+     *
+     * @param sourceIdentifier represents the source unit identifier.
+     * @param targetIdentifier represents the target unit identifier.
+     */
+    public UnitsConverter(String sourceIdentifier, String targetIdentifier) {
+        this(
+                MeasureUnitImpl.forIdentifier(sourceIdentifier),
+                MeasureUnitImpl.forIdentifier(targetIdentifier),
+                new ConversionRates()
+        );
+    }
+
+    /**
+     * Constructor of <code>UnitsConverter</code>.
+     * NOTE:
+     * - source and target must be under the same category
+     * - e.g. meter to mile --> both of them are length units.
      *
      * @param source          represents the source unit.
      * @param target          represents the target unit.
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
index 952ffcd..107503a 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
@@ -371,14 +371,14 @@
     @Test
     public void testConverter() {
         class TestData {
-            MeasureUnitImpl source;
-            MeasureUnitImpl target;
-            BigDecimal input;
-            BigDecimal expected;
+            final String sourceIdentifier;
+            final String targetIdentifier;
+            final BigDecimal input;
+            final BigDecimal expected;
 
-            TestData(String source, String target, double input, double expected) {
-                this.source = MeasureUnitImpl.UnitsParser.parseForIdentifier(source);
-                this.target = MeasureUnitImpl.UnitsParser.parseForIdentifier(target);
+            TestData(String sourceIdentifier, String targetIdentifier, double input, double expected) {
+                this.sourceIdentifier = sourceIdentifier;
+                this.targetIdentifier = targetIdentifier;
                 this.input = BigDecimal.valueOf(input);
                 this.expected = BigDecimal.valueOf(expected);
             }
@@ -440,23 +440,48 @@
 
         ConversionRates conversionRates = new ConversionRates();
         for (TestData test : tests) {
-            UnitsConverter converter = new UnitsConverter(test.source, test.target, conversionRates);
+            MeasureUnitImpl source = MeasureUnitImpl.forIdentifier(test.sourceIdentifier);
+            MeasureUnitImpl target = MeasureUnitImpl.forIdentifier(test.targetIdentifier);
+
+            UnitsConverter converter = new UnitsConverter(source, target, conversionRates);
 
             double maxDelta = 1e-6 * Math.abs(test.expected.doubleValue());
             if (test.expected.doubleValue() == 0) {
                 maxDelta = 1e-12;
             }
-            assertEquals("testConverter: " + test.source + " to " + test.target,
-                         test.expected.doubleValue(), converter.convert(test.input).doubleValue(),
-                         maxDelta);
+            assertEquals("testConverter: " + test.sourceIdentifier + " to " + test.targetIdentifier,
+                    test.expected.doubleValue(), converter.convert(test.input).doubleValue(),
+                    maxDelta);
 
             maxDelta = 1e-6 * Math.abs(test.input.doubleValue());
             if (test.input.doubleValue() == 0) {
                 maxDelta = 1e-12;
             }
-            assertEquals("testConverter inverse: " + test.target + " back to " + test.source,
-                         test.input.doubleValue(), converter.convertInverse(test.expected).doubleValue(),
-                         maxDelta);
+            assertEquals(
+                    "testConverter inverse: " + test.targetIdentifier + " back to " + test.sourceIdentifier,
+                    test.input.doubleValue(), converter.convertInverse(test.expected).doubleValue(),
+                    maxDelta);
+
+
+            // TODO: Test UnitsConverter created using CLDR separately.
+            // Test UnitsConverter created by CLDR unit identifiers
+            UnitsConverter converter2 = new UnitsConverter(test.sourceIdentifier, test.targetIdentifier);
+
+            maxDelta = 1e-6 * Math.abs(test.expected.doubleValue());
+            if (test.expected.doubleValue() == 0) {
+                maxDelta = 1e-12;
+            }
+            assertEquals("testConverter2: " + test.sourceIdentifier + " to " + test.targetIdentifier,
+                    test.expected.doubleValue(), converter2.convert(test.input).doubleValue(),
+                    maxDelta);
+
+            maxDelta = 1e-6 * Math.abs(test.input.doubleValue());
+            if (test.input.doubleValue() == 0) {
+                maxDelta = 1e-12;
+            }
+            assertEquals("testConverter2 inverse: " + test.targetIdentifier + " back to " + test.sourceIdentifier,
+                    test.input.doubleValue(), converter2.convertInverse(test.expected).doubleValue(),
+                    maxDelta);
         }
     }