]> granicus.if.org Git - icu/commitdiff
ICU-21349 Add extra ComplexUnitsConverter constructor that takes only CLDR units...
authorYounies Mahmoud <younies.mahmoud@gmail.com>
Fri, 19 Feb 2021 01:50:46 +0000 (01:50 +0000)
committerYounies Mahmoud <younies@chromium.org>
Fri, 19 Feb 2021 06:54:39 +0000 (07:54 +0100)
See #1586

icu4c/source/i18n/units_complexconverter.cpp
icu4c/source/i18n/units_complexconverter.h
icu4c/source/test/intltest/units_test.cpp
icu4j/main/classes/core/src/com/ibm/icu/impl/units/ComplexUnitsConverter.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java

index 2aa331ac3f0724537f39cf1751feb41ee005c514..8ecb1b38531a0b5fa6cf6cfe298d495ea4eb492f 100644 (file)
@@ -42,9 +42,24 @@ ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnitImpl &targetUnit,
             return;
         }
     }
+
     this->init(*biggestUnit, ratesInfo, status);
 }
 
+ComplexUnitsConverter::ComplexUnitsConverter(StringPiece inputUnitIdentifier,
+                                             StringPiece outputUnitsIdentifier, UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    MeasureUnitImpl inputUnit = MeasureUnitImpl::forIdentifier(inputUnitIdentifier, status);
+    MeasureUnitImpl outputUnits = MeasureUnitImpl::forIdentifier(outputUnitsIdentifier, status);
+
+    this->units_ = outputUnits.extractIndividualUnitsWithIndices(status);
+    U_ASSERT(units_.length() != 0);
+
+    this->init(inputUnit, ConversionRates(status), status);
+}
+
 ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnitImpl &inputUnit,
                                              const MeasureUnitImpl &outputUnits,
                                              const ConversionRates &ratesInfo, UErrorCode &status)
index 581e52f3cb3e5b0cb3f35bc4d64f719045fd2fba..21f3d9b49b9786eea83cd4b258bed14af978d9f8 100644 (file)
@@ -52,14 +52,15 @@ class U_I18N_API ComplexUnitsConverter : public UMemory {
      * Constructs `ComplexUnitsConverter` for an `targetUnit` that could be Single, Compound or Mixed.
      * In case of:
      * 1- Single and Compound units,
-     * the conversion will not perform anything, the input will be equal to the output.
+     *    the conversion will not perform anything, the input will be equal to the output.
      * 2- Mixed Unit
-     * the conversion will consider the input is the biggest unit. And will convert it to be spread
-     * through the target units. For example: if target unit is "inch-and-foot", and the input is 2.5. The
-     * converter will consider the input value in "foot", because foot is the biggest unit. Then, it
-     * will convert 2.5 feet to "inch-and-foot".
+     *    the conversion will consider the input is the biggest unit. And will convert it to be spread
+     *    through the target units. For example: if target unit is "inch-and-foot", and the input is 2.5.
+     *    The converter will consider the input value in "foot", because foot is the biggest unit.
+     *    Then, it will convert 2.5 feet to "inch-and-foot".
      *
-     * @param targetUnit could be any type. (single, compound or mixed).
+     * @param targetUnit could be any units type (single, compound or mixed).
+     * @param ratesInfo
      * @param status
      */
     ComplexUnitsConverter(const MeasureUnitImpl &targetUnit, const ConversionRates &ratesInfo,
@@ -74,6 +75,20 @@ class U_I18N_API ComplexUnitsConverter : public UMemory {
      * @param outputUnits represents the output unit. could be any type. (single, compound or mixed).
      * @param status
      */
+    ComplexUnitsConverter(StringPiece inputUnitIdentifier, StringPiece outputUnitsIdentifier,
+                          UErrorCode &status);
+
+    /**
+     * Constructor of `ComplexUnitsConverter`.
+     * NOTE:
+     *   - inputUnit and outputUnits must be under the same category
+     *      - e.g. meter to feet and inches --> all of them are length units.
+     *
+     * @param inputUnit represents the source unit. (should be single or compound unit).
+     * @param outputUnits represents the output unit. could be any type. (single, compound or mixed).
+     * @param ratesInfo
+     * @param status
+     */
     ComplexUnitsConverter(const MeasureUnitImpl &inputUnit, const MeasureUnitImpl &outputUnits,
                           const ConversionRates &ratesInfo, UErrorCode &status);
 
index 93b865aeb186dbf2644538fb064c081f78f3778e..3a8522bf46f659d599ef3815ad0274e575271743 100644 (file)
@@ -515,7 +515,7 @@ void UnitsTest::testConverterWithCLDRTests() {
 void UnitsTest::testComplexUnitsConverter() {
     IcuTestErrorCode status(*this, "UnitsTest::testComplexUnitsConverter");
 
-    // DBL_EPSILON is aproximately 2.22E-16, and is the precision of double for
+    // DBL_EPSILON is approximately 2.22E-16, and is the precision of double for
     // values in the range [1.0, 2.0), but half the precision of double for
     // [2.0, 4.0).
     U_ASSERT(1.0 + DBL_EPSILON > 1.0);
@@ -621,15 +621,10 @@ void UnitsTest::testComplexUnitsConverter() {
     MeasureUnit input, output;
     MeasureUnitImpl tempInput, tempOutput;
     MaybeStackVector<Measure> measures;
-    for (const TestCase &testCase : testCases) {
-        input = MeasureUnit::forIdentifier(testCase.input, status);
-        output = MeasureUnit::forIdentifier(testCase.output, status);
-        const MeasureUnitImpl& inputImpl = MeasureUnitImpl::forMeasureUnit(input, tempInput, status);
-        const MeasureUnitImpl& outputImpl = MeasureUnitImpl::forMeasureUnit(output, tempOutput, status);
-        auto converter = ComplexUnitsConverter(inputImpl, outputImpl, rates, status);
+    auto testATestCase = [&](const ComplexUnitsConverter& converter ,StringPiece initMsg , const TestCase &testCase) {
         measures = converter.convert(testCase.value, nullptr, status);
 
-        CharString msg;
+        CharString msg(initMsg, status);
         msg.append(testCase.msg, status);
         msg.append(" ", status);
         msg.append(testCase.input, status);
@@ -650,7 +645,24 @@ void UnitsTest::testComplexUnitsConverter() {
             assertEquals(msg.data(), testCase.expected[i].getUnit().getIdentifier(),
                          measures[i]->getUnit().getIdentifier());
         }
+    };
+
+    for (const auto &testCase : testCases)
+    {
+        input = MeasureUnit::forIdentifier(testCase.input, status);
+        output = MeasureUnit::forIdentifier(testCase.output, status);
+        const MeasureUnitImpl& inputImpl = MeasureUnitImpl::forMeasureUnit(input, tempInput, status);
+        const MeasureUnitImpl& outputImpl = MeasureUnitImpl::forMeasureUnit(output, tempOutput, status);
+
+        ComplexUnitsConverter converter1(inputImpl, outputImpl, rates, status);
+        testATestCase(converter1, "ComplexUnitsConverter #1 " , testCase);
+
+        // Test ComplexUnitsConverter created with CLDR units identifiers.
+        ComplexUnitsConverter converter2( testCase.input, testCase.output, status);
+        testATestCase(converter2, "ComplexUnitsConverter #1 " , testCase);
     }
+    
+    
     status.assertSuccess();
 
     // TODO(icu-units#63): test negative numbers!
@@ -714,20 +726,6 @@ void UnitsTest::testComplexUnitsConverterSorting() {
             }
         }
     }
-
-    MeasureUnitImpl source = MeasureUnitImpl::forIdentifier("meter", status);
-    MeasureUnitImpl target = MeasureUnitImpl::forIdentifier("inch-and-foot", status);
-
-    ComplexUnitsConverter complexConverter(source, target, conversionRates, status);
-    auto measures = complexConverter.convert(10.0, nullptr, status);
-
-    if (2 == measures.length()) {
-        assertEquals("inch-and-foot unit 0", "inch", measures[0]->getUnit().getIdentifier());
-        assertEquals("inch-and-foot unit 1", "foot", measures[1]->getUnit().getIdentifier());
-
-        assertEqualsNear("inch-and-foot value 0", 9.7008, measures[0]->getNumber().getDouble(), 0.0001);
-        assertEqualsNear("inch-and-foot value 1", 32, measures[1]->getNumber().getInt64(), 0.00001);
-    }
 }
 
 /**
index 134faa33e266359f17a2a43cefcea21b04ffd35b..8a2b2f27f755879fe10a12d01682c50d3c0b4749 100644 (file)
@@ -64,14 +64,32 @@ public class ComplexUnitsConverter {
      * Constructs <code>ComplexUnitsConverter</code> NOTE: - inputUnit and outputUnits must be under the same category -
      * e.g. meter to feet and inches --> all of them are length units.
      *
-     * @param targetUnit
+     * @param inputUnitIdentifier
+     *              represents the source unit identifier. (should be single or compound unit).
+     * @param outputUnitsIdentifier
+     *              represents the output unit identifier. could be any type. (single, compound or mixed).
+     */
+    public ComplexUnitsConverter(String inputUnitIdentifier, String outputUnitsIdentifier) {
+        this(
+                MeasureUnitImpl.forIdentifier(inputUnitIdentifier),
+                MeasureUnitImpl.forIdentifier(outputUnitsIdentifier),
+                new ConversionRates()
+        );
+    }
+
+    /**
+     * Constructs <code>ComplexUnitsConverter</code> NOTE: - inputUnit and outputUnits must be under the same category -
+     * e.g. meter to feet and inches --> all of them are length units.
+     *
+     * @param inputUnit
      *            represents the source unit. (should be single or compound unit).
      * @param outputUnits
      *            represents the output unit. could be any type. (single, compound or mixed).
+     * @param conversionRates
      */
-    public ComplexUnitsConverter(MeasureUnitImpl targetUnit, MeasureUnitImpl outputUnits,
+    public ComplexUnitsConverter(MeasureUnitImpl inputUnit, MeasureUnitImpl outputUnits,
             ConversionRates conversionRates) {
-        this.inputUnit_ = targetUnit;
+        this.inputUnit_ = inputUnit;
         this.units_ = outputUnits.extractIndividualUnitsWithIndices();
         assert (!this.units_.isEmpty());
 
index 8155be3bb3338cfe7305f4ffefee20c70a97b5d9..952ffcdc756af3578a3658b72af307a545e2783e 100644 (file)
@@ -55,7 +55,31 @@ public class UnitsTest {
                 this.expected = expected;
                 this.accuracy = accuracy;
             }
+
+            void testATestCase(ComplexUnitsConverter converter) {
+                List<Measure> measures = converter.convert(value, null).measures;
+
+                assertEquals("measures length", expected.length, measures.size());
+                int i = 0;
+                for (Measure measure : measures) {
+                    double accuracy = 0.0;
+                    if (i == expected.length - 1) {
+                        accuracy = accuracy;
+                    }
+                    assertTrue("input " + value + ", output measure " + i + ": expected " +
+                                    expected[i] + ", expected unit " +
+                                    expected[i].getUnit() + " got unit " + measure.getUnit(),
+                            expected[i].getUnit().equals(measure.getUnit()));
+                    assertEquals("input " + value + ", output measure " + i + ": expected " +
+                                    expected[i] + ", expected number " +
+                                    expected[i].getNumber() + " got number " + measure.getNumber(),
+                            expected[i].getNumber().doubleValue(),
+                            measure.getNumber().doubleValue(), accuracy);
+                    i++;
+                }
+            }
         }
+
         TestCase[] testCases = new TestCase[] {
             // Significantly less than 2.0.
             new TestCase(
@@ -129,35 +153,21 @@ public class UnitsTest {
                          0),
         };
 
+
         ConversionRates rates = new ConversionRates();
         MeasureUnit input, output;
-        List<Measure> measures;
         for (TestCase testCase : testCases) {
             input = MeasureUnit.forIdentifier(testCase.input);
             output = MeasureUnit.forIdentifier(testCase.output);
             final MeasureUnitImpl inputImpl = MeasureUnitImpl.forIdentifier(input.getIdentifier());
             final MeasureUnitImpl outputImpl = MeasureUnitImpl.forIdentifier(output.getIdentifier());
-            ComplexUnitsConverter converter = new ComplexUnitsConverter(inputImpl, outputImpl, rates);
-            measures = converter.convert(testCase.value, null).measures;
-
-            assertEquals("measures length", testCase.expected.length, measures.size());
-            int i = 0;
-            for (Measure measure : measures) {
-                double accuracy = 0.0;
-                if (i == testCase.expected.length - 1) {
-                    accuracy = testCase.accuracy;
-                }
-                assertTrue("input " + testCase.value + ", output measure " + i + ": expected " +
-                               testCase.expected[i] + ", expected unit " +
-                               testCase.expected[i].getUnit() + " got unit " + measure.getUnit(),
-                           testCase.expected[i].getUnit().equals(measure.getUnit()));
-                assertEquals("input " + testCase.value + ", output measure " + i + ": expected " +
-                                 testCase.expected[i] + ", expected number " +
-                                 testCase.expected[i].getNumber() + " got number " + measure.getNumber(),
-                             testCase.expected[i].getNumber().doubleValue(),
-                             measure.getNumber().doubleValue(), accuracy);
-                i++;
-            }
+            ComplexUnitsConverter converter1 = new ComplexUnitsConverter(inputImpl, outputImpl, rates);
+
+            testCase.testATestCase(converter1);
+
+            // Test ComplexUnitsConverter created with CLDR units identifiers.
+            ComplexUnitsConverter converter2 = new ComplexUnitsConverter(testCase.input, testCase.output);
+            testCase.testATestCase(converter2);
         }
 
         // TODO(icu-units#63): test negative numbers!
@@ -165,7 +175,7 @@ public class UnitsTest {
 
 
     @Test
-    public void testComplexUnitConverterSorting() {
+    public void testComplexUnitsConverterSorting() {
         class TestCase {
             String message;
             String inputUnit;