/*
*******************************************************************************
-* Copyright (C) 2007-2011, International Business Machines Corporation and
+* Copyright (C) 2007-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
#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"
#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
/**
/* Must be C linkage to pass function pointer to the sort function */
-#if !defined (OS390) && !defined (OS400)
-extern "C"
-#endif
+U_CDECL_BEGIN
+
static int32_t U_CALLCONV
ures_a_codepointSort(const void *context, const void *left, const void *right) {
//CompareContext *cmp=(CompareContext *)context;
((const UResAEntry *)right)->key);
}
+U_CDECL_END
static void ures_a_open(UResourceBundleAIterator *aiter, UResourceBundle *bund, UErrorCode *status) {
if(U_FAILURE(*status)) {
U_NAMESPACE_BEGIN
-
// *****************************************************************************
// class DateTimePatternGenerator
// *****************************************************************************
{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},
{CAP_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC, 1, 2},
{CAP_Q, UDATPG_QUARTER_FIELD, DT_SHORT, 3, 0},
{CAP_Q, UDATPG_QUARTER_FIELD, DT_LONG, 4, 0},
{CAP_L, UDATPG_MONTH_FIELD, DT_SHORT - DT_DELTA, 3, 0},
{CAP_L, UDATPG_MONTH_FIELD, DT_LONG - DT_DELTA, 4, 0},
{CAP_L, UDATPG_MONTH_FIELD, DT_NARROW - DT_DELTA, 5, 0},
+ {LOW_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 1},
{LOW_W, UDATPG_WEEK_OF_YEAR_FIELD, DT_NUMERIC, 1, 2},
{CAP_W, UDATPG_WEEK_OF_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 0},
{CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORT, 1, 3},
{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},
{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[]
};
};
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"
};
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";
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<DateTimePatternGenerator> result(
+ new DateTimePatternGenerator(locale, status), status);
+ return U_SUCCESS(status) ? result.orphan() : NULL;
}
DateTimePatternGenerator* U_EXPORT2
DateTimePatternGenerator&
DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) {
+ // reflexive case
+ if (&other == this) {
+ return *this;
+ }
pLocale = other.pLocale;
fDefaultHourFormatChar = other.fDefaultHourFormatChar;
*fp = *(other.fp);
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
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<int32_t *>(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<int32_t *>(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<char *>(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<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*/) {
- 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
DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& status) {
UnicodeString dfPattern;
UnicodeString conflictingString;
- UDateTimePatternConflict conflictingStatus;
DateFormat* df;
if (U_FAILURE(status)) {
df = DateFormat::createDateInstance(style, locale);
SimpleDateFormat* sdf;
if (df != NULL && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != NULL) {
- conflictingStatus = addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status);
+ addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status);
}
// TODO Maybe we should return an error when the date format isn't simple.
delete df;
df = DateFormat::createTimeInstance(style, locale);
if (df != NULL && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != NULL) {
- conflictingStatus = addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status);
+ addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status);
// HACK for hh:ss
if ( i==DateFormat::kMedium ) {
hackPattern = dfPattern;
void
DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode& status) {
- UDateTimePatternConflict conflictingStatus;
UnicodeString conflictingString;
fp->set(hackPattern);
break;
}
mmss+= field;
- conflictingStatus = addPattern(mmss, FALSE, conflictingString, status);
+ addPattern(mmss, FALSE, conflictingString, status);
break;
}
else {
UResourceBundle *patBundle, *fieldBundle, *fBundle;
UnicodeString rbPattern, value, field;
UnicodeString conflictingPattern;
- UDateTimePatternConflict conflictingStatus;
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; i<UDATPG_FIELD_COUNT; ++i ) {
appendItemNames[i]=CAP_F;
key=NULL;
err = U_ZERO_ERROR;
- fBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimeFieldsTag, NULL, &err);
+ 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);
ures_close(fBundle);
// add available formats
+ UBool firstTimeThrough = TRUE;
err = U_ZERO_ERROR;
initHashtable(err);
- 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);
- setAvailableFormat(retKey, err);
- // Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
- // but not a previous availableFormats entry:
- conflictingStatus = addPatternWithSkeleton(format, &retKey, TRUE, conflictingPattern, err);
- }
-#if defined(U_USE_ASCII_BUNDLE_ITERATOR)
- ures_a_close(&aiter);
-#endif
- }
- ures_close(patBundle);
- ures_close(calTypeBundle);
- ures_close(calBundle);
- ures_close(rb);
-
- err = U_ZERO_ERROR;
- char parentLocale[50];
- int32_t localeNameLen=0;
- uprv_strcpy(parentLocale, curLocaleName);
- while((localeNameLen=uloc_getParent(parentLocale, parentLocale, 50, &err))>=0 ) {
- rb = ures_open(NULL, parentLocale, &err);
- curLocaleName=ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &err);
- uprv_strcpy(parentLocale, curLocaleName);
- calBundle = ures_getByKey(rb, DT_DateTimeCalendarTag, NULL, &err);
- calTypeBundle = ures_getByKey(calBundle, calendarTypeToUse, NULL, &err);
- patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimeAvailableFormatsTag, NULL, &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)) {
- int32_t numberKeys = ures_getSize(patBundle);
- int32_t len;
- const UChar *retPattern;
- key=NULL;
+ // 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);
+ UResourceBundleAIterator aiter;
+ ures_a_open(&aiter, patBundle, &err);
#endif
- for(i=0; i<numberKeys; ++i) {
+ for(i=0; i<numberKeys; ++i) {
#if defined(U_USE_ASCII_BUNDLE_ITERATOR)
- retPattern=ures_a_getNextString(&aiter, &len, &key, &err);
+ retPattern=ures_a_getNextString(&aiter, &len, &key, &err);
#else
- retPattern=ures_getNextString(patBundle, &len, &key, &err);
+ retPattern=ures_getNextString(patBundle, &len, &key, &err);
#endif
- UnicodeString format=UnicodeString(retPattern);
- UnicodeString retKey=UnicodeString(key, -1, US_INV);
- if ( !isAvailableFormatSet(retKey) ) {
- setAvailableFormat(retKey, err);
- // Add pattern with its associated skeleton. Override any duplicate derived from std patterns,
- // but not a previous availableFormats entry:
- conflictingStatus = addPatternWithSkeleton(format, &retKey, TRUE, conflictingPattern, err);
+ 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);
+ }
}
- }
#if defined(U_USE_ASCII_BUNDLE_ITERATOR)
- ures_a_close(&aiter);
+ ures_a_close(&aiter);
#endif
+ ures_close(patBundle);
+ }
+ 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;
}
- err = U_ZERO_ERROR; // reset; if this locale lacks the necessary data, need to keep checking up to root.
- ures_close(patBundle);
- ures_close(calTypeBundle);
- ures_close(calBundle);
+ // 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;
+ }
+ }
+ // Close current locale bundle
ures_close(rb);
- if (localeNameLen==0) {
+ // 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);
+ }
+ }
+ // Go to the top of the loop to process contents of calTypeBundle
}
if (hackPattern.length()>0) {
const UnicodeString *bestPattern=NULL;
UnicodeString dtFormat;
UnicodeString resultPattern;
+ int32_t flags = kDTPGNoFlags;
int32_t dateMask=(1<<UDATPG_DAYPERIOD_FIELD) - 1;
int32_t timeMask=(1<<UDATPG_FIELD_COUNT) - 1 - dateMask;
+ // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
UnicodeString patternFormCopy = UnicodeString(patternForm);
- patternFormCopy.findAndReplace(UnicodeString(LOW_J), UnicodeString(fDefaultHourFormatChar));
+ UChar hourFormatSkeletonCharForLowJ = fDefaultHourFormatChar;
+ switch (options & UADATPG_FORCE_HOUR_CYCLE_MASK) {
+ case UADATPG_FORCE_12_HOUR_CYCLE: hourFormatSkeletonCharForLowJ = LOW_H; break;
+ case UADATPG_FORCE_24_HOUR_CYCLE: hourFormatSkeletonCharForLowJ = CAP_H; break;
+ default: break;
+ }
+ int32_t patPos, patLen = patternFormCopy.length();
+ UBool inQuoted = FALSE;
+ for (patPos = 0; patPos < patLen; patPos++) {
+ UChar patChr = patternFormCopy.charAt(patPos);
+ if (patChr == SINGLE_QUOTE) {
+ inQuoted = !inQuoted;
+ } else if (!inQuoted) {
+ if (patChr == LOW_J) {
+ patternFormCopy.setCharAt(patPos, hourFormatSkeletonCharForLowJ);
+ } 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 in adjustFieldTypes
+ // replace hour pattern characters as necessary.
+ patternFormCopy.setCharAt(patPos, CAP_H);
+ flags |= kDTPGSkeletonUsesCapJ;
+ }
+ }
+ }
resultPattern.remove();
dtMatcher->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();
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;
}
UDateTimePatternMatchOptions options,
UErrorCode& /*status*/) {
dtMatcher->set(skeleton, fp);
- UnicodeString result = adjustFieldTypes(pattern, NULL, FALSE, options);
+ UnicodeString result = adjustFieldTypes(pattern, NULL, kDTPGNoFlags, options);
return result;
}
void
DateTimePatternGenerator::addCanonicalItems() {
UnicodeString conflictingPattern;
- UDateTimePatternConflict conflictingStatus;
UErrorCode status = U_ZERO_ERROR;
for (int32_t i=0; i<UDATPG_FIELD_COUNT; i++) {
- conflictingStatus = addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status);
+ addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status);
}
}
matcher.set(pattern, fp, skeleton);
matcher.getBasePattern(basePattern);
} else {
- matcher.set(*skeletonToUse, fp, skeleton); // this still trims skeleton fields to max len 3, may need to change it.
+ 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 ) {
UnicodeString
DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
const PtnSkeleton* specifiedSkeleton,
- UBool fixFractionalSeconds,
+ int32_t flags,
UDateTimePatternMatchOptions options) {
UnicodeString newPattern;
fp->set(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) {
//
// The adjusted field should consist of characters from the originally requested
// skeleton, except in the case of UDATPG_HOUR_FIELD or UDATPG_MONTH_FIELD or
- // UDATPG_WEEKDAY_FIELD, in which case it should consist of characters from the
- // found pattern.
+ // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist
+ // of characters from the found pattern.
//
// The length of the adjusted field (adjFieldLen) should match that in the originally
// requested skeleton, except that in the following cases the length of the adjusted field
adjFieldLen = field.length();
}
}
- UChar c = (typeValue!= UDATPG_HOUR_FIELD && typeValue!= UDATPG_MONTH_FIELD && typeValue!= UDATPG_WEEKDAY_FIELD)?
+ 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);
+ 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;
}
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;
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;
}
}
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;
}
}
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){
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
-
+ curElem->skeletonWasSpecified = otherElem->skeletonWasSpecified;
if (prevElem!=NULL) {
prevElem->next=curElem;
}
}
// 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
DateTimeMatcher::DateTimeMatcher(void) {
}
+DateTimeMatcher::~DateTimeMatcher() {}
+
DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher& other) {
copyFrom(other.skeleton);
}
int32_t typeValue = row->field;
skeletonResult.original[typeValue]=field;
UChar repeatChar = row->patternChar;
- int32_t repeatCount = row->minLen > 3 ? 3: row->minLen;
+ int32_t repeatCount = row->minLen; // #7930 removes cap at 3
while (repeatCount-- > 0) {
skeletonResult.baseOriginal[typeValue] += repeatChar;
}
}
UBool
-FormatParser::isQuoteLiteral(const UnicodeString& s) const {
+FormatParser::isQuoteLiteral(const UnicodeString& s) {
return (UBool)(s.charAt(0)==SINGLE_QUOTE);
}
return TRUE;
}
+DistanceInfo::~DistanceInfo() {}
+
void
DistanceInfo::setTo(DistanceInfo &other) {
missingFieldMask = other.missingFieldMask;