mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-11 08:01:32 +00:00
ICU-21877 Fixed it so that getAllChildrenWithFallback() correctly calls its sink with every possible resource the
one the user requested might be inheriting elements from.
This commit is contained in:
parent
6a42197331
commit
1b980e5999
5 changed files with 364 additions and 2 deletions
|
@ -2351,7 +2351,66 @@ struct GetAllChildrenSink : public ResourceSink {
|
|||
aliasedValue.setData(aliasRB->getResData());
|
||||
aliasedValue.setValidLocaleDataEntry(aliasRB->fValidLocaleDataEntry);
|
||||
aliasedValue.setResource(aliasRB->fRes, ResourceTracer(aliasRB));
|
||||
dest.put(key, aliasedValue, isRoot, errorCode);
|
||||
|
||||
if (aliasedValue.getType() != URES_TABLE) {
|
||||
dest.put(key, aliasedValue, isRoot, errorCode);
|
||||
} else {
|
||||
// if the resource we're aliasing over to is a table, the sink might iterate over its contents.
|
||||
// If it does, it'll get only the things defined in the actual alias target, not the things
|
||||
// the target inherits from its parent resources. So we walk the parent chain for the *alias target*,
|
||||
// calling dest.put() for each of the parent tables we could be inheriting from. This means
|
||||
// that dest.put() has to iterate over the children of multiple tables to get all of the inherited
|
||||
// resource values, but it already has to do that to handle normal vertical inheritance.
|
||||
UResType aliasedValueType = URES_TABLE;
|
||||
CharString tablePath;
|
||||
tablePath.append(aliasRB->fResPath, errorCode);
|
||||
const char* parentKey = key; // dest.put() changes the key
|
||||
dest.put(parentKey, aliasedValue, isRoot, errorCode);
|
||||
UResourceDataEntry* entry = aliasRB->fData;
|
||||
Resource res = aliasRB->fRes;
|
||||
while (aliasedValueType == URES_TABLE && entry->fParent != nullptr) {
|
||||
CharString localPath;
|
||||
localPath.copyFrom(tablePath, errorCode);
|
||||
char* localPathAsCharPtr = localPath.data();
|
||||
const char* childKey;
|
||||
entry = entry->fParent;
|
||||
res = entry->fData.rootRes;
|
||||
Resource newRes = res_findResource(&entry->fData, res, &localPathAsCharPtr, &childKey);
|
||||
if (newRes != RES_BOGUS) {
|
||||
aliasedValue.setData(entry->fData);
|
||||
// TODO: do I also need to call aliasedValue.setValueLocaleDataEntry() ?
|
||||
aliasedValue.setResource(newRes, ResourceTracer(aliasRB)); // probably wrong to use aliasRB here
|
||||
aliasedValueType = aliasedValue.getType();
|
||||
if (aliasedValueType == URES_ALIAS) {
|
||||
// in a few rare cases, when we get to the root resource bundle, the resource in question
|
||||
// won't be an actual table, but will instead be an alias to a table. That is, we have
|
||||
// two aliases in the inheritance path. (For some locales, such as Zulu, we see this with
|
||||
// children of the "fields" resource: "day-narrow" aliases to "day-short", which aliases
|
||||
// to "day".) When this happens, we need to make sure we follow all the aliases.
|
||||
ResourceDataValue& rdv2 = static_cast<ResourceDataValue&>(aliasedValue);
|
||||
aliasRB = getAliasTargetAsResourceBundle(rdv2.getData(), rdv2.getResource(), nullptr, -1,
|
||||
rdv2.getValidLocaleDataEntry(), nullptr, 0,
|
||||
stackTempBundle.getAlias(), &errorCode);
|
||||
tablePath.clear();
|
||||
tablePath.append(aliasRB->fResPath, errorCode);
|
||||
entry = aliasRB->fData;
|
||||
res = aliasRB->fRes;
|
||||
aliasedValue.setData(entry->fData);
|
||||
// TODO: do I also need to call aliasedValue.setValueLocaleDataEntry() ?
|
||||
aliasedValue.setResource(res, ResourceTracer(aliasRB)); // probably wrong to use aliasRB here
|
||||
aliasedValueType = aliasedValue.getType();
|
||||
}
|
||||
if (aliasedValueType == URES_TABLE) {
|
||||
dest.put(parentKey, aliasedValue, isRoot, errorCode);
|
||||
} else {
|
||||
// once we've followed the alias, the resource we're looking at really should
|
||||
// be a table
|
||||
errorCode = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dest.put(key, value, isRoot, errorCode);
|
||||
|
|
|
@ -15,11 +15,18 @@
|
|||
#include "unicode/resbund.h"
|
||||
#include "restest.h"
|
||||
|
||||
#include "uresimp.h"
|
||||
#include "ureslocs.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
//***************************************************************************************
|
||||
|
||||
static const char16_t kErrorUChars[] = { 0x45, 0x52, 0x52, 0x4f, 0x52, 0 };
|
||||
|
@ -219,6 +226,8 @@ void ResourceBundleTest::runIndexedTest( int32_t index, UBool exec, const char*
|
|||
#endif
|
||||
|
||||
case 4: name = "TestExemplar"; if (exec) TestExemplar(); break;
|
||||
case 5: name = "TestPersonUnits"; if (exec) TestPersonUnits(); break;
|
||||
case 6: name = "TestZuluFields"; if (exec) TestZuluFields(); break;
|
||||
default: name = ""; break; //needed to end loop
|
||||
}
|
||||
}
|
||||
|
@ -655,3 +664,158 @@ ResourceBundleTest::TestGetLocaleByType()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ResourceBundleTest::TestPersonUnits() {
|
||||
// Test for ICU-21877: ICUResourceBundle.getAllChildrenWithFallback() doesn't return all of the children of the resource
|
||||
// that's passed into it. If you have to follow an alias to get some of the children, we get the resources in the
|
||||
// bundle we're aliasing to, and its children, but things that the bundle we're aliasing to inherits from its parent
|
||||
// don't show up.
|
||||
// This example is from the en_CA resource in the "unit" tree: The four test cases below show what we get when we call
|
||||
// getStringWithFallback():
|
||||
// - units/duration/day-person/other doesn't exist in either en_CA or en, so we fall back on root.
|
||||
// - root/units aliases over to LOCALE/unitsShort.
|
||||
// - unitsShort/duration/day-person/other also doesn't exist in either en_CA or en, so we fall back to root again.
|
||||
// - root/unitsShort/duration/day-person aliases over to LOCALE/unitsShort/duration/day.
|
||||
// - unitsShort/duration/day/other also doesn't exist in en_CA, so we fallback to en.
|
||||
// - en/unitsShort/duration/day/other DOES exist and has "{0} days", so we return that.
|
||||
// It's the same basic story for week-person, month-person, and year-person, except that:
|
||||
// - unitsShort/duration/day doesn't exist at all in en_CA
|
||||
// - unitsShort/duration/week DOES exist in en_CA, but only contains "per", so we inherit "other" from en
|
||||
// - unitsShort/duration/month/other DOES exist in en_CA (it overrides "{0} mths" with "{0} mos")
|
||||
// - unitsShort/duration/year DOES exist in en_CA, but only contains "per", so we inherit "other" from en
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
LocalUResourceBundlePointer en_ca(ures_open(U_ICUDATA_UNIT, "en_CA", &err));
|
||||
|
||||
if (!assertSuccess("Failed to load en_CA resource in units tree", err)) {
|
||||
return;
|
||||
}
|
||||
assertEquals("Wrong result for units/duration/day-person/other", u"{0} days", ures_getStringByKeyWithFallback(en_ca.getAlias(), "units/duration/day-person/other", nullptr, &err));
|
||||
assertEquals("Wrong result for units/duration/week-person/other", u"{0} wks", ures_getStringByKeyWithFallback(en_ca.getAlias(), "units/duration/week-person/other", nullptr, &err));
|
||||
assertEquals("Wrong result for units/duration/month-person/other", u"{0} mos", ures_getStringByKeyWithFallback(en_ca.getAlias(), "units/duration/month-person/other", nullptr, &err));
|
||||
assertEquals("Wrong result for units/duration/year-person/other", u"{0} yrs", ures_getStringByKeyWithFallback(en_ca.getAlias(), "units/duration/year-person/other", nullptr, &err));
|
||||
|
||||
// getAllChildrenWithFallback() wasn't bringing all of those things back, however. When en_CA/units/year-person
|
||||
// aliased over to en_CA/unitsShort/year, the sink would only be called on en_CA/unitsShort/year, which means it'd
|
||||
// only see en_CA/unitsShort/year/per, because "per" was the only thing contained in en_CA/unitsShort/year. But
|
||||
// en_CA/unitsShort/year should be inheriting "dnam", "one", and "other" from en/unitsShort/year.
|
||||
// getAllChildrenWithFallback() had to be modified to walk the parent chain and call the sink again with
|
||||
// en/unitsShort/year and root/unitsShort/year.
|
||||
struct TestPersonUnitsSink : public ResourceSink {
|
||||
typedef std::set<std::string> FoundKeysType;
|
||||
typedef std::map<std::string, FoundKeysType > FoundResourcesType;
|
||||
FoundResourcesType foundResources;
|
||||
IntlTest& owningTestCase;
|
||||
|
||||
TestPersonUnitsSink(IntlTest& owningTestCase) : owningTestCase(owningTestCase) {}
|
||||
|
||||
virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
|
||||
UErrorCode &errorCode) override {
|
||||
if (value.getType() != URES_TABLE) {
|
||||
owningTestCase.errln("%s is not a table!", key);
|
||||
return;
|
||||
}
|
||||
|
||||
FoundKeysType& foundKeys(foundResources[key]);
|
||||
|
||||
ResourceTable childTable = value.getTable(errorCode);
|
||||
if (owningTestCase.assertSuccess("value.getTable() didn't work", errorCode)) {
|
||||
const char* childKey;
|
||||
for (int32_t i = 0; i < childTable.getSize(); i++) {
|
||||
childTable.getKeyAndValue(i, childKey, value);
|
||||
foundKeys.insert(childKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TestPersonUnitsSink sink(*this);
|
||||
ures_getAllChildrenWithFallback(en_ca.getAlias(), "units/duration", sink, err);
|
||||
|
||||
if (assertSuccess("ures_getAllChildrenWithFallback() failed", err)) {
|
||||
for (auto foundResourcesEntry : sink.foundResources) {
|
||||
std::string foundResourcesKey = foundResourcesEntry.first;
|
||||
if (foundResourcesKey.rfind("-person") == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TestPersonUnitsSink::FoundKeysType foundKeys = foundResourcesEntry.second;
|
||||
if (foundKeys.find("dnam") == foundKeys.end()) {
|
||||
errln("%s doesn't contain 'dnam'!", foundResourcesKey.c_str());
|
||||
}
|
||||
if (foundKeys.find("one") == foundKeys.end()) {
|
||||
errln("%s doesn't contain 'one'!", foundResourcesKey.c_str());
|
||||
}
|
||||
if (foundKeys.find("other") == foundKeys.end()) {
|
||||
errln("%s doesn't contain 'other'!", foundResourcesKey.c_str());
|
||||
}
|
||||
if (foundKeys.find("per") == foundKeys.end()) {
|
||||
errln("%s doesn't contain 'per'!", foundResourcesKey.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ResourceBundleTest::TestZuluFields() {
|
||||
// Test for ICU-21877: Similar to the above test, except that here we're bringing back the wrong values.
|
||||
// In some resources under the "locales" tree, some resources under "fields" would either have no display
|
||||
// names or bring back the _wrong_ display names. The underlying cause was the same as in TestPersonUnits()
|
||||
// above, except that there were two levels of indirection: At the root level, *-narrow aliased to *-short,
|
||||
// which in turn aliased to *. If (say) day-narrow and day-short both didn't have "dn" resources, you could
|
||||
// iterate over fields/day-short and find the "dn" resource that should have been inherited over from
|
||||
// fields/day, but if you iterated over fields/day-narrow, you WOULDN'T get back the "dn" resource from
|
||||
// fields/day (or, with my original fix for ICU-21877, you'd get back the wrong value). This test verifies
|
||||
// that the double indirection works correctly.
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
LocalUResourceBundlePointer zu(ures_open(nullptr, "zu", &err));
|
||||
|
||||
if (!assertSuccess("Failed to load zu resource in locales tree", err)) {
|
||||
return;
|
||||
}
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/day/dn", u"Usuku", ures_getStringByKeyWithFallback(zu.getAlias(), "fields/day/dn", nullptr, &err));
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/day-short/dn", u"Usuku", ures_getStringByKeyWithFallback(zu.getAlias(), "fields/day-short/dn", nullptr, &err));
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/day-narrow/dn", u"Usuku", ures_getStringByKeyWithFallback(zu.getAlias(), "fields/day-narrow/dn", nullptr, &err));
|
||||
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/month/dn", u"Inyanga", ures_getStringByKeyWithFallback(zu.getAlias(), "fields/month/dn", nullptr, &err));
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/month-short/dn", u"Inyanga", ures_getStringByKeyWithFallback(zu.getAlias(), "fields/month-short/dn", nullptr, &err));
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/month-narrow/dn", u"Inyanga", ures_getStringByKeyWithFallback(zu.getAlias(), "fields/month-narrow/dn", nullptr, &err));
|
||||
|
||||
struct TestZuluFieldsSink : public ResourceSink {
|
||||
typedef std::map<std::string, const UChar* > FoundResourcesType;
|
||||
FoundResourcesType foundResources;
|
||||
IntlTest& owningTestCase;
|
||||
|
||||
TestZuluFieldsSink(IntlTest& owningTestCase) : owningTestCase(owningTestCase) {}
|
||||
|
||||
virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
|
||||
UErrorCode &errorCode) override {
|
||||
if (value.getType() != URES_TABLE) {
|
||||
owningTestCase.errln("%s is not a table!", key);
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceTable childTable = value.getTable(errorCode);
|
||||
if (owningTestCase.assertSuccess("value.getTable() didn't work", errorCode)) {
|
||||
if (childTable.findValue("dn", value)) {
|
||||
int32_t dummyLength;
|
||||
if (foundResources.find(key) == foundResources.end()) {
|
||||
foundResources.insert(std::make_pair(key, value.getString(dummyLength, errorCode)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TestZuluFieldsSink sink(*this);
|
||||
ures_getAllChildrenWithFallback(zu.getAlias(), "fields", sink, err);
|
||||
|
||||
if (assertSuccess("ures_getAllChildrenWithFallback() failed", err)) {
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/day/dn", u"Usuku", sink.foundResources["day"]);
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/day-short/dn", u"Usuku", sink.foundResources["day-short"]);
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/day-narrow/dn", u"Usuku", sink.foundResources["day-narrow"]);
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/month/dn", u"Inyanga", sink.foundResources["month"]);
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/month-short/dn", u"Inyanga", sink.foundResources["month-short"]);
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/month-narrow/dn", u"Inyanga", sink.foundResources["month-narrow"]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,9 @@ public:
|
|||
|
||||
void TestGetSize();
|
||||
void TestGetLocaleByType();
|
||||
|
||||
void TestPersonUnits();
|
||||
void TestZuluFields();
|
||||
|
||||
private:
|
||||
/**
|
||||
|
|
|
@ -15,14 +15,17 @@ import java.net.URL;
|
|||
import java.net.URLConnection;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.jar.JarEntry;
|
||||
|
||||
import com.ibm.icu.impl.UResource;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
@ -1196,5 +1199,102 @@ public final class ICUResourceBundleTest extends TestFmwk {
|
|||
// enrure is that we don't crash with a StackOverflowError when trying to retrieve the bundle
|
||||
}
|
||||
ULocale.setDefault(oldDefaultLocale);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestPersonUnits() {
|
||||
// Test for ICU-21877: ICUResourceBundle.getAllChildrenWithFallback() doesn't return all of the children of the resource
|
||||
// that's passed into it. If you have to follow an alias to get some of the children, we get the resources in the
|
||||
// bundle we're aliasing to, and its children, but things that the bundle we're aliasing to inherits from its parent
|
||||
// don't show up.
|
||||
// This example is from the en_CA resource in the "unit" tree: The four test cases below show what we get when we call
|
||||
// getStringWithFallback():
|
||||
// - units/duration/day-person/other doesn't exist in either en_CA or en, so we fall back on root.
|
||||
// - root/units aliases over to LOCALE/unitsShort.
|
||||
// - unitsShort/duration/day-person/other also doesn't exist in either en_CA or en, so we fall back to root again.
|
||||
// - root/unitsShort/duration/day-person aliases over to LOCALE/unitsShort/duration/day.
|
||||
// - unitsShort/duration/day/other also doesn't exist in en_CA, so we fallback to en.
|
||||
// - en/unitsShort/duration/day/other DOES exist and has "{0} days", so we return that.
|
||||
// It's the same basic story for week-person, month-person, and year-person, except that:
|
||||
// - unitsShort/duration/day doesn't exist at all in en_CA
|
||||
// - unitsShort/duration/week DOES exist in en_CA, but only contains "per", so we inherit "other" from en
|
||||
// - unitsShort/duration/month/other DOES exist in en_CA (it overrides "{0} mths" with "{0} mos")
|
||||
// - unitsShort/duration/year DOES exist in en_CA, but only contains "per", so we inherit "other" from en
|
||||
ICUResourceBundle en_ca = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME, "en_CA");
|
||||
assertEquals("Wrong result for units/duration/day-person/other", "{0} days", en_ca.getStringWithFallback("units/duration/day-person/other"));
|
||||
assertEquals("Wrong result for units/duration/week-person/other", "{0} wks", en_ca.getStringWithFallback("units/duration/week-person/other"));
|
||||
assertEquals("Wrong result for units/duration/month-person/other", "{0} mos", en_ca.getStringWithFallback("units/duration/month-person/other"));
|
||||
assertEquals("Wrong result for units/duration/year-person/other", "{0} yrs", en_ca.getStringWithFallback("units/duration/year-person/other"));
|
||||
|
||||
// getAllChildrenWithFallback() wasn't bringing all of those things back, however. When en_CA/units/year-person
|
||||
// aliased over to en_CA/unitsShort/year, the sink would only be called on en_CA/unitsShort/year, which means it'd
|
||||
// only see en_CA/unitsShort/year/per, because "per" was the only thing contained in en_CA/unitsShort/year. But
|
||||
// en_CA/unitsShort/year should be inheriting "dnam", "one", and "other" from en/unitsShort/year. getAllChildrenWithFallback()
|
||||
// had to be modified to walk the parent chain and call the sink again with en/unitsShort/year and root/unitsShort/year.
|
||||
final Map<String, Set<String>> foundResources = new HashMap<>();
|
||||
en_ca.getAllChildrenWithFallback("units/duration", new UResource.Sink() {
|
||||
@Override
|
||||
public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
|
||||
assertEquals(key + " isn't a table!", value.getType(), UResourceBundle.TABLE);
|
||||
|
||||
Set<String> keys = foundResources.computeIfAbsent(key.toString(), k -> new HashSet<>());
|
||||
UResource.Table childTable = value.getTable();
|
||||
for (int i = 0; i < childTable.getSize(); i++) {
|
||||
childTable.getKeyAndValue(i, key, value);
|
||||
keys.add(key.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
for (String key : foundResources.keySet()) {
|
||||
if (!key.endsWith("-person")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<String> foundChildren = foundResources.get(key);
|
||||
assertTrue(key + " doesn't contain 'dnam'!", foundChildren.contains("dnam"));
|
||||
assertTrue(key + " doesn't contain 'one'!", foundChildren.contains("one"));
|
||||
assertTrue(key + " doesn't contain 'other'!", foundChildren.contains("other"));
|
||||
assertTrue(key + " doesn't contain 'per'!", foundChildren.contains("per"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestZuluFields() {
|
||||
// Test for ICU-21877: Similar to the above test, except that here we're bringing back the wrong values.
|
||||
// In some resources under the "locales" tree, some resources under "fields" would either have no display
|
||||
// names or bring back the _wrong_ display names. The underlying cause was the same as in TestPersonUnits()
|
||||
// above, except that there were two levels of indirection: At the root level, *-narrow aliased to *-short,
|
||||
// which in turn aliased to *. If (say) day-narrow and day-short both didn't have "dn" resources, you could
|
||||
// iterate over fields/day-short and find the "dn" resource that should have been inherited over from
|
||||
// fields/day, but if you iterated over fields/day-narrow, you WOULDN'T get back the "dn" resource from
|
||||
// fields/day (or, with my original fix for ICU-21877, you'd get back the wrong value). This test verifies
|
||||
// that the double indirection works correctly.
|
||||
ICUResourceBundle zu = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "zu");
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/day/dn", "Usuku", zu.getStringWithFallback("fields/day/dn"));
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/day-short/dn", "Usuku", zu.getStringWithFallback("fields/day-short/dn"));
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/day-narrow/dn", "Usuku", zu.getStringWithFallback("fields/day-narrow/dn"));
|
||||
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/month/dn", "Inyanga", zu.getStringWithFallback("fields/month/dn"));
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/month-short/dn", "Inyanga", zu.getStringWithFallback("fields/month-short/dn"));
|
||||
assertEquals("Wrong getStringWithFallback() result for fields/month-narrow/dn", "Inyanga", zu.getStringWithFallback("fields/month-narrow/dn"));
|
||||
|
||||
final Map<String, String> foundNames = new HashMap<>();
|
||||
zu.getAllChildrenWithFallback("fields", new UResource.Sink() {
|
||||
@Override
|
||||
public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
|
||||
assertEquals(key + " isn't a table!", value.getType(), UResourceBundle.TABLE);
|
||||
|
||||
UResource.Table childTable = value.getTable();
|
||||
if (childTable.findValue("dn", value)) {
|
||||
foundNames.putIfAbsent(key.toString(), value.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/day/dn", "Usuku", foundNames.get("day"));
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/day-short/dn", "Usuku", foundNames.get("day-short"));
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/day-narrow/dn", "Usuku", foundNames.get("day-narrow"));
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/month/dn", "Inyanga", foundNames.get("month"));
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/month-short/dn", "Inyanga", foundNames.get("month-short"));
|
||||
assertEquals("Wrong getAllChildrenWithFallback() result for fields/month-narrow/dn", "Inyanga", foundNames.get("month-narrow"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -459,7 +459,43 @@ public class ICUResourceBundle extends UResourceBundle {
|
|||
ReaderValue aliasedValue = new ReaderValue();
|
||||
aliasedValue.reader = aliasedResourceImpl.wholeBundle.reader;
|
||||
aliasedValue.res = aliasedResourceImpl.getResource();
|
||||
sink.put(key, aliasedValue, noFallback);
|
||||
|
||||
if (aliasedValue.getType() != TABLE) {
|
||||
sink.put(key, aliasedValue, noFallback);
|
||||
} else {
|
||||
// if the resource we're aliasing over to is a table, the sink might iterate over its contents.
|
||||
// If it does, it'll get only the things defined in the actual alias target, not the things
|
||||
// the target inherits from its parent resources. So we walk the parent chain for the *alias target*,
|
||||
// calling sink.put() for each of the parent tables we could be inheriting from. This means
|
||||
// that sink.put() has to iterate over the children of multiple tables to get all of the inherited
|
||||
// resource values, but it already has to do that to handle normal vertical inheritance.
|
||||
int aliasedValueType = TABLE;
|
||||
String tablePath = aliasPath.substring("/LOCALE/".length());
|
||||
UResource.Key keyCopy = key.clone(); // sink.put() changes the key
|
||||
sink.put(keyCopy, aliasedValue, noFallback);
|
||||
while (aliasedValueType == TABLE && aliasedResource.getParent() != null) {
|
||||
ICUResourceBundle newAliasedResource = aliasedResource.getParent().findWithFallback(tablePath);
|
||||
if (newAliasedResource.key.equals(aliasedResource.key)) {
|
||||
aliasedResource = newAliasedResource;
|
||||
} else {
|
||||
// the findWithFallback() call above might follow an alias. If it does, we'll get
|
||||
// back the alias target at the wrong level (e.g., if we're in en_CA, we're calling
|
||||
// findWithFallback() on en, and if it follows an alias, we get back the alias target
|
||||
// in en, even if it also exists in en_CA). So we check the keys to see if we followed
|
||||
// an alias, and if we did, we re-fetch the alias target from our original resource bundle
|
||||
tablePath = tablePath.substring(0, tablePath.lastIndexOf('/'));
|
||||
tablePath += "/" + newAliasedResource.key;
|
||||
aliasedResource = ICUResourceBundle.this.findWithFallback(tablePath);
|
||||
}
|
||||
aliasedResourceImpl = (ICUResourceBundleImpl) aliasedResource;
|
||||
aliasedValue = new ReaderValue();
|
||||
aliasedValue.reader = aliasedResourceImpl.wholeBundle.reader;
|
||||
aliasedValue.res = aliasedResourceImpl.getResource();
|
||||
aliasedValueType = aliasedValue.getType(); // sink.put() messes up the value
|
||||
keyCopy = key.clone(); // sink.put() messes up the key
|
||||
sink.put(keyCopy, aliasedValue, noFallback);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sink.put(key, value, noFallback);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue