Index: source/i18n/dtptngen.cpp |
diff --git a/source/i18n/dtptngen.cpp b/source/i18n/dtptngen.cpp |
index 0a7d4b3833bab732f2e306cd6878284fc969ea52..17e7ec7cde8a219c44ac4603dedcc5465402c9a2 100644 |
--- a/source/i18n/dtptngen.cpp |
+++ b/source/i18n/dtptngen.cpp |
@@ -1,6 +1,8 @@ |
+// Copyright (C) 2016 and later: Unicode, Inc. and others. |
+// License & terms of use: http://www.unicode.org/copyright.html |
/* |
******************************************************************************* |
-* Copyright (C) 2007-2015, International Business Machines Corporation and |
+* Copyright (C) 2007-2016, International Business Machines Corporation and |
* others. All Rights Reserved. |
******************************************************************************* |
* |
@@ -16,7 +18,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" |
@@ -27,13 +29,17 @@ |
#include "unicode/rep.h" |
#include "cpputils.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" |
+#include "ucln_in.h" |
+#include "charstr.h" |
+#include "uassert.h" |
#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY |
/** |
@@ -202,6 +208,8 @@ static const dtTypeElem dtTypes[] = { |
{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[] |
}; |
@@ -215,10 +223,6 @@ static const char* const CLDR_FIELD_NAME[] = { |
"hour", "minute", "second", "*", "zone" |
}; |
-static const char* const Resource_Fields[] = { |
- "day", "dayperiod", "era", "hour", "minute", "month", "second", "week", |
- "weekday", "year", "zone", "quarter" }; |
- |
// For appendItems |
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 |
@@ -380,22 +384,186 @@ DateTimePatternGenerator::~DateTimePatternGenerator() { |
if (skipMatcher != NULL) delete skipMatcher; |
} |
+namespace { |
+ |
+UInitOnce initOnce = U_INITONCE_INITIALIZER; |
+UHashtable *localeToAllowedHourFormatsMap = NULL; |
+ |
+// Value deleter for hashmap. |
+U_CFUNC void U_CALLCONV deleteAllowedHourFormats(void *ptr) { |
+ uprv_free(ptr); |
+} |
+ |
+// Close hashmap at cleanup. |
+U_CFUNC UBool U_CALLCONV 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 |
skipMatcher = NULL; |
fAvailableFormatKeyHash=NULL; |
- addCanonicalItems(); |
+ addCanonicalItems(status); |
addICUPatterns(locale, status); |
- if (U_FAILURE(status)) { |
- return; |
- } |
addCLDRData(locale, status); |
setDateTimeFromCalendar(locale, status); |
setDecimalSymbols(locale, status); |
+ umtx_initOnce(initOnce, loadAllowedHourFormatsData, status); |
+ getAllowedHourFormats(locale, status); |
} // DateTimePatternGenerator::initData |
+namespace { |
+ |
+struct AllowedHourFormatsSink : public ResourceSink { |
+ // Initialize sub-sinks. |
+ AllowedHourFormatsSink() {} |
+ virtual ~AllowedHourFormatsSink(); |
+ |
+ virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, |
+ UErrorCode &errorCode) { |
+ ResourceTable timeData = value.getTable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ for (int32_t i = 0; timeData.getKeyAndValue(i, key, value); ++i) { |
+ const char *regionOrLocale = key; |
+ ResourceTable formatList = value.getTable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ for (int32_t j = 0; formatList.getKeyAndValue(j, key, value); ++j) { |
+ if (uprv_strcmp(key, "allowed") == 0) { // Ignore "preferred" list. |
+ LocalMemory<int32_t> list; |
+ int32_t length; |
+ if (value.getType() == URES_STRING) { |
+ if (list.allocateInsteadAndReset(2) == NULL) { |
+ errorCode = U_MEMORY_ALLOCATION_ERROR; |
+ return; |
+ } |
+ list[0] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode)); |
+ length = 1; |
+ } |
+ else { |
+ ResourceArray allowedFormats = value.getArray(errorCode); |
+ length = allowedFormats.getSize(); |
+ if (list.allocateInsteadAndReset(length + 1) == NULL) { |
+ errorCode = U_MEMORY_ALLOCATION_ERROR; |
+ return; |
+ } |
+ for (int32_t k = 0; k < length; ++k) { |
+ allowedFormats.getValue(k, value); |
+ list[k] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode)); |
+ } |
+ } |
+ list[length] = ALLOWED_HOUR_FORMAT_UNKNOWN; |
+ uhash_put(localeToAllowedHourFormatsMap, |
+ const_cast<char *>(regionOrLocale), list.orphan(), &errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ } |
+ } |
+ } |
+ } |
+ |
+ 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() {} |
+ |
+U_CFUNC void U_CALLCONV 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_getAllItemsWithFallback(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; |
+ } |
+} |
+ |
+UnicodeString |
+DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode& |
+/*status*/) { |
+ FormatParser fp; |
+ DateTimeMatcher matcher; |
+ PtnSkeleton localSkeleton; |
+ matcher.set(pattern, &fp, localSkeleton); |
+ return localSkeleton.getSkeleton(); |
+} |
+ |
UnicodeString |
DateTimePatternGenerator::staticGetSkeleton( |
const UnicodeString& pattern, UErrorCode& /*status*/) { |
@@ -407,6 +575,15 @@ DateTimePatternGenerator::staticGetSkeleton( |
} |
UnicodeString |
+DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) { |
+ FormatParser fp; |
+ DateTimeMatcher matcher; |
+ PtnSkeleton localSkeleton; |
+ matcher.set(pattern, &fp, localSkeleton); |
+ return localSkeleton.getBaseSkeleton(); |
+} |
+ |
+UnicodeString |
DateTimePatternGenerator::staticGetBaseSkeleton( |
const UnicodeString& pattern, UErrorCode& /*status*/) { |
FormatParser fp; |
@@ -418,41 +595,38 @@ DateTimePatternGenerator::staticGetBaseSkeleton( |
void |
DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& status) { |
+ if (U_FAILURE(status)) { return; } |
UnicodeString dfPattern; |
UnicodeString conflictingString; |
DateFormat* df; |
- if (U_FAILURE(status)) { |
- return; |
- } |
- |
// Load with ICU patterns |
for (int32_t i=DateFormat::kFull; i<=DateFormat::kShort; i++) { |
DateFormat::EStyle style = (DateFormat::EStyle)i; |
df = DateFormat::createDateInstance(style, locale); |
SimpleDateFormat* sdf; |
if (df != NULL && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != NULL) { |
- addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status); |
+ sdf->toPattern(dfPattern); |
+ addPattern(dfPattern, FALSE, conflictingString, status); |
} |
// TODO Maybe we should return an error when the date format isn't simple. |
delete df; |
- if (U_FAILURE(status)) { |
- return; |
- } |
+ if (U_FAILURE(status)) { return; } |
df = DateFormat::createTimeInstance(style, locale); |
if (df != NULL && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != NULL) { |
- addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status); |
- // HACK for hh:ss |
- if ( i==DateFormat::kMedium ) { |
- hackPattern = dfPattern; |
+ sdf->toPattern(dfPattern); |
+ addPattern(dfPattern, FALSE, conflictingString, status); |
+ |
+ // TODO: C++ and Java are inconsistent (see #12568). |
+ // C++ uses MEDIUM, but Java uses SHORT. |
+ if ( i==DateFormat::kShort && !dfPattern.isEmpty() ) { |
+ consumeShortTimePattern(dfPattern, status); |
} |
} |
// TODO Maybe we should return an error when the date format isn't simple. |
delete df; |
- if (U_FAILURE(status)) { |
- return; |
- } |
+ if (U_FAILURE(status)) { return; } |
} |
} |
@@ -507,221 +681,223 @@ DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode |
static const UChar hourFormatChars[] = { CAP_H, LOW_H, CAP_K, LOW_K, 0 }; // HhKk, the hour format characters |
void |
-DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& err) { |
- UResourceBundle *rb, *calTypeBundle, *calBundle; |
- UResourceBundle *patBundle, *fieldBundle, *fBundle; |
- UnicodeString rbPattern, value, field; |
- UnicodeString conflictingPattern; |
- const char *key=NULL; |
- int32_t i; |
- |
- UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, UPRV_LENGTHOF(UDATPG_ItemFormat)-1); // Read-only alias. |
- |
- err = U_ZERO_ERROR; |
- |
- fDefaultHourFormatChar = 0; |
- for (i=0; i<UDATPG_FIELD_COUNT; ++i ) { |
- appendItemNames[i]=CAP_F; |
- if (i<10) { |
- appendItemNames[i]+=(UChar)(i+0x30); |
- } |
- else { |
- appendItemNames[i]+=(UChar)0x31; |
- appendItemNames[i]+=(UChar)(i-10 + 0x30); |
- } |
- // NUL-terminate for the C API. |
- appendItemNames[i].getTerminatedBuffer(); |
- } |
- |
- rb = ures_open(NULL, locale.getName(), &err); |
- if (rb == NULL || U_FAILURE(err)) { |
- return; |
- } |
- const char *curLocaleName=ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &err); |
- const char * calendarTypeToUse = DT_DateTimeGregorianTag; // initial default |
- char calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well |
+DateTimePatternGenerator::getCalendarTypeToUse(const Locale& locale, CharString& destination, UErrorCode& err) { |
+ destination.clear().append(DT_DateTimeGregorianTag, -1, err); // initial default |
if ( U_SUCCESS(err) ) { |
- char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY]; |
+ char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY]; |
// obtain a locale that always has the calendar key value that should be used |
- (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, NULL, |
- "calendar", "calendar", locale.getName(), NULL, FALSE, &err); |
+ ures_getFunctionalEquivalent( |
+ localeWithCalendarKey, |
+ ULOC_LOCALE_IDENTIFIER_CAPACITY, |
+ NULL, |
+ "calendar", |
+ "calendar", |
+ locale.getName(), |
+ NULL, |
+ FALSE, |
+ &err); |
localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination |
// now get the calendar key value from that locale |
- int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType, ULOC_KEYWORDS_CAPACITY, &err); |
+ char calendarType[ULOC_KEYWORDS_CAPACITY]; |
+ int32_t calendarTypeLen = uloc_getKeywordValue( |
+ localeWithCalendarKey, |
+ "calendar", |
+ calendarType, |
+ ULOC_KEYWORDS_CAPACITY, |
+ &err); |
if (U_SUCCESS(err) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) { |
- calendarTypeToUse = calendarType; |
+ destination.clear().append(calendarType, -1, err); |
+ if (U_FAILURE(err)) { return; } |
} |
err = U_ZERO_ERROR; |
} |
- calBundle = ures_getByKeyWithFallback(rb, DT_DateTimeCalendarTag, NULL, &err); |
- calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &err); |
+} |
- key=NULL; |
- int32_t dtCount=0; |
- patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimePatternsTag, NULL, &err); |
- while (U_SUCCESS(err)) { |
- rbPattern = ures_getNextUnicodeString(patBundle, &key, &err); |
- dtCount++; |
- if (rbPattern.length()==0 ) { |
- break; // no more pattern |
- } |
- else { |
- if (dtCount==9) { |
- setDateTimeFormat(rbPattern); |
- } else if (dtCount==4) { // short time format |
- // set fDefaultHourFormatChar to the hour format character from this pattern |
- int32_t tfIdx, tfLen = rbPattern.length(); |
- UBool ignoreChars = FALSE; |
- for (tfIdx = 0; tfIdx < tfLen; tfIdx++) { |
- UChar tfChar = rbPattern.charAt(tfIdx); |
- if ( tfChar == SINGLE_QUOTE ) { |
- ignoreChars = !ignoreChars; // toggle (handle quoted literals & '' for single quote) |
- } else if ( !ignoreChars && u_strchr(hourFormatChars, tfChar) != NULL ) { |
- fDefaultHourFormatChar = tfChar; |
- break; |
- } |
- } |
- } |
+void |
+DateTimePatternGenerator::consumeShortTimePattern(const UnicodeString& shortTimePattern, |
+ UErrorCode& status) { |
+ |
+ // set fDefaultHourFormatChar to the hour format character from this pattern |
+ int32_t tfIdx, tfLen = shortTimePattern.length(); |
+ UBool ignoreChars = FALSE; |
+ for (tfIdx = 0; tfIdx < tfLen; tfIdx++) { |
+ UChar tfChar = shortTimePattern.charAt(tfIdx); |
+ if ( tfChar == SINGLE_QUOTE ) { |
+ ignoreChars = !ignoreChars; // toggle (handle quoted literals & '' for single quote) |
+ } else if ( !ignoreChars && u_strchr(hourFormatChars, tfChar) != NULL ) { |
+ fDefaultHourFormatChar = tfChar; |
+ break; |
} |
} |
- ures_close(patBundle); |
- err = U_ZERO_ERROR; |
- patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimeAppendItemsTag, NULL, &err); |
- key=NULL; |
- UnicodeString itemKey; |
- while (U_SUCCESS(err)) { |
- rbPattern = ures_getNextUnicodeString(patBundle, &key, &err); |
- if (rbPattern.length()==0 ) { |
- break; // no more pattern |
- } |
- else { |
- setAppendItemFormat(getAppendFormatNumber(key), rbPattern); |
+ // HACK for hh:ss |
+ hackTimes(shortTimePattern, status); |
+} |
+ |
+struct DateTimePatternGenerator::AppendItemFormatsSink : public ResourceSink { |
+ |
+ // Destination for data, modified via setters. |
+ DateTimePatternGenerator& dtpg; |
+ |
+ AppendItemFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {} |
+ virtual ~AppendItemFormatsSink(); |
+ |
+ virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, |
+ UErrorCode &errorCode) { |
+ ResourceTable itemsTable = value.getTable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) { |
+ UDateTimePatternField field = dtpg.getAppendFormatNumber(key); |
+ if (field == UDATPG_FIELD_COUNT) { continue; } |
+ const UnicodeString& valueStr = value.getUnicodeString(errorCode); |
+ if (dtpg.getAppendItemFormat(field).isEmpty() && !valueStr.isEmpty()) { |
+ dtpg.setAppendItemFormat(field, valueStr); |
+ } |
} |
} |
- ures_close(patBundle); |
- key=NULL; |
- err = U_ZERO_ERROR; |
- fBundle = ures_getByKeyWithFallback(rb, DT_DateTimeFieldsTag, NULL, &err); |
- for (i=0; i<MAX_RESOURCE_FIELD; ++i) { |
- err = U_ZERO_ERROR; |
- patBundle = ures_getByKeyWithFallback(fBundle, Resource_Fields[i], NULL, &err); |
- fieldBundle = ures_getByKeyWithFallback(patBundle, "dn", NULL, &err); |
- rbPattern = ures_getNextUnicodeString(fieldBundle, &key, &err); |
- ures_close(fieldBundle); |
- ures_close(patBundle); |
- if (rbPattern.length()==0 ) { |
- continue; |
- } |
- else { |
- setAppendItemName(getAppendNameNumber(Resource_Fields[i]), rbPattern); |
+ void fillInMissing() { |
+ UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, UPRV_LENGTHOF(UDATPG_ItemFormat)-1); // Read-only alias. |
+ for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) { |
+ UDateTimePatternField field = (UDateTimePatternField)i; |
+ if (dtpg.getAppendItemFormat(field).isEmpty()) { |
+ dtpg.setAppendItemFormat(field, defaultItemFormat); |
+ } |
} |
} |
- ures_close(fBundle); |
+}; |
- // add available formats |
- UBool firstTimeThrough = TRUE; |
- err = U_ZERO_ERROR; |
- initHashtable(err); |
- UBool override = TRUE; |
- while (TRUE) { |
- // At the start of the loop: |
- // - rb is the open resource bundle for the current locale being processed, |
- // whose actual name is in curLocaleName. |
- // - if U_SUCCESS(err), then calBundle and calTypeBundle are open; |
- // process contents of calTypeBundle, then close calBundle and calTypeBundle. |
- if (U_SUCCESS(err)) { |
- // process contents of calTypeBundle |
- patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimeAvailableFormatsTag, NULL, &err); |
- if (U_SUCCESS(err)) { |
- int32_t numberKeys = ures_getSize(patBundle); |
- int32_t len; |
- const UChar *retPattern; |
- key=NULL; |
-#if defined(U_USE_ASCII_BUNDLE_ITERATOR) |
- UResourceBundleAIterator aiter; |
- ures_a_open(&aiter, patBundle, &err); |
-#endif |
- for(i=0; i<numberKeys; ++i) { |
-#if defined(U_USE_ASCII_BUNDLE_ITERATOR) |
- retPattern=ures_a_getNextString(&aiter, &len, &key, &err); |
-#else |
- retPattern=ures_getNextString(patBundle, &len, &key, &err); |
-#endif |
- UnicodeString format=UnicodeString(retPattern); |
- UnicodeString retKey=UnicodeString(key, -1, US_INV); |
- if ( firstTimeThrough || !isAvailableFormatSet(retKey) ) { |
- setAvailableFormat(retKey, err); |
- // Add pattern with its associated skeleton. Override any duplicate derived from std patterns, |
- // but not a previous availableFormats entry: |
- addPatternWithSkeleton(format, &retKey, override, conflictingPattern, err); |
- } |
+struct DateTimePatternGenerator::AppendItemNamesSink : public ResourceSink { |
+ |
+ // Destination for data, modified via setters. |
+ DateTimePatternGenerator& dtpg; |
+ |
+ AppendItemNamesSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {} |
+ virtual ~AppendItemNamesSink(); |
+ |
+ virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, |
+ UErrorCode &errorCode) { |
+ ResourceTable itemsTable = value.getTable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) { |
+ UDateTimePatternField field = dtpg.getAppendNameNumber(key); |
+ if (field == UDATPG_FIELD_COUNT) { continue; } |
+ ResourceTable detailsTable = value.getTable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ for (int32_t j = 0; detailsTable.getKeyAndValue(j, key, value); ++j) { |
+ if (uprv_strcmp(key, "dn") != 0) { continue; } |
+ const UnicodeString& valueStr = value.getUnicodeString(errorCode); |
+ if (dtpg.getAppendItemName(field).isEmpty() && !valueStr.isEmpty()) { |
+ dtpg.setAppendItemName(field, valueStr); |
} |
-#if defined(U_USE_ASCII_BUNDLE_ITERATOR) |
- ures_a_close(&aiter); |
-#endif |
- ures_close(patBundle); |
+ break; |
} |
- firstTimeThrough = FALSE; |
- // close calBundle and calTypeBundle |
- ures_close(calTypeBundle); |
- ures_close(calBundle); |
- } |
- if (uprv_strcmp(curLocaleName,"root")==0 || uprv_strlen(curLocaleName)==0) { |
- // we just finished handling root, nothing more to check |
- ures_close(rb); |
- break; |
} |
- // Find the name of the appropriate parent locale (from %%Parent if present, else |
- // uloc_getParent on the actual locale name) |
- // (It would be nice to have a ures function that did this...) |
- err = U_ZERO_ERROR; |
- char parentLocale[ULOC_FULLNAME_CAPACITY]; |
- int32_t locNameLen; |
- const UChar * parentUName = ures_getStringByKey(rb, "%%Parent", &locNameLen, &err); |
- if (U_SUCCESS(err) && err != U_USING_FALLBACK_WARNING && locNameLen < ULOC_FULLNAME_CAPACITY) { |
- u_UCharsToChars(parentUName, parentLocale, locNameLen + 1); |
- } else { |
- err = U_ZERO_ERROR; |
- uloc_getParent(curLocaleName, parentLocale, ULOC_FULLNAME_CAPACITY, &err); |
- if (U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) { |
- // just fallback to root, since we are not already there |
- parentLocale[0] = 0; |
- err = U_ZERO_ERROR; |
+ } |
+ |
+ void fillInMissing() { |
+ for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) { |
+ UDateTimePatternField field = (UDateTimePatternField)i; |
+ UnicodeString& valueStr = dtpg.getMutableAppendItemName(field); |
+ if (valueStr.isEmpty()) { |
+ valueStr = CAP_F; |
+ U_ASSERT(i < 20); |
+ if (i < 10) { |
+ // F0, F1, ..., F9 |
+ valueStr += (UChar)(i+0x30); |
+ } else { |
+ // F10, F11, ... |
+ valueStr += (UChar)0x31; |
+ valueStr += (UChar)(i-10 + 0x30); |
+ } |
+ // NUL-terminate for the C API. |
+ valueStr.getTerminatedBuffer(); |
} |
} |
- // Close current locale bundle |
- ures_close(rb); |
- // And open its parent, which becomes the new current locale being processed |
- rb = ures_open(NULL, parentLocale, &err); |
- if ( U_FAILURE(err) ) { |
- err = U_ZERO_ERROR; |
- break; |
- } |
- // Get the name of the parent / new current locale |
- curLocaleName=ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &err); |
- if ( U_FAILURE(err) ) { |
- curLocaleName = parentLocale; |
- err = U_ZERO_ERROR; |
- } |
- if (uprv_strcmp(curLocaleName,"root")==0 || uprv_strlen(curLocaleName)==0) { |
- override = FALSE; |
- } |
- // Open calBundle and calTypeBundle |
- calBundle = ures_getByKeyWithFallback(rb, DT_DateTimeCalendarTag, NULL, &err); |
- if (U_SUCCESS(err)) { |
- calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &err); |
- if ( U_FAILURE(err) ) { |
- ures_close(calBundle); |
+ } |
+}; |
+ |
+struct DateTimePatternGenerator::AvailableFormatsSink : public ResourceSink { |
+ |
+ // Destination for data, modified via setters. |
+ DateTimePatternGenerator& dtpg; |
+ |
+ // Temporary variable, required for calling addPatternWithSkeleton. |
+ UnicodeString conflictingPattern; |
+ |
+ AvailableFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {} |
+ virtual ~AvailableFormatsSink(); |
+ |
+ virtual void put(const char *key, ResourceValue &value, UBool isRoot, |
+ UErrorCode &errorCode) { |
+ ResourceTable itemsTable = value.getTable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) { |
+ const UnicodeString formatKey(key, -1, US_INV); |
+ if (!dtpg.isAvailableFormatSet(formatKey) ) { |
+ dtpg.setAvailableFormat(formatKey, errorCode); |
+ // Add pattern with its associated skeleton. Override any duplicate |
+ // derived from std patterns, but not a previous availableFormats entry: |
+ const UnicodeString& formatValue = value.getUnicodeString(errorCode); |
+ conflictingPattern.remove(); |
+ dtpg.addPatternWithSkeleton(formatValue, &formatKey, !isRoot, conflictingPattern, errorCode); |
} |
} |
- // Go to the top of the loop to process contents of calTypeBundle |
- } |
- |
- if (hackPattern.length()>0) { |
- hackTimes(hackPattern, err); |
} |
+}; |
+ |
+// Virtual destructors must be defined out of line. |
+DateTimePatternGenerator::AppendItemFormatsSink::~AppendItemFormatsSink() {} |
+DateTimePatternGenerator::AppendItemNamesSink::~AppendItemNamesSink() {} |
+DateTimePatternGenerator::AvailableFormatsSink::~AvailableFormatsSink() {} |
+ |
+void |
+DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& errorCode) { |
+ if (U_FAILURE(errorCode)) { return; } |
+ UnicodeString rbPattern, value, field; |
+ CharString path; |
+ |
+ LocalUResourceBundlePointer rb(ures_open(NULL, locale.getName(), &errorCode)); |
+ if (U_FAILURE(errorCode)) { return; } |
+ |
+ CharString calendarTypeToUse; // to be filled in with the type to use, if all goes well |
+ getCalendarTypeToUse(locale, calendarTypeToUse, errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ |
+ // Local err to ignore resource not found exceptions |
+ UErrorCode err = U_ZERO_ERROR; |
+ |
+ // Load append item formats. |
+ AppendItemFormatsSink appendItemFormatsSink(*this); |
+ path.clear() |
+ .append(DT_DateTimeCalendarTag, errorCode) |
+ .append('/', errorCode) |
+ .append(calendarTypeToUse, errorCode) |
+ .append('/', errorCode) |
+ .append(DT_DateTimeAppendItemsTag, errorCode); // i.e., calendar/xxx/appendItems |
+ if (U_FAILURE(errorCode)) { return; } |
+ ures_getAllItemsWithFallback(rb.getAlias(), path.data(), appendItemFormatsSink, err); |
+ appendItemFormatsSink.fillInMissing(); |
+ |
+ // Load CLDR item names. |
+ err = U_ZERO_ERROR; |
+ AppendItemNamesSink appendItemNamesSink(*this); |
+ ures_getAllItemsWithFallback(rb.getAlias(), DT_DateTimeFieldsTag, appendItemNamesSink, err); |
+ appendItemNamesSink.fillInMissing(); |
+ |
+ // Load the available formats from CLDR. |
+ err = U_ZERO_ERROR; |
+ initHashtable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ AvailableFormatsSink availableFormatsSink(*this); |
+ path.clear() |
+ .append(DT_DateTimeCalendarTag, errorCode) |
+ .append('/', errorCode) |
+ .append(calendarTypeToUse, errorCode) |
+ .append('/', errorCode) |
+ .append(DT_DateTimeAvailableFormatsTag, errorCode); // i.e., calendar/xxx/availableFormats |
+ if (U_FAILURE(errorCode)) { return; } |
+ ures_getAllItemsWithFallback(rb.getAlias(), path.data(), availableFormatsSink, err); |
} |
void |
@@ -735,7 +911,6 @@ DateTimePatternGenerator::initHashtable(UErrorCode& err) { |
} |
} |
- |
void |
DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field, const UnicodeString& value) { |
appendItemFormats[field] = value; |
@@ -756,7 +931,12 @@ DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field, const U |
} |
const UnicodeString& |
-DateTimePatternGenerator:: getAppendItemName(UDateTimePatternField field) const { |
+DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field) const { |
+ return appendItemNames[field]; |
+} |
+ |
+UnicodeString& |
+DateTimePatternGenerator::getMutableAppendItemName(UDateTimePatternField field) { |
return appendItemNames[field]; |
} |
@@ -782,7 +962,7 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate |
int32_t dateMask=(1<<UDATPG_DAYPERIOD_FIELD) - 1; |
int32_t timeMask=(1<<UDATPG_FIELD_COUNT) - 1 - dateMask; |
- // Replace hour metacharacters 'j' and 'J', set flags as necessary |
+ // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary |
UnicodeString patternFormCopy = UnicodeString(patternForm); |
int32_t patPos, patLen = patternFormCopy.length(); |
UBool inQuoted = FALSE; |
@@ -793,6 +973,26 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate |
} else if (!inQuoted) { |
if (patChr == LOW_J) { |
patternFormCopy.setCharAt(patPos, fDefaultHourFormatChar); |
+ } else if (patChr == CAP_C) { |
+ AllowedHourFormat preferred; |
+ if (fAllowedHourFormats[0] != ALLOWED_HOUR_FORMAT_UNKNOWN) { |
+ preferred = (AllowedHourFormat)fAllowedHourFormats[0]; |
+ } else { |
+ status = U_INVALID_FORMAT_ERROR; |
+ return UnicodeString(); |
+ } |
+ |
+ if (preferred == ALLOWED_HOUR_FORMAT_H || preferred == ALLOWED_HOUR_FORMAT_HB || preferred == ALLOWED_HOUR_FORMAT_Hb) { |
+ patternFormCopy.setCharAt(patPos, CAP_H); |
+ } else { |
+ patternFormCopy.setCharAt(patPos, LOW_H); |
+ } |
+ |
+ if (preferred == ALLOWED_HOUR_FORMAT_HB || preferred == ALLOWED_HOUR_FORMAT_hB) { |
+ flags |= kDTPGSkeletonUsesCapB; |
+ } else if (preferred == ALLOWED_HOUR_FORMAT_Hb || preferred == ALLOWED_HOUR_FORMAT_hb) { |
+ flags |= kDTPGSkeletonUsesLowB; |
+ } |
} else if (patChr == CAP_J) { |
// Get pattern for skeleton with H, then replace H or k |
// with fDefaultHourFormatChar (if different) |
@@ -828,8 +1028,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; |
} |
@@ -863,12 +1062,15 @@ DateTimePatternGenerator::getDecimal() const { |
} |
void |
-DateTimePatternGenerator::addCanonicalItems() { |
+DateTimePatternGenerator::addCanonicalItems(UErrorCode& status) { |
+ if (U_FAILURE(status)) { return; } |
UnicodeString conflictingPattern; |
- UErrorCode status = U_ZERO_ERROR; |
for (int32_t i=0; i<UDATPG_FIELD_COUNT; i++) { |
- addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status); |
+ if (Canonical_Items[i] > 0) { |
+ addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status); |
+ } |
+ if (U_FAILURE(status)) { return; } |
} |
} |
@@ -890,16 +1092,35 @@ DateTimePatternGenerator::setDateTimeFromCalendar(const Locale& locale, UErrorCo |
int32_t resStrLen = 0; |
Calendar* fCalendar = Calendar::createInstance(locale, status); |
- CalendarData calData(locale, fCalendar?fCalendar->getType():NULL, status); |
- UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, status); |
- if (U_FAILURE(status)) return; |
+ if (U_FAILURE(status)) { return; } |
+ |
+ LocalUResourceBundlePointer calData(ures_open(NULL, locale.getBaseName(), &status)); |
+ ures_getByKey(calData.getAlias(), DT_DateTimeCalendarTag, calData.getAlias(), &status); |
- if (ures_getSize(dateTimePatterns) <= DateFormat::kDateTime) |
+ LocalUResourceBundlePointer dateTimePatterns; |
+ if (fCalendar != NULL && fCalendar->getType() != NULL && *fCalendar->getType() != '\0' |
+ && uprv_strcmp(fCalendar->getType(), DT_DateTimeGregorianTag) != 0) { |
+ dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), fCalendar->getType(), |
+ NULL, &status)); |
+ ures_getByKeyWithFallback(dateTimePatterns.getAlias(), DT_DateTimePatternsTag, |
+ dateTimePatterns.getAlias(), &status); |
+ } |
+ |
+ if (dateTimePatterns.isNull() || status == U_MISSING_RESOURCE_ERROR) { |
+ status = U_ZERO_ERROR; |
+ dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), DT_DateTimeGregorianTag, |
+ dateTimePatterns.orphan(), &status)); |
+ ures_getByKeyWithFallback(dateTimePatterns.getAlias(), DT_DateTimePatternsTag, |
+ dateTimePatterns.getAlias(), &status); |
+ } |
+ if (U_FAILURE(status)) { return; } |
+ |
+ if (ures_getSize(dateTimePatterns.getAlias()) <= DateFormat::kDateTime) |
{ |
status = U_INVALID_FORMAT_ERROR; |
return; |
} |
- resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)DateFormat::kDateTime, &resStrLen, &status); |
+ resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), (int32_t)DateFormat::kDateTime, &resStrLen, &status); |
setDateTimeFormat(UnicodeString(TRUE, resStr, resStrLen)); |
delete fCalendar; |
@@ -1077,9 +1298,22 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, |
} |
const dtTypeElem *row = &dtTypes[canonicalIndex]; |
int32_t typeValue = row->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; |
+ field += decimal; |
+ dtMatcher->skeleton.original.appendFieldTo(UDATPG_FRACTIONAL_SECOND_FIELD, field); |
} else if (dtMatcher->skeleton.type[typeValue]!=0) { |
// Here: |
// - "reqField" is the field from the originally requested skeleton, with length |
@@ -1103,9 +1337,9 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, |
// a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen. |
// b) The pattern field is numeric and the skeleton field is not, or vice versa. |
- UnicodeString reqField = dtMatcher->skeleton.original[typeValue]; |
- int32_t reqFieldLen = reqField.length(); |
- if (reqField.charAt(0) == CAP_E && reqFieldLen < 3) |
+ UChar reqFieldChar = dtMatcher->skeleton.original.getFieldChar(typeValue); |
+ int32_t reqFieldLen = dtMatcher->skeleton.original.getFieldLength(typeValue); |
+ if (reqFieldChar == CAP_E && reqFieldLen < 3) |
reqFieldLen = 3; // 1-3 for E are equivalent to 3 for c,e |
int32_t adjFieldLen = reqFieldLen; |
if ( (typeValue==UDATPG_HOUR_FIELD && (options & UDATPG_MATCH_HOUR_FIELD_LENGTH)==0) || |
@@ -1113,8 +1347,7 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, |
(typeValue==UDATPG_SECOND_FIELD && (options & UDATPG_MATCH_SECOND_FIELD_LENGTH)==0) ) { |
adjFieldLen = field.length(); |
} else if (specifiedSkeleton) { |
- UnicodeString skelField = specifiedSkeleton->original[typeValue]; |
- int32_t skelFieldLen = skelField.length(); |
+ int32_t skelFieldLen = specifiedSkeleton->original.getFieldLength(typeValue); |
UBool patFieldIsNumeric = (row->type > 0); |
UBool skelFieldIsNumeric = (specifiedSkeleton->type[typeValue] > 0); |
if (skelFieldLen == reqFieldLen || (patFieldIsNumeric && !skelFieldIsNumeric) || (skelFieldIsNumeric && !patFieldIsNumeric)) { |
@@ -1122,9 +1355,12 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, |
adjFieldLen = field.length(); |
} |
} |
- UChar c = (typeValue!= UDATPG_HOUR_FIELD && typeValue!= UDATPG_MONTH_FIELD && |
- typeValue!= UDATPG_WEEKDAY_FIELD && (typeValue!= UDATPG_YEAR_FIELD || reqField.charAt(0)==CAP_Y))? |
- reqField.charAt(0): field.charAt(0); |
+ UChar c = (typeValue!= UDATPG_HOUR_FIELD |
+ && typeValue!= UDATPG_MONTH_FIELD |
+ && typeValue!= UDATPG_WEEKDAY_FIELD |
+ && (typeValue!= UDATPG_YEAR_FIELD || reqFieldChar==CAP_Y)) |
+ ? reqFieldChar |
+ : field.charAt(0); |
if (typeValue == UDATPG_HOUR_FIELD && (flags & kDTPGSkeletonUsesCapJ) != 0) { |
c = fDefaultHourFormatChar; |
} |
@@ -1169,13 +1405,13 @@ DateTimePatternGenerator::getBestAppending(int32_t missingFields, int32_t flags, |
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; |
} |
} |
@@ -1487,36 +1723,19 @@ PatternMap::getPatternFromSkeleton(PtnSkeleton& skeleton, const PtnSkeleton** sp |
} |
// find boot entry |
- UChar baseChar='\0'; |
- for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
- if (skeleton.baseOriginal[i].length() !=0 ) { |
- baseChar = skeleton.baseOriginal[i].charAt(0); |
- break; |
- } |
- } |
- |
+ UChar baseChar = skeleton.getFirstChar(); |
if ((curElem=getHeader(baseChar))==NULL) { |
return NULL; // no match |
} |
do { |
- int32_t i=0; |
+ UBool equal; |
if (specifiedSkeletonPtr != NULL) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original |
- for (i=0; i<UDATPG_FIELD_COUNT; ++i) { |
- if (curElem->skeleton->original[i].compare(skeleton.original[i]) != 0 ) |
- { |
- break; |
- } |
- } |
+ equal = curElem->skeleton->original == skeleton.original; |
} else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal |
- for (i=0; i<UDATPG_FIELD_COUNT; ++i) { |
- if (curElem->skeleton->baseOriginal[i].compare(skeleton.baseOriginal[i]) != 0 ) |
- { |
- break; |
- } |
- } |
+ equal = curElem->skeleton->baseOriginal == skeleton.baseOriginal; |
} |
- if (i == UDATPG_FIELD_COUNT) { |
+ if (equal) { |
if (specifiedSkeletonPtr && curElem->skeletonWasSpecified) { |
*specifiedSkeletonPtr = curElem->skeleton; |
} |
@@ -1620,37 +1839,35 @@ void |
DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton& skeletonResult) { |
int32_t i; |
for (i=0; i<UDATPG_FIELD_COUNT; ++i) { |
- skeletonResult.type[i]=NONE; |
+ skeletonResult.type[i] = NONE; |
} |
fp->set(pattern); |
for (i=0; i < fp->itemNumber; i++) { |
- UnicodeString field = fp->items[i]; |
- if ( field.charAt(0) == LOW_A ) { |
+ const UnicodeString& value = fp->items[i]; |
+ if ( value.charAt(0) == LOW_A ) { |
continue; // skip 'a' |
} |
- if ( fp->isQuoteLiteral(field) ) { |
+ if ( fp->isQuoteLiteral(value) ) { |
UnicodeString quoteLiteral; |
fp->getQuoteLiteral(quoteLiteral, &i); |
continue; |
} |
- int32_t canonicalIndex = fp->getCanonicalIndex(field); |
+ int32_t canonicalIndex = fp->getCanonicalIndex(value); |
if (canonicalIndex < 0 ) { |
continue; |
} |
const dtTypeElem *row = &dtTypes[canonicalIndex]; |
- int32_t typeValue = row->field; |
- skeletonResult.original[typeValue]=field; |
+ int32_t field = row->field; |
+ skeletonResult.original.populate(field, value); |
UChar repeatChar = row->patternChar; |
int32_t repeatCount = row->minLen; // #7930 removes cap at 3 |
- while (repeatCount-- > 0) { |
- skeletonResult.baseOriginal[typeValue] += repeatChar; |
- } |
- int16_t subTypeValue = row->type; |
+ skeletonResult.baseOriginal.populate(field, repeatChar, repeatCount); |
+ int16_t subField = row->type; |
if ( row->type > 0) { |
- subTypeValue += field.length(); |
+ subField += value.length(); |
} |
- skeletonResult.type[typeValue] = subTypeValue; |
+ skeletonResult.type[field] = subField; |
} |
copyFrom(skeletonResult); |
} |
@@ -1658,23 +1875,13 @@ DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton |
void |
DateTimeMatcher::getBasePattern(UnicodeString &result ) { |
result.remove(); // Reset the result first. |
- for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) { |
- if (skeleton.baseOriginal[i].length()!=0) { |
- result += skeleton.baseOriginal[i]; |
- } |
- } |
+ skeleton.baseOriginal.appendTo(result); |
} |
UnicodeString |
DateTimeMatcher::getPattern() { |
UnicodeString result; |
- |
- for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) { |
- if (skeleton.original[i].length()!=0) { |
- result += skeleton.original[i]; |
- } |
- } |
- return result; |
+ return skeleton.original.appendTo(result); |
} |
int32_t |
@@ -1707,34 +1914,19 @@ DateTimeMatcher::getDistance(const DateTimeMatcher& other, int32_t includeMask, |
void |
DateTimeMatcher::copyFrom(const PtnSkeleton& newSkeleton) { |
- for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
- this->skeleton.type[i]=newSkeleton.type[i]; |
- this->skeleton.original[i]=newSkeleton.original[i]; |
- this->skeleton.baseOriginal[i]=newSkeleton.baseOriginal[i]; |
- } |
+ skeleton.copyFrom(newSkeleton); |
} |
void |
DateTimeMatcher::copyFrom() { |
// same as clear |
- for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
- this->skeleton.type[i]=0; |
- this->skeleton.original[i].remove(); |
- this->skeleton.baseOriginal[i].remove(); |
- } |
+ skeleton.clear(); |
} |
UBool |
DateTimeMatcher::equals(const DateTimeMatcher* other) const { |
- if (other==NULL) { |
- return FALSE; |
- } |
- for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
- if (this->skeleton.original[i]!=other->skeleton.original[i] ) { |
- return FALSE; |
- } |
- } |
- return TRUE; |
+ if (other==NULL) { return FALSE; } |
+ return skeleton.original == other->skeleton.original; |
} |
int32_t |
@@ -1828,7 +2020,7 @@ FormatParser::getCanonicalIndex(const UnicodeString& s, UBool strict) { |
} |
int32_t i = 0; |
int32_t bestRow = -1; |
- while (dtTypes[i].patternChar != '\0') { |
+ while (dtTypes[i].patternChar != 0x0000) { |
if ( dtTypes[i].patternChar != ch ) { |
++i; |
continue; |
@@ -2000,52 +2192,116 @@ PatternMapIterator::next() { |
return *matcher; |
} |
-PtnSkeleton::PtnSkeleton() { |
+ |
+SkeletonFields::SkeletonFields() { |
+ // Set initial values to zero |
+ clear(); |
} |
+void SkeletonFields::clear() { |
+ uprv_memset(chars, 0, sizeof(chars)); |
+ uprv_memset(lengths, 0, sizeof(lengths)); |
+} |
-PtnSkeleton::PtnSkeleton(const PtnSkeleton& other) { |
- for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
- this->type[i]=other.type[i]; |
- this->original[i]=other.original[i]; |
- this->baseOriginal[i]=other.baseOriginal[i]; |
+void SkeletonFields::copyFrom(const SkeletonFields& other) { |
+ uprv_memcpy(chars, other.chars, sizeof(chars)); |
+ uprv_memcpy(lengths, other.lengths, sizeof(lengths)); |
+} |
+ |
+void SkeletonFields::clearField(int32_t field) { |
+ chars[field] = 0; |
+ lengths[field] = 0; |
+} |
+ |
+UChar SkeletonFields::getFieldChar(int32_t field) const { |
+ return chars[field]; |
+} |
+ |
+int32_t SkeletonFields::getFieldLength(int32_t field) const { |
+ return lengths[field]; |
+} |
+ |
+void SkeletonFields::populate(int32_t field, const UnicodeString& value) { |
+ populate(field, value.charAt(0), value.length()); |
+} |
+ |
+void SkeletonFields::populate(int32_t field, UChar ch, int32_t length) { |
+ chars[field] = (int8_t) ch; |
+ lengths[field] = (int8_t) length; |
+} |
+ |
+UBool SkeletonFields::isFieldEmpty(int32_t field) const { |
+ return lengths[field] == 0; |
+} |
+ |
+UnicodeString& SkeletonFields::appendTo(UnicodeString& string) const { |
+ for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) { |
+ appendFieldTo(i, string); |
} |
+ return string; |
} |
-UBool |
-PtnSkeleton::equals(const PtnSkeleton& other) { |
- for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
- if ( (type[i]!= other.type[i]) || |
- (original[i]!=other.original[i]) || |
- (baseOriginal[i]!=other.baseOriginal[i]) ) { |
- return FALSE; |
+UnicodeString& SkeletonFields::appendFieldTo(int32_t field, UnicodeString& string) const { |
+ UChar ch(chars[field]); |
+ int32_t length = (int32_t) lengths[field]; |
+ |
+ for (int32_t i=0; i<length; i++) { |
+ string += ch; |
+ } |
+ return string; |
+} |
+ |
+UChar SkeletonFields::getFirstChar() const { |
+ for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) { |
+ if (lengths[i] != 0) { |
+ return chars[i]; |
} |
} |
- return TRUE; |
+ return '\0'; |
+} |
+ |
+ |
+PtnSkeleton::PtnSkeleton() { |
+} |
+ |
+PtnSkeleton::PtnSkeleton(const PtnSkeleton& other) { |
+ copyFrom(other); |
+} |
+ |
+void PtnSkeleton::copyFrom(const PtnSkeleton& other) { |
+ uprv_memcpy(type, other.type, sizeof(type)); |
+ original.copyFrom(other.original); |
+ baseOriginal.copyFrom(other.baseOriginal); |
+} |
+ |
+void PtnSkeleton::clear() { |
+ uprv_memset(type, 0, sizeof(type)); |
+ original.clear(); |
+ baseOriginal.clear(); |
+} |
+ |
+UBool |
+PtnSkeleton::equals(const PtnSkeleton& other) const { |
+ return (original == other.original) |
+ && (baseOriginal == other.baseOriginal) |
+ && (uprv_memcmp(type, other.type, sizeof(type)) == 0); |
} |
UnicodeString |
-PtnSkeleton::getSkeleton() { |
+PtnSkeleton::getSkeleton() const { |
UnicodeString result; |
- |
- for(int32_t i=0; i< UDATPG_FIELD_COUNT; ++i) { |
- if (original[i].length()!=0) { |
- result += original[i]; |
- } |
- } |
- return result; |
+ return original.appendTo(result); |
} |
UnicodeString |
-PtnSkeleton::getBaseSkeleton() { |
+PtnSkeleton::getBaseSkeleton() const { |
UnicodeString result; |
+ return baseOriginal.appendTo(result); |
+} |
- for(int32_t i=0; i< UDATPG_FIELD_COUNT; ++i) { |
- if (baseOriginal[i].length()!=0) { |
- result += baseOriginal[i]; |
- } |
- } |
- return result; |
+UChar |
+PtnSkeleton::getFirstChar() const { |
+ return baseOriginal.getFirstChar(); |
} |
PtnSkeleton::~PtnSkeleton() { |