mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 22:44:49 +00:00
ICU-13717 Optimizing SimpleDateFormat's use of NumberFormat for new number formatting pipeline.
X-SVN-Rev: 41441
This commit is contained in:
commit
49960567e6
4 changed files with 176 additions and 98 deletions
|
@ -53,6 +53,7 @@
|
|||
#include "unicode/vtzone.h"
|
||||
#include "unicode/udisplaycontext.h"
|
||||
#include "unicode/brkiter.h"
|
||||
#include "unicode/rbnf.h"
|
||||
#include "uresimp.h"
|
||||
#include "olsontz.h"
|
||||
#include "patternprops.h"
|
||||
|
@ -72,6 +73,7 @@
|
|||
#include "cstr.h"
|
||||
#include "dayperiodrules.h"
|
||||
#include "tznames_impl.h" // ZONE_NAME_U16_MAX
|
||||
#include "number_utypes.h"
|
||||
|
||||
#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
|
||||
#include <stdio.h>
|
||||
|
@ -312,57 +314,6 @@ const NumberFormat *SimpleDateFormat::getNumberFormatByIndex(
|
|||
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()
|
||||
|
@ -374,6 +325,7 @@ SimpleDateFormat::~SimpleDateFormat()
|
|||
if (fTimeZoneFormat) {
|
||||
delete fTimeZoneFormat;
|
||||
}
|
||||
freeFastNumberFormatters();
|
||||
|
||||
#if !UCONFIG_NO_BREAK_ITERATION
|
||||
delete fCapitalizationBrkIter;
|
||||
|
@ -659,6 +611,10 @@ SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
|
|||
}
|
||||
}
|
||||
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
freeFastNumberFormatters();
|
||||
initFastNumberFormatters(localStatus);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -908,7 +864,8 @@ SimpleDateFormat::initialize(const Locale& locale,
|
|||
fixNumberFormatForDates(*fNumberFormat);
|
||||
//fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
|
||||
|
||||
initNumberFormatters(locale,status);
|
||||
initNumberFormatters(locale, status);
|
||||
initFastNumberFormatters(status);
|
||||
|
||||
}
|
||||
else if (U_SUCCESS(status))
|
||||
|
@ -1023,11 +980,6 @@ 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];
|
||||
|
@ -1035,7 +987,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, mutableNFs, status);
|
||||
subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, status);
|
||||
count = 0;
|
||||
}
|
||||
if (ch == QUOTE) {
|
||||
|
@ -1062,7 +1014,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, mutableNFs, status);
|
||||
subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, status);
|
||||
}
|
||||
|
||||
if (calClone != NULL) {
|
||||
|
@ -1257,6 +1209,43 @@ _appendSymbolWithMonthPattern(UnicodeString& dst, int32_t value, const UnicodeSt
|
|||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static number::LocalizedNumberFormatter*
|
||||
createFastFormatter(const DecimalFormat* df, int32_t minInt, int32_t maxInt) {
|
||||
return new number::LocalizedNumberFormatter(
|
||||
df->toNumberFormatter()
|
||||
.integerWidth(number::IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt)));
|
||||
}
|
||||
|
||||
void SimpleDateFormat::initFastNumberFormatters(UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
auto* df = dynamic_cast<const DecimalFormat*>(fNumberFormat);
|
||||
if (df == nullptr) {
|
||||
return;
|
||||
}
|
||||
fFastNumberFormatters[SMPDTFMT_NF_1x10] = createFastFormatter(df, 1, 10);
|
||||
fFastNumberFormatters[SMPDTFMT_NF_2x10] = createFastFormatter(df, 2, 10);
|
||||
fFastNumberFormatters[SMPDTFMT_NF_3x10] = createFastFormatter(df, 3, 10);
|
||||
fFastNumberFormatters[SMPDTFMT_NF_4x10] = createFastFormatter(df, 4, 10);
|
||||
fFastNumberFormatters[SMPDTFMT_NF_2x2] = createFastFormatter(df, 2, 2);
|
||||
}
|
||||
|
||||
void SimpleDateFormat::freeFastNumberFormatters() {
|
||||
delete fFastNumberFormatters[SMPDTFMT_NF_1x10];
|
||||
delete fFastNumberFormatters[SMPDTFMT_NF_2x10];
|
||||
delete fFastNumberFormatters[SMPDTFMT_NF_3x10];
|
||||
delete fFastNumberFormatters[SMPDTFMT_NF_4x10];
|
||||
delete fFastNumberFormatters[SMPDTFMT_NF_2x2];
|
||||
fFastNumberFormatters[SMPDTFMT_NF_1x10] = nullptr;
|
||||
fFastNumberFormatters[SMPDTFMT_NF_2x10] = nullptr;
|
||||
fFastNumberFormatters[SMPDTFMT_NF_3x10] = nullptr;
|
||||
fFastNumberFormatters[SMPDTFMT_NF_4x10] = nullptr;
|
||||
fFastNumberFormatters[SMPDTFMT_NF_2x2] = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
|
@ -1406,7 +1395,6 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
|
|||
int32_t fieldNum,
|
||||
FieldPositionHandler& handler,
|
||||
Calendar& cal,
|
||||
SimpleDateFormatMutableNFs &mutableNFs,
|
||||
UErrorCode& status) const
|
||||
{
|
||||
if (U_FAILURE(status)) {
|
||||
|
@ -1419,7 +1407,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
|
|||
UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
|
||||
const int32_t maxIntCount = 10;
|
||||
int32_t beginOffset = appendTo.length();
|
||||
NumberFormat *currentNumberFormat;
|
||||
const NumberFormat *currentNumberFormat;
|
||||
DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType = DateFormatSymbols::kCapContextUsageOther;
|
||||
|
||||
UBool isHebrewCalendar = (uprv_strcmp(cal.getType(),"hebrew") == 0);
|
||||
|
@ -1444,9 +1432,9 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
|
|||
return;
|
||||
}
|
||||
|
||||
currentNumberFormat = mutableNFs.get(getNumberFormatByIndex(patternCharIndex));
|
||||
currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
|
||||
if (currentNumberFormat == NULL) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
UnicodeString hebr("hebr", 4, US_INV);
|
||||
|
@ -1566,18 +1554,15 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
|
|||
case UDAT_FRACTIONAL_SECOND_FIELD:
|
||||
// Fractional seconds left-justify
|
||||
{
|
||||
currentNumberFormat->setMinimumIntegerDigits((count > 3) ? 3 : count);
|
||||
currentNumberFormat->setMaximumIntegerDigits(maxIntCount);
|
||||
int32_t minDigits = (count > 3) ? 3 : count;
|
||||
if (count == 1) {
|
||||
value /= 100;
|
||||
} else if (count == 2) {
|
||||
value /= 10;
|
||||
}
|
||||
FieldPosition p(FieldPosition::DONT_CARE);
|
||||
currentNumberFormat->format(value, appendTo, p);
|
||||
zeroPaddingNumber(currentNumberFormat, appendTo, value, minDigits, maxIntCount);
|
||||
if (count > 3) {
|
||||
currentNumberFormat->setMinimumIntegerDigits(count - 3);
|
||||
currentNumberFormat->format((int32_t)0, appendTo, p);
|
||||
zeroPaddingNumber(currentNumberFormat, appendTo, 0, count - 3, maxIntCount);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1856,7 +1841,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
|
|||
if (toAppend == NULL || toAppend->isBogus()) {
|
||||
// Reformat with identical arguments except ch, now changed to 'a'.
|
||||
subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
|
||||
handler, cal, mutableNFs, status);
|
||||
handler, cal, status);
|
||||
} else {
|
||||
appendTo += *toAppend;
|
||||
}
|
||||
|
@ -1877,7 +1862,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
|
|||
// Data doesn't exist for the locale we're looking for.
|
||||
// Falling back to am/pm.
|
||||
subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
|
||||
handler, cal, mutableNFs, status);
|
||||
handler, cal, status);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1948,7 +1933,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
|
|||
periodType == DayPeriodRules::DAYPERIOD_PM ||
|
||||
toAppend->isBogus()) {
|
||||
subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
|
||||
handler, cal, mutableNFs, status);
|
||||
handler, cal, status);
|
||||
}
|
||||
else {
|
||||
appendTo += *toAppend;
|
||||
|
@ -2005,6 +1990,11 @@ void SimpleDateFormat::adoptNumberFormat(NumberFormat *formatToAdopt) {
|
|||
freeSharedNumberFormatters(fSharedNumberFormatters);
|
||||
fSharedNumberFormatters = NULL;
|
||||
}
|
||||
|
||||
// Also re-compute the fast formatters.
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
freeFastNumberFormatters();
|
||||
initFastNumberFormatters(localStatus);
|
||||
}
|
||||
|
||||
void SimpleDateFormat::adoptNumberFormat(const UnicodeString& fields, NumberFormat *formatToAdopt, UErrorCode &status){
|
||||
|
@ -2056,16 +2046,58 @@ SimpleDateFormat::getNumberFormatForField(UChar field) const {
|
|||
//----------------------------------------------------------------------
|
||||
void
|
||||
SimpleDateFormat::zeroPaddingNumber(
|
||||
NumberFormat *currentNumberFormat,
|
||||
const NumberFormat *currentNumberFormat,
|
||||
UnicodeString &appendTo,
|
||||
int32_t value, int32_t minDigits, int32_t maxDigits) const
|
||||
{
|
||||
if (currentNumberFormat!=NULL) {
|
||||
FieldPosition pos(FieldPosition::DONT_CARE);
|
||||
const number::LocalizedNumberFormatter* fastFormatter = nullptr;
|
||||
// NOTE: This uses the heuristic that these five min/max int settings account for the vast majority
|
||||
// of SimpleDateFormat number formatting cases at the time of writing (ICU 62).
|
||||
if (currentNumberFormat == fNumberFormat) {
|
||||
if (maxDigits == 10) {
|
||||
if (minDigits == 1) {
|
||||
fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_1x10];
|
||||
} else if (minDigits == 2) {
|
||||
fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_2x10];
|
||||
} else if (minDigits == 3) {
|
||||
fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_3x10];
|
||||
} else if (minDigits == 4) {
|
||||
fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_4x10];
|
||||
}
|
||||
} else if (maxDigits == 2) {
|
||||
if (minDigits == 2) {
|
||||
fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_2x2];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fastFormatter != nullptr) {
|
||||
// Can use fast path
|
||||
number::impl::UFormattedNumberData result;
|
||||
result.quantity.setToInt(value);
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
fastFormatter->formatImpl(&result, localStatus);
|
||||
if (U_FAILURE(localStatus)) {
|
||||
return;
|
||||
}
|
||||
appendTo.append(result.string.toTempUnicodeString());
|
||||
return;
|
||||
}
|
||||
|
||||
currentNumberFormat->setMinimumIntegerDigits(minDigits);
|
||||
currentNumberFormat->setMaximumIntegerDigits(maxDigits);
|
||||
currentNumberFormat->format(value, appendTo, pos); // 3rd arg is there to speed up processing
|
||||
// Check for RBNF (no clone necessary)
|
||||
auto* rbnf = dynamic_cast<const RuleBasedNumberFormat*>(currentNumberFormat);
|
||||
if (rbnf != nullptr) {
|
||||
FieldPosition pos(FieldPosition::DONT_CARE);
|
||||
rbnf->format(value, appendTo, pos); // 3rd arg is there to speed up processing
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall back to slow path (clone and mutate the NumberFormat)
|
||||
if (currentNumberFormat != nullptr) {
|
||||
FieldPosition pos(FieldPosition::DONT_CARE);
|
||||
LocalPointer<NumberFormat> nf(dynamic_cast<NumberFormat*>(currentNumberFormat->clone()));
|
||||
nf->setMinimumIntegerDigits(minDigits);
|
||||
nf->setMaximumIntegerDigits(maxDigits);
|
||||
nf->format(value, appendTo, pos); // 3rd arg is there to speed up processing
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2132,7 +2164,6 @@ 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
|
||||
|
@ -2226,7 +2257,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
|
|||
}
|
||||
|
||||
pos = subParse(text, pos, ch, count,
|
||||
TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, mutableNFs);
|
||||
TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType);
|
||||
|
||||
// If the parse fails anywhere in the run, back up to the
|
||||
// start of the run and retry.
|
||||
|
@ -2241,7 +2272,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, mutableNFs, &dayPeriodInt);
|
||||
FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, &dayPeriodInt);
|
||||
|
||||
if (s == -pos-1) {
|
||||
// era not present, in special cases allow this to continue
|
||||
|
@ -2859,7 +2890,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, SimpleDateFormatMutableNFs &mutableNFs,
|
||||
int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType,
|
||||
int32_t *dayPeriod) const
|
||||
{
|
||||
Formattable number;
|
||||
|
@ -2869,7 +2900,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
|
|||
UErrorCode status = U_ZERO_ERROR;
|
||||
ParsePosition pos(0);
|
||||
UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
|
||||
NumberFormat *currentNumberFormat;
|
||||
const NumberFormat *currentNumberFormat;
|
||||
UnicodeString temp;
|
||||
UBool gotNumber = FALSE;
|
||||
|
||||
|
@ -2881,7 +2912,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
|
|||
return -start;
|
||||
}
|
||||
|
||||
currentNumberFormat = mutableNFs.get(getNumberFormatByIndex(patternCharIndex));
|
||||
currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
|
||||
if (currentNumberFormat == NULL) {
|
||||
return -start;
|
||||
}
|
||||
|
@ -3562,7 +3593,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC
|
|||
U_ASSERT(dayPeriod != NULL);
|
||||
int32_t ampmStart = subParse(text, start, 0x61, count,
|
||||
obeyCount, allowNegative, ambiguousYear, saveHebrewMonth, cal,
|
||||
patLoc, numericLeapMonthFormatter, tzTimeType, mutableNFs);
|
||||
patLoc, numericLeapMonthFormatter, tzTimeType);
|
||||
|
||||
if (ampmStart > 0) {
|
||||
return ampmStart;
|
||||
|
@ -3709,7 +3740,7 @@ void SimpleDateFormat::parseInt(const UnicodeString& text,
|
|||
Formattable& number,
|
||||
ParsePosition& pos,
|
||||
UBool allowNegative,
|
||||
NumberFormat *fmt) const {
|
||||
const NumberFormat *fmt) const {
|
||||
parseInt(text, number, -1, pos, allowNegative,fmt);
|
||||
}
|
||||
|
||||
|
@ -3721,18 +3752,21 @@ void SimpleDateFormat::parseInt(const UnicodeString& text,
|
|||
int32_t maxDigits,
|
||||
ParsePosition& pos,
|
||||
UBool allowNegative,
|
||||
NumberFormat *fmt) const {
|
||||
const NumberFormat *fmt) const {
|
||||
UnicodeString oldPrefix;
|
||||
DecimalFormat* df = NULL;
|
||||
if (!allowNegative && (df = dynamic_cast<DecimalFormat*>(fmt)) != NULL) {
|
||||
df->getNegativePrefix(oldPrefix);
|
||||
auto* fmtAsDF = dynamic_cast<const DecimalFormat*>(fmt);
|
||||
LocalPointer<DecimalFormat> df;
|
||||
if (!allowNegative && fmtAsDF != nullptr) {
|
||||
df.adoptInstead(dynamic_cast<DecimalFormat*>(fmtAsDF->clone()));
|
||||
if (df.isNull()) {
|
||||
// Memory allocation error
|
||||
return;
|
||||
}
|
||||
df->setNegativePrefix(UnicodeString(TRUE, SUPPRESS_NEGATIVE_PREFIX, -1));
|
||||
fmt = df.getAlias();
|
||||
}
|
||||
int32_t oldPos = pos.getIndex();
|
||||
fmt->parse(text, number, pos);
|
||||
if (df != NULL) {
|
||||
df->setNegativePrefix(oldPrefix);
|
||||
}
|
||||
|
||||
if (maxDigits > 0) {
|
||||
// adjust the result to fit into
|
||||
|
|
|
@ -50,6 +50,10 @@ class TimeZoneFormat;
|
|||
class SharedNumberFormat;
|
||||
class SimpleDateFormatMutableNFs;
|
||||
|
||||
namespace number {
|
||||
class LocalizedNumberFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* SimpleDateFormat is a concrete class for formatting and parsing dates in a
|
||||
|
@ -1268,7 +1272,6 @@ private:
|
|||
int32_t fieldNum,
|
||||
FieldPositionHandler& handler,
|
||||
Calendar& cal,
|
||||
SimpleDateFormatMutableNFs &mutableNFs,
|
||||
UErrorCode& status) const; // in case of illegal argument
|
||||
|
||||
/**
|
||||
|
@ -1284,7 +1287,7 @@ private:
|
|||
* @param minDigits Minimum number of digits the result should have
|
||||
* @param maxDigits Maximum number of digits the result should have
|
||||
*/
|
||||
void zeroPaddingNumber(NumberFormat *currentNumberFormat,
|
||||
void zeroPaddingNumber(const NumberFormat *currentNumberFormat,
|
||||
UnicodeString &appendTo,
|
||||
int32_t value,
|
||||
int32_t minDigits,
|
||||
|
@ -1414,21 +1417,21 @@ private:
|
|||
*/
|
||||
int32_t subParse(const UnicodeString& text, int32_t& start, char16_t ch, int32_t count,
|
||||
UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal,
|
||||
int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType, SimpleDateFormatMutableNFs &mutableNFs,
|
||||
int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType,
|
||||
int32_t *dayPeriod=NULL) const;
|
||||
|
||||
void parseInt(const UnicodeString& text,
|
||||
Formattable& number,
|
||||
ParsePosition& pos,
|
||||
UBool allowNegative,
|
||||
NumberFormat *fmt) const;
|
||||
const NumberFormat *fmt) const;
|
||||
|
||||
void parseInt(const UnicodeString& text,
|
||||
Formattable& number,
|
||||
int32_t maxDigits,
|
||||
ParsePosition& pos,
|
||||
UBool allowNegative,
|
||||
NumberFormat *fmt) const;
|
||||
const NumberFormat *fmt) const;
|
||||
|
||||
int32_t checkIntSuffix(const UnicodeString& text, int32_t start,
|
||||
int32_t patLoc, UBool isNegative) const;
|
||||
|
@ -1495,6 +1498,16 @@ private:
|
|||
*/
|
||||
int32_t skipUWhiteSpace(const UnicodeString& text, int32_t pos) const;
|
||||
|
||||
/**
|
||||
* Initialize LocalizedNumberFormatter instances used for speedup.
|
||||
*/
|
||||
void initFastNumberFormatters(UErrorCode& status);
|
||||
|
||||
/**
|
||||
* Delete the LocalizedNumberFormatter instances used for speedup.
|
||||
*/
|
||||
void freeFastNumberFormatters();
|
||||
|
||||
/**
|
||||
* Initialize NumberFormat instances used for numbering system overrides.
|
||||
*/
|
||||
|
@ -1611,6 +1624,20 @@ private:
|
|||
*/
|
||||
const SharedNumberFormat **fSharedNumberFormatters;
|
||||
|
||||
enum NumberFormatterKey {
|
||||
SMPDTFMT_NF_1x10,
|
||||
SMPDTFMT_NF_2x10,
|
||||
SMPDTFMT_NF_3x10,
|
||||
SMPDTFMT_NF_4x10,
|
||||
SMPDTFMT_NF_2x2,
|
||||
SMPDTFMT_NF_COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
* Number formatters pre-allocated for fast performance on the most common integer lengths.
|
||||
*/
|
||||
const number::LocalizedNumberFormatter* fFastNumberFormatters[SMPDTFMT_NF_COUNT] = {};
|
||||
|
||||
UBool fHaveDefaultCentury;
|
||||
|
||||
BreakIterator* fCapitalizationBrkIter;
|
||||
|
|
|
@ -76,6 +76,7 @@ void IntlTestRBNF::runIndexedTest(int32_t index, UBool exec, const char* &name,
|
|||
TESTCASE(24, TestLargeNumbers);
|
||||
TESTCASE(25, TestCompactDecimalFormatStyle);
|
||||
TESTCASE(26, TestParseFailure);
|
||||
TESTCASE(27, TestMinMaxIntegerDigitsIgnored);
|
||||
#else
|
||||
TESTCASE(0, TestRBNFDisabled);
|
||||
#endif
|
||||
|
@ -2300,6 +2301,21 @@ void IntlTestRBNF::TestParseFailure() {
|
|||
}
|
||||
}
|
||||
|
||||
void IntlTestRBNF::TestMinMaxIntegerDigitsIgnored() {
|
||||
IcuTestErrorCode status(*this, "TestMinMaxIntegerDigitsIgnored");
|
||||
|
||||
// NOTE: SimpleDateFormat has an optimization that depends on the fact that min/max integer digits
|
||||
// do not affect RBNF (see SimpleDateFormat#zeroPaddingNumber).
|
||||
RuleBasedNumberFormat rbnf(URBNF_SPELLOUT, "en", status);
|
||||
rbnf.setMinimumIntegerDigits(2);
|
||||
rbnf.setMaximumIntegerDigits(3);
|
||||
UnicodeString result;
|
||||
rbnf.format(3, result.remove(), status);
|
||||
assertEquals("Min integer digits are ignored", u"three", result);
|
||||
rbnf.format(1012, result.remove(), status);
|
||||
assertEquals("Max integer digits are ignored", u"one thousand twelve", result);
|
||||
}
|
||||
|
||||
void
|
||||
IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testData[][2], UBool testParsing)
|
||||
{
|
||||
|
|
|
@ -148,6 +148,7 @@ class IntlTestRBNF : public IntlTest {
|
|||
void TestLargeNumbers();
|
||||
void TestCompactDecimalFormatStyle();
|
||||
void TestParseFailure();
|
||||
void TestMinMaxIntegerDigitsIgnored();
|
||||
|
||||
protected:
|
||||
virtual void doTest(RuleBasedNumberFormat* formatter, const char* const testData[][2], UBool testParsing);
|
||||
|
|
Loading…
Add table
Reference in a new issue