X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/4388f060552cc537e71e957d32f35e9d75a61233..2ca993e82fb37b597a3c73ecd1586a139a6579c5:/icuSources/i18n/dtptngen.cpp diff --git a/icuSources/i18n/dtptngen.cpp b/icuSources/i18n/dtptngen.cpp index b4e39b50..c0cf9079 100644 --- a/icuSources/i18n/dtptngen.cpp +++ b/icuSources/i18n/dtptngen.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2007-2012, International Business Machines Corporation and +* Copyright (C) 2007-2016, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* * @@ -16,7 +16,7 @@ #include "unicode/decimfmt.h" #include "unicode/dtfmtsym.h" #include "unicode/dtptngen.h" -#include "unicode/msgfmt.h" +#include "unicode/simpleformatter.h" #include "unicode/smpdtfmt.h" #include "unicode/udat.h" #include "unicode/udatpg.h" @@ -26,17 +26,18 @@ #include "unicode/ustring.h" #include "unicode/rep.h" #include "cpputils.h" -#include "ucln_in.h" #include "mutex.h" +#include "umutex.h" #include "cmemory.h" #include "cstring.h" #include "locbased.h" #include "gregoimp.h" #include "hash.h" +#include "uhash.h" #include "uresimp.h" #include "dtptngen_impl.h" - -#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) +#include "ucln_in.h" +#include "charstr.h" #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY /** @@ -127,7 +128,6 @@ static const UChar *ures_a_getNextString(UResourceBundleAIterator *aiter, int32_ U_NAMESPACE_BEGIN - // ***************************************************************************** // class DateTimePatternGenerator // ***************************************************************************** @@ -144,6 +144,7 @@ static const dtTypeElem dtTypes[] = { {LOW_Y, UDATPG_YEAR_FIELD, DT_NUMERIC, 1, 20}, {CAP_Y, UDATPG_YEAR_FIELD, DT_NUMERIC + DT_DELTA, 1, 20}, {LOW_U, UDATPG_YEAR_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 20}, + {LOW_R, UDATPG_YEAR_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20}, {CAP_U, UDATPG_YEAR_FIELD, DT_SHORT, 1, 3}, {CAP_U, UDATPG_YEAR_FIELD, DT_LONG, 4, 0}, {CAP_U, UDATPG_YEAR_FIELD, DT_NARROW, 5, 0}, @@ -181,9 +182,9 @@ static const dtTypeElem dtTypes[] = { {LOW_G, UDATPG_DAY_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20}, // really internal use, so we don't care {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_SHORT, 1, 0}, {CAP_H, UDATPG_HOUR_FIELD, DT_NUMERIC + 10*DT_DELTA, 1, 2}, // 24 hour - {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2}, + {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2}, // 24 hour {LOW_H, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12 hour - {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, + {CAP_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // 12 hour {LOW_M, UDATPG_MINUTE_FIELD, DT_NUMERIC, 1, 2}, {LOW_S, UDATPG_SECOND_FIELD, DT_NUMERIC, 1, 2}, {CAP_S, UDATPG_FRACTIONAL_SECOND_FIELD, DT_NUMERIC + DT_DELTA, 1, 1000}, @@ -192,10 +193,21 @@ static const dtTypeElem dtTypes[] = { {LOW_V, UDATPG_ZONE_FIELD, DT_LONG - 2*DT_DELTA, 4, 0}, {LOW_Z, UDATPG_ZONE_FIELD, DT_SHORT, 1, 3}, {LOW_Z, UDATPG_ZONE_FIELD, DT_LONG, 4, 0}, - {CAP_Z, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 3}, + {CAP_Z, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 3}, {CAP_Z, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, - {CAP_V, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 3}, - {CAP_V, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {CAP_Z, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 5, 0}, + {CAP_O, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0}, + {CAP_O, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {CAP_V, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0}, + {CAP_V, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 2, 0}, + {CAP_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0}, + {CAP_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0}, + {CAP_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {LOW_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0}, + {LOW_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0}, + {LOW_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {LOW_J, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12/24 hour + {CAP_J, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12/24 hour no AM/PM {0, UDATPG_FIELD_COUNT, 0, 0, 0} , // last row of dtTypes[] }; @@ -205,7 +217,7 @@ static const char* const CLDR_FIELD_APPEND[] = { }; static const char* const CLDR_FIELD_NAME[] = { - "era", "year", "quarter", "month", "week", "*", "weekday", "day", "*", "*", "dayperiod", + "era", "year", "quarter", "month", "week", "*", "weekday", "*", "*", "day", "dayperiod", "hour", "minute", "second", "*", "zone" }; @@ -217,7 +229,7 @@ static const char* const Resource_Fields[] = { static const UChar UDATPG_ItemFormat[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A, 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524 -static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ" +//static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ" static const char DT_DateTimePatternsTag[]="DateTimePatterns"; static const char DT_DateTimeCalendarTag[]="calendar"; @@ -238,15 +250,12 @@ DateTimePatternGenerator::createInstance(UErrorCode& status) { DateTimePatternGenerator* U_EXPORT2 DateTimePatternGenerator::createInstance(const Locale& locale, UErrorCode& status) { - DateTimePatternGenerator *result = new DateTimePatternGenerator(locale, status); - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } if (U_FAILURE(status)) { - delete result; - result = NULL; + return NULL; } - return result; + LocalPointer result( + new DateTimePatternGenerator(locale, status), status); + return U_SUCCESS(status) ? result.orphan() : NULL; } DateTimePatternGenerator* U_EXPORT2 @@ -305,6 +314,10 @@ DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerato DateTimePatternGenerator& DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) { + // reflexive case + if (&other == this) { + return *this; + } pLocale = other.pLocale; fDefaultHourFormatChar = other.fDefaultHourFormatChar; *fp = *(other.fp); @@ -373,6 +386,34 @@ DateTimePatternGenerator::~DateTimePatternGenerator() { if (skipMatcher != NULL) delete skipMatcher; } +namespace { + +UInitOnce initOnce = U_INITONCE_INITIALIZER; +UHashtable *localeToAllowedHourFormatsMap = NULL; + +// Value deleter for hashmap. +void deleteAllowedHourFormats(void *ptr) { + uprv_free(ptr); +} + +// Close hashmap at cleanup. +UBool allowedHourFormatsCleanup() { + uhash_close(localeToAllowedHourFormatsMap); + return TRUE; +} + +enum AllowedHourFormat{ + ALLOWED_HOUR_FORMAT_UNKNOWN = -1, + ALLOWED_HOUR_FORMAT_h, + ALLOWED_HOUR_FORMAT_H, + ALLOWED_HOUR_FORMAT_hb, + ALLOWED_HOUR_FORMAT_Hb, + ALLOWED_HOUR_FORMAT_hB, + ALLOWED_HOUR_FORMAT_HB +}; + +} // namespace + void DateTimePatternGenerator::initData(const Locale& locale, UErrorCode &status) { //const char *baseLangName = locale.getBaseName(); // unused @@ -387,19 +428,206 @@ DateTimePatternGenerator::initData(const Locale& locale, UErrorCode &status) { addCLDRData(locale, status); setDateTimeFromCalendar(locale, status); setDecimalSymbols(locale, status); + umtx_initOnce(initOnce, loadAllowedHourFormatsData, status); + getAllowedHourFormats(locale, status); } // DateTimePatternGenerator::initData +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(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(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(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(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; + } +} + UnicodeString DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) { - dtMatcher->set(pattern, fp); - return dtMatcher->getSkeletonPtr()->getSkeleton(); + FormatParser fp; + DateTimeMatcher matcher; + PtnSkeleton localSkeleton; + matcher.set(pattern, &fp, localSkeleton); + return localSkeleton.getSkeleton(); +} + +UnicodeString +DateTimePatternGenerator::staticGetSkeleton( + const UnicodeString& pattern, UErrorCode& /*status*/) { + FormatParser fp; + DateTimeMatcher matcher; + PtnSkeleton localSkeleton; + matcher.set(pattern, &fp, localSkeleton); + return localSkeleton.getSkeleton(); } UnicodeString DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) { - dtMatcher->set(pattern, fp); - return dtMatcher->getSkeletonPtr()->getBaseSkeleton(); + FormatParser fp; + DateTimeMatcher matcher; + PtnSkeleton localSkeleton; + matcher.set(pattern, &fp, localSkeleton); + return localSkeleton.getBaseSkeleton(); +} + +UnicodeString +DateTimePatternGenerator::staticGetBaseSkeleton( + const UnicodeString& pattern, UErrorCode& /*status*/) { + FormatParser fp; + DateTimeMatcher matcher; + PtnSkeleton localSkeleton; + matcher.set(pattern, &fp, localSkeleton); + return localSkeleton.getBaseSkeleton(); } void @@ -501,10 +729,10 @@ DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& err) { const char *key=NULL; int32_t i; - UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, LENGTHOF(UDATPG_ItemFormat)-1); // Read-only alias. + UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, UPRV_LENGTHOF(UDATPG_ItemFormat)-1); // Read-only alias. err = U_ZERO_ERROR; - + fDefaultHourFormatChar = 0; for (i=0; i0) { hackTimes(hackPattern, err); @@ -759,25 +991,69 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate const UnicodeString *bestPattern=NULL; UnicodeString dtFormat; UnicodeString resultPattern; + int32_t flags = kDTPGNoFlags; int32_t dateMask=(1<set(patternFormCopy, fp); const PtnSkeleton* specifiedSkeleton=NULL; bestPattern=getBestRaw(*dtMatcher, -1, distanceInfo, &specifiedSkeleton); if ( distanceInfo->missingFieldMask==0 && distanceInfo->extraFieldMask==0 ) { - resultPattern = adjustFieldTypes(*bestPattern, specifiedSkeleton, FALSE, options); + resultPattern = adjustFieldTypes(*bestPattern, specifiedSkeleton, flags, options); return resultPattern; } int32_t neededFields = dtMatcher->getFieldMask(); - UnicodeString datePattern=getBestAppending(neededFields & dateMask, options); - UnicodeString timePattern=getBestAppending(neededFields & timeMask, options); + UnicodeString datePattern=getBestAppending(neededFields & dateMask, flags, options); + UnicodeString timePattern=getBestAppending(neededFields & timeMask, flags, options); if (datePattern.length()==0) { if (timePattern.length()==0) { resultPattern.remove(); @@ -792,8 +1068,7 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate resultPattern.remove(); status = U_ZERO_ERROR; dtFormat=getDateTimeFormat(); - Formattable dateTimeObject[] = { timePattern, datePattern }; - resultPattern = MessageFormat::format(dtFormat, dateTimeObject, 2, resultPattern, status ); + SimpleFormatter(dtFormat, 2, 2, status).format(timePattern, datePattern, resultPattern, status); return resultPattern; } @@ -810,7 +1085,7 @@ DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern, UDateTimePatternMatchOptions options, UErrorCode& /*status*/) { dtMatcher->set(skeleton, fp); - UnicodeString result = adjustFieldTypes(pattern, NULL, FALSE, options); + UnicodeString result = adjustFieldTypes(pattern, NULL, kDTPGNoFlags, options); return result; } @@ -920,15 +1195,26 @@ DateTimePatternGenerator::addPatternWithSkeleton( matcher.set(*skeletonToUse, fp, skeleton); // no longer trims skeleton fields to max len 3, per #7930 matcher.getBasePattern(basePattern); // or perhaps instead: basePattern = *skeletonToUse; } + // We only care about base conflicts - and replacing the pattern associated with a base - if: + // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous + // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or + // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen + // if we are getting here from a subsequent call to addPattern). + // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking + // availableFormats items from root, which should not override any previous entry with the same base. UBool entryHadSpecifiedSkeleton; const UnicodeString *duplicatePattern = patternMap->getPatternFromBasePattern(basePattern, entryHadSpecifiedSkeleton); - if (duplicatePattern != NULL ) { + if (duplicatePattern != NULL && (!entryHadSpecifiedSkeleton || (skeletonToUse != NULL && !override))) { conflictingStatus = UDATPG_BASE_CONFLICT; conflictingPattern = *duplicatePattern; - if (!override || (skeletonToUse != NULL && entryHadSpecifiedSkeleton)) { + if (!override) { return conflictingStatus; } } + // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats + // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with + // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for + // the previously-specified conflicting item. const PtnSkeleton* entrySpecifiedSkeleton = NULL; duplicatePattern = patternMap->getPatternFromSkeleton(skeleton, &entrySpecifiedSkeleton); if (duplicatePattern != NULL ) { @@ -1006,7 +1292,7 @@ DateTimePatternGenerator::getBestRaw(DateTimeMatcher& source, UnicodeString DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, const PtnSkeleton* specifiedSkeleton, - UBool fixFractionalSeconds, + int32_t flags, UDateTimePatternMatchOptions options) { UnicodeString newPattern; fp->set(pattern); @@ -1030,7 +1316,20 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, } const dtTypeElem *row = &dtTypes[canonicalIndex]; int32_t typeValue = row->field; - if (fixFractionalSeconds && typeValue == UDATPG_SECOND_FIELD) { + + // Handle special day periods. + if (typeValue == UDATPG_DAYPERIOD_FIELD && flags != 0) { + UChar c = NONE; // '0' + if (flags & kDTPGSkeletonUsesCapB) { c = CAP_B; } + if (flags & kDTPGSkeletonUsesLowB) { c = LOW_B; } + + if (c != NONE) { + for (int32_t i = 0; i < field.length(); ++i) + field.setCharAt(i, c); + } + } + + if ((flags & kDTPGFixFractionalSeconds) != 0 && typeValue == UDATPG_SECOND_FIELD) { UnicodeString newField=dtMatcher->skeleton.original[UDATPG_FRACTIONAL_SECOND_FIELD]; field = field + decimal + newField; } else if (dtMatcher->skeleton.type[typeValue]!=0) { @@ -1076,8 +1375,29 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, } } UChar c = (typeValue!= UDATPG_HOUR_FIELD && typeValue!= UDATPG_MONTH_FIELD && - typeValue!= UDATPG_WEEKDAY_FIELD && typeValue!= UDATPG_YEAR_FIELD)? + typeValue!= UDATPG_WEEKDAY_FIELD && (typeValue!= UDATPG_YEAR_FIELD || reqField.charAt(0)==CAP_Y))? reqField.charAt(0): field.charAt(0); + if (typeValue == UDATPG_HOUR_FIELD && (flags & kDTPGSkeletonUsesCapJ) != 0) { + c = fDefaultHourFormatChar; + switch (options & UADATPG_FORCE_HOUR_CYCLE_MASK) { + case UADATPG_FORCE_12_HOUR_CYCLE: + if (c == CAP_H || c == LOW_K) { + // Have 24-hour cycle, change to 12-hour cycle. + // Should have better way to pick 'h' or 'K'. + c = LOW_H; + }; + break; + case UADATPG_FORCE_24_HOUR_CYCLE: + if (c == LOW_H || c == CAP_K) { + // Have 12-hour cycle, change to 24-hour cycle. + // Should have better way to pick 'H' or 'k'. + c = CAP_H; + }; + break; + default: + break; + } + } field.remove(); for (int32_t i=adjFieldLen; i>0; --i) { field+=c; @@ -1090,7 +1410,7 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, } UnicodeString -DateTimePatternGenerator::getBestAppending(int32_t missingFields, UDateTimePatternMatchOptions options) { +DateTimePatternGenerator::getBestAppending(int32_t missingFields, int32_t flags, UDateTimePatternMatchOptions options) { UnicodeString resultPattern, tempPattern; UErrorCode err=U_ZERO_ERROR; int32_t lastMissingFieldMask=0; @@ -1098,7 +1418,7 @@ DateTimePatternGenerator::getBestAppending(int32_t missingFields, UDateTimePatte resultPattern=UnicodeString(); const PtnSkeleton* specifiedSkeleton=NULL; tempPattern = *getBestRaw(*dtMatcher, missingFields, distanceInfo, &specifiedSkeleton); - resultPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, FALSE, options); + resultPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options); if ( distanceInfo->missingFieldMask==0 ) { return resultPattern; } @@ -1108,24 +1428,24 @@ DateTimePatternGenerator::getBestAppending(int32_t missingFields, UDateTimePatte } if (((distanceInfo->missingFieldMask & UDATPG_SECOND_AND_FRACTIONAL_MASK)==UDATPG_FRACTIONAL_MASK) && ((missingFields & UDATPG_SECOND_AND_FRACTIONAL_MASK) == UDATPG_SECOND_AND_FRACTIONAL_MASK)) { - resultPattern = adjustFieldTypes(resultPattern, specifiedSkeleton, TRUE, options); + resultPattern = adjustFieldTypes(resultPattern, specifiedSkeleton, flags | kDTPGFixFractionalSeconds, options); distanceInfo->missingFieldMask &= ~UDATPG_FRACTIONAL_MASK; continue; } int32_t startingMask = distanceInfo->missingFieldMask; tempPattern = *getBestRaw(*dtMatcher, distanceInfo->missingFieldMask, distanceInfo, &specifiedSkeleton); - tempPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, FALSE, options); + tempPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options); int32_t foundMask=startingMask& ~distanceInfo->missingFieldMask; int32_t topField=getTopBitNumber(foundMask); UnicodeString appendName; getAppendName((UDateTimePatternField)topField, appendName); - const Formattable formatPattern[] = { - resultPattern, - tempPattern, - appendName + const UnicodeString *values[3] = { + &resultPattern, + &tempPattern, + &appendName }; - UnicodeString emptyStr; - resultPattern = MessageFormat::format(appendItemFormats[topField], formatPattern, 3, emptyStr, err); + SimpleFormatter(appendItemFormats[topField], 2, 3, err). + formatAndReplace(values, 3, resultPattern, NULL, 0, err); lastMissingFieldMask = distanceInfo->missingFieldMask; } } @@ -1174,7 +1494,7 @@ DateTimePatternGenerator::copyHashtable(Hashtable *other, UErrorCode &status) { if(U_FAILURE(status)){ return; } - int32_t pos = -1; + int32_t pos = UHASH_FIRST; const UHashElement* elem = NULL; // walk through the hash table and create a deep clone while((elem = other->nextElement(pos))!= NULL){ @@ -1290,7 +1610,7 @@ PatternMap::copyFrom(const PatternMap& other, UErrorCode& status) { status = U_MEMORY_ALLOCATION_ERROR; return; } - + curElem->skeletonWasSpecified = otherElem->skeletonWasSpecified; if (prevElem!=NULL) { prevElem->next=curElem; } @@ -1394,6 +1714,9 @@ PatternMap::add(const UnicodeString& basePattern, } // Overwrite the value. curElem->pattern = value; + // It was a bug that we were not doing the following previously, + // though that bug hid other problems by making things partly work. + curElem->skeletonWasSpecified = skeletonWasSpecified; } } } // PatternMap::add @@ -1794,7 +2117,7 @@ FormatParser::getCanonicalIndex(const UnicodeString& s, UBool strict) { } UBool -FormatParser::isQuoteLiteral(const UnicodeString& s) const { +FormatParser::isQuoteLiteral(const UnicodeString& s) { return (UBool)(s.charAt(0)==SINGLE_QUOTE); }