ICU-20992 Added new API for iterating child resources while following aliases, and changed DateTimePatternGenerator

to use it.
This commit is contained in:
Rich Gillam 2021-07-23 17:18:41 -07:00
parent 01e1adc9e4
commit 19b8b076f9
8 changed files with 247 additions and 127 deletions

View file

@ -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));
}

View file

@ -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;
};

View file

@ -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 */
/**

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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,

View file

@ -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) {

View file

@ -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) {