Merge pull request #45 from hugovdm/usagefix

Support usage component dropping, and more
This commit is contained in:
Hugo 2020-05-28 10:41:28 +02:00 committed by GitHub
commit 6b14d7f1a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 39 deletions

View file

@ -259,10 +259,9 @@ class UnitPreferencesSink : public ResourceSink {
MaybeStackVector<UnitPreferenceMetadata> *metadata;
};
int32_t binarySearch(const MaybeStackVector<UnitPreferenceMetadata> *metadata, const char *category,
const char *usage, const char *region, bool *foundCategory, bool *foundUsage,
int32_t binarySearch(const MaybeStackVector<UnitPreferenceMetadata> *metadata,
const UnitPreferenceMetadata &desired, bool *foundCategory, bool *foundUsage,
bool *foundRegion, UErrorCode &status) {
UnitPreferenceMetadata desired(category, usage, region, -1, -1, status);
if (U_FAILURE(status)) { return -1; }
int32_t start = 0;
int32_t end = metadata->length();
@ -285,21 +284,15 @@ int32_t binarySearch(const MaybeStackVector<UnitPreferenceMetadata> *metadata, c
/**
* Finds the UnitPreferenceMetadata instance that matches the given category,
* usage and region: if missing, region falls back to "001", and usage falls
* back to "default".
*
* This is implemented as a binary search, with fallback restarting the search
* from the search range at which the parent in the category/usage/region
* hierarchy was found.
* usage and region: if missing, region falls back to "001", and usage
* repeatedly drops tailing components, eventually trying "default"
* ("land-agriculture-grain" -> "land-agriculture" -> "land" -> "default").
*
* @param metadata The full list of UnitPreferenceMetadata instances.
* @param category The category to search for. If category is not known, it can
* be resolved from the baseunit of the input (for supported unit categories).
* TODO(hugovdm): implement the unit->category lookup (via "unitQuantities" in
* the units resource bundle).
* @param category The category to search for. See getUnitCategory().
* @param usage The usage for which formatting preferences is needed. If the
* given usage is not known, this function automatically falls back to "default"
* usage.
* given usage is not known, automatic fallback occurs, see function description
* above.
* @param region The region for which preferences are needed. If there are no
* region-specific preferences, this function automatically falls back to the
* "001" region (global).
@ -315,37 +308,38 @@ int32_t getPreferenceMetadataIndex(const MaybeStackVector<UnitPreferenceMetadata
UErrorCode &status) {
if (U_FAILURE(status)) { return -1; }
bool foundCategory, foundUsage, foundRegion;
int32_t idx = binarySearch(metadata, category, usage, region, &foundCategory, &foundUsage,
&foundRegion, status);
UnitPreferenceMetadata desired(category, usage, region, -1, -1, status);
int32_t idx = binarySearch(metadata, desired, &foundCategory, &foundUsage, &foundRegion, status);
if (U_FAILURE(status)) { return -1; }
if (idx >= 0) { return idx; }
if (!foundCategory) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return idx;
return -1;
}
U_ASSERT(foundCategory);
if (!foundUsage) {
if (uprv_strcmp(usage, "default") != 0) {
usage = "default";
idx = binarySearch(metadata, category, usage, region, &foundCategory, &foundUsage,
&foundRegion, status);
}
if (!foundUsage) {
while (!foundUsage) {
int32_t lastDashIdx = desired.usage.lastIndexOf('-');
if (lastDashIdx > 0) {
desired.usage.truncate(lastDashIdx);
} else if (uprv_strcmp(desired.usage.data(), "default") != 0) {
desired.usage.truncate(0).append("default", status);
} else {
status = U_MISSING_RESOURCE_ERROR;
return idx;
return -1;
}
idx = binarySearch(metadata, desired, &foundCategory, &foundUsage, &foundRegion, status);
if (U_FAILURE(status)) { return -1; }
}
U_ASSERT(foundCategory);
U_ASSERT(foundUsage);
if (!foundRegion) {
if (uprv_strcmp(region, "001") != 0) {
region = "001";
idx = binarySearch(metadata, category, usage, region, &foundCategory, &foundUsage,
&foundRegion, status);
if (uprv_strcmp(desired.region.data(), "001") != 0) {
desired.region.truncate(0).append("001", status);
idx = binarySearch(metadata, desired, &foundCategory, &foundUsage, &foundRegion, status);
}
if (!foundRegion) {
status = U_MISSING_RESOURCE_ERROR;
return idx;
return -1;
}
}
U_ASSERT(foundCategory);

View file

@ -106,12 +106,15 @@ namespace {
/**
* Metadata about the preferences in UnitPreferences::unitPrefs_.
*
* This class owns all of its data.
*
* UnitPreferenceMetadata lives in the anonymous namespace, because it should
* only be useful to internal code and unit testing code.
*/
class U_I18N_API UnitPreferenceMetadata : public UMemory {
public:
UnitPreferenceMetadata(){};
// Constructor, makes copies of the parameters passed to it.
UnitPreferenceMetadata(const char *category, const char *usage, const char *region,
int32_t prefsOffset, int32_t prefsCount, UErrorCode &status);
@ -139,9 +142,6 @@ class U_I18N_API UnitPreferenceMetadata : public UMemory {
/**
* Unit Preferences information for various locales and usages.
*
* TODO(hugovdm): add a function to look up the category based on the input
* unit.
*/
class U_I18N_API UnitPreferences {
public:
@ -153,12 +153,11 @@ class U_I18N_API UnitPreferences {
UnitPreferences(UErrorCode &status);
/**
* Returns the set of unit preferences in the particular cateogry that best
* Returns the set of unit preferences in the particular category that best
* matches the specified usage and region.
*
* If region can't be found, falls back to global (001). If usage can't be
* found, falls back to "default". Copies the preferences structures.
* TODO(hugovdm/review): Consider returning pointers (references) instead?
* found, falls back to "default".
*
* @param category The category within which to look up usage and region.
* (TODO(hugovdm): improve docs on how to find the category, once the lookup
@ -170,10 +169,13 @@ class U_I18N_API UnitPreferences {
* @param region The region whose preferences are desired. If there are no
* specific preferences for the requested region, the method automatically
* falls back to region "001" ("world").
* @param outPreferences The vector to which preferences will be added.
* @param outPreferences A pointer into an array of preferences: essentially
* an array slice in combination with preferenceCount.
* @param preferenceCount The number of unit preferences that belong to the
* result set.
* @param status Receives status.
*
* TODO: maybe replace `UnitPreference **&outPrefrences` with a slice class?
* TODO(hugovdm): maybe replace `UnitPreference **&outPrefrences` with a slice class?
*/
void getPreferencesFor(const char *category, const char *usage, const char *region,
const UnitPreference *const *&outPreferences, int32_t &preferenceCount,

View file

@ -103,6 +103,10 @@ void UnitsDataTest::testGetPreferences() {
{"XX default falls back to 001", "length", "default", "XX", WorldLenMax, WorldLenMin},
{"Unknown usage US", "length", "foobar", "US", USLenMax, USLenMin},
{"Unknown usage 001", "length", "foobar", "XX", WorldLenMax, WorldLenMin},
{"Fallback", "length", "person-height-xyzzy", "DE", "meter-and-centimeter",
"meter-and-centimeter"},
{"Fallback twice", "length", "person-height-xyzzy-foo", "DE", "meter-and-centimeter",
"meter-and-centimeter"},
};
IcuTestErrorCode status(*this, "testGetPreferences");
UnitPreferencesOpenedUp preferences(status);