diff --git a/icu4c/source/common/resource.cpp b/icu4c/source/common/resource.cpp index c8c02f033f..0d41dae5c8 100644 --- a/icu4c/source/common/resource.cpp +++ b/icu4c/source/common/resource.cpp @@ -19,6 +19,7 @@ U_NAMESPACE_BEGIN ResourceValue::~ResourceValue() {} +ResourceSink::~ResourceSink() {} ResourceArraySink::~ResourceArraySink() {} diff --git a/icu4c/source/common/resource.h b/icu4c/source/common/resource.h index 20e3e270d5..12d0b16091 100644 --- a/icu4c/source/common/resource.h +++ b/icu4c/source/common/resource.h @@ -27,13 +27,81 @@ #include "unicode/unistr.h" #include "unicode/ures.h" +struct ResourceData; + U_NAMESPACE_BEGIN class ResourceTableSink; +class ResourceValue; // Note: In C++, we use const char * pointers for keys, // rather than an abstraction like Java UResource.Key. +/** + * Interface for iterating over a resource bundle array resource. + */ +class U_COMMON_API ResourceArray { +public: + /** Constructs an empty array object. */ + ResourceArray() : items16(NULL), items32(NULL), length(0) {} + + /** Only for implementation use. @internal */ + ResourceArray(const uint16_t *i16, const uint32_t *i32, int32_t len) : + items16(i16), items32(i32), length(len) {} + + /** + * @return The number of items in the array resource. + */ + int32_t getSize() const { return length; } + /** + * @param i Array item index. + * @param value Output-only, receives the value of the i'th item. + * @return TRUE if i is non-negative and less than getSize(). + */ + UBool getValue(int32_t i, ResourceValue &value) const; + + /** Only for implementation use. @internal */ + uint32_t internalGetResource(const ResourceData *pResData, int32_t i) const; + +private: + const uint16_t *items16; + const uint32_t *items32; + int32_t length; +}; + +/** + * Interface for iterating over a resource bundle table resource. + */ +class U_COMMON_API ResourceTable { +public: + /** Constructs an empty table object. */ + ResourceTable() : keys16(NULL), keys32(NULL), items16(NULL), items32(NULL), length(0) {} + + /** Only for implementation use. @internal */ + ResourceTable(const uint16_t *k16, const int32_t *k32, + const uint16_t *i16, const uint32_t *i32, int32_t len) : + keys16(k16), keys32(k32), items16(i16), items32(i32), length(len) {} + + /** + * @return The number of items in the array resource. + */ + int32_t getSize() const { return length; } + /** + * @param i Array item index. + * @param key Output-only, receives the key of the i'th item. + * @param value Output-only, receives the value of the i'th item. + * @return TRUE if i is non-negative and less than getSize(). + */ + UBool getKeyAndValue(int32_t i, const char *&key, ResourceValue &value) const; + +private: + const uint16_t *keys16; + const int32_t *keys32; + const uint16_t *items16; + const uint32_t *items32; + int32_t length; +}; + /** * Represents a resource bundle item's value. * Avoids object creations as much as possible. @@ -100,6 +168,73 @@ public: */ virtual const uint8_t *getBinary(int32_t &length, UErrorCode &errorCode) const = 0; + /** + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an array resource + */ + virtual ResourceArray getArray(UErrorCode &errorCode) const = 0; + + /** + * Sets U_RESOURCE_TYPE_MISMATCH if this is not a table resource + */ + virtual ResourceTable getTable(UErrorCode &errorCode) const = 0; + + /** + * Is this a no-fallback/no-inheritance marker string? + * Such a marker is used for + * CLDR no-fallback data values of (three empty-set symbols)=={2205, 2205, 2205} + * when enumerating tables with fallback from the specific resource bundle to root. + * + * @return TRUE if this is a no-inheritance marker string + */ + virtual UBool isNoInheritanceMarker() const = 0; + + /** + * Sets the dest strings from the string values in this array resource. + * + * @return the number of strings in this array resource. + * If greater than capacity, then an overflow error is set. + * + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an array resource + * or if any of the array items is not a string + */ + virtual int32_t getStringArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const = 0; + + /** + * Same as + *
+ * if (getType() == URES_STRING) { + * return new String[] { getString(); } + * } else { + * return getStringArray(); + * } + *+ * + * Sets U_RESOURCE_TYPE_MISMATCH if this is + * neither a string resource nor an array resource containing strings + * @see getString() + * @see getStringArray() + */ + virtual int32_t getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const = 0; + + /** + * Same as + *
+ * if (getType() == URES_STRING) { + * return getString(); + * } else { + * return getStringArray()[0]; + * } + *+ * + * Sets U_RESOURCE_TYPE_MISMATCH if this is + * neither a string resource nor an array resource containing strings + * @see getString() + * @see getStringArray() + */ + virtual UnicodeString getStringOrFirstOfArray(UErrorCode &errorCode) const = 0; + protected: ResourceValue() {} @@ -108,6 +243,36 @@ private: ResourceValue &operator=(const ResourceValue &); // no assignment operator }; +/** + * Sink for ICU resource bundle contents. + */ +class U_COMMON_API ResourceSink : public UObject { +public: + ResourceSink() {} + virtual ~ResourceSink(); + + /** + * Called once for each bundle (child-parent-...-root). + * The value is normally an array or table resource, + * and implementations of this method normally iterate over the + * tree of resource items stored there. + * + * @param key The key string of the enumeration-start resource. + * Empty if the enumeration starts at the top level of the bundle. + * @param value Call getArray() or getTable() as appropriate. + * Then reuse for output values from Array and Table getters. + * @param noFallback true if the bundle has no parent; + * that is, its top-level table has the nofallback attribute, + * or it is the root bundle of a locale tree. + */ + virtual void put(const char *key, ResourceValue &value, UBool noFallback, + UErrorCode &errorCode) = 0; + +private: + ResourceSink(const ResourceSink &); // no copy constructor + ResourceSink &operator=(const ResourceSink &); // no assignment operator +}; + /** * Sink for ICU resource array contents. * The base class does nothing. diff --git a/icu4c/source/common/uresbund.cpp b/icu4c/source/common/uresbund.cpp index 62460f8e20..a14b84a7e9 100644 --- a/icu4c/source/common/uresbund.cpp +++ b/icu4c/source/common/uresbund.cpp @@ -1885,32 +1885,41 @@ ures_getByKeyWithFallback(const UResourceBundle *resB, namespace { -void getAllContainerItemsWithFallback( +void getAllItemsWithFallback( const UResourceBundle *bundle, ResourceDataValue &value, + ResourceSink *sink, ResourceArraySink *arraySink, ResourceTableSink *tableSink, UErrorCode &errorCode) { if (U_FAILURE(errorCode)) { return; } // We recursively enumerate child-first, // only storing parent items in the absence of child items. - // We store a placeholder value for the no-fallback/no-inheritance marker + // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker // to prevent a parent item from being stored. // // It would be possible to recursively enumerate parent-first, // overriding parent items with child items. - // When we see the no-fallback/no-inheritance marker, - // then we would remove the parent's item. + // When the sink sees the no-fallback/no-inheritance marker, + // then it would remove the parent's item. // We would deserialize parent values even though they are overridden in a child bundle. - UResType expectedType = arraySink != NULL ? URES_ARRAY : URES_TABLE; - if (ures_getType(bundle) == expectedType) { - value.pResData = &bundle->fResData; - if (arraySink != NULL) { - ures_getAllArrayItems(&bundle->fResData, bundle->fRes, value, *arraySink, errorCode); - } else /* tableSink != NULL */ { - ures_getAllTableItems(&bundle->fResData, bundle->fRes, value, *tableSink, errorCode); + value.pResData = &bundle->fResData; + UResourceDataEntry *parentEntry = bundle->fData->fParent; + UBool hasParent = parentEntry != NULL && U_SUCCESS(parentEntry->fBogus); + UResType expectedType; + if (sink != NULL) { + expectedType = URES_NONE; + value.setResource(bundle->fRes); + sink->put(bundle->fKey, value, !hasParent, errorCode); + } else { + expectedType = arraySink != NULL ? URES_ARRAY : URES_TABLE; + if (ures_getType(bundle) == expectedType) { + if (arraySink != NULL) { + ures_getAllArrayItems(&bundle->fResData, bundle->fRes, value, *arraySink, errorCode); + } else /* tableSink != NULL */ { + ures_getAllTableItems(&bundle->fResData, bundle->fRes, value, *tableSink, errorCode); + } } } - UResourceDataEntry *entry = bundle->fData->fParent; - if (entry != NULL && U_SUCCESS(entry->fBogus)) { + if (hasParent) { // We might try to query the sink whether // any fallback from the parent bundle is still possible. @@ -1921,16 +1930,16 @@ void getAllContainerItemsWithFallback( // so that we need not create UResourceBundle objects. UResourceBundle parentBundle; ures_initStackObject(&parentBundle); - parentBundle.fTopLevelData = parentBundle.fData = entry; + parentBundle.fTopLevelData = parentBundle.fData = parentEntry; // TODO: What is the difference between bundle fData and fTopLevelData? - uprv_memcpy(&parentBundle.fResData, &entry->fData, sizeof(ResourceData)); + uprv_memcpy(&parentBundle.fResData, &parentEntry->fData, sizeof(ResourceData)); // TODO: Try to replace bundle.fResData with just using bundle.fData->fData. parentBundle.fHasFallback = !parentBundle.fResData.noFallback; parentBundle.fIsTopLevel = TRUE; parentBundle.fRes = parentBundle.fResData.rootRes; parentBundle.fSize = res_countArrayItems(&(parentBundle.fResData), parentBundle.fRes); parentBundle.fIndex = -1; - entryIncrease(entry); + entryIncrease(parentEntry); // Look up the container item in the parent bundle. UResourceBundle containerBundle; @@ -1942,17 +1951,17 @@ void getAllContainerItemsWithFallback( rb = ures_getByKeyWithFallback(&parentBundle, bundle->fResPath, &containerBundle, &errorCode); } - if (U_SUCCESS(errorCode) && ures_getType(rb) == expectedType) { - getAllContainerItemsWithFallback(rb, value, - arraySink, tableSink, errorCode); + if (U_SUCCESS(errorCode) && (expectedType == URES_NONE || ures_getType(rb) == expectedType)) { + getAllItemsWithFallback(rb, value, sink, arraySink, tableSink, errorCode); } ures_close(&containerBundle); ures_close(&parentBundle); } } -void getAllContainerItemsWithFallback( +void getAllItemsWithFallback( const UResourceBundle *bundle, const char *path, + ResourceSink *sink, ResourceArraySink *arraySink, ResourceTableSink *tableSink, UErrorCode &errorCode) { if (U_FAILURE(errorCode)) { return; } @@ -1973,30 +1982,38 @@ void getAllContainerItemsWithFallback( return; } } - UResType expectedType = arraySink != NULL ? URES_ARRAY : URES_TABLE; - if (ures_getType(rb) != expectedType) { - errorCode = U_RESOURCE_TYPE_MISMATCH; - ures_close(&stackBundle); - return; + if (sink == NULL) { + UResType expectedType = arraySink != NULL ? URES_ARRAY : URES_TABLE; + if (ures_getType(rb) != expectedType) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + ures_close(&stackBundle); + return; + } } // Get all table items with fallback. ResourceDataValue value; - getAllContainerItemsWithFallback(rb, value, arraySink, tableSink, errorCode); + getAllItemsWithFallback(rb, value, sink, arraySink, tableSink, errorCode); ures_close(&stackBundle); } } // namespace +U_CAPI void U_EXPORT2 +ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path, + icu::ResourceSink &sink, UErrorCode &errorCode) { + getAllItemsWithFallback(bundle, path, &sink, NULL, NULL, errorCode); +} + U_CAPI void U_EXPORT2 ures_getAllArrayItemsWithFallback(const UResourceBundle *bundle, const char *path, ResourceArraySink &sink, UErrorCode &errorCode) { - getAllContainerItemsWithFallback(bundle, path, &sink, NULL, errorCode); + getAllItemsWithFallback(bundle, path, NULL, &sink, NULL, errorCode); } U_CAPI void U_EXPORT2 ures_getAllTableItemsWithFallback(const UResourceBundle *bundle, const char *path, ResourceTableSink &sink, UErrorCode &errorCode) { - getAllContainerItemsWithFallback(bundle, path, NULL, &sink, errorCode); + getAllItemsWithFallback(bundle, path, NULL, NULL, &sink, errorCode); } U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) { diff --git a/icu4c/source/common/uresdata.cpp b/icu4c/source/common/uresdata.cpp index 90034ca241..cc33372d76 100644 --- a/icu4c/source/common/uresdata.cpp +++ b/icu4c/source/common/uresdata.cpp @@ -380,6 +380,36 @@ UBool isNoInheritanceMarker(const ResourceData *pResData, Resource res) { return FALSE; } +int32_t getStringArray(const ResourceData *pResData, const icu::ResourceArray &array, + icu::UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return 0; + } + if(dest == NULL ? capacity != 0 : capacity < 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + int32_t length = array.getSize(); + if(length == 0) { + return 0; + } + if(length > capacity) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return length; + } + for(int32_t i = 0; i < length; ++i) { + int32_t sLength; + const UChar *s = res_getString(pResData, array.internalGetResource(pResData, i), &sLength); + if(s == NULL) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + return 0; + } + dest[i].setTo(TRUE, s, sLength); + } + return length; +} + } // namespace U_CAPI const UChar * U_EXPORT2 @@ -535,6 +565,130 @@ const uint8_t *ResourceDataValue::getBinary(int32_t &length, UErrorCode &errorCo return b; } +ResourceArray ResourceDataValue::getArray(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return ResourceArray(); + } + const uint16_t *items16 = NULL; + const Resource *items32 = NULL; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length = 0; + switch(RES_GET_TYPE(res)) { + case URES_ARRAY: + if (offset!=0) { // empty if offset==0 + items32 = (const Resource *)pResData->pRoot+offset; + length = *items32++; + } + break; + case URES_ARRAY16: + items16 = pResData->p16BitUnits+offset; + length = *items16++; + break; + default: + errorCode = U_RESOURCE_TYPE_MISMATCH; + return ResourceArray(); + } + return ResourceArray(items16, items32, length); +} + +ResourceTable ResourceDataValue::getTable(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return ResourceTable(); + } + const uint16_t *keys16 = NULL; + const int32_t *keys32 = NULL; + const uint16_t *items16 = NULL; + const Resource *items32 = NULL; + uint32_t offset = RES_GET_OFFSET(res); + int32_t length = 0; + switch(RES_GET_TYPE(res)) { + case URES_TABLE: + if (offset != 0) { // empty if offset==0 + keys16 = (const uint16_t *)(pResData->pRoot+offset); + length = *keys16++; + items32 = (const Resource *)(keys16+length+(~length&1)); + } + break; + case URES_TABLE16: + keys16 = pResData->p16BitUnits+offset; + length = *keys16++; + items16 = keys16 + length; + break; + case URES_TABLE32: + if (offset != 0) { // empty if offset==0 + keys32 = pResData->pRoot+offset; + length = *keys32++; + items32 = (const Resource *)keys32 + length; + } + break; + default: + errorCode = U_RESOURCE_TYPE_MISMATCH; + return ResourceTable(); + } + return ResourceTable(keys16, keys32, items16, items32, length); +} + +UBool ResourceDataValue::isNoInheritanceMarker() const { + return ::isNoInheritanceMarker(pResData, res); +} + +int32_t ResourceDataValue::getStringArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const { + return ::getStringArray(pResData, getArray(errorCode), dest, capacity, errorCode); +} + +int32_t ResourceDataValue::getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const { + if(URES_IS_ARRAY(res)) { + return ::getStringArray(pResData, getArray(errorCode), dest, capacity, errorCode); + } + if(U_FAILURE(errorCode)) { + return 0; + } + if(dest == NULL ? capacity != 0 : capacity < 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + if(capacity < 1) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 1; + } + int32_t sLength; + const UChar *s = res_getString(pResData, res, &sLength); + if(s != NULL) { + dest[0].setTo(TRUE, s, sLength); + return 1; + } + errorCode = U_RESOURCE_TYPE_MISMATCH; + return 0; +} + +UnicodeString ResourceDataValue::getStringOrFirstOfArray(UErrorCode &errorCode) const { + UnicodeString us; + if(U_FAILURE(errorCode)) { + return us; + } + int32_t sLength; + const UChar *s = res_getString(pResData, res, &sLength); + if(s != NULL) { + us.setTo(TRUE, s, sLength); + return us; + } + ResourceArray array = getArray(errorCode); + if(U_FAILURE(errorCode)) { + return us; + } + if(array.getSize() > 0) { + s = res_getString(pResData, array.internalGetResource(pResData, 0), &sLength); + if(s != NULL) { + us.setTo(TRUE, s, sLength); + return us; + } + } + errorCode = U_RESOURCE_TYPE_MISMATCH; + return us; +} + U_NAMESPACE_END static Resource @@ -673,28 +827,25 @@ ures_getAllTableItems(const ResourceData *pResData, Resource table, uint32_t offset = RES_GET_OFFSET(table); int32_t length = 0; switch(RES_GET_TYPE(table)) { - case URES_TABLE: { - if (offset != 0) { /* empty if offset==0 */ + case URES_TABLE: + if (offset != 0) { // empty if offset==0 keys16 = (const uint16_t *)(pResData->pRoot+offset); length = *keys16++; items32 = (const Resource *)(keys16+length+(~length&1)); } break; - } - case URES_TABLE16: { + case URES_TABLE16: keys16 = pResData->p16BitUnits+offset; length = *keys16++; items16 = keys16 + length; break; - } - case URES_TABLE32: { - if (offset != 0) { /* empty if offset==0 */ + case URES_TABLE32: + if (offset != 0) { // empty if offset==0 keys32 = pResData->pRoot+offset; length = *keys32++; items32 = (const Resource *)keys32 + length; } break; - } default: errorCode = U_RESOURCE_TYPE_MISMATCH; return; @@ -742,6 +893,27 @@ ures_getAllTableItems(const ResourceData *pResData, Resource table, sink.leave(errorCode); } +UBool icu::ResourceTable::getKeyAndValue(int32_t i, + const char *&key, icu::ResourceValue &value) const { + if(0 <= i && i < length) { + icu::ResourceDataValue &rdValue = static_cast