diff --git a/icu4c/source/i18n/calendar.cpp b/icu4c/source/i18n/calendar.cpp index dcec071a0e5..431292da3b1 100644 --- a/icu4c/source/i18n/calendar.cpp +++ b/icu4c/source/i18n/calendar.cpp @@ -203,70 +203,37 @@ class DefaultCalendarFactory : public ICUResourceBundleFactory { // attempt keyword lookup char keyword[128]; - if(loc.getKeywordValue("calendar", keyword, sizeof(keyword)-1, status)) { + + if(!loc.getKeywordValue("calendar", keyword, sizeof(keyword)-1, status)) { + // fetch default calendar id + const char *defaultCal = NULL; + char funcEquiv[256]; + char keywordVal[ULOC_KEYWORD_AND_VALUES_CAPACITY]; + ures_getFunctionalEquivalent(funcEquiv, sizeof(funcEquiv)-1, + NULL, "calendar", "calendar", + loc.getName(), + NULL, FALSE, &status); + uloc_getKeywordValue(funcEquiv, "calendar", keyword, + sizeof(keyword)-1, &status); #ifdef U_DEBUG_CALSVC - fprintf(stderr, "DefaultCalendar factory %p: looking up %s, keyword: %s\n", - this, (const char*)loc.getName(), keyword); + fprintf(stderr, " getFunctionalEquivalent calendar=%s [%s]\n", keyword, u_errorName(status)); #endif + } +#ifdef U_DEBUG_CALSVC + else { fprintf(stderr, " explicit calendar=%s\n", keyword); } +#endif + + + if(U_FAILURE(status)) { + return NULL; + } else { + int i; + for(i=0;keyword[i];i++) { + keyword[i] = ::tolower(keyword[i]); + } UnicodeString *ret = new UnicodeString("@calendar=",""); (*ret) += UnicodeString(keyword, ""); return ret; - } else { -#ifdef U_DEBUG_CALSVC - fprintf(stderr, "DefaultCalendar factory %p: looking up %s\n", - this, (const char*)loc.getName()); -#endif - - - UErrorCode resStatus = U_ZERO_ERROR; - - UResourceBundle *rb = ures_open(NULL, (const char*)loc.getName(), &resStatus); - -#ifdef U_DEBUG_CALSVC - fprintf(stderr, "... ures_open -> %s\n", u_errorName(resStatus)); -#endif - if(U_FAILURE(resStatus) || - (resStatus == U_USING_DEFAULT_WARNING) || (resStatus==U_USING_FALLBACK_WARNING)) { //Don't want to handle fallback data. - ures_close(rb); - status = resStatus; // propagate err back to caller -#ifdef U_DEBUG_CALSVC - fprintf(stderr, "... exitting (NULL)\n"); -#endif - - return NULL; - } - - UnicodeString myString = ures_getUnicodeStringByKey(rb, Calendar::kDefaultCalendar, &status); - myString = UnicodeString("@calendar=") + myString; - -#ifdef U_DEBUG_CALSVC - int32_t len = 0; - UErrorCode debugStatus = U_ZERO_ERROR; - const UChar *defCal = ures_getStringByKey(rb, Calendar::kDefaultCalendar, &len, &debugStatus); - fprintf(stderr, "... get string(%d) -> %s\n", len, u_errorName(debugStatus)); -#endif - - ures_close(rb); - - if(U_FAILURE(status)) { - return NULL; - } - - -#ifdef U_DEBUG_CALSVC - { - char defCalStr[200] = "@calendar="; - int32_t prefixLen = uprv_strlen(defCalStr); - if(len > (199-prefixLen)) { - len = (199-prefixLen); - } - u_UCharsToChars(defCal, defCalStr+prefixLen, len); - defCalStr[len+prefixLen]=0; - fprintf(stderr, "DefaultCalendarFactory: looked up %s, got DefaultCalendar= %s\n", (const char*)loc.getName(), defCalStr); - } -#endif - - return myString.clone(); } } }; @@ -416,7 +383,6 @@ static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = { // Resource bundle tags read by this class const char Calendar::kDateTimeElements[] = "DateTimeElements"; -const char Calendar::kDefaultCalendar[] = "DefaultCalendar"; // Data flow in Calendar // --------------------- @@ -2896,22 +2862,20 @@ Calendar::setWeekCountData(const Locale& desiredLocale, UErrorCode& status) fFirstDayOfWeek = UCAL_SUNDAY; fMinimalDaysInFirstWeek = 1; - UResourceBundle *resource = ures_open(NULL, desiredLocale.getName(), &status); - + CalendarData calData(desiredLocale, "gregorian", status); // TODO: fixme // If the resource data doesn't seem to be present at all, then use last-resort // hard-coded data. + UResourceBundle *dateTimeElements = calData.getByKey(kDateTimeElements, status); + if (U_FAILURE(status)) { status = U_USING_FALLBACK_WARNING; - ures_close(resource); return; } - //dateTimeElements = resource.getStringArray(kDateTimeElements, count, status); - UResourceBundle *dateTimeElements = ures_getByKey(resource, kDateTimeElements, NULL, &status); // TODO: should be per calendar?! U_LOCALE_BASED(locBased, *this); - locBased.setLocaleIDs(ures_getLocaleByType(resource, ULOC_VALID_LOCALE, &status), - ures_getLocaleByType(resource, ULOC_ACTUAL_LOCALE, &status)); + locBased.setLocaleIDs(ures_getLocaleByType(dateTimeElements, ULOC_VALID_LOCALE, &status), + ures_getLocaleByType(dateTimeElements, ULOC_ACTUAL_LOCALE, &status)); if (U_SUCCESS(status)) { int32_t arrLen; const int32_t *dateTimeElementsArr = ures_getIntVector(dateTimeElements, &arrLen, &status); @@ -2927,9 +2891,8 @@ Calendar::setWeekCountData(const Locale& desiredLocale, UErrorCode& status) status = U_INVALID_FORMAT_ERROR; } } - - ures_close(dateTimeElements); - ures_close(resource); + + // do NOT close dateTimeElements } /** diff --git a/icu4c/source/i18n/dtfmtsym.cpp b/icu4c/source/i18n/dtfmtsym.cpp index 01eae291fa7..7ca7455b462 100644 --- a/icu4c/source/i18n/dtfmtsym.cpp +++ b/icu4c/source/i18n/dtfmtsym.cpp @@ -30,6 +30,7 @@ #include "cmemory.h" #include "cstring.h" #include "locbased.h" +#include "gregoimp.h" // ***************************************************************************** // class DateFormatSymbols @@ -117,13 +118,13 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateFormatSymbols) /** * These are the tags we expect to see in normal resource bundle files associated - * with a locale. + * with a locale and calendar */ -const char gErasTag[]="Eras"; -const char gMonthNamesTag[]="MonthNames"; -const char gMonthAbbreviationsTag[]="MonthAbbreviations"; -const char gDayNamesTag[]="DayNames"; -const char gDayAbbreviationsTag[]="DayAbbreviations"; +const char gErasTag[]="eras"; +const char gMonthNamesTag[]="monthNames"; +const char gDayNamesTag[]="dayNames"; +const char gNamesWideTag[]="wide"; +const char gNamesAbbrTag[]="abbreviated"; const char gAmPmMarkersTag[]="AmPmMarkers"; /** @@ -545,41 +546,6 @@ DateFormatSymbols::initField(UnicodeString **field, int32_t& length, const UChar } } -ResourceBundle -DateFormatSymbols::getData(ResourceBundle &rb, const char *tag, const char *type, UErrorCode& status ) -{ - if(type && *type && (uprv_strcmp(type, "gregorian") != 0)) { - char tmp[100]; - char *fullTag = tmp; - int32_t len = uprv_strlen(tag) + 1 + uprv_strlen(type); // tag + _ + type (i.e. Eras_Japanese ) - - if(len >= (int32_t)sizeof(tmp)) { - fullTag = (char*)uprv_malloc(len+1); - } - - uprv_strcpy(fullTag, tag); - uprv_strcat(fullTag, "_"); - uprv_strcat(fullTag, type); - - ResourceBundle resource(rb.get(fullTag, status)); - - if(fullTag != tmp) { - uprv_free(fullTag); // not stack allocated - } - - // fallback if not found - if(status == U_MISSING_RESOURCE_ERROR) { - status = U_ZERO_ERROR; - resource = rb.get(tag, status); - } - - return resource; - } else { - // Gregorian case - return rb.get(tag, status); - } -} - void DateFormatSymbols::initializeData(const Locale& locale, const char *type, UErrorCode& status, UBool useLastResortData) { @@ -610,7 +576,10 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError * We cast away const here, but that's okay; we won't delete any of * these. */ - ResourceBundle resource((char *)0, locale, status); + CalendarData calData(locale, type, status); + ResourceBundle nonCalendarData((char*)0, locale, status); + + ResourceBundle data = calData.getBundleByKey(gErasTag, status); // load the first data item if (U_FAILURE(status)) { @@ -646,18 +615,18 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError // if we make it to here, the resource data is cool, and we can get everything out // of it that we need except for the time-zone and localized-pattern data, which // are stored in a separate file - ResourceBundle data = getData(resource, gErasTag, type, status); U_LOCALE_BASED(locBased, *this); locBased.setLocaleIDs(data.getLocale(ULOC_VALID_LOCALE, status).getName(), data.getLocale(ULOC_ACTUAL_LOCALE, status).getName()); initField(&fEras, fErasCount, data, status); - initField(&fMonths, fMonthsCount, getData(resource, gMonthNamesTag, type, status), status); - initField(&fShortMonths, fShortMonthsCount, getData(resource, gMonthAbbreviationsTag, type, status), status); - initField(&fAmPms, fAmPmsCount, getData(resource, gAmPmMarkersTag, type, status), status); - // fastCopyFrom() - see assignArray comments - fLocalPatternChars.fastCopyFrom(resource.getStringEx(gLocalPatternCharsTag, status)); + initField(&fMonths, fMonthsCount, calData.getBundleByKey2(gMonthNamesTag, gNamesWideTag, status), status); + initField(&fShortMonths, fShortMonthsCount, calData.getBundleByKey2(gMonthNamesTag, gNamesAbbrTag, status), status); + initField(&fAmPms, fAmPmsCount, calData.getBundleByKey(gAmPmMarkersTag, status), status); - ResourceBundle zoneArray(resource.get(gZoneStringsTag, status)); + // fastCopyFrom() - see assignArray comments + fLocalPatternChars.fastCopyFrom(nonCalendarData.getStringEx(gLocalPatternCharsTag, status)); + + ResourceBundle zoneArray(nonCalendarData.get(gZoneStringsTag, status)); fZoneStringsRowCount = zoneArray.getSize(); ResourceBundle zoneRow(zoneArray.get((int32_t)0, status)); /* TODO: Fix the case where the zoneStrings is not a perfect square array of information. */ @@ -683,7 +652,7 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError } // {sfb} fixed to handle 1-based weekdays - ResourceBundle weekdaysData(getData(resource, gDayNamesTag, type, status)); + ResourceBundle weekdaysData(calData.getBundleByKey2(gDayNamesTag, gNamesWideTag, status)); fWeekdaysCount = weekdaysData.getSize(); fWeekdays = new UnicodeString[fWeekdaysCount+1]; /* test for NULL */ @@ -698,7 +667,7 @@ DateFormatSymbols::initializeData(const Locale& locale, const char *type, UError } fWeekdaysCount++; - ResourceBundle lsweekdaysData(getData(resource, gDayAbbreviationsTag, type, status)); + ResourceBundle lsweekdaysData(calData.getBundleByKey2(gDayNamesTag, gNamesAbbrTag, status)); fShortWeekdaysCount = lsweekdaysData.getSize(); fShortWeekdays = new UnicodeString[fShortWeekdaysCount+1]; /* test for NULL */ diff --git a/icu4c/source/i18n/gregoimp.cpp b/icu4c/source/i18n/gregoimp.cpp index 33c4ddeff2f..b35ca14d15e 100644 --- a/icu4c/source/i18n/gregoimp.cpp +++ b/icu4c/source/i18n/gregoimp.cpp @@ -13,6 +13,8 @@ #if !UCONFIG_NO_FORMATTING #include "unicode/ucal.h" +#include "uresimp.h" +#include "cstring.h" int32_t Math::floorDivide(int32_t numerator, int32_t denominator) { return (numerator >= 0) ? @@ -87,5 +89,155 @@ void Grego::dayToFields(double day, int32_t& year, int32_t& month, doy++; // one-based doy } +/* ---- CalendarData ------ */ + +#define U_CALENDAR_KEY "calendar" +#define U_GREGORIAN_KEY "gregorian" +#define U_FORMAT_KEY "format" +#define U_DEFAULT_KEY "default" +#define U_CALENDAR_DATA ((char*)0) + + +#if defined( U_DEBUG_CALDATA) +#include +#endif + +// CalendarData::CalendarData(const Locale& loc, UErrorCode& status) +// : fFillin(NULL), fBundle(NULL), fFallback(NULL) { +// initData(loc.getBaseName(), (char*) "???", status); +// } + +CalendarData::CalendarData(const Locale& loc, const char *type, UErrorCode& status) + : fFillin(NULL), fOtherFillin(NULL), fBundle(NULL), fFallback(NULL) { + initData(loc.getBaseName(), type, status); +} + +void CalendarData::initData(const char *locale, const char *type, UErrorCode& status) { + UResourceBundle *tmp = NULL; + fOtherFillin = ures_open(U_CALENDAR_DATA, locale, &status); + fFillin = ures_getByKey(fOtherFillin, U_CALENDAR_KEY, fFillin, &status); + + if((type != NULL) && + (*type != NULL) && + (uprv_strcmp(type, U_GREGORIAN_KEY))) { + fBundle = ures_getByKeyWithFallback(fFillin, type, NULL, &status); + fFallback = ures_getByKeyWithFallback(fFillin, U_GREGORIAN_KEY, NULL, &status); + +#if defined (U_DEBUG_CALDATA) + fprintf(stderr, "%p: CalendarData(%s, %s, %s) -> main(%p, %s)=%s, fallback(%p, %s)=%s\n", + this, locale, type, u_errorName(status), fBundle, type, fBundle?ures_getLocale(fBundle, &status):"", + fFallback, U_GREGORIAN_KEY, fFallback?ures_getLocale(fFallback, &status):""); +#endif + + } else { + fBundle = ures_getByKeyWithFallback(fFillin, U_GREGORIAN_KEY, NULL, &status); +#if defined (U_DEBUG_CALDATA) + fprintf(stderr, "%p: CalendarData(%s, %s, %s) -> main(%p, %s)=%s, fallback = NULL\n", + this, locale, type, u_errorName(status), fBundle, U_GREGORIAN_KEY, fBundle?ures_getLocale(fBundle, &status):"" ); +#endif + } +} + +CalendarData::~CalendarData() { + ures_close(fFillin); + ures_close(fBundle); + ures_close(fFallback); + ures_close(fOtherFillin); +} + +UResourceBundle* +CalendarData::getByKey(const char *key, UErrorCode& status) { + if(U_FAILURE(status)) { + return NULL; + } + + if(fBundle) { + fFillin = ures_getByKeyWithFallback(fBundle, key, fFillin, &status); +#if defined (U_DEBUG_CALDATA) + fprintf(stderr, "%p: get %s -> %s - from MAIN %s\n",this, key, u_errorName(status), ures_getLocale(fFillin, &status)); +#endif + } + if(fFallback && (status == U_MISSING_RESOURCE_ERROR)) { + status = U_ZERO_ERROR; // retry with fallback (gregorian) + fFillin = ures_getByKeyWithFallback(fFallback, key, fFillin, &status); +#if defined (U_DEBUG_CALDATA) + fprintf(stderr, "%p: get %s -> %s - from FALLBACK %s\n",this, key, u_errorName(status), ures_getLocale(fFillin, &status)); +#endif + } + return fFillin; +} + +ResourceBundle CalendarData::getBundleByKey(const char *key, UErrorCode &status) { + return ResourceBundle(getByKey(key,status), status); +} + +UResourceBundle* CalendarData::getByKey2(const char *key, const char *subKey, UErrorCode& status) { + if(U_FAILURE(status)) { + return NULL; + } + + if(fBundle) { +#if defined (U_DEBUG_CALDATA) + fprintf(stderr, "%p: //\n"); +#endif + fFillin = ures_getByKeyWithFallback(fBundle, key, fFillin, &status); + fOtherFillin = ures_getByKeyWithFallback(fFillin, U_FORMAT_KEY, fOtherFillin, &status); + fFillin = ures_getByKeyWithFallback(fOtherFillin, subKey, fFillin, &status); +#if defined (U_DEBUG_CALDATA) + fprintf(stderr, "%p: get %s/format/%s -> %s - from MAIN %s\n", this, key, subKey, u_errorName(status), ures_getLocale(fFillin, &status)); +#endif + } + if(fFallback && (status == U_MISSING_RESOURCE_ERROR)) { + status = U_ZERO_ERROR; // retry with fallback (gregorian) + fFillin = ures_getByKeyWithFallback(fFallback, key, fFillin, &status); + fOtherFillin = ures_getByKeyWithFallback(fFillin, U_FORMAT_KEY, fOtherFillin, &status); + fFillin = ures_getByKeyWithFallback(fOtherFillin, subKey, fFillin, &status); +#if defined (U_DEBUG_CALDATA) + fprintf(stderr, "%p: get %s/format/%s -> %s - from FALLBACK %s\n",this, key, subKey, u_errorName(status), ures_getLocale(fFillin,&status)); +#endif + } + +//// handling of 'default' keyword on failure: Commented out for 3.0. +// if((status == U_MISSING_RESOURCE_ERROR) && +// uprv_strcmp(subKey,U_DEFAULT_KEY)) { // avoid recursion +// #if defined (U_DEBUG_CALDATA) +// fprintf(stderr, "%p: - attempting fallback -\n", this); +// fflush(stderr); +// #endif +// UErrorCode subStatus = U_ZERO_ERROR; +// int32_t len; +// char kwBuf[128] = ""; +// const UChar *kw; +// /* fFillin = */ getByKey2(key, U_DEFAULT_KEY, subStatus); +// kw = ures_getString(fFillin, &len, &subStatus); +// if(len>126) { // too big +// len = 0; +// } +// if(U_SUCCESS(subStatus) && (len>0)) { +// u_UCharsToChars(kw, kwBuf, len+1); +// if(*kwBuf && uprv_strcmp(kwBuf,subKey)) { +// #if defined (U_DEBUG_CALDATA) +// fprintf(stderr, "%p: trying %s/format/default -> \"%s\"\n",this, key, kwBuf); +// #endif +// // now try again with the default +// status = U_ZERO_ERROR; +// /* fFillin = */ getByKey2(key, kwBuf, status); +// } +// #if defined (U_DEBUG_CALDATA) +// } else { +// fprintf(stderr, "%p: could not load %s/format/default - fail out (%s)\n",this, key, kwBuf, u_errorName(status)); +// #endif +// } +// } + + return fFillin; +} + +ResourceBundle CalendarData::getBundleByKey2(const char *key, const char *subKey, UErrorCode& status) { + return ResourceBundle(getByKey2(key, subKey, status), status); +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CalendarData); + #endif //eof diff --git a/icu4c/source/i18n/gregoimp.h b/icu4c/source/i18n/gregoimp.h index 0f0efc63a2a..a259abeddf5 100644 --- a/icu4c/source/i18n/gregoimp.h +++ b/icu4c/source/i18n/gregoimp.h @@ -11,9 +11,11 @@ #ifndef GREGOIMP_H #define GREGOIMP_H #include "unicode/utypes.h" - #if !UCONFIG_NO_FORMATTING +#include "unicode/ures.h" +#include "unicode/resbund.h" + U_NAMESPACE_BEGIN /** @@ -220,6 +222,106 @@ inline int32_t Grego::gregorianShift(int32_t eyear) { return gregShift; } +/** + * This class provides convenient access to the data needed for a calendar. + * @internal ICU 3.0 + */ +class U_I18N_API CalendarData : public UObject { + public: + /** + * Construct a CalendarData from the given locale. + * @param loc locale to use - the 'calendar' keyword will be used, respecting the + * 'default' value in the resource bundle. + * @param status error code + */ + //CalendarData(const Locale& loc, UErrorCode& status); + + /** + * Construct a CalendarData from the given locale. + * @param loc locale to use. The 'calendar' keyword will be ignored. + * @param type calendar type. NULL indicates the gregorian calendar. + * No default lookup is done. + * @param status error code + */ + CalendarData(const Locale& loc, const char *type, UErrorCode& status); + + /** + * Load data for calendar. Note, this object owns the resources, do NOT call ures_close()! + * + * @param key Resource key to data + * @param status Error Status + * @internal + */ + UResourceBundle* getByKey(const char *key, UErrorCode& status); + + /** + * Load data for calendar. returns a ResourceBundle object + * + * @param key Resource key to data + * @param status Error Status + * @internal + */ + ResourceBundle getBundleByKey(const char *key, UErrorCode& status); + + /** + * Load data for calendar. Note, this object owns the resources, do NOT call ures_close()! + * There is an implicit key of 'format' + * data is located in: "calendar/key/format/subKey" + * for example, calendar/dayNames/format/abbreviated + * + * @param key Resource key to data + * @param subKey Resource key to data + * @param status Error Status + * @internal + */ + UResourceBundle* getByKey2(const char *key, const char *subKey, UErrorCode& status); + + /** + * Load data for calendar. Returns a ResourceBundle object + * + * @param key Resource key to data + * @param subKey Resource key to data + * @param status Error Status + * @internal + */ + ResourceBundle getBundleByKey2(const char *key, const char *subKey, UErrorCode& status); + + ~CalendarData(); + + /** + * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual + * override. This method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call + * this method. + * + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @stable ICU 2.0 + */ + virtual UClassID getDynamicClassID(void) const; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @stable ICU 2.0 + */ + static UClassID getStaticClassID(void); + + private: + void initData(const char *locale, const char *type, UErrorCode& status); + + UResourceBundle *fFillin; + UResourceBundle *fOtherFillin; + UResourceBundle *fBundle; + UResourceBundle *fFallback; + CalendarData(); // Not implemented. +}; U_NAMESPACE_END diff --git a/icu4c/source/i18n/smpdtfmt.cpp b/icu4c/source/i18n/smpdtfmt.cpp index 6967abb1904..d8ba011e338 100644 --- a/icu4c/source/i18n/smpdtfmt.cpp +++ b/icu4c/source/i18n/smpdtfmt.cpp @@ -40,6 +40,7 @@ #include "unicode/uchar.h" #include "unicode/ustring.h" #include "uprops.h" +#include "gregoimp.h" #include "cstring.h" #include @@ -270,19 +271,13 @@ void SimpleDateFormat::construct(EStyle timeStyle, UErrorCode& status) { // called by several constructors to load pattern data from the resources - if (U_FAILURE(status)) return; - // load up the DateTimePatterns resource from the appropriate locale (throw - // an error if for some weird reason the resource is malformed) - - ResourceBundle resources((char *)0, locale, status); - // We will need the calendar to know what type of symbols to load. initializeCalendar(NULL, locale, status); - // use Date Format Symbols' helper function to do the actual load. - ResourceBundle dateTimePatterns = DateFormatSymbols::getData(resources, gDateTimePatternsTag, fCalendar?fCalendar->getType():NULL, status); + CalendarData calData(locale, fCalendar?fCalendar->getType():NULL, status); + ResourceBundle dateTimePatterns = calData.getBundleByKey(gDateTimePatternsTag, status); if (U_FAILURE(status)) return; if (dateTimePatterns.getSize() <= kDateTime)