Index: source/i18n/smpdtfmt.cpp |
diff --git a/source/i18n/smpdtfmt.cpp b/source/i18n/smpdtfmt.cpp |
index 901664115cc773d2b57651e90f2de01a8c12871c..01c9f7c7981b40e3e62b06091baa559c8c2909a5 100644 |
--- a/source/i18n/smpdtfmt.cpp |
+++ b/source/i18n/smpdtfmt.cpp |
@@ -1,6 +1,6 @@ |
/* |
******************************************************************************* |
-* Copyright (C) 1997-2014, International Business Machines Corporation and * |
+* Copyright (C) 1997-2015, International Business Machines Corporation and * |
* others. All Rights Reserved. * |
******************************************************************************* |
* |
@@ -60,6 +60,8 @@ |
#include "umutex.h" |
#include <float.h> |
#include "smpdtfst.h" |
+#include "sharednumberformat.h" |
+#include "ustr_imp.h" |
#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) |
#include <stdio.h> |
@@ -71,8 +73,6 @@ |
U_NAMESPACE_BEGIN |
-static const UChar PATTERN_CHAR_BASE = 0x40; |
- |
/** |
* Last-resort string to use for "GMT" when constructing time zone strings. |
*/ |
@@ -181,7 +181,7 @@ static const int32_t gFieldRangeBias[] = { |
-1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD |
-1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD |
0, // 'm' - UDAT_MINUTE_FIELD |
- 0, // 's' - UDAT_SEOND_FIELD |
+ 0, // 's' - UDAT_SECOND_FIELD |
-1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?) |
-1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?) |
-1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?) |
@@ -209,6 +209,11 @@ static const int32_t gFieldRangeBias[] = { |
-1, // 'X' - UDAT_TIMEZONE_ISO_FIELD |
-1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD |
-1, // 'r' - UDAT_RELATED_YEAR_FIELD |
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR |
+ -1, // ':' - UDAT_TIME_SEPARATOR_FIELD |
+#else |
+ -1, // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD |
+#endif |
}; |
// When calendar uses hebr numbering (i.e. he@calendar=hebrew), |
@@ -220,25 +225,149 @@ static UMutex LOCK = U_MUTEX_INITIALIZER; |
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat) |
+SimpleDateFormat::NSOverride::~NSOverride() { |
+ if (snf != NULL) { |
+ snf->removeRef(); |
+ } |
+} |
+ |
+ |
+void SimpleDateFormat::NSOverride::free() { |
+ NSOverride *cur = this; |
+ while (cur) { |
+ NSOverride *next = cur->next; |
+ delete cur; |
+ cur = next; |
+ } |
+} |
+ |
+// no matter what the locale's default number format looked like, we want |
+// to modify it so that it doesn't use thousands separators, doesn't always |
+// show the decimal point, and recognizes integers only when parsing |
+static void fixNumberFormatForDates(NumberFormat &nf) { |
+ nf.setGroupingUsed(FALSE); |
+ DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(&nf); |
+ if (decfmt != NULL) { |
+ decfmt->setDecimalSeparatorAlwaysShown(FALSE); |
+ } |
+ nf.setParseIntegerOnly(TRUE); |
+ nf.setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00" |
+} |
+ |
+static const SharedNumberFormat *createSharedNumberFormat( |
+ NumberFormat *nfToAdopt) { |
+ fixNumberFormatForDates(*nfToAdopt); |
+ const SharedNumberFormat *result = new SharedNumberFormat(nfToAdopt); |
+ if (result == NULL) { |
+ delete nfToAdopt; |
+ } |
+ return result; |
+} |
+ |
+static const SharedNumberFormat *createSharedNumberFormat( |
+ const Locale &loc, UErrorCode &status) { |
+ NumberFormat *nf = NumberFormat::createInstance(loc, status); |
+ if (U_FAILURE(status)) { |
+ return NULL; |
+ } |
+ const SharedNumberFormat *result = createSharedNumberFormat(nf); |
+ if (result == NULL) { |
+ status = U_MEMORY_ALLOCATION_ERROR; |
+ } |
+ return result; |
+} |
+ |
+static const SharedNumberFormat **allocSharedNumberFormatters() { |
+ const SharedNumberFormat **result = (const SharedNumberFormat**) |
+ uprv_malloc(UDAT_FIELD_COUNT * sizeof(const SharedNumberFormat*)); |
+ if (result == NULL) { |
+ return NULL; |
+ } |
+ for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) { |
+ result[i] = NULL; |
+ } |
+ return result; |
+} |
+ |
+static void freeSharedNumberFormatters(const SharedNumberFormat ** list) { |
+ for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) { |
+ SharedObject::clearPtr(list[i]); |
+ } |
+ uprv_free(list); |
+} |
+ |
+const NumberFormat *SimpleDateFormat::getNumberFormatByIndex( |
+ UDateFormatField index) const { |
+ if (fSharedNumberFormatters == NULL || |
+ fSharedNumberFormatters[index] == NULL) { |
+ return fNumberFormat; |
+ } |
+ return &(**fSharedNumberFormatters[index]); |
+} |
+ |
+class SimpleDateFormatMutableNFNode { |
+ public: |
+ const NumberFormat *key; |
+ NumberFormat *value; |
+ SimpleDateFormatMutableNFNode() |
+ : key(NULL), value(NULL) { } |
+ ~SimpleDateFormatMutableNFNode() { |
+ delete value; |
+ } |
+ private: |
+ SimpleDateFormatMutableNFNode(const SimpleDateFormatMutableNFNode &); |
+ SimpleDateFormatMutableNFNode &operator=(const SimpleDateFormatMutableNFNode &); |
+}; |
+ |
+// Single threaded cache of non const NumberFormats. Designed to be stack |
+// allocated and used for a single format call. |
+class SimpleDateFormatMutableNFs : public UMemory { |
+ public: |
+ SimpleDateFormatMutableNFs() { |
+ } |
+ |
+ // Returns a non-const clone of nf which can be safely modified. |
+ // Subsequent calls with same nf will return the same non-const clone. |
+ // This object maintains ownership of all returned non-const |
+ // NumberFormat objects. On memory allocation error returns NULL. |
+ // Caller must check for NULL return value. |
+ NumberFormat *get(const NumberFormat *nf) { |
+ if (nf == NULL) { |
+ return NULL; |
+ } |
+ int32_t idx = 0; |
+ while (nodes[idx].value) { |
+ if (nf == nodes[idx].key) { |
+ return nodes[idx].value; |
+ } |
+ ++idx; |
+ } |
+ U_ASSERT(idx < UDAT_FIELD_COUNT); |
+ nodes[idx].key = nf; |
+ nodes[idx].value = (NumberFormat *) nf->clone(); |
+ return nodes[idx].value; |
+ } |
+ private: |
+ // +1 extra for sentinel. If each field had its own NumberFormat, this |
+ // cache would have to allocate UDAT_FIELD_COUNT mutable versions worst |
+ // case. |
+ SimpleDateFormatMutableNFNode nodes[UDAT_FIELD_COUNT + 1]; |
+ SimpleDateFormatMutableNFs(const SimpleDateFormatMutableNFs &); |
+ SimpleDateFormatMutableNFs &operator=(const SimpleDateFormatMutableNFs &); |
+}; |
+ |
//---------------------------------------------------------------------- |
SimpleDateFormat::~SimpleDateFormat() |
{ |
delete fSymbols; |
- if (fNumberFormatters) { |
- uprv_free(fNumberFormatters); |
+ if (fSharedNumberFormatters) { |
+ freeSharedNumberFormatters(fSharedNumberFormatters); |
} |
if (fTimeZoneFormat) { |
delete fTimeZoneFormat; |
} |
- while (fOverrideList) { |
- NSOverride *cur = fOverrideList; |
- fOverrideList = cur->next; |
- delete cur->nf; |
- uprv_free(cur); |
- } |
- |
#if !UCONFIG_NO_BREAK_ITERATION |
delete fCapitalizationBrkIter; |
#endif |
@@ -250,8 +379,7 @@ SimpleDateFormat::SimpleDateFormat(UErrorCode& status) |
: fLocale(Locale::getDefault()), |
fSymbols(NULL), |
fTimeZoneFormat(NULL), |
- fNumberFormatters(NULL), |
- fOverrideList(NULL), |
+ fSharedNumberFormatters(NULL), |
fCapitalizationBrkIter(NULL) |
{ |
initializeBooleanAttributes(); |
@@ -267,14 +395,14 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
fLocale(Locale::getDefault()), |
fSymbols(NULL), |
fTimeZoneFormat(NULL), |
- fNumberFormatters(NULL), |
- fOverrideList(NULL), |
+ fSharedNumberFormatters(NULL), |
fCapitalizationBrkIter(NULL) |
{ |
fDateOverride.setToBogus(); |
fTimeOverride.setToBogus(); |
initializeBooleanAttributes(); |
- initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); |
+ initializeCalendar(NULL,fLocale,status); |
+ fSymbols = DateFormatSymbols::createForLocale(fLocale, status); |
initialize(fLocale, status); |
initializeDefaultCentury(); |
@@ -288,14 +416,14 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
fLocale(Locale::getDefault()), |
fSymbols(NULL), |
fTimeZoneFormat(NULL), |
- fNumberFormatters(NULL), |
- fOverrideList(NULL), |
+ fSharedNumberFormatters(NULL), |
fCapitalizationBrkIter(NULL) |
{ |
fDateOverride.setTo(override); |
fTimeOverride.setToBogus(); |
initializeBooleanAttributes(); |
- initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); |
+ initializeCalendar(NULL,fLocale,status); |
+ fSymbols = DateFormatSymbols::createForLocale(fLocale, status); |
initialize(fLocale, status); |
initializeDefaultCentury(); |
@@ -311,8 +439,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
: fPattern(pattern), |
fLocale(locale), |
fTimeZoneFormat(NULL), |
- fNumberFormatters(NULL), |
- fOverrideList(NULL), |
+ fSharedNumberFormatters(NULL), |
fCapitalizationBrkIter(NULL) |
{ |
@@ -320,7 +447,8 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
fTimeOverride.setToBogus(); |
initializeBooleanAttributes(); |
- initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); |
+ initializeCalendar(NULL,fLocale,status); |
+ fSymbols = DateFormatSymbols::createForLocale(fLocale, status); |
initialize(fLocale, status); |
initializeDefaultCentury(); |
} |
@@ -334,8 +462,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
: fPattern(pattern), |
fLocale(locale), |
fTimeZoneFormat(NULL), |
- fNumberFormatters(NULL), |
- fOverrideList(NULL), |
+ fSharedNumberFormatters(NULL), |
fCapitalizationBrkIter(NULL) |
{ |
@@ -343,7 +470,8 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
fTimeOverride.setToBogus(); |
initializeBooleanAttributes(); |
- initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); |
+ initializeCalendar(NULL,fLocale,status); |
+ fSymbols = DateFormatSymbols::createForLocale(fLocale, status); |
initialize(fLocale, status); |
initializeDefaultCentury(); |
@@ -360,8 +488,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
fLocale(Locale::getDefault()), |
fSymbols(symbolsToAdopt), |
fTimeZoneFormat(NULL), |
- fNumberFormatters(NULL), |
- fOverrideList(NULL), |
+ fSharedNumberFormatters(NULL), |
fCapitalizationBrkIter(NULL) |
{ |
@@ -383,8 +510,7 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, |
fLocale(Locale::getDefault()), |
fSymbols(new DateFormatSymbols(symbols)), |
fTimeZoneFormat(NULL), |
- fNumberFormatters(NULL), |
- fOverrideList(NULL), |
+ fSharedNumberFormatters(NULL), |
fCapitalizationBrkIter(NULL) |
{ |
@@ -407,8 +533,7 @@ SimpleDateFormat::SimpleDateFormat(EStyle timeStyle, |
: fLocale(locale), |
fSymbols(NULL), |
fTimeZoneFormat(NULL), |
- fNumberFormatters(NULL), |
- fOverrideList(NULL), |
+ fSharedNumberFormatters(NULL), |
fCapitalizationBrkIter(NULL) |
{ |
initializeBooleanAttributes(); |
@@ -431,13 +556,13 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale, |
fLocale(locale), |
fSymbols(NULL), |
fTimeZoneFormat(NULL), |
- fNumberFormatters(NULL), |
- fOverrideList(NULL), |
+ fSharedNumberFormatters(NULL), |
fCapitalizationBrkIter(NULL) |
{ |
if (U_FAILURE(status)) return; |
initializeBooleanAttributes(); |
- initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status); |
+ initializeCalendar(NULL, fLocale, status); |
+ fSymbols = DateFormatSymbols::createForLocale(fLocale, status); |
if (U_FAILURE(status)) |
{ |
status = U_ZERO_ERROR; |
@@ -467,8 +592,7 @@ SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other) |
fLocale(other.fLocale), |
fSymbols(NULL), |
fTimeZoneFormat(NULL), |
- fNumberFormatters(NULL), |
- fOverrideList(NULL), |
+ fSharedNumberFormatters(NULL), |
fCapitalizationBrkIter(NULL) |
{ |
initializeBooleanAttributes(); |
@@ -483,6 +607,8 @@ SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other) |
return *this; |
} |
DateFormat::operator=(other); |
+ fDateOverride = other.fDateOverride; |
+ fTimeOverride = other.fTimeOverride; |
delete fSymbols; |
fSymbols = NULL; |
@@ -509,6 +635,21 @@ SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other) |
} |
#endif |
+ if (fSharedNumberFormatters != NULL) { |
+ freeSharedNumberFormatters(fSharedNumberFormatters); |
+ fSharedNumberFormatters = NULL; |
+ } |
+ if (other.fSharedNumberFormatters != NULL) { |
+ fSharedNumberFormatters = allocSharedNumberFormatters(); |
+ if (fSharedNumberFormatters) { |
+ for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) { |
+ SharedObject::copyPtr( |
+ other.fSharedNumberFormatters[i], |
+ fSharedNumberFormatters[i]); |
+ } |
+ } |
+ } |
+ |
return *this; |
} |
@@ -570,7 +711,7 @@ void SimpleDateFormat::construct(EStyle timeStyle, |
ures_getLocaleByType(dateTimePatterns, ULOC_ACTUAL_LOCALE, &status)); |
// create a symbols object from the locale |
- initializeSymbols(locale,fCalendar, status); |
+ fSymbols = DateFormatSymbols::createForLocale(locale, status); |
if (U_FAILURE(status)) return; |
/* test for NULL */ |
if (fSymbols == 0) { |
@@ -741,29 +882,10 @@ SimpleDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, |
if(!U_FAILURE(status)) { |
fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status); |
} |
- if (U_SUCCESS(status) && fCalendar == NULL) { |
- status = U_MEMORY_ALLOCATION_ERROR; |
- } |
return fCalendar; |
} |
void |
-SimpleDateFormat::initializeSymbols(const Locale& locale, Calendar* calendar, UErrorCode& status) |
-{ |
- if(U_FAILURE(status)) { |
- fSymbols = NULL; |
- } else { |
- // pass in calendar type - use NULL (default) if no calendar set (or err). |
- fSymbols = new DateFormatSymbols(locale, calendar?calendar->getType() :NULL , status); |
- // Null pointer check |
- if (fSymbols == NULL) { |
- status = U_MEMORY_ALLOCATION_ERROR; |
- return; |
- } |
- } |
-} |
- |
-void |
SimpleDateFormat::initialize(const Locale& locale, |
UErrorCode& status) |
{ |
@@ -774,18 +896,7 @@ SimpleDateFormat::initialize(const Locale& locale, |
fNumberFormat = NumberFormat::createInstance(locale, status); |
if (fNumberFormat != NULL && U_SUCCESS(status)) |
{ |
- // no matter what the locale's default number format looked like, we want |
- // to modify it so that it doesn't use thousands separators, doesn't always |
- // show the decimal point, and recognizes integers only when parsing |
- |
- fNumberFormat->setGroupingUsed(FALSE); |
- DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fNumberFormat); |
- if (decfmt != NULL) { |
- decfmt->setDecimalSeparatorAlwaysShown(FALSE); |
- } |
- fNumberFormat->setParseIntegerOnly(TRUE); |
- fNumberFormat->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00" |
- |
+ fixNumberFormatForDates(*fNumberFormat); |
//fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse |
initNumberFormatters(locale,status); |
@@ -823,7 +934,7 @@ void SimpleDateFormat::initializeBooleanAttributes() |
setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status); |
setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status); |
- setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, true, status); |
+ setBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, true, status); |
setBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, true, status); |
} |
@@ -901,6 +1012,11 @@ SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, |
int32_t fieldNum = 0; |
UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); |
+ // Create temporary cache of mutable number format objects. This way |
+ // subFormat won't have to clone the const NumberFormat for each field. |
+ // if several fields share the same NumberFormat, which will almost |
+ // always be the case, this is a big save. |
+ SimpleDateFormatMutableNFs mutableNFs; |
// loop through the pattern string character by character |
for (int32_t i = 0; i < fPattern.length() && U_SUCCESS(status); ++i) { |
UChar ch = fPattern[i]; |
@@ -908,7 +1024,7 @@ SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, |
// Use subFormat() to format a repeated pattern character |
// when a different pattern or non-pattern character is seen |
if (ch != prevCh && count > 0) { |
- subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, status); |
+ subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, mutableNFs, status); |
count = 0; |
} |
if (ch == QUOTE) { |
@@ -921,8 +1037,7 @@ SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, |
inQuote = ! inQuote; |
} |
} |
- else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) |
- || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { |
+ else if (!inQuote && isSyntaxChar(ch)) { |
// ch is a date-time pattern character to be interpreted |
// by subFormat(); count the number of times it is repeated |
prevCh = ch; |
@@ -936,7 +1051,7 @@ SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, |
// Format the last item in the pattern, if any |
if (count > 0) { |
- subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, status); |
+ subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, mutableNFs, status); |
} |
if (calClone != NULL) { |
@@ -967,23 +1082,79 @@ SimpleDateFormat::fgCalendarFieldToLevel[] = |
/*A?.*/ 40, 0, 0 |
}; |
- |
-/* Map calendar field LETTER into calendar field level. |
- * the larger the level, the smaller the field unit. |
- * NOTE: if new fields adds in, the table needs to update. |
- */ |
-const int32_t |
-SimpleDateFormat::fgPatternCharToLevel[] = { |
- // A B C D E F G H I J K L M N O |
- -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0, |
- // P Q R S T U V W X Y Z |
- -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1, |
- // a b c d e f g h i j k l m n o |
- -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, -1, 60, -1, -1, |
- // p q r s t u v w x y z |
- -1, 20, 10, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1 |
-}; |
- |
+int32_t SimpleDateFormat::getLevelFromChar(UChar ch) { |
+ // Map date field LETTER into calendar field level. |
+ // the larger the level, the smaller the field unit. |
+ // NOTE: if new fields adds in, the table needs to update. |
+ static const int32_t mapCharToLevel[] = { |
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
+ // |
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
+ // ! " # $ % & ' ( ) * + , - . / |
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR |
+ // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? |
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, |
+#else |
+ // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? |
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
+#endif |
+ // @ A B C D E F G H I J K L M N O |
+ -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0, |
+ // P Q R S T U V W X Y Z [ \ ] ^ _ |
+ -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1, |
+ // ` a b c d e f g h i j k l m n o |
+ -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, 0, 60, -1, -1, |
+ // p q r s t u v w x y z { | } ~ |
+ -1, 20, 10, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1 |
+ }; |
+ |
+ return ch < UPRV_LENGTHOF(mapCharToLevel) ? mapCharToLevel[ch] : -1; |
+} |
+ |
+UBool SimpleDateFormat::isSyntaxChar(UChar ch) { |
+ static const UBool mapCharToIsSyntax[] = { |
+ // |
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, |
+ // |
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, |
+ // |
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, |
+ // |
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, |
+ // ! " # $ % & ' |
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, |
+ // ( ) * + , - . / |
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, |
+ // 0 1 2 3 4 5 6 7 |
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, |
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR |
+ // 8 9 : ; < = > ? |
+ FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, |
+#else |
+ // 8 9 : ; < = > ? |
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, |
+#endif |
+ // @ A B C D E F G |
+ FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, |
+ // H I J K L M N O |
+ TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, |
+ // P Q R S T U V W |
+ TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, |
+ // X Y Z [ \ ] ^ _ |
+ TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, |
+ // ` a b c d e f g |
+ FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, |
+ // h i j k l m n o |
+ TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, |
+ // p q r s t u v w |
+ TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, |
+ // x y z { | } ~ |
+ TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE |
+ }; |
+ |
+ return ch < UPRV_LENGTHOF(mapCharToIsSyntax) ? mapCharToIsSyntax[ch] : FALSE; |
+} |
// Map index into pattern character string to Calendar field number. |
const UCalendarDateFields |
@@ -1007,6 +1178,11 @@ SimpleDateFormat::fgPatternIndexToCalendarField[] = |
/*O*/ UCAL_ZONE_OFFSET, |
/*Xx*/ UCAL_ZONE_OFFSET, UCAL_ZONE_OFFSET, |
/*r*/ UCAL_EXTENDED_YEAR, |
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR |
+ /*:*/ UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */ |
+#else |
+ /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */ |
+#endif |
}; |
// Map index into pattern character string to DateFormat field number |
@@ -1030,6 +1206,11 @@ SimpleDateFormat::fgPatternIndexToDateFormatField[] = { |
/*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD, |
/*Xx*/ UDAT_TIMEZONE_ISO_FIELD, UDAT_TIMEZONE_ISO_LOCAL_FIELD, |
/*r*/ UDAT_RELATED_YEAR_FIELD, |
+#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR |
+ /*:*/ UDAT_TIME_SEPARATOR_FIELD, |
+#else |
+ /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UDAT_TIME_SEPARATOR_FIELD, |
+#endif |
}; |
//---------------------------------------------------------------------- |
@@ -1073,13 +1254,9 @@ SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) |
return; |
} |
umtx_lock(&LOCK); |
- if (fNumberFormatters == NULL) { |
- fNumberFormatters = (NumberFormat**)uprv_malloc(UDAT_FIELD_COUNT * sizeof(NumberFormat*)); |
- if (fNumberFormatters) { |
- for (int32_t i = 0; i < UDAT_FIELD_COUNT; i++) { |
- fNumberFormatters[i] = fNumberFormat; |
- } |
- } else { |
+ if (fSharedNumberFormatters == NULL) { |
+ fSharedNumberFormatters = allocSharedNumberFormatters(); |
+ if (fSharedNumberFormatters == NULL) { |
status = U_MEMORY_ALLOCATION_ERROR; |
} |
} |
@@ -1099,13 +1276,12 @@ SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeStrin |
return; |
} |
- U_ASSERT(fNumberFormatters != NULL); |
- |
int32_t start = 0; |
int32_t len; |
UnicodeString nsName; |
UnicodeString ovrField; |
UBool moreToProcess = TRUE; |
+ NSOverride *overrideList = NULL; |
while (moreToProcess) { |
int32_t delimiterPosition = str.indexOf((UChar)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE,start); |
@@ -1127,55 +1303,42 @@ SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeStrin |
int32_t nsNameHash = nsName.hashCode(); |
// See if the numbering system is in the override list, if not, then add it. |
- NSOverride *cur = fOverrideList; |
- NumberFormat *nf = NULL; |
+ NSOverride *cur = overrideList; |
+ const SharedNumberFormat *snf = NULL; |
UBool found = FALSE; |
while ( cur && !found ) { |
if ( cur->hash == nsNameHash ) { |
- nf = cur->nf; |
+ snf = cur->snf; |
found = TRUE; |
} |
cur = cur->next; |
} |
if (!found) { |
- cur = (NSOverride *)uprv_malloc(sizeof(NSOverride)); |
- if (cur) { |
+ LocalPointer<NSOverride> cur(new NSOverride); |
+ if (!cur.isNull()) { |
char kw[ULOC_KEYWORD_AND_VALUES_CAPACITY]; |
uprv_strcpy(kw,"numbers="); |
nsName.extract(0,len,kw+8,ULOC_KEYWORD_AND_VALUES_CAPACITY-8,US_INV); |
Locale ovrLoc(locale.getLanguage(),locale.getCountry(),locale.getVariant(),kw); |
- nf = NumberFormat::createInstance(ovrLoc,status); |
- |
- // no matter what the locale's default number format looked like, we want |
- // to modify it so that it doesn't use thousands separators, doesn't always |
- // show the decimal point, and recognizes integers only when parsing |
- |
- if (U_SUCCESS(status)) { |
- nf->setGroupingUsed(FALSE); |
- DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(nf); |
- if (decfmt != NULL) { |
- decfmt->setDecimalSeparatorAlwaysShown(FALSE); |
+ cur->hash = nsNameHash; |
+ cur->next = overrideList; |
+ SharedObject::copyPtr( |
+ createSharedNumberFormat(ovrLoc, status), cur->snf); |
+ if (U_FAILURE(status)) { |
+ if (overrideList) { |
+ overrideList->free(); |
} |
- nf->setParseIntegerOnly(TRUE); |
- nf->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00" |
- |
- cur->nf = nf; |
- cur->hash = nsNameHash; |
- cur->next = fOverrideList; |
- fOverrideList = cur; |
+ return; |
} |
- else { |
- // clean up before returning |
- if (cur != NULL) { |
- uprv_free(cur); |
- } |
- return; |
- } |
- |
+ snf = cur->snf; |
+ overrideList = cur.orphan(); |
} else { |
status = U_MEMORY_ALLOCATION_ERROR; |
+ if (overrideList) { |
+ overrideList->free(); |
+ } |
return; |
} |
} |
@@ -1187,7 +1350,7 @@ SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeStrin |
case kOvrStrDate: |
case kOvrStrBoth: { |
for ( int8_t i=0 ; i<kDateFieldsCount; i++ ) { |
- fNumberFormatters[kDateFields[i]] = nf; |
+ SharedObject::copyPtr(snf, fSharedNumberFormatters[kDateFields[i]]); |
} |
if (type==kOvrStrDate) { |
break; |
@@ -1195,7 +1358,7 @@ SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeStrin |
} |
case kOvrStrTime : { |
for ( int8_t i=0 ; i<kTimeFieldsCount; i++ ) { |
- fNumberFormatters[kTimeFields[i]] = nf; |
+ SharedObject::copyPtr(snf, fSharedNumberFormatters[kTimeFields[i]]); |
} |
break; |
} |
@@ -1206,15 +1369,19 @@ SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeStrin |
DateFormatSymbols::getPatternCharIndex(ovrField.charAt(0)); |
if (patternCharIndex == UDAT_FIELD_COUNT) { |
status = U_INVALID_FORMAT_ERROR; |
+ if (overrideList) { |
+ overrideList->free(); |
+ } |
return; |
} |
- |
- // Set the number formatter in the table |
- fNumberFormatters[patternCharIndex] = nf; |
+ SharedObject::copyPtr(snf, fSharedNumberFormatters[patternCharIndex]); |
} |
start = delimiterPosition + 1; |
} |
+ if (overrideList) { |
+ overrideList->free(); |
+ } |
} |
//--------------------------------------------------------------------- |
@@ -1226,6 +1393,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, |
int32_t fieldNum, |
FieldPositionHandler& handler, |
Calendar& cal, |
+ SimpleDateFormatMutableNFs &mutableNFs, |
UErrorCode& status) const |
{ |
if (U_FAILURE(status)) { |
@@ -1254,12 +1422,20 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, |
} |
UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; |
- int32_t value = (patternCharIndex != UDAT_RELATED_YEAR_FIELD)? cal.get(field, status): cal.getRelatedYear(status); |
+ int32_t value = 0; |
+ // Don't get value unless it is useful |
+ if (field < UCAL_FIELD_COUNT) { |
+ value = (patternCharIndex != UDAT_RELATED_YEAR_FIELD)? cal.get(field, status): cal.getRelatedYear(status); |
+ } |
if (U_FAILURE(status)) { |
return; |
} |
- currentNumberFormat = getNumberFormatByIndex(patternCharIndex); |
+ currentNumberFormat = mutableNFs.get(getNumberFormatByIndex(patternCharIndex)); |
+ if (currentNumberFormat == NULL) { |
+ status = U_MEMORY_ALLOCATION_ERROR; |
+ return; |
+ } |
UnicodeString hebr("hebr", 4, US_INV); |
switch (patternCharIndex) { |
@@ -1463,10 +1639,24 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, |
} |
break; |
- // for and "a" symbol, write out the whole AM/PM string |
+ // for "a" symbol, write out the whole AM/PM string |
case UDAT_AM_PM_FIELD: |
- _appendSymbol(appendTo, value, fSymbols->fAmPms, |
- fSymbols->fAmPmsCount); |
+ if (count < 5) { |
+ _appendSymbol(appendTo, value, fSymbols->fAmPms, |
+ fSymbols->fAmPmsCount); |
+ } else { |
+ _appendSymbol(appendTo, value, fSymbols->fNarrowAmPms, |
+ fSymbols->fNarrowAmPmsCount); |
+ } |
+ break; |
+ |
+ // if we see pattern character for UDAT_TIME_SEPARATOR_FIELD (none currently defined), |
+ // write out the time separator string. Leave support in for future definition. |
+ case UDAT_TIME_SEPARATOR_FIELD: |
+ { |
+ UnicodeString separator; |
+ appendTo += fSymbols->getTimeSeparatorString(separator); |
+ } |
break; |
// for "h" and "hh", write out the hour, adjusting noon and midnight to show up |
@@ -1486,7 +1676,8 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, |
case UDAT_TIMEZONE_ISO_FIELD: // 'X' |
case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x' |
{ |
- UnicodeString zoneString; |
+ UChar zsbuf[64]; |
+ UnicodeString zoneString(zsbuf, 0, UPRV_LENGTHOF(zsbuf)); |
const TimeZone& tz = cal.getTimeZone(); |
UDate date = cal.getTime(status); |
if (U_SUCCESS(status)) { |
@@ -1654,116 +1845,70 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, |
//---------------------------------------------------------------------- |
void SimpleDateFormat::adoptNumberFormat(NumberFormat *formatToAdopt) { |
- formatToAdopt->setParseIntegerOnly(TRUE); |
- if (fNumberFormat && fNumberFormat != formatToAdopt){ |
- delete fNumberFormat; |
- } |
+ fixNumberFormatForDates(*formatToAdopt); |
+ delete fNumberFormat; |
fNumberFormat = formatToAdopt; |
- |
- if (fNumberFormatters) { |
- for (int32_t i = 0; i < UDAT_FIELD_COUNT; i++) { |
- if (fNumberFormatters[i] == formatToAdopt) { |
- fNumberFormatters[i] = NULL; |
- } |
- } |
- uprv_free(fNumberFormatters); |
- fNumberFormatters = NULL; |
- } |
- while (fOverrideList) { |
- NSOverride *cur = fOverrideList; |
- fOverrideList = cur->next; |
- if (cur->nf != formatToAdopt) { // only delete those not duplicate |
- delete cur->nf; |
- uprv_free(cur); |
- } else { |
- cur->nf = NULL; |
- uprv_free(cur); |
- } |
+ // We successfully set the default number format. Now delete the overrides |
+ // (can't fail). |
+ if (fSharedNumberFormatters) { |
+ freeSharedNumberFormatters(fSharedNumberFormatters); |
+ fSharedNumberFormatters = NULL; |
} |
} |
void SimpleDateFormat::adoptNumberFormat(const UnicodeString& fields, NumberFormat *formatToAdopt, UErrorCode &status){ |
- // if it has not been initialized yet, initialize |
- if (fNumberFormatters == NULL) { |
- fNumberFormatters = (NumberFormat**)uprv_malloc(UDAT_FIELD_COUNT * sizeof(NumberFormat*)); |
- if (fNumberFormatters) { |
- for (int32_t i = 0; i < UDAT_FIELD_COUNT; i++) { |
- fNumberFormatters[i] = fNumberFormat; |
- } |
- } else { |
- status = U_MEMORY_ALLOCATION_ERROR; |
- return; |
- } |
+ fixNumberFormatForDates(*formatToAdopt); |
+ LocalPointer<NumberFormat> fmt(formatToAdopt); |
+ if (U_FAILURE(status)) { |
+ return; |
} |
- |
- // See if the numbering format is in the override list, if not, then add it. |
- NSOverride *cur = fOverrideList; |
- UBool found = FALSE; |
- while (cur && !found) { |
- if ( cur->nf == formatToAdopt ) { |
- found = TRUE; |
- } |
- cur = cur->next; |
- } |
- |
- if (!found) { |
- cur = (NSOverride *)uprv_malloc(sizeof(NSOverride)); |
- if (cur) { |
- // no matter what the locale's default number format looked like, we want |
- // to modify it so that it doesn't use thousands separators, doesn't always |
- // show the decimal point, and recognizes integers only when parsing |
- formatToAdopt->setGroupingUsed(FALSE); |
- DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(formatToAdopt); |
- if (decfmt != NULL) { |
- decfmt->setDecimalSeparatorAlwaysShown(FALSE); |
- } |
- formatToAdopt->setParseIntegerOnly(TRUE); |
- formatToAdopt->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00" |
- |
- cur->nf = formatToAdopt; |
- cur->hash = -1; // set duplicate here (before we set it with NumberSystem Hash, here we cannot get nor use it) |
- cur->next = fOverrideList; |
- fOverrideList = cur; |
- } else { |
+ |
+ // We must ensure fSharedNumberFormatters is allocated. |
+ if (fSharedNumberFormatters == NULL) { |
+ fSharedNumberFormatters = allocSharedNumberFormatters(); |
+ if (fSharedNumberFormatters == NULL) { |
status = U_MEMORY_ALLOCATION_ERROR; |
return; |
} |
} |
- |
+ const SharedNumberFormat *newFormat = createSharedNumberFormat(fmt.orphan()); |
+ if (newFormat == NULL) { |
+ status = U_MEMORY_ALLOCATION_ERROR; |
+ return; |
+ } |
for (int i=0; i<fields.length(); i++) { |
UChar field = fields.charAt(i); |
// if the pattern character is unrecognized, signal an error and bail out |
UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(field); |
if (patternCharIndex == UDAT_FIELD_COUNT) { |
status = U_INVALID_FORMAT_ERROR; |
+ newFormat->deleteIfZeroRefCount(); |
return; |
} |
// Set the number formatter in the table |
- fNumberFormatters[patternCharIndex] = formatToAdopt; |
+ SharedObject::copyPtr( |
+ newFormat, fSharedNumberFormatters[patternCharIndex]); |
} |
+ newFormat->deleteIfZeroRefCount(); |
} |
const NumberFormat * |
SimpleDateFormat::getNumberFormatForField(UChar field) const { |
UDateFormatField index = DateFormatSymbols::getPatternCharIndex(field); |
- return getNumberFormatByIndex(index); |
-} |
- |
-NumberFormat * |
-SimpleDateFormat::getNumberFormatByIndex(UDateFormatField index) const { |
- if (fNumberFormatters != NULL) { |
- return fNumberFormatters[index]; |
- } else { |
- return fNumberFormat; |
+ if (index == UDAT_FIELD_COUNT) { |
+ return NULL; |
} |
+ return getNumberFormatByIndex(index); |
} |
//---------------------------------------------------------------------- |
void |
-SimpleDateFormat::zeroPaddingNumber(NumberFormat *currentNumberFormat,UnicodeString &appendTo, |
- int32_t value, int32_t minDigits, int32_t maxDigits) const |
+SimpleDateFormat::zeroPaddingNumber( |
+ NumberFormat *currentNumberFormat, |
+ UnicodeString &appendTo, |
+ int32_t value, int32_t minDigits, int32_t maxDigits) const |
{ |
if (currentNumberFormat!=NULL) { |
FieldPosition pos(0); |
@@ -1834,6 +1979,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& |
int32_t saveHebrewMonth = -1; |
int32_t count = 0; |
UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN; |
+ SimpleDateFormatMutableNFs mutableNFs; |
// For parsing abutting numeric fields. 'abutPat' is the |
// offset into 'pattern' of the first of 2 or more abutting |
@@ -1881,7 +2027,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& |
UChar ch = fPattern.charAt(i); |
// Handle alphabetic field characters. |
- if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { // [A-Za-z] |
+ if (!inQuote && isSyntaxChar(ch)) { |
int32_t fieldPat = i; |
// Count the length of this field specifier |
@@ -1927,7 +2073,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& |
} |
pos = subParse(text, pos, ch, count, |
- TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType); |
+ TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, mutableNFs); |
// If the parse fails anywhere in the run, back up to the |
// start of the run and retry. |
@@ -1942,7 +2088,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& |
// fields. |
else if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored |
int32_t s = subParse(text, pos, ch, count, |
- FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType); |
+ FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, mutableNFs); |
if (s == -pos-1) { |
// era not present, in special cases allow this to continue |
@@ -1979,7 +2125,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& |
abutPat = -1; // End of any abutting fields |
- if (! matchLiterals(fPattern, i, text, pos, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status), getBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH, status), isLenient())) { |
+ if (! matchLiterals(fPattern, i, text, pos, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status), getBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, status), isLenient())) { |
status = U_PARSE_ERROR; |
goto ExitParse; |
} |
@@ -2180,11 +2326,10 @@ ExitParse: |
//---------------------------------------------------------------------- |
-static UBool |
-newBestMatchWithOptionalDot(const UnicodeString &lcaseText, |
- const UnicodeString &data, |
- UnicodeString &bestMatchName, |
- int32_t &bestMatchLength); |
+static int32_t |
+matchStringWithOptionalDot(const UnicodeString &text, |
+ int32_t index, |
+ const UnicodeString &data); |
int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text, |
int32_t start, |
@@ -2203,54 +2348,17 @@ int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text, |
int32_t bestMatchLength = 0, bestMatch = -1; |
UnicodeString bestMatchName; |
- // {sfb} kludge to support case-insensitive comparison |
- // {markus 2002oct11} do not just use caseCompareBetween because we do not know |
- // the length of the match after case folding |
- // {alan 20040607} don't case change the whole string, since the length |
- // can change |
- // TODO we need a case-insensitive startsWith function |
- UnicodeString lcaseText; |
- text.extract(start, INT32_MAX, lcaseText); |
- lcaseText.foldCase(); |
- |
- for (; i < count; ++i) |
- { |
- // Always compare if we have no match yet; otherwise only compare |
- // against potentially better matches (longer strings). |
- |
- if (newBestMatchWithOptionalDot(lcaseText, data[i], bestMatchName, bestMatchLength)) { |
+ for (; i < count; ++i) { |
+ int32_t matchLength = 0; |
+ if ((matchLength = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) { |
+ bestMatchLength = matchLength; |
bestMatch = i; |
} |
} |
- if (bestMatch >= 0) |
- { |
- cal.set(field, bestMatch * 3); |
- |
- // Once we have a match, we have to determine the length of the |
- // original source string. This will usually be == the length of |
- // the case folded string, but it may differ (e.g. sharp s). |
- // Most of the time, the length will be the same as the length |
- // of the string from the locale data. Sometimes it will be |
- // different, in which case we will have to figure it out by |
- // adding a character at a time, until we have a match. We do |
- // this all in one loop, where we try 'len' first (at index |
- // i==0). |
- int32_t len = bestMatchName.length(); // 99+% of the time |
- int32_t n = text.length() - start; |
- for (i=0; i<=n; ++i) { |
- int32_t j=i; |
- if (i == 0) { |
- j = len; |
- } else if (i == len) { |
- continue; // already tried this when i was 0 |
- } |
- text.extract(start, j, lcaseText); |
- lcaseText.foldCase(); |
- if (bestMatchName == lcaseText) { |
- return start + j; |
- } |
- } |
+ if (bestMatch >= 0) { |
+ cal.set(field, bestMatch * 3); |
+ return start + bestMatchLength; |
} |
return -start; |
@@ -2268,12 +2376,12 @@ UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern, |
UBool inQuote = FALSE; |
UnicodeString literal; |
int32_t i = patternOffset; |
- |
+ |
// scan pattern looking for contiguous literal characters |
for ( ; i < pattern.length(); i += 1) { |
UChar ch = pattern.charAt(i); |
- if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { // unquoted [A-Za-z] |
+ if (!inQuote && isSyntaxChar(ch)) { |
break; |
} |
@@ -2424,24 +2532,11 @@ int32_t SimpleDateFormat::matchString(const UnicodeString& text, |
UnicodeString bestMatchName; |
int32_t isLeapMonth = 0; |
- // {sfb} kludge to support case-insensitive comparison |
- // {markus 2002oct11} do not just use caseCompareBetween because we do not know |
- // the length of the match after case folding |
- // {alan 20040607} don't case change the whole string, since the length |
- // can change |
- // TODO we need a case-insensitive startsWith function |
- UnicodeString lcaseText; |
- text.extract(start, INT32_MAX, lcaseText); |
- lcaseText.foldCase(); |
- |
- for (; i < count; ++i) |
- { |
- // Always compare if we have no match yet; otherwise only compare |
- // against potentially better matches (longer strings). |
- |
- if (newBestMatchWithOptionalDot(lcaseText, data[i], bestMatchName, bestMatchLength)) { |
+ for (; i < count; ++i) { |
+ int32_t matchLen = 0; |
+ if ((matchLen = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) { |
bestMatch = i; |
- isLeapMonth = 0; |
+ bestMatchLength = matchLen; |
} |
if (monthPattern != NULL) { |
@@ -2450,88 +2545,59 @@ int32_t SimpleDateFormat::matchString(const UnicodeString& text, |
Formattable monthName((const UnicodeString&)(data[i])); |
MessageFormat::format(*monthPattern, &monthName, 1, leapMonthName, status); |
if (U_SUCCESS(status)) { |
- if (newBestMatchWithOptionalDot(lcaseText, leapMonthName, bestMatchName, bestMatchLength)) { |
+ if ((matchLen = matchStringWithOptionalDot(text, start, leapMonthName)) > bestMatchLength) { |
bestMatch = i; |
+ bestMatchLength = matchLen; |
isLeapMonth = 1; |
} |
} |
} |
} |
- if (bestMatch >= 0) |
- { |
- // Adjustment for Hebrew Calendar month Adar II |
- if (!strcmp(cal.getType(),"hebrew") && field==UCAL_MONTH && bestMatch==13) { |
- cal.set(field,6); |
- } |
- else { |
- if (field == UCAL_YEAR) { |
- bestMatch++; // only get here for cyclic year names, which match 1-based years 1-60 |
- } |
- cal.set(field, bestMatch); |
- } |
- if (monthPattern != NULL) { |
- cal.set(UCAL_IS_LEAP_MONTH, isLeapMonth); |
- } |
- |
- // Once we have a match, we have to determine the length of the |
- // original source string. This will usually be == the length of |
- // the case folded string, but it may differ (e.g. sharp s). |
- // Most of the time, the length will be the same as the length |
- // of the string from the locale data. Sometimes it will be |
- // different, in which case we will have to figure it out by |
- // adding a character at a time, until we have a match. We do |
- // this all in one loop, where we try 'len' first (at index |
- // i==0). |
- int32_t len = bestMatchName.length(); // 99+% of the time |
- int32_t n = text.length() - start; |
- for (i=0; i<=n; ++i) { |
- int32_t j=i; |
- if (i == 0) { |
- j = len; |
- } else if (i == len) { |
- continue; // already tried this when i was 0 |
+ if (bestMatch >= 0) { |
+ if (field < UCAL_FIELD_COUNT) { |
+ // Adjustment for Hebrew Calendar month Adar II |
+ if (!strcmp(cal.getType(),"hebrew") && field==UCAL_MONTH && bestMatch==13) { |
+ cal.set(field,6); |
+ } else { |
+ if (field == UCAL_YEAR) { |
+ bestMatch++; // only get here for cyclic year names, which match 1-based years 1-60 |
+ } |
+ cal.set(field, bestMatch); |
} |
- text.extract(start, j, lcaseText); |
- lcaseText.foldCase(); |
- if (bestMatchName == lcaseText) { |
- return start + j; |
+ if (monthPattern != NULL) { |
+ cal.set(UCAL_IS_LEAP_MONTH, isLeapMonth); |
} |
} |
+ |
+ return start + bestMatchLength; |
} |
return -start; |
} |
-static UBool |
-newBestMatchWithOptionalDot(const UnicodeString &lcaseText, |
- const UnicodeString &data, |
- UnicodeString &bestMatchName, |
- int32_t &bestMatchLength) { |
- UnicodeString lcase; |
- lcase.fastCopyFrom(data).foldCase(); |
- int32_t length = lcase.length(); |
- if (length <= bestMatchLength) { |
- // data cannot provide a better match. |
- return FALSE; |
- } |
+static int32_t |
+matchStringWithOptionalDot(const UnicodeString &text, |
+ int32_t index, |
+ const UnicodeString &data) { |
+ UErrorCode sts = U_ZERO_ERROR; |
+ int32_t matchLenText = 0; |
+ int32_t matchLenData = 0; |
- if (lcaseText.compareBetween(0, length, lcase, 0, length) == 0) { |
- // normal match |
- bestMatchName = lcase; |
- bestMatchLength = length; |
- return TRUE; |
- } |
- if (lcase.charAt(--length) == 0x2e) { |
- if (lcaseText.compareBetween(0, length, lcase, 0, length) == 0) { |
- // The input text matches the data except for data's trailing dot. |
- bestMatchName = lcase; |
- bestMatchName.truncate(length); |
- bestMatchLength = length; |
- return TRUE; |
- } |
+ u_caseInsensitivePrefixMatch(text.getBuffer() + index, text.length() - index, |
+ data.getBuffer(), data.length(), |
+ 0 /* default case option */, |
+ &matchLenText, &matchLenData, |
+ &sts); |
+ U_ASSERT (U_SUCCESS(sts)); |
+ |
+ if (matchLenData == data.length() /* normal match */ |
+ || (data.charAt(data.length() - 1) == 0x2e |
+ && matchLenData == data.length() - 1 /* match without trailing dot */)) { |
+ return matchLenText; |
} |
- return FALSE; |
+ |
+ return 0; |
} |
//---------------------------------------------------------------------- |
@@ -2548,7 +2614,7 @@ SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status) |
*/ |
int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count, |
UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal, |
- int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType) const |
+ int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType, SimpleDateFormatMutableNFs &mutableNFs) const |
{ |
Formattable number; |
int32_t value = 0; |
@@ -2569,8 +2635,11 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC |
return -start; |
} |
- currentNumberFormat = getNumberFormatByIndex(patternCharIndex); |
- UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; |
+ currentNumberFormat = mutableNFs.get(getNumberFormatByIndex(patternCharIndex)); |
+ if (currentNumberFormat == NULL) { |
+ return -start; |
+ } |
+ UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; // UCAL_FIELD_COUNT if irrelevant |
UnicodeString hebr("hebr", 4, US_INV); |
if (numericLeapMonthFormatter != NULL) { |
@@ -2981,7 +3050,24 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC |
break; |
case UDAT_AM_PM_FIELD: |
- return matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, NULL, cal); |
+ { |
+ // optionally try both wide/abbrev and narrow forms |
+ int32_t newStart = 0; |
+ // try wide/abbrev |
+ if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count < 5 ) { |
+ if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, NULL, cal)) > 0) { |
+ return newStart; |
+ } |
+ } |
+ // try narrow |
+ if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count >= 5 ) { |
+ if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fNarrowAmPms, fSymbols->fNarrowAmPmsCount, NULL, cal)) > 0) { |
+ return newStart; |
+ } |
+ } |
+ // no matches for given options |
+ return -start; |
+ } |
case UDAT_HOUR1_FIELD: |
// [We computed 'value' above.] |
@@ -3177,6 +3263,30 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC |
} |
return -start; |
} |
+ // currently no pattern character is defined for UDAT_TIME_SEPARATOR_FIELD |
+ // so we should not get here. Leave support in for future definition. |
+ case UDAT_TIME_SEPARATOR_FIELD: // |
+ { |
+ static const UChar def_sep = DateFormatSymbols::DEFAULT_TIME_SEPARATOR; |
+ static const UChar alt_sep = DateFormatSymbols::ALTERNATE_TIME_SEPARATOR; |
+ |
+ // Try matching a time separator. |
+ int32_t count = 1; |
+ UnicodeString data[3]; |
+ fSymbols->getTimeSeparatorString(data[0]); |
+ |
+ // Add the default, if different from the locale. |
+ if (data[0].compare(&def_sep, 1) != 0) { |
+ data[count++].setTo(def_sep); |
+ } |
+ |
+ // If lenient, add also the alternate, if different from the locale. |
+ if (isLenient() && data[0].compare(&alt_sep, 1) != 0) { |
+ data[count++].setTo(alt_sep); |
+ } |
+ |
+ return matchString(text, start, UCAL_FIELD_COUNT /* => nothing to set */, data, count, NULL, cal); |
+ } |
default: |
// Handle "generic" fields |
@@ -3316,42 +3426,42 @@ void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern, |
const UnicodeString& to, |
UErrorCode& status) |
{ |
- // run through the pattern and convert any pattern symbols from the version |
- // in "from" to the corresponding character ion "to". This code takes |
- // quoted strings into account (it doesn't try to translate them), and it signals |
- // an error if a particular "pattern character" doesn't appear in "from". |
- // Depending on the values of "from" and "to" this can convert from generic |
- // to localized patterns or localized to generic. |
- if (U_FAILURE(status)) |
- return; |
- |
- translatedPattern.remove(); |
- UBool inQuote = FALSE; |
- for (int32_t i = 0; i < originalPattern.length(); ++i) { |
- UChar c = originalPattern[i]; |
- if (inQuote) { |
- if (c == QUOTE) |
- inQuote = FALSE; |
- } |
- else { |
- if (c == QUOTE) |
- inQuote = TRUE; |
- else if ((c >= 0x0061 /*'a'*/ && c <= 0x007A) /*'z'*/ |
- || (c >= 0x0041 /*'A'*/ && c <= 0x005A /*'Z'*/)) { |
- int32_t ci = from.indexOf(c); |
- if (ci == -1) { |
- status = U_INVALID_FORMAT_ERROR; |
- return; |
+ // run through the pattern and convert any pattern symbols from the version |
+ // in "from" to the corresponding character ion "to". This code takes |
+ // quoted strings into account (it doesn't try to translate them), and it signals |
+ // an error if a particular "pattern character" doesn't appear in "from". |
+ // Depending on the values of "from" and "to" this can convert from generic |
+ // to localized patterns or localized to generic. |
+ if (U_FAILURE(status)) { |
+ return; |
} |
- c = to[ci]; |
- } |
+ |
+ translatedPattern.remove(); |
+ UBool inQuote = FALSE; |
+ for (int32_t i = 0; i < originalPattern.length(); ++i) { |
+ UChar c = originalPattern[i]; |
+ if (inQuote) { |
+ if (c == QUOTE) { |
+ inQuote = FALSE; |
+ } |
+ } else { |
+ if (c == QUOTE) { |
+ inQuote = TRUE; |
+ } else if (isSyntaxChar(c)) { |
+ int32_t ci = from.indexOf(c); |
+ if (ci == -1) { |
+ status = U_INVALID_FORMAT_ERROR; |
+ return; |
+ } |
+ c = to[ci]; |
+ } |
+ } |
+ translatedPattern += c; |
+ } |
+ if (inQuote) { |
+ status = U_INVALID_FORMAT_ERROR; |
+ return; |
} |
- translatedPattern += c; |
- } |
- if (inQuote) { |
- status = U_INVALID_FORMAT_ERROR; |
- return; |
- } |
} |
//---------------------------------------------------------------------- |
@@ -3447,10 +3557,16 @@ SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat& newTimeZoneFormat) |
void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt) |
{ |
UErrorCode status = U_ZERO_ERROR; |
+ Locale calLocale(fLocale); |
+ calLocale.setKeywordValue("calendar", calendarToAdopt->getType(), status); |
+ DateFormatSymbols *newSymbols = |
+ DateFormatSymbols::createForLocale(calLocale, status); |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
DateFormat::adoptCalendar(calendarToAdopt); |
delete fSymbols; |
- fSymbols=NULL; |
- initializeSymbols(fLocale, fCalendar, status); // we need new symbols |
+ fSymbols = newSymbols; |
initializeDefaultCentury(); // we need a new century (possibly) |
} |
@@ -3502,9 +3618,9 @@ SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern, |
for (int32_t i = 0; i < pattern.length(); ++i) { |
ch = pattern[i]; |
if (ch != prevCh && count > 0) { |
- level = fgPatternCharToLevel[prevCh - PATTERN_CHAR_BASE]; |
+ level = getLevelFromChar(prevCh); |
// the larger the level, the smaller the field unit. |
- if ( fieldLevel <= level ) { |
+ if (fieldLevel <= level) { |
return FALSE; |
} |
count = 0; |
@@ -3516,18 +3632,17 @@ SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern, |
inQuote = ! inQuote; |
} |
} |
- else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) |
- || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { |
+ else if (!inQuote && isSyntaxChar(ch)) { |
prevCh = ch; |
++count; |
} |
} |
- if ( count > 0 ) { |
+ if (count > 0) { |
// last item |
- level = fgPatternCharToLevel[prevCh - PATTERN_CHAR_BASE]; |
- if ( fieldLevel <= level ) { |
- return FALSE; |
- } |
+ level = getLevelFromChar(prevCh); |
+ if (fieldLevel <= level) { |
+ return FALSE; |
+ } |
} |
return TRUE; |
} |