+
+static const int32_t PATH_PREFIX_LENGTH = 17;
+static const UChar PATH_PREFIX[] = {SOLIDUS, CAP_L, CAP_O, CAP_C, CAP_A, CAP_L, CAP_E, SOLIDUS,
+ LOW_C, LOW_A, LOW_L, LOW_E, LOW_N, LOW_D, LOW_A, LOW_R, SOLIDUS};
+static const int32_t PATH_SUFFIX_LENGTH = 16;
+static const UChar PATH_SUFFIX[] = {SOLIDUS, LOW_I, LOW_N, LOW_T, LOW_E, LOW_R, LOW_V, LOW_A,
+ LOW_L, CAP_F, LOW_O, LOW_R, LOW_M, LOW_A, LOW_T, LOW_S};
+
+/**
+ * Sink for enumerating all of the date interval skeletons.
+ * Contains inner sink structs, each one corresponding to a type of resource table.
+ * The outer struct finds the dateInterval table or an alias.
+ */
+struct DateIntervalSink : public ResourceTableSink {
+
+ /**
+ * Sink to handle each skeleton table.
+ */
+ struct SkeletonSink : public ResourceTableSink {
+ SkeletonSink(DateIntervalSink &sink) : outer(sink) {}
+ virtual ~SkeletonSink();
+
+ virtual ResourceTableSink *getOrCreateTableSink(
+ const char *key, int32_t, UErrorCode &errorCode) {
+ if (U_SUCCESS(errorCode)) {
+ outer.currentSkeleton = key;
+ return &outer.patternSink;
+ }
+ return NULL;
+ }
+
+ DateIntervalSink &outer;
+ } skeletonSink;
+
+ /**
+ * Sink to store the date interval pattern for each skeleton pattern character.
+ */
+ struct PatternSink : public ResourceTableSink {
+ PatternSink(DateIntervalSink &sink) : outer(sink) {}
+ virtual ~PatternSink();
+
+ virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
+ if (U_FAILURE(errorCode)) { return; }
+
+ // Process the key
+ UCalendarDateFields calendarField = validateAndProcessPatternLetter(key);
+
+ // If the calendar field has a valid value
+ if (calendarField < UCAL_FIELD_COUNT) {
+ // Set the interval pattern
+ setIntervalPatternIfAbsent(calendarField, value, errorCode);
+ } else {
+ errorCode = U_INVALID_FORMAT_ERROR;
+ }
+ }
+
+ UCalendarDateFields validateAndProcessPatternLetter(const char *patternLetter) {
+ // Check that patternLetter is just one letter
+ char c0;
+ if ((c0 = patternLetter[0]) != 0 && patternLetter[1] == 0) {
+ // Check that the pattern letter is accepted
+ if (c0 == 'y') {
+ return UCAL_YEAR;
+ } else if (c0 == 'M') {
+ return UCAL_MONTH;
+ } else if (c0 == 'd') {
+ return UCAL_DATE;
+ } else if (c0 == 'a') {
+ return UCAL_AM_PM;
+ } else if (c0 == 'h' || c0 == 'H') {
+ return UCAL_HOUR;
+ } else if (c0 == 'm') {
+ return UCAL_MINUTE;
+ }// TODO(ticket:12190): Why icu4c doesn't accept the calendar field "s" but icu4j does?
+ }
+ return UCAL_FIELD_COUNT;
+ }
+
+ /**
+ * Stores the interval pattern for the current skeleton in the internal data structure
+ * if it's not present.
+ */
+ void setIntervalPatternIfAbsent(UCalendarDateFields lrgDiffCalUnit,
+ const ResourceValue &value, UErrorCode &errorCode) {
+ // Check if the pattern has already been stored on the data structure
+ DateIntervalInfo::IntervalPatternIndex index =
+ outer.dateIntervalInfo.calendarFieldToIntervalIndex(lrgDiffCalUnit, errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ UnicodeString skeleton(outer.currentSkeleton, -1, US_INV);
+ UnicodeString* patternsOfOneSkeleton =
+ (UnicodeString*)(outer.dateIntervalInfo.fIntervalPatterns->get(skeleton));
+
+ if (patternsOfOneSkeleton == NULL || patternsOfOneSkeleton[index].isEmpty()) {
+ UnicodeString pattern = value.getUnicodeString(errorCode);
+ outer.dateIntervalInfo.setIntervalPatternInternally(skeleton, lrgDiffCalUnit,
+ pattern, errorCode);
+ }
+ }
+
+ DateIntervalSink &outer;
+ } patternSink;
+
+
+ DateIntervalSink(DateIntervalInfo &diInfo, const char *currentCalendarType)
+ : skeletonSink(*this), patternSink(*this), dateIntervalInfo(diInfo),
+ nextCalendarType(currentCalendarType, -1, US_INV), currentSkeleton(NULL) { }
+ virtual ~DateIntervalSink();
+
+ virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
+ // Check if it's an alias of intervalFormats
+ if (U_FAILURE(errorCode) || value.getType() != URES_ALIAS
+ || uprv_strcmp(key, gIntervalDateTimePatternTag) != 0) {
+ return;
+ }
+
+ // Get the calendar type for the alias path.
+ const UnicodeString &aliasPath = value.getAliasUnicodeString(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ nextCalendarType.remove();
+ getCalendarTypeFromPath(aliasPath, nextCalendarType, errorCode);
+
+ if (U_FAILURE(errorCode)) {
+ resetNextCalendarType();
+ }
+ }
+
+ virtual ResourceTableSink *getOrCreateTableSink(
+ const char *key, int32_t, UErrorCode &errorCode) {
+ // Check if it's the intervalFormat table
+ if (U_SUCCESS(errorCode) && uprv_strcmp(key, gIntervalDateTimePatternTag) == 0) {
+ return &skeletonSink;
+ }
+ return NULL;
+ }
+
+ /**
+ * Extracts the calendar type from the path.
+ */
+ static void getCalendarTypeFromPath(const UnicodeString &path, UnicodeString &calendarType,
+ UErrorCode &errorCode) {
+ if (U_FAILURE(errorCode)) { return; }
+
+ if (!path.startsWith(PATH_PREFIX, PATH_PREFIX_LENGTH) || !path.endsWith(PATH_SUFFIX, PATH_SUFFIX_LENGTH)) {
+ errorCode = U_INVALID_FORMAT_ERROR;
+ return;
+ }
+
+ path.extractBetween(PATH_PREFIX_LENGTH, path.length() - PATH_SUFFIX_LENGTH, calendarType);
+ }
+
+ const UnicodeString &getNextCalendarType() {
+ return nextCalendarType;
+ }
+
+ void resetNextCalendarType() {
+ nextCalendarType.setToBogus();
+ }
+
+
+ // Output data
+ DateIntervalInfo &dateIntervalInfo;
+
+ // Next calendar type
+ UnicodeString nextCalendarType;
+
+ // Current skeleton table being enumerated
+ const char *currentSkeleton;
+};
+
+// Virtual destructors must be defined out of line.
+DateIntervalSink::SkeletonSink::~SkeletonSink() {}
+DateIntervalSink::PatternSink::~PatternSink() {}
+DateIntervalSink::~DateIntervalSink() {}
+
+
+
+void
+DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status)