From: Hugo van der Merwe <17109322+hugovdm@users.noreply.github.com> Date: Tue, 17 Mar 2020 21:31:59 +0000 (+0100) Subject: WIP: c-style ures_* handling. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=16d9f0b538c76f1c18308f9ccef498d72de19e7a;p=icu WIP: c-style ures_* handling. --- diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in index 594d491f6f7..ff640d523d5 100644 --- a/icu4c/source/test/intltest/Makefile.in +++ b/icu4c/source/test/intltest/Makefile.in @@ -69,7 +69,7 @@ string_segment_test.o \ numbertest_parse.o numbertest_doubleconversion.o numbertest_skeletons.o \ static_unisets_test.o numfmtdatadriventest.o numbertest_range.o erarulestest.o \ formattedvaluetest.o formatted_string_builder_test.o numbertest_permutation.o \ -unitstest.o +unitstest.o WIP_units_resources.o DEPS = $(OBJECTS:.o=.d) diff --git a/icu4c/source/test/intltest/WIP_units_resources.cpp b/icu4c/source/test/intltest/WIP_units_resources.cpp new file mode 100644 index 00000000000..a4419316f0a --- /dev/null +++ b/icu4c/source/test/intltest/WIP_units_resources.cpp @@ -0,0 +1,198 @@ +#include "wip_units_resources.h" +#include "cstring.h" +#include "intltest.h" +#include "unicode/ctest.h" +#include "unicode/measunit.h" +#include "unicode/testlog.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" + +// Resources. It should be migrated to a permanent location with updated API, +// once we know what that will look like and where that will be. + + +void loadResources(const char *usage, const char *inputUnit, const char *outputRegion, + UnitConversionResourceBundle *result, UErrorCode &status) { + // WIP/TODO: pull category from unit? unitQuantities? Or at least cross-check? + if (U_FAILURE(status)) return; + + MeasureUnit inpUnit = MeasureUnit::forIdentifier(inputUnit, status); + fprintf(stderr, "MeasureUnit Identifier: %s\n", inpUnit.getIdentifier()); + fprintf(stderr, "MeasureUnit Type: %s\n", inpUnit.getType()); + fprintf(stderr, "MeasureUnit Subtype: %s\n", inpUnit.getSubtype()); + fprintf(stderr, "MeasureUnit SI prefix: %d\n", inpUnit.getSIPrefix(status)); + + // FIXME: if kilogram don't drop the SI! + // + // FIXME: To find the base unit, we must traverse targets - so this is wrong. + MeasureUnit inputUnitNoSI = inpUnit.withSIPrefix(UMEASURE_SI_PREFIX_ONE, status); + if (strcmp(inputUnitNoSI.getIdentifier(), "gram") == 0) inputUnitNoSI = MeasureUnit::getKilogram(); + UMeasureSIPrefix siPrefix = inpUnit.getSIPrefix(status); + fprintf(stderr, "SI Prefix: %d\n", siPrefix); + fprintf(stderr, "base unit: %s\n", inputUnitNoSI.getIdentifier()); + + + + // WIP: consider using the LocalUResourceBundlePointer "Smart pointer" class: + // LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "units", &status)); + UResourceBundle *units = ures_openDirect(NULL, "units", &status); + if (U_FAILURE(status)) { + fprintf(stderr, "open units failed\n"); + return; + } + + int32_t len = -1; + + // UResourceBundle struct can be reused via "fillin" if we need only one: + // ures_getByKey(convertUnits, "convertUnits", convertUnits, &status); + UResourceBundle *convertUnits = ures_getByKey(units, "convertUnits", NULL, &status); + if (U_FAILURE(status)) { + fprintf(stderr, "open convertUnits failed\n"); + return; + } + UResourceBundle *unitDetails = ures_getByKey(convertUnits, inputUnitNoSI.getIdentifier(), NULL, &status); + if (U_FAILURE(status)) { + fprintf(stderr, "get convertUnits/%s failed\n", inputUnitNoSI.getIdentifier()); + return; + } + const UChar *factor = ures_getStringByKey(unitDetails, "factor", &len, &status); + if (U_FAILURE(status)) { + fprintf(stderr, "get factor failed\n"); + return; + } + const UChar *offset = ures_getStringByKey(unitDetails, "offset", &len, &status); + if (status == U_MISSING_RESOURCE_ERROR) status = U_ZERO_ERROR; + const UChar *uTarget = ures_getStringByKey(unitDetails, "target", &len, &status); + if (U_FAILURE(status)) { + fprintf(stderr, "get target failed\n"); + return; + } + LocalArray targetP(new char[len+1]); + int32_t outputLen; + u_strToUTF8(targetP.getAlias(), len + 1, &outputLen, uTarget, len, &status); + UnicodeString debugOutp = "factor: \"" + UnicodeString(factor) + "\", offset: \"" + + UnicodeString(offset) + "\", target: \"" + UnicodeString(uTarget) + + "\", len: " + Int64ToUnicodeString(len) + "\n"; + std::string debugOut; + debugOutp.toUTF8String(debugOut); + fprintf(stderr, "%s", debugOut.c_str()); + + + + + UResourceBundle *unitQuantities = ures_getByKey(units, "unitQuantities", NULL, &status); + if (U_FAILURE(status)) { + fprintf(stderr, "open unitQuantities failed\n"); + return; + } + const UChar *uCategory = + ures_getStringByKey(unitQuantities, targetP.getAlias(), &len, &status); + if (U_FAILURE(status)) { + fprintf(stderr, "unitQuantities/getStringByKey failed: key: %s, len: %d\n", targetP.getAlias(), len); + return; + } + + // LocalArray category(new char[len+1]); + + int32_t resCap; + char *appBuf = result->category.getAppendBuffer(len+1, len+1, resCap, status); + u_strToUTF8(appBuf, len, &outputLen, uCategory, len, &status); + result->category.append(appBuf, outputLen, status); + if (U_FAILURE(status)) { + fprintf(stderr, "category: u_strToUTF8 failed: key: %s, len: %d\n", inputUnitNoSI.getIdentifier(), len); + return; + } + fprintf(stderr, "=== category: %s, srclen: %d, outputLen: %d\n", result->category.data(), len, + outputLen); + + // look up rates for other preferences. + + ures_close(convertUnits); + + + // MeasureUnit testUnit = MeasureUnit::forIdentifier("gram", status); + // if (U_FAILURE(status)) return; + + // fprintf(stderr, "testUnit Identifier: %s\n", testUnit.getIdentifier()); + // fprintf(stderr, "testUnit Type: %s\n", testUnit.getType()); + // fprintf(stderr, "testUnit Subtype: %s\n", testUnit.getSubtype()); + // fprintf(stderr, "testUnit SI prefix: %d\n", testUnit.getSIPrefix(status)); + // if (U_FAILURE(status)) return; + + // MeasureUnit withPrfx = testUnit.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status); + // if (U_FAILURE(status)) return; + // withPrfx = MeasureUnit::forIdentifier("centigram", status); + // if (U_FAILURE(status)) return; + // withPrfx = MeasureUnit::forIdentifier("centiliter", status); + // if (U_FAILURE(status)) return; + // fprintf(stderr, "withPrfx Identifier: %s\n", withPrfx.getIdentifier()); + // fprintf(stderr, "withPrfx Type: %s\n", withPrfx.getType()); + // fprintf(stderr, "withPrfx Subtype: %s\n", withPrfx.getSubtype()); + // fprintf(stderr, "withPrfx SI prefix: %d\n", withPrfx.getSIPrefix(status)); + // if (U_FAILURE(status)) return; + + + // Temporarily storing unitPreferenceData then category data here: + UResourceBundle *usageData = ures_getByKey(units, "unitPreferenceData", NULL, &status); + if (U_FAILURE(status)) { + fprintf(stderr, "open unitPreferenceData failed\n"); + return; + } + ures_getByKey(usageData, result->category.data(), usageData, &status); + if (U_FAILURE(status)) { + fprintf(stderr, "get unitPreferenceData/%s failed\n", result->category.data()); + return; + } + ures_getByKey(usageData, usage, usageData, &status); + if (U_FAILURE(status)) { + status = U_ZERO_ERROR; + ures_getByKey(usageData, "default", usageData, &status); + if (U_SUCCESS(status)) { + // FIXME: save "default" somewhere? + fprintf(stderr, "get usage unitPreferenceData/%s/default succeeded, %s failed\n", + result->category.data(), usage); + } else { + fprintf(stderr, "get usage unitPreferenceData/%s/%s failed\n", result->category.data(), usage); + return; + } + } + ures_getByKey(usageData, outputRegion, usageData, &status); + if (U_FAILURE(status)) { + status = U_ZERO_ERROR; + ures_getByKey(usageData, "001", usageData, &status); + if (U_SUCCESS(status)) { + fprintf(stderr, "get region unitPreferenceData/%s//001 succeeded, %s failed\n", + result->category.data(), outputRegion); + } else { + fprintf(stderr, "get unitPreferenceData/%s//%s failed\n", result->category.data(), + outputRegion); + return; + } + } + + UResourceBundle *pref = NULL; + int32_t numPrefs = ures_getSize(usageData); + for (int i = 0; i < numPrefs; i++) { // FIXME + pref = ures_getByIndex(usageData, i, pref, &status); + if (U_FAILURE(status)) { + status = U_ZERO_ERROR; + break; + } + int32_t len1; + const UChar *unitIdent = ures_getStringByKey(pref, "unit", &len1, &status); + if (U_FAILURE(status)) { + fprintf(stderr, "open unit failed\n"); + return; + } + UnicodeString debugOutp1 = "unit: " + UnicodeString(unitIdent); + debugOutp1 += "\n"; + std::string debugOut1; + debugOutp1.toUTF8String(debugOut1); + fprintf(stderr, "%s", debugOut1.c_str()); + } + + ures_close(pref); + ures_close(usageData); + + ures_close(units); +} diff --git a/icu4c/source/test/intltest/unitstest.cpp b/icu4c/source/test/intltest/unitstest.cpp index 41d761132fa..d7d3f9ac954 100644 --- a/icu4c/source/test/intltest/unitstest.cpp +++ b/icu4c/source/test/intltest/unitstest.cpp @@ -17,6 +17,8 @@ using icu::number::impl::DecimalQuantity; +#include "wip_units_resources.h" + class UnitsTest : public IntlTest { public: UnitsTest() {} @@ -30,6 +32,8 @@ class UnitsTest : public IntlTest { void testMass(); void testTemperature(); void testArea(); + void testUSVolumeResourceLoading(); + void testSIMassResourceLoading(); }; extern IntlTest *createUnitsTest() { return new UnitsTest(); } @@ -46,6 +50,8 @@ void UnitsTest::runIndexedTest(int32_t index, UBool exec, const char *&name, cha TESTCASE_AUTO(testMass); TESTCASE_AUTO(testTemperature); TESTCASE_AUTO(testArea); + TESTCASE_AUTO(testUSVolumeResourceLoading); + TESTCASE_AUTO(testSIMassResourceLoading); TESTCASE_AUTO_END; } @@ -521,4 +527,68 @@ void UnitsTest::testPreferences() { } } +void UnitsTest::testUSVolumeResourceLoading() { + const char *category = "volume"; + const char *usage = "fluid"; + const char *inputUnit = "centiliter"; + const char *outputRegion = "US"; + const char *expectedOutputUnits[] = { + "gallon", + "quart", + "pint", + "cup", + "fluid-ounce", + "tablespoon", + "teaspoon", + }; + + // UErrorCode status = U_ZERO_ERROR; + IcuTestErrorCode status(*this, "testUSVolumeResourceLoading"); + UnitConversionResourceBundle resources; + loadResources(usage, inputUnit, outputRegion, &resources, status); + if (status.errIfFailureAndReset("loadResources(\"%s\", \"%s\", \"%s\", \"%s\", status)", + resources.category.data(), usage, inputUnit, outputRegion)) { + return; + } + + fprintf(stderr, "category: %s\n", resources.category.data()); + fprintf(stderr, "usage: %s\n", resources.usage); + fprintf(stderr, "outputRegion: %s\n", resources.outputRegion); + + fprintf(stderr, "inputUnit: %s\n", resources.inputUnit); + fprintf(stderr, "baseUnit: %s\n", resources.baseUnit); +} + +void UnitsTest::testSIMassResourceLoading() { + const char *category = "mass"; + const char *usage = "zz_nonexistant"; // expecting default usage. + // WIP(fails) // const char *inputUnit = "centigram"; + // WIP(fails) // const char *inputUnit = "milligram"; + const char *inputUnit = "centigram"; + const char *outputRegion = "XZ"; // non-existant, expecting 001 results. + const char *expectedOutputUnits[] = { + "metric-ton", + "kilogram", + "gram", + "milligram", + "microgram", + }; + + // UErrorCode status = U_ZERO_ERROR; + IcuTestErrorCode status(*this, "testSIMassResourceLoading"); + UnitConversionResourceBundle resources; + loadResources(usage, inputUnit, outputRegion, &resources, status); + if (status.errIfFailureAndReset("loadResources(\"%s\", \"%s\", \"%s\", \"%s\", status)", category, + usage, inputUnit, outputRegion)) { + return; + } + + fprintf(stderr, "category: %s\n", resources.category.data()); + fprintf(stderr, "usage: %s\n", resources.usage); + fprintf(stderr, "outputRegion: %s\n", resources.outputRegion); + + fprintf(stderr, "inputUnit: %s\n", resources.inputUnit); + fprintf(stderr, "baseUnit: %s\n", resources.baseUnit); +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/wip_units_resources.h b/icu4c/source/test/intltest/wip_units_resources.h new file mode 100644 index 00000000000..10fe1c5abdb --- /dev/null +++ b/icu4c/source/test/intltest/wip_units_resources.h @@ -0,0 +1,26 @@ +#ifndef WIP_UNITS_RESOURCES_H +#define WIP_UNITS_RESOURCES_H + +#include "charstr.h" +#include "unicode/localpointer.h" + +class UnitConversionResourceBundle { + public: + char *inputUnit; + icu::CharString category; + char *usage; + char *outputRegion; + char *baseUnit; + icu::LocalArray preferences; +}; + +/** + * Loads resources FIXME. + * @param usage FIXME: result keeps a pointer to this, so must outlive result. + * @param inputUnit FIXME: result keeps a pointer to this, so must outlive + * result. + */ +void loadResources(const char *usage, const char *inputUnit, const char *ouptutRegion, + UnitConversionResourceBundle *result, UErrorCode &status); + +#endif // WIP_UNITS_RESOURCES_H