From 19b8b076f9d757f6119b43f5f08b86e757cbb66b Mon Sep 17 00:00:00 2001 From: Rich Gillam <62772518+richgillam@users.noreply.github.com> Date: Fri, 23 Jul 2021 17:18:41 -0700 Subject: [PATCH] ICU-20992 Added new API for iterating child resources while following aliases, and changed DateTimePatternGenerator to use it. --- icu4c/source/common/uresbund.cpp | 45 +++++++++++ icu4c/source/common/uresdata.h | 10 +++ icu4c/source/common/uresimp.h | 29 +++++++ icu4c/source/i18n/dtptngen.cpp | 63 ++++++--------- icu4c/source/test/intltest/dtptngts.cpp | 81 ++++++++++--------- .../com/ibm/icu/impl/ICUResourceBundle.java | 80 +++++++++++++++--- .../icu/text/DateTimePatternGenerator.java | 62 +++++--------- .../test/format/DateTimeGeneratorTest.java | 4 + 8 files changed, 247 insertions(+), 127 deletions(-) diff --git a/icu4c/source/common/uresbund.cpp b/icu4c/source/common/uresbund.cpp index e0fba611363..119faf7f293 100644 --- a/icu4c/source/common/uresbund.cpp +++ b/icu4c/source/common/uresbund.cpp @@ -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(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)); } diff --git a/icu4c/source/common/uresdata.h b/icu4c/source/common/uresdata.h index 3596a2282a2..8f6ba64dcd6 100644 --- a/icu4c/source/common/uresdata.h +++ b/icu4c/source/common/uresdata.h @@ -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(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; }; diff --git a/icu4c/source/common/uresimp.h b/icu4c/source/common/uresimp.h index 0060a436028..399abcfe868 100644 --- a/icu4c/source/common/uresimp.h +++ b/icu4c/source/common/uresimp.h @@ -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 */ /** diff --git a/icu4c/source/i18n/dtptngen.cpp b/icu4c/source/i18n/dtptngen.cpp index 4f40ea8877f..a74add021cf 100644 --- a/icu4c/source/i18n/dtptngen.cpp +++ b/icu4c/source/i18n/dtptngen.cpp @@ -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 diff --git a/icu4c/source/test/intltest/dtptngts.cpp b/icu4c/source/test/intltest/dtptngts.cpp index b4eee1c2d5b..65ff807241f 100644 --- a/icu4c/source/test/intltest/dtptngts.cpp +++ b/icu4c/source/test/intltest/dtptngts.cpp @@ -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 dtpg(DateTimePatternGenerator::createInstance(localeID, err), err); - actualPattern = dtpg->getBestPattern(UnicodeString(skeleton), err); + if (uprv_strcmp(testCases[i].skeleton, "full") != 0) { + LocalPointer dtpg(DateTimePatternGenerator::createInstance(testCases[i].localeID, err), err); + actualPattern = dtpg->getBestPattern(UnicodeString(testCases[i].skeleton), err); } else { - LocalPointer df(DateFormat::createDateInstance(DateFormat::kFull, localeID)); + LocalPointer df(DateFormat::createDateInstance(DateFormat::kFull, testCases[i].localeID)); SimpleDateFormat* sdf = dynamic_cast(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); } } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundle.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundle.java index 9494c1d99be..c1ff388c632 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundle.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundle.java @@ -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 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, diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java index 56add4cedd8..1fa92146c9e 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java @@ -213,13 +213,10 @@ public class DateTimePatternGenerator implements Freezable