+namespace {
+
+// Constants declarations
+static const UChar kCalendarAliasPrefixUChar[] = {
+ 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 UChar kGregorianTagUChar[] = {
+ LOW_G, LOW_R, LOW_E, LOW_G, LOW_O, LOW_R, LOW_I, LOW_A, LOW_N
+};
+static const UChar kVariantTagUChar[] = {
+ PERCENT, LOW_V, LOW_A, LOW_R, LOW_I, LOW_A, LOW_N, LOW_T
+};
+static const UChar kLeapTagUChar[] = {
+ LOW_L, LOW_E, LOW_A, LOW_P
+};
+static const UChar kCyclicNameSetsTagUChar[] = {
+ LOW_C, LOW_Y, LOW_C, LOW_L, LOW_I, LOW_C, CAP_N, LOW_A, LOW_M, LOW_E, CAP_S, LOW_E, LOW_T, LOW_S
+};
+static const UChar kYearsTagUChar[] = {
+ SOLIDUS, LOW_Y, LOW_E, LOW_A, LOW_R, LOW_S
+};
+static const UChar kZodiacsUChar[] = {
+ SOLIDUS, LOW_Z, LOW_O, LOW_D, LOW_I, LOW_A, LOW_C, LOW_S
+};
+static const UChar kDayPartsTagUChar[] = {
+ SOLIDUS, LOW_D, LOW_A, LOW_Y, CAP_P, LOW_A, LOW_R, LOW_T, LOW_S
+};
+static const UChar kFormatTagUChar[] = {
+ SOLIDUS, LOW_F, LOW_O, LOW_R, LOW_M, LOW_A, LOW_T
+};
+static const UChar kAbbrTagUChar[] = {
+ SOLIDUS, LOW_A, LOW_B, LOW_B, LOW_R, LOW_E, LOW_V, LOW_I, LOW_A, LOW_T, LOW_E, LOW_D
+};
+
+// ResourceSink to enumerate all calendar resources
+struct CalendarDataSink : public ResourceSink {
+
+ // Enum which specifies the type of alias received, or no alias
+ enum AliasType {
+ SAME_CALENDAR,
+ DIFFERENT_CALENDAR,
+ GREGORIAN,
+ NONE
+ };
+
+ // Data structures to store resources from the current resource bundle
+ Hashtable arrays;
+ Hashtable arraySizes;
+ Hashtable maps;
+ /**
+ * Whenever there are aliases, the same object will be added twice to 'map'.
+ * To avoid double deletion, 'maps' won't take ownership of the objects. Instead,
+ * 'mapRefs' will own them and will delete them when CalendarDataSink is deleted.
+ */
+ MemoryPool<Hashtable> mapRefs;
+
+ // Paths and the aliases they point to
+ UVector aliasPathPairs;
+
+ // Current and next calendar resource table which should be loaded
+ UnicodeString currentCalendarType;
+ UnicodeString nextCalendarType;
+
+ // Resources to visit when enumerating fallback calendars
+ LocalPointer<UVector> resourcesToVisit;
+
+ // Alias' relative path populated whenever an alias is read
+ UnicodeString aliasRelativePath;
+
+ // Initializes CalendarDataSink with default values
+ CalendarDataSink(UErrorCode& status)
+ : arrays(FALSE, status), arraySizes(FALSE, status), maps(FALSE, status),
+ mapRefs(),
+ aliasPathPairs(uprv_deleteUObject, uhash_compareUnicodeString, status),
+ currentCalendarType(), nextCalendarType(),
+ resourcesToVisit(NULL), aliasRelativePath() {
+ if (U_FAILURE(status)) { return; }
+ }
+ virtual ~CalendarDataSink();
+
+ // Configure the CalendarSink to visit all the resources
+ void visitAllResources() {
+ resourcesToVisit.adoptInstead(NULL);
+ }
+
+ // Actions to be done before enumerating
+ void preEnumerate(const UnicodeString &calendarType) {
+ currentCalendarType = calendarType;
+ nextCalendarType.setToBogus();
+ aliasPathPairs.removeAllElements();
+ }
+
+ virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) {
+ if (U_FAILURE(errorCode)) { return; }
+ U_ASSERT(!currentCalendarType.isEmpty());
+
+ // Stores the resources to visit on the next calendar.
+ LocalPointer<UVector> resourcesToVisitNext(NULL);
+ ResourceTable calendarData = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ // Enumerate all resources for this calendar
+ for (int i = 0; calendarData.getKeyAndValue(i, key, value); i++) {
+ UnicodeString keyUString(key, -1, US_INV);
+
+ // == Handle aliases ==
+ AliasType aliasType = processAliasFromValue(keyUString, value, errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ if (aliasType == GREGORIAN) {
+ // Ignore aliases to the gregorian calendar, all of its resources will be loaded anyway.
+ continue;
+
+ } else if (aliasType == DIFFERENT_CALENDAR) {
+ // Whenever an alias to the next calendar (except gregorian) is encountered, register the
+ // calendar type it's pointing to
+ if (resourcesToVisitNext.isNull()) {
+ resourcesToVisitNext
+ .adoptInsteadAndCheckErrorCode(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, errorCode),
+ errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ }
+ LocalPointer<UnicodeString> aliasRelativePathCopy(new UnicodeString(aliasRelativePath), errorCode);
+ resourcesToVisitNext->addElement(aliasRelativePathCopy.getAlias(), errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ // Only release ownership after resourcesToVisitNext takes it (no error happened):
+ aliasRelativePathCopy.orphan();
+ continue;
+
+ } else if (aliasType == SAME_CALENDAR) {
+ // Register same-calendar alias
+ if (arrays.get(aliasRelativePath) == NULL && maps.get(aliasRelativePath) == NULL) {
+ LocalPointer<UnicodeString> aliasRelativePathCopy(new UnicodeString(aliasRelativePath), errorCode);
+ aliasPathPairs.addElement(aliasRelativePathCopy.getAlias(), errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ // Only release ownership after aliasPathPairs takes it (no error happened):
+ aliasRelativePathCopy.orphan();
+ LocalPointer<UnicodeString> keyUStringCopy(new UnicodeString(keyUString), errorCode);
+ aliasPathPairs.addElement(keyUStringCopy.getAlias(), errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ // Only release ownership after aliasPathPairs takes it (no error happened):
+ keyUStringCopy.orphan();
+ }
+ continue;
+ }
+
+ // Only visit the resources that were referenced by an alias on the previous calendar
+ // (AmPmMarkersAbbr is an exception).
+ if (!resourcesToVisit.isNull() && !resourcesToVisit->isEmpty() && !resourcesToVisit->contains(&keyUString)
+ && uprv_strcmp(key, gAmPmMarkersAbbrTag) != 0) { continue; }
+
+ // == Handle data ==
+ if (uprv_strcmp(key, gAmPmMarkersTag) == 0
+ || uprv_strcmp(key, gAmPmMarkersAbbrTag) == 0
+ || uprv_strcmp(key, gAmPmMarkersNarrowTag) == 0) {
+ if (arrays.get(keyUString) == NULL) {
+ ResourceArray resourceArray = value.getArray(errorCode);
+ int32_t arraySize = resourceArray.getSize();
+ LocalArray<UnicodeString> stringArray(new UnicodeString[arraySize], errorCode);
+ value.getStringArray(stringArray.getAlias(), arraySize, errorCode);
+ arrays.put(keyUString, stringArray.orphan(), errorCode);
+ arraySizes.puti(keyUString, arraySize, errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ }
+ } else if (uprv_strcmp(key, gErasTag) == 0
+ || uprv_strcmp(key, gDayNamesTag) == 0
+ || uprv_strcmp(key, gMonthNamesTag) == 0
+ || uprv_strcmp(key, gQuartersTag) == 0
+ || uprv_strcmp(key, gDayPeriodTag) == 0
+ || uprv_strcmp(key, gMonthPatternsTag) == 0
+ || uprv_strcmp(key, gCyclicNameSetsTag) == 0) {
+ processResource(keyUString, key, value, errorCode);