+namespace {
+
+struct AllowedHourFormatsSink : public ResourceTableSink {
+ // Initialize sub-sinks.
+ AllowedHourFormatsSink() : localeSink(*this), allowedListSink(*this) {}
+ virtual ~AllowedHourFormatsSink();
+
+ // Entry point.
+ virtual ResourceTableSink *getOrCreateTableSink(const char *key, int32_t, UErrorCode &status) {
+ if (U_FAILURE(status)) { return NULL; }
+
+ locale = key;
+ return &localeSink;
+ }
+
+ struct LocaleSink : public ResourceTableSink {
+ AllowedHourFormatsSink &outer;
+ LocaleSink(AllowedHourFormatsSink &outer) : outer(outer) {}
+ virtual ~LocaleSink();
+
+ virtual void put(const char *key, const ResourceValue &value, UErrorCode &status) {
+ if (U_FAILURE(status)) { return; }
+
+ if (uprv_strcmp(key, "allowed") == 0) {
+ outer.allowedFormats = static_cast<int32_t *>(uprv_malloc(2 * sizeof(int32_t)));
+ outer.allowedFormatsLength = 1;
+ if (outer.allowedFormats == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ outer.allowedFormats[0] = outer.getHourFormatFromUnicodeString(
+ value.getUnicodeString(status));
+ }
+ }
+
+ virtual ResourceArraySink *getOrCreateArraySink(const char *key, int32_t size, UErrorCode &status) {
+ if (U_FAILURE(status)) { return NULL; }
+
+ if (uprv_strcmp(key, "allowed") == 0) {
+ outer.allowedFormats = static_cast<int32_t *>(uprv_malloc((size + 1) * sizeof(int32_t)));
+ outer.allowedFormatsLength = size;
+ if (outer.allowedFormats == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ } else {
+ return &outer.allowedListSink;
+ }
+ }
+ return NULL;
+ }
+
+ virtual void leave(UErrorCode &status) {
+ if (U_FAILURE(status) || outer.allowedFormats == NULL) { return; }
+
+ outer.allowedFormats[outer.allowedFormatsLength] = ALLOWED_HOUR_FORMAT_UNKNOWN;
+ uhash_put(localeToAllowedHourFormatsMap, const_cast<char *>(outer.locale), outer.allowedFormats, &status);
+ outer.allowedFormats = NULL;
+ }
+ } localeSink;
+
+ struct AllowedListSink : public ResourceArraySink {
+ AllowedHourFormatsSink &outer;
+ AllowedListSink(AllowedHourFormatsSink &outer) : outer(outer) {}
+ virtual ~AllowedListSink();
+
+ virtual void put(int32_t index, const ResourceValue &value, UErrorCode &status) {
+ if (U_FAILURE(status)) { return; }
+
+ outer.allowedFormats[index] = outer.getHourFormatFromUnicodeString(
+ value.getUnicodeString(status));
+ }
+ } allowedListSink;
+
+ const char *locale;
+ int32_t *allowedFormats;
+ int32_t allowedFormatsLength;
+
+ AllowedHourFormat getHourFormatFromUnicodeString(UnicodeString s) {
+ if (s.length() == 1) {
+ if (s[0] == LOW_H) { return ALLOWED_HOUR_FORMAT_h; }
+ if (s[0] == CAP_H) { return ALLOWED_HOUR_FORMAT_H; }
+ } else if (s.length() == 2) {
+ if (s[0] == LOW_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_hb; }
+ if (s[0] == CAP_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Hb; }
+ if (s[0] == LOW_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_hB; }
+ if (s[0] == CAP_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_HB; }
+ }
+
+ return ALLOWED_HOUR_FORMAT_UNKNOWN;
+ }
+};
+
+} // namespace
+
+AllowedHourFormatsSink::~AllowedHourFormatsSink() {}
+AllowedHourFormatsSink::LocaleSink::~LocaleSink() {}
+AllowedHourFormatsSink::AllowedListSink::~AllowedListSink() {}
+
+void DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode &status) {
+ if (U_FAILURE(status)) { return; }
+ localeToAllowedHourFormatsMap = uhash_open(
+ uhash_hashChars, uhash_compareChars, NULL, &status);
+ uhash_setValueDeleter(localeToAllowedHourFormatsMap, deleteAllowedHourFormats);
+ LocalUResourceBundlePointer rb(ures_openDirect(NULL, "supplementalData", &status));
+
+ AllowedHourFormatsSink sink;
+ // TODO: Currently in the enumeration each table allocates a new array.
+ // Try to reduce the number of memory allocations. Consider storing a
+ // UVector32 with the concatenation of all of the sub-arrays, put the start index
+ // into the hashmap, store 6 single-value sub-arrays right at the beginning of the
+ // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime
+ // object. Remember to clean up the vector, too.
+ ures_getAllTableItemsWithFallback(rb.getAlias(), "timeData", sink, status);
+
+ ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS, allowedHourFormatsCleanup);
+}
+
+void DateTimePatternGenerator::getAllowedHourFormats(const Locale &locale, UErrorCode &status) {
+ if (U_FAILURE(status)) { return; }
+
+ const char *localeID = locale.getName();
+ char maxLocaleID[ULOC_FULLNAME_CAPACITY];
+ int32_t length = uloc_addLikelySubtags(localeID, maxLocaleID, ULOC_FULLNAME_CAPACITY, &status);
+ if (U_FAILURE(status)) {
+ return;
+ } else if (length == ULOC_FULLNAME_CAPACITY) { // no room for NUL
+ status = U_BUFFER_OVERFLOW_ERROR;
+ return;
+ }
+ Locale maxLocale = Locale(maxLocaleID);
+
+ const char *country = maxLocale.getCountry();
+ if (*country == '\0') { country = "001"; }
+ const char *language = maxLocale.getLanguage();
+
+ CharString langCountry;
+ langCountry.append(language, uprv_strlen(language), status);
+ langCountry.append('_', status);
+ langCountry.append(country, uprv_strlen(country), status);
+
+ int32_t *allowedFormats;
+ allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, langCountry.data());
+ if (allowedFormats == NULL) {
+ allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, const_cast<char *>(country));
+ }
+
+ if (allowedFormats != NULL) { // Lookup is successful
+ for (int32_t i = 0; i < UPRV_LENGTHOF(fAllowedHourFormats); ++i) {
+ fAllowedHourFormats[i] = allowedFormats[i];
+ if (allowedFormats[i] == ALLOWED_HOUR_FORMAT_UNKNOWN) {
+ break;
+ }
+ }
+ } else { // Lookup failed, twice
+ fAllowedHourFormats[0] = ALLOWED_HOUR_FORMAT_H;
+ fAllowedHourFormats[1] = ALLOWED_HOUR_FORMAT_UNKNOWN;
+ }
+}
+