mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-06 22:15:31 +00:00
ICU-20992 Added new API for iterating child resources while following aliases, and changed DateTimePatternGenerator
to use it.
This commit is contained in:
parent
01e1adc9e4
commit
19b8b076f9
8 changed files with 247 additions and 127 deletions
|
@ -2015,6 +2015,7 @@ void getAllItemsWithFallback(
|
|||
// then it would remove the parent's item.
|
||||
// We would deserialize parent values even though they are overridden in a child bundle.
|
||||
value.setData(bundle->getResData());
|
||||
value.setValidLocaleDataEntry(bundle->fValidLocaleDataEntry);
|
||||
UResourceDataEntry *parentEntry = bundle->fData->fParent;
|
||||
UBool hasParent = parentEntry != NULL && U_SUCCESS(parentEntry->fBogus);
|
||||
value.setResource(bundle->fRes, ResourceTracer(bundle));
|
||||
|
@ -2055,6 +2056,49 @@ void getAllItemsWithFallback(
|
|||
}
|
||||
}
|
||||
|
||||
struct GetAllChildrenSink : public ResourceSink {
|
||||
// Destination sink
|
||||
ResourceSink& dest;
|
||||
|
||||
GetAllChildrenSink(ResourceSink& dest)
|
||||
: dest(dest) {}
|
||||
virtual ~GetAllChildrenSink() override;
|
||||
virtual void put(const char *key, ResourceValue &value, UBool isRoot,
|
||||
UErrorCode &errorCode) override {
|
||||
ResourceTable itemsTable = value.getTable(errorCode);
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
|
||||
if (value.getType() == URES_ALIAS) {
|
||||
ResourceDataValue& rdv = static_cast<ResourceDataValue&>(value);
|
||||
StackUResourceBundle stackTempBundle;
|
||||
UResourceBundle* aliasRB = getAliasTargetAsResourceBundle(rdv.getData(), rdv.getResource(), nullptr, -1,
|
||||
rdv.getValidLocaleDataEntry(), nullptr, 0,
|
||||
stackTempBundle.getAlias(), &errorCode);
|
||||
if (U_SUCCESS(errorCode)) {
|
||||
ResourceDataValue aliasedValue;
|
||||
aliasedValue.setData(aliasRB->getResData());
|
||||
aliasedValue.setValidLocaleDataEntry(aliasRB->fValidLocaleDataEntry);
|
||||
aliasedValue.setResource(aliasRB->fRes, ResourceTracer(aliasRB));
|
||||
dest.put(key, aliasedValue, isRoot, errorCode);
|
||||
}
|
||||
} else {
|
||||
dest.put(key, value, isRoot, errorCode);
|
||||
}
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Virtual destructors must be defined out of line.
|
||||
GetAllChildrenSink::~GetAllChildrenSink() {}
|
||||
|
||||
U_CAPI void U_EXPORT2
|
||||
ures_getAllChildrenWithFallback(const UResourceBundle *bundle, const char *path,
|
||||
icu::ResourceSink &sink, UErrorCode &errorCode) {
|
||||
GetAllChildrenSink allChildrenSink(sink);
|
||||
ures_getAllItemsWithFallback(bundle, path, allChildrenSink, errorCode);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Requires a ResourceDataValue fill-in, so that we need not cast from a ResourceValue.
|
||||
|
@ -2086,6 +2130,7 @@ ures_getValueWithFallback(const UResourceBundle *bundle, const char *path,
|
|||
}
|
||||
}
|
||||
value.setData(rb->getResData());
|
||||
value.setValidLocaleDataEntry(rb->fValidLocaleDataEntry);
|
||||
value.setResource(rb->fRes, ResourceTracer(rb));
|
||||
}
|
||||
|
||||
|
|
|
@ -399,6 +399,8 @@ typedef struct ResourceData {
|
|||
UBool useNativeStrcmp;
|
||||
} ResourceData;
|
||||
|
||||
struct UResourceDataEntry; // forward declared for ResoureDataValue below; actually defined in uresimp.h
|
||||
|
||||
/*
|
||||
* Read a resource bundle from memory.
|
||||
*/
|
||||
|
@ -512,6 +514,7 @@ class ResourceDataValue : public ResourceValue {
|
|||
public:
|
||||
ResourceDataValue() :
|
||||
pResData(nullptr),
|
||||
validLocaleDataEntry(nullptr),
|
||||
res(static_cast<Resource>(URES_NONE)),
|
||||
fTraceInfo() {}
|
||||
virtual ~ResourceDataValue();
|
||||
|
@ -519,6 +522,10 @@ public:
|
|||
void setData(const ResourceData &data) {
|
||||
pResData = &data;
|
||||
}
|
||||
|
||||
void setValidLocaleDataEntry(UResourceDataEntry *entry) {
|
||||
validLocaleDataEntry = entry;
|
||||
}
|
||||
|
||||
void setResource(Resource r, ResourceTracer&& traceInfo) {
|
||||
res = r;
|
||||
|
@ -526,6 +533,8 @@ public:
|
|||
}
|
||||
|
||||
const ResourceData &getData() const { return *pResData; }
|
||||
UResourceDataEntry *getValidLocaleDataEntry() const { return validLocaleDataEntry; }
|
||||
Resource getResource() const { return res; }
|
||||
virtual UResType getType() const;
|
||||
virtual const UChar *getString(int32_t &length, UErrorCode &errorCode) const;
|
||||
virtual const UChar *getAliasString(int32_t &length, UErrorCode &errorCode) const;
|
||||
|
@ -544,6 +553,7 @@ public:
|
|||
|
||||
private:
|
||||
const ResourceData *pResData;
|
||||
UResourceDataEntry *validLocaleDataEntry;
|
||||
Resource res;
|
||||
ResourceTracer fTraceInfo;
|
||||
};
|
||||
|
|
|
@ -294,10 +294,39 @@ ures_getValueWithFallback(const UResourceBundle *bundle, const char *path,
|
|||
UResourceBundle *tempFillIn,
|
||||
icu::ResourceDataValue &value, UErrorCode &errorCode);
|
||||
|
||||
/**
|
||||
* Locates the resource specified by `path` in the resource bundle specified by `bundle` (performing any
|
||||
* necessary fallback and following any aliases) and calls the specified `sink`'s `put()` method with that
|
||||
* resource. Then walks the bundle's parent chain, calling `put()` on the sink for each item in the
|
||||
* parent chain.
|
||||
* @param bundle The bundle to search
|
||||
* @param path The path of the desired resource
|
||||
* @param sink A `ResourceSink` that gets called for each resource in the parent chain
|
||||
* @param errorCode The error code
|
||||
*/
|
||||
U_CAPI void U_EXPORT2
|
||||
ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path,
|
||||
icu::ResourceSink &sink, UErrorCode &errorCode);
|
||||
|
||||
/**
|
||||
* Locates the resource specified by `path` in the resource bundle specified by `bundle` (performing any
|
||||
* necessary fallback and following any aliases) and, if the resource is a table resource, iterates over its
|
||||
* immediate child resources (again, following any aliases to get the individual resource values), and calls the specified
|
||||
* `sink`'s `put()` method for each child resource (passing it that resource's key and either its actual value or,
|
||||
* if that value is an alias, the value you get by following the alias). Then walks back over the bundle's
|
||||
* parent chain, similarly iterating over each parent table resource's child resources.
|
||||
* Does not descend beyond one level of table children.
|
||||
* @param bundle The bundle to search
|
||||
* @param path The path of the desired resource
|
||||
* @param sink A `ResourceSink` that gets called for each child resource of the specified resource (and each child
|
||||
* of the resources in its parent chain).
|
||||
* @param errorCode The error code. This will be U_RESOURCE_TYPE_MISMATCH if the resource the caller
|
||||
* is asking for isn't a table resource.
|
||||
*/
|
||||
U_CAPI void U_EXPORT2
|
||||
ures_getAllChildrenWithFallback(const UResourceBundle *bundle, const char *path,
|
||||
icu::ResourceSink &sink, UErrorCode &errorCode);
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
|
|
|
@ -934,15 +934,11 @@ struct DateTimePatternGenerator::AppendItemFormatsSink : public ResourceSink {
|
|||
|
||||
virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
|
||||
UErrorCode &errorCode) {
|
||||
ResourceTable itemsTable = value.getTable(errorCode);
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
|
||||
UDateTimePatternField field = dtpg.getAppendFormatNumber(key);
|
||||
if (field == UDATPG_FIELD_COUNT) { continue; }
|
||||
const UnicodeString& valueStr = value.getUnicodeString(errorCode);
|
||||
if (dtpg.getAppendItemFormat(field).isEmpty() && !valueStr.isEmpty()) {
|
||||
dtpg.setAppendItemFormat(field, valueStr);
|
||||
}
|
||||
UDateTimePatternField field = dtpg.getAppendFormatNumber(key);
|
||||
if (field == UDATPG_FIELD_COUNT) { return; }
|
||||
const UnicodeString& valueStr = value.getUnicodeString(errorCode);
|
||||
if (dtpg.getAppendItemFormat(field).isEmpty() && !valueStr.isEmpty()) {
|
||||
dtpg.setAppendItemFormat(field, valueStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -967,22 +963,15 @@ struct DateTimePatternGenerator::AppendItemNamesSink : public ResourceSink {
|
|||
|
||||
virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
|
||||
UErrorCode &errorCode) {
|
||||
ResourceTable itemsTable = value.getTable(errorCode);
|
||||
UDateTimePGDisplayWidth width;
|
||||
UDateTimePatternField field = dtpg.getFieldAndWidthIndices(key, &width);
|
||||
if (field == UDATPG_FIELD_COUNT) { return; }
|
||||
ResourceTable detailsTable = value.getTable(errorCode);
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
|
||||
UDateTimePGDisplayWidth width;
|
||||
UDateTimePatternField field = dtpg.getFieldAndWidthIndices(key, &width);
|
||||
if (field == UDATPG_FIELD_COUNT) { continue; }
|
||||
ResourceTable detailsTable = value.getTable(errorCode);
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
for (int32_t j = 0; detailsTable.getKeyAndValue(j, key, value); ++j) {
|
||||
if (uprv_strcmp(key, "dn") != 0) { continue; }
|
||||
const UnicodeString& valueStr = value.getUnicodeString(errorCode);
|
||||
if (dtpg.getFieldDisplayName(field,width).isEmpty() && !valueStr.isEmpty()) {
|
||||
dtpg.setFieldDisplayName(field,width,valueStr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!detailsTable.findValue("dn", value)) { return; }
|
||||
const UnicodeString& valueStr = value.getUnicodeString(errorCode);
|
||||
if (U_SUCCESS(errorCode) && dtpg.getFieldDisplayName(field,width).isEmpty() && !valueStr.isEmpty()) {
|
||||
dtpg.setFieldDisplayName(field,width,valueStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1026,18 +1015,14 @@ struct DateTimePatternGenerator::AvailableFormatsSink : public ResourceSink {
|
|||
|
||||
virtual void put(const char *key, ResourceValue &value, UBool isRoot,
|
||||
UErrorCode &errorCode) {
|
||||
ResourceTable itemsTable = value.getTable(errorCode);
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
|
||||
const UnicodeString formatKey(key, -1, US_INV);
|
||||
if (!dtpg.isAvailableFormatSet(formatKey) ) {
|
||||
dtpg.setAvailableFormat(formatKey, errorCode);
|
||||
// Add pattern with its associated skeleton. Override any duplicate
|
||||
// derived from std patterns, but not a previous availableFormats entry:
|
||||
const UnicodeString& formatValue = value.getUnicodeString(errorCode);
|
||||
conflictingPattern.remove();
|
||||
dtpg.addPatternWithSkeleton(formatValue, &formatKey, !isRoot, conflictingPattern, errorCode);
|
||||
}
|
||||
const UnicodeString formatKey(key, -1, US_INV);
|
||||
if (!dtpg.isAvailableFormatSet(formatKey) ) {
|
||||
dtpg.setAvailableFormat(formatKey, errorCode);
|
||||
// Add pattern with its associated skeleton. Override any duplicate
|
||||
// derived from std patterns, but not a previous availableFormats entry:
|
||||
const UnicodeString& formatValue = value.getUnicodeString(errorCode);
|
||||
conflictingPattern.remove();
|
||||
dtpg.addPatternWithSkeleton(formatValue, &formatKey, !isRoot, conflictingPattern, errorCode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1072,13 +1057,13 @@ DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& errorCod
|
|||
.append('/', errorCode)
|
||||
.append(DT_DateTimeAppendItemsTag, errorCode); // i.e., calendar/xxx/appendItems
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
ures_getAllItemsWithFallback(rb.getAlias(), path.data(), appendItemFormatsSink, err);
|
||||
ures_getAllChildrenWithFallback(rb.getAlias(), path.data(), appendItemFormatsSink, err);
|
||||
appendItemFormatsSink.fillInMissing();
|
||||
|
||||
// Load CLDR item names.
|
||||
err = U_ZERO_ERROR;
|
||||
AppendItemNamesSink appendItemNamesSink(*this);
|
||||
ures_getAllItemsWithFallback(rb.getAlias(), DT_DateTimeFieldsTag, appendItemNamesSink, err);
|
||||
ures_getAllChildrenWithFallback(rb.getAlias(), DT_DateTimeFieldsTag, appendItemNamesSink, err);
|
||||
appendItemNamesSink.fillInMissing();
|
||||
|
||||
// Load the available formats from CLDR.
|
||||
|
@ -1093,7 +1078,7 @@ DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& errorCod
|
|||
.append('/', errorCode)
|
||||
.append(DT_DateTimeAvailableFormatsTag, errorCode); // i.e., calendar/xxx/availableFormats
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
ures_getAllItemsWithFallback(rb.getAlias(), path.data(), availableFormatsSink, err);
|
||||
ures_getAllChildrenWithFallback(rb.getAlias(), path.data(), availableFormatsSink, err);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1561,53 +1561,56 @@ void IntlTestDateTimePatternGeneratorAPI::test_jConsistencyOddLocales() { // ICU
|
|||
void IntlTestDateTimePatternGeneratorAPI::testBestPattern() {
|
||||
// generic test for DateTimePatternGenerator::getBestPattern() that can be used to test multiple
|
||||
// bugs in the resource data
|
||||
static const char* testCases[] = {
|
||||
const struct TestCase {
|
||||
const char* localeID;
|
||||
const char* skeleton;
|
||||
const UChar* expectedPattern;
|
||||
} testCases[] = {
|
||||
// ICU-21650: (See the "week day" section of https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
|
||||
// for a full explanation of why this is the desired behavior)
|
||||
// if the user asks for E, the minimum field length is 3, but if he asks for c or e, it's 1
|
||||
"en_US", "E", "ccc",
|
||||
"en_US", "c", "c",
|
||||
"en_US", "e", "c",
|
||||
"en_US", "EE", "ccc",
|
||||
"en_US", "cc", "cc",
|
||||
"en_US", "ee", "cc",
|
||||
"en_US", "EEE", "ccc",
|
||||
"en_US", "ccc", "ccc",
|
||||
"en_US", "eee", "ccc",
|
||||
{ "en_US", "E", u"ccc" },
|
||||
{ "en_US", "c", u"c" },
|
||||
{ "en_US", "e", u"c" },
|
||||
{ "en_US", "EE", u"ccc" },
|
||||
{ "en_US", "cc", u"cc" },
|
||||
{ "en_US", "ee", u"cc" },
|
||||
{ "en_US", "EEE", u"ccc" },
|
||||
{ "en_US", "ccc", u"ccc" },
|
||||
{ "en_US", "eee", u"ccc" },
|
||||
// and if the user asked for c or e and the field length is 1 or 2, the output pattern should contain
|
||||
// e instead of E (e supports numeric abbreviations; E doesn't)
|
||||
"en_US", "yMEd", "EEE, M/d/y",
|
||||
"en_US", "yMcd", "e, M/d/y",
|
||||
"en_US", "yMed", "e, M/d/y",
|
||||
"en_US", "yMMEEdd", "EEE, MM/dd/y",
|
||||
"en_US", "yMMccdd", "ee, MM/dd/y",
|
||||
"en_US", "yMMeedd", "ee, MM/dd/y",
|
||||
"en_US", "yMMMEd", "EEE, MMM d, y",
|
||||
"en_US", "yMMMcccd", "EEE, MMM d, y",
|
||||
"en_US", "yMMMeeed", "EEE, MMM d, y",
|
||||
"en_US", "yMMMMEEEEd", "EEEE, MMMM d, y",
|
||||
"en_US", "yMMMMccccd", "EEEE, MMMM d, y",
|
||||
"en_US", "yMMMMeeeed", "EEEE, MMMM d, y",
|
||||
{ "en_US", "yMEd", u"EEE, M/d/y" },
|
||||
{ "en_US", "yMcd", u"e, M/d/y" },
|
||||
{ "en_US", "yMed", u"e, M/d/y" },
|
||||
{ "en_US", "yMMEEdd", u"EEE, MM/dd/y" },
|
||||
{ "en_US", "yMMccdd", u"ee, MM/dd/y" },
|
||||
{ "en_US", "yMMeedd", u"ee, MM/dd/y" },
|
||||
{ "en_US", "yMMMEd", u"EEE, MMM d, y" },
|
||||
{ "en_US", "yMMMcccd", u"EEE, MMM d, y" },
|
||||
{ "en_US", "yMMMeeed", u"EEE, MMM d, y" },
|
||||
{ "en_US", "yMMMMEEEEd", u"EEEE, MMMM d, y" },
|
||||
{ "en_US", "yMMMMccccd", u"EEEE, MMMM d, y" },
|
||||
{ "en_US", "yMMMMeeeed", u"EEEE, MMMM d, y" },
|
||||
// ICU-21428: Bad patterns for nonstandard calendars
|
||||
"en_GB", "yMd", "dd/MM/y",
|
||||
"en_GB@calendar=coptic", "yMd", "dd/MM/y GGGGG",
|
||||
"en_GB@calendar=japanese", "yMd", "dd/MM/y GGGGG",
|
||||
"en_GB@calendar=buddhist", "yMd", "dd/MM/y GGGGG",
|
||||
{ "en_GB", "yMd", u"dd/MM/y" },
|
||||
{ "en_GB@calendar=coptic", "yMd", u"dd/MM/y GGGGG" },
|
||||
{ "en_GB@calendar=japanese", "yMd", u"dd/MM/y GGGGG" },
|
||||
{ "en_GB@calendar=buddhist", "yMd", u"dd/MM/y GGGGG" },
|
||||
// ICU-20992: Bad patterns for missing fields
|
||||
{ "ckb_IR", "mmSSS", u"mm:ss\u066bSSS" },
|
||||
{ "ckb_IR", "BSSS", u"SSS \u251c'Dayperiod': B\u2524" },
|
||||
};
|
||||
|
||||
for (int32_t i = 0; i < UPRV_LENGTHOF(testCases); i += 3) {
|
||||
const char* localeID(testCases[i]);
|
||||
const char* skeleton(testCases[i + 1]);
|
||||
const char* expectedPattern(testCases[i + 2]);
|
||||
|
||||
for (int32_t i = 0; i < UPRV_LENGTHOF(testCases); i++) {
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
UnicodeString actualPattern;
|
||||
|
||||
if (uprv_strcmp(skeleton, "full") != 0) {
|
||||
LocalPointer<DateTimePatternGenerator> dtpg(DateTimePatternGenerator::createInstance(localeID, err), err);
|
||||
actualPattern = dtpg->getBestPattern(UnicodeString(skeleton), err);
|
||||
if (uprv_strcmp(testCases[i].skeleton, "full") != 0) {
|
||||
LocalPointer<DateTimePatternGenerator> dtpg(DateTimePatternGenerator::createInstance(testCases[i].localeID, err), err);
|
||||
actualPattern = dtpg->getBestPattern(UnicodeString(testCases[i].skeleton), err);
|
||||
} else {
|
||||
LocalPointer<DateFormat> df(DateFormat::createDateInstance(DateFormat::kFull, localeID));
|
||||
LocalPointer<DateFormat> df(DateFormat::createDateInstance(DateFormat::kFull, testCases[i].localeID));
|
||||
SimpleDateFormat* sdf = dynamic_cast<SimpleDateFormat*>(df.getAlias());
|
||||
|
||||
if (sdf != NULL) {
|
||||
|
@ -1616,14 +1619,14 @@ void IntlTestDateTimePatternGeneratorAPI::testBestPattern() {
|
|||
}
|
||||
|
||||
if (U_FAILURE(err)) {
|
||||
errln("Failure for test case %s/%s: %s", localeID, skeleton, u_errorName(err));
|
||||
errln("Failure for test case %s/%s: %s", testCases[i].localeID, testCases[i].skeleton, u_errorName(err));
|
||||
} else {
|
||||
char failureMessage[100];
|
||||
strcpy(failureMessage, "Wrong result for test case ");
|
||||
strcat(failureMessage, localeID);
|
||||
strcat(failureMessage, testCases[i].localeID);
|
||||
strcat(failureMessage, "/");
|
||||
strcat(failureMessage, skeleton);
|
||||
assertEquals(failureMessage, UnicodeString(expectedPattern), actualPattern);
|
||||
strcat(failureMessage, testCases[i].skeleton);
|
||||
assertEquals(failureMessage, testCases[i].expectedPattern, actualPattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -395,6 +395,13 @@ public class ICUResourceBundle extends UResourceBundle {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the resource specified by `path` in this resource bundle (performing any necessary fallback and
|
||||
* following any aliases) and calls the specified `sink`'s `put()` method with that resource. Then walks the
|
||||
* bundle's parent chain, calling `put()` on the sink for each item in the parent chain.
|
||||
* @param path The path of the desired resource
|
||||
* @param sink A `UResource.Sink` that gets called for each resource in the parent chain
|
||||
*/
|
||||
public void getAllItemsWithFallback(String path, UResource.Sink sink)
|
||||
throws MissingResourceException {
|
||||
// Collect existing and parsed key objects into an array of keys,
|
||||
|
@ -421,6 +428,47 @@ public class ICUResourceBundle extends UResourceBundle {
|
|||
rb.getAllItemsWithFallback(key, readerValue, sink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the resource specified by `path` in this resource bundle (performing any necessary fallback and
|
||||
* following any aliases) and, if the resource is a table resource, iterates over its immediate child resources (again,
|
||||
* following any aliases to get the individual resource values), and calls the specified `sink`'s `put()` method
|
||||
* for each child resource (passing it that resource's key and either its actual value or, if that value is an
|
||||
* alias, the value you get by following the alias). Then walks back over the bundle's parent chain,
|
||||
* similarly iterating over each parent table resource's child resources.
|
||||
* Does not descend beyond one level of table children.
|
||||
* @param path The path of the desired resource
|
||||
* @param sink A `UResource.Sink` that gets called for each child resource of the specified resource (and each child
|
||||
* of the resources in its parent chain).
|
||||
*/
|
||||
public void getAllChildrenWithFallback(final String path, final UResource.Sink sink)
|
||||
throws MissingResourceException {
|
||||
class AllChildrenSink extends UResource.Sink {
|
||||
@Override
|
||||
public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
|
||||
UResource.Table itemsTable = value.getTable();
|
||||
for (int i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
|
||||
if (value.getType() == ALIAS) {
|
||||
// if the current entry in the table is an alias, re-fetch it using getAliasedResource():
|
||||
// this will follow the alias (and any aliases it points to) and bring back the real value
|
||||
String aliasPath = value.getAliasString();
|
||||
ICUResourceBundle aliasedResource = getAliasedResource(aliasPath, wholeBundle.loader,
|
||||
"", null, 0, null,
|
||||
null, ICUResourceBundle.this);
|
||||
ICUResourceBundleImpl aliasedResourceImpl = (ICUResourceBundleImpl)aliasedResource;
|
||||
ReaderValue aliasedValue = new ReaderValue();
|
||||
aliasedValue.reader = aliasedResourceImpl.wholeBundle.reader;
|
||||
aliasedValue.res = aliasedResourceImpl.getResource();
|
||||
sink.put(key, aliasedValue, noFallback);
|
||||
} else {
|
||||
sink.put(key, value, noFallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAllItemsWithFallback(path, new AllChildrenSink());
|
||||
}
|
||||
|
||||
private void getAllItemsWithFallback(
|
||||
UResource.Key key, ReaderValue readerValue, UResource.Sink sink) {
|
||||
// We recursively enumerate child-first,
|
||||
|
@ -1481,10 +1529,27 @@ public class ICUResourceBundle extends UResourceBundle {
|
|||
UResourceBundle requested) {
|
||||
WholeBundle wholeBundle = base.wholeBundle;
|
||||
ClassLoader loaderToUse = wholeBundle.loader;
|
||||
String rpath = wholeBundle.reader.getAlias(_resource);
|
||||
String baseName = wholeBundle.baseName;
|
||||
|
||||
// TODO: We need not build the baseKeyPath array if the rpath includes a keyPath
|
||||
// (except for an exception message string).
|
||||
// Try to avoid unnecessary work+allocation.
|
||||
int baseDepth = base.getResDepth();
|
||||
String[] baseKeyPath = new String[baseDepth + 1];
|
||||
base.getResPathKeys(baseKeyPath, baseDepth);
|
||||
baseKeyPath[baseDepth] = key;
|
||||
return getAliasedResource(rpath, loaderToUse, baseName, keys, depth, baseKeyPath, aliasesVisited, requested);
|
||||
}
|
||||
|
||||
protected static ICUResourceBundle getAliasedResource(
|
||||
String rpath, ClassLoader loaderToUse, String baseName,
|
||||
String[] keys, int depth, String[] baseKeyPath,
|
||||
HashMap<String, String> aliasesVisited,
|
||||
UResourceBundle requested) {
|
||||
String locale;
|
||||
String keyPath = null;
|
||||
String bundleName;
|
||||
String rpath = wholeBundle.reader.getAlias(_resource);
|
||||
if (aliasesVisited == null) {
|
||||
aliasesVisited = new HashMap<>();
|
||||
}
|
||||
|
@ -1523,12 +1588,12 @@ public class ICUResourceBundle extends UResourceBundle {
|
|||
} else {
|
||||
locale = rpath;
|
||||
}
|
||||
bundleName = wholeBundle.baseName;
|
||||
bundleName = baseName;
|
||||
}
|
||||
ICUResourceBundle bundle = null;
|
||||
ICUResourceBundle sub = null;
|
||||
if(bundleName.equals(LOCALE)){
|
||||
bundleName = wholeBundle.baseName;
|
||||
bundleName = baseName;
|
||||
keyPath = rpath.substring(LOCALE.length() + 2/* prepending and appending / */, rpath.length());
|
||||
|
||||
// Get the top bundle of the requested bundle
|
||||
|
@ -1550,11 +1615,8 @@ public class ICUResourceBundle extends UResourceBundle {
|
|||
} else if (keys != null) {
|
||||
numKeys = depth;
|
||||
} else {
|
||||
depth = base.getResDepth();
|
||||
numKeys = depth + 1;
|
||||
keys = new String[numKeys];
|
||||
base.getResPathKeys(keys, depth);
|
||||
keys[depth] = key;
|
||||
keys = baseKeyPath;
|
||||
numKeys = baseKeyPath.length;
|
||||
}
|
||||
if (numKeys > 0) {
|
||||
sub = bundle;
|
||||
|
@ -1564,7 +1626,7 @@ public class ICUResourceBundle extends UResourceBundle {
|
|||
}
|
||||
}
|
||||
if (sub == null) {
|
||||
throw new MissingResourceException(wholeBundle.localeID, wholeBundle.baseName, key);
|
||||
throw new MissingResourceException(locale, baseName, baseKeyPath[baseKeyPath.length - 1]);
|
||||
}
|
||||
// TODO: If we know that sub is not cached,
|
||||
// then we should set its container and key to the alias' location,
|
||||
|
|
|
@ -213,13 +213,10 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
|||
private class AppendItemFormatsSink extends UResource.Sink {
|
||||
@Override
|
||||
public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
|
||||
UResource.Table itemsTable = value.getTable();
|
||||
for (int i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
|
||||
int field = getAppendFormatNumber(key);
|
||||
assert field != -1;
|
||||
if (getAppendItemFormat(field) == null) {
|
||||
setAppendItemFormat(field, value.toString());
|
||||
}
|
||||
int field = getAppendFormatNumber(key);
|
||||
if (field < 0) { return; }
|
||||
if (getAppendItemFormat(field) == null) {
|
||||
setAppendItemFormat(field, value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -227,26 +224,14 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
|||
private class AppendItemNamesSink extends UResource.Sink {
|
||||
@Override
|
||||
public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
|
||||
UResource.Table itemsTable = value.getTable();
|
||||
for (int i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
|
||||
if (value.getType() != UResourceBundle.TABLE) {
|
||||
// Typically get either UResourceBundle.TABLE = 2 or ICUResourceBundle.ALIAS = 3.
|
||||
// Currently fillInMissing() is being used instead of following the ALIAS, so
|
||||
// skip ALIAS entries which cause UResourceTypeMismatchException in the line
|
||||
// UResource.Table detailsTable = value.getTable()
|
||||
continue;
|
||||
}
|
||||
int fieldAndWidth = getCLDRFieldAndWidthNumber(key);
|
||||
if (fieldAndWidth == -1) { continue; }
|
||||
int field = fieldAndWidth / DisplayWidth.COUNT;
|
||||
DisplayWidth width = CLDR_FIELD_WIDTH[fieldAndWidth % DisplayWidth.COUNT];
|
||||
UResource.Table detailsTable = value.getTable();
|
||||
for (int j = 0; detailsTable.getKeyAndValue(j, key, value); ++j) {
|
||||
if (!key.contentEquals("dn")) continue;
|
||||
if (getFieldDisplayName(field, width) == null) {
|
||||
setFieldDisplayName(field, width, value.toString());
|
||||
}
|
||||
break;
|
||||
int fieldAndWidth = getCLDRFieldAndWidthNumber(key);
|
||||
if (fieldAndWidth == -1) { return; }
|
||||
int field = fieldAndWidth / DisplayWidth.COUNT;
|
||||
DisplayWidth width = CLDR_FIELD_WIDTH[fieldAndWidth % DisplayWidth.COUNT];
|
||||
UResource.Table detailsTable = value.getTable();
|
||||
if (detailsTable.findValue("dn", value)) {
|
||||
if (getFieldDisplayName(field, width) == null) {
|
||||
setFieldDisplayName(field, width, value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -277,16 +262,13 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
|||
|
||||
@Override
|
||||
public void put(UResource.Key key, UResource.Value value, boolean isRoot) {
|
||||
UResource.Table formatsTable = value.getTable();
|
||||
for (int i = 0; formatsTable.getKeyAndValue(i, key, value); ++i) {
|
||||
String formatKey = key.toString();
|
||||
if (!isAvailableFormatSet(formatKey)) {
|
||||
setAvailableFormat(formatKey);
|
||||
// Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
|
||||
// but not a previous availableFormats entry:
|
||||
String formatValue = value.toString();
|
||||
addPatternWithSkeleton(formatValue, formatKey, !isRoot, returnInfo);
|
||||
}
|
||||
String formatKey = key.toString();
|
||||
if (!isAvailableFormatSet(formatKey)) {
|
||||
setAvailableFormat(formatKey);
|
||||
// Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
|
||||
// but not a previous availableFormats entry:
|
||||
String formatValue = value.toString();
|
||||
addPatternWithSkeleton(formatValue, formatKey, !isRoot, returnInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -305,7 +287,7 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
|||
// Load append item formats.
|
||||
AppendItemFormatsSink appendItemFormatsSink = new AppendItemFormatsSink();
|
||||
try {
|
||||
rb.getAllItemsWithFallback(
|
||||
rb.getAllChildrenWithFallback(
|
||||
"calendar/" + calendarTypeToUse + "/appendItems",
|
||||
appendItemFormatsSink);
|
||||
}catch(MissingResourceException e) {
|
||||
|
@ -314,7 +296,7 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
|||
// Load CLDR item names.
|
||||
AppendItemNamesSink appendItemNamesSink = new AppendItemNamesSink();
|
||||
try {
|
||||
rb.getAllItemsWithFallback(
|
||||
rb.getAllChildrenWithFallback(
|
||||
"fields",
|
||||
appendItemNamesSink);
|
||||
}catch(MissingResourceException e) {
|
||||
|
@ -323,7 +305,7 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
|||
// Load the available formats from CLDR.
|
||||
AvailableFormatsSink availableFormatsSink = new AvailableFormatsSink(returnInfo);
|
||||
try {
|
||||
rb.getAllItemsWithFallback(
|
||||
rb.getAllChildrenWithFallback(
|
||||
"calendar/" + calendarTypeToUse + "/availableFormats",
|
||||
availableFormatsSink);
|
||||
} catch (MissingResourceException e) {
|
||||
|
|
|
@ -1825,6 +1825,10 @@ public class DateTimeGeneratorTest extends TestFmwk {
|
|||
"en_US", "yMMMMEEEEd", "EEEE, MMMM d, y",
|
||||
"en_US", "yMMMMccccd", "EEEE, MMMM d, y",
|
||||
"en_US", "yMMMMeeeed", "EEEE, MMMM d, y",
|
||||
|
||||
// ICU-20992: Bad patterns for missing fields
|
||||
"ckb_IR", "mmSSS", "mm:ss٫SSS",
|
||||
"ckb_IR", "BSSS", "SSS ├'Dayperiod': B┤",
|
||||
};
|
||||
|
||||
for (int i = 0; i < testCases.length; i += 3) {
|
||||
|
|
Loading…
Add table
Reference in a new issue