ICU-6192 add support for getting field positions to NumberFormat and DateFormat

X-SVN-Rev: 27093
This commit is contained in:
Doug Felt 2009-12-17 22:15:20 +00:00
parent 8a58804ef1
commit 52c23e0a47
22 changed files with 1465 additions and 177 deletions

View file

@ -83,7 +83,7 @@ wintzimpl.o windtfmt.o winnmfmt.o basictz.o dtrule.o rbtz.o tzrule.o tztrans.o v
zonemeta.o zstrfmt.o plurrule.o plurfmt.o dtitvfmt.o dtitvinf.o \
tmunit.o tmutamt.o tmutfmt.o colldata.o bmsearch.o bms.o currpinf.o \
uspoof.o uspoof_impl.o uspoof_build.o uspoof_conf.o uspoof_wsconf.o \
ztrans.o zrule.o vzone.o
ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o
## Header files to install
HEADERS = $(srcdir)/unicode/*.h

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 1997-2008, International Business Machines Corporation and *
* Copyright (C) 1997-2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
@ -141,6 +141,55 @@ DateFormat::format(const Formattable& obj,
//----------------------------------------------------------------------
UnicodeString&
DateFormat::format(const Formattable& obj,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const
{
if (U_FAILURE(status)) return appendTo;
// if the type of the Formattable is double or long, treat it as if it were a Date
UDate date = 0;
switch (obj.getType())
{
case Formattable::kDate:
date = obj.getDate();
break;
case Formattable::kDouble:
date = (UDate)obj.getDouble();
break;
case Formattable::kLong:
date = (UDate)obj.getLong();
break;
default:
status = U_ILLEGAL_ARGUMENT_ERROR;
return appendTo;
}
// Is this right?
//if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex())
// status = U_ILLEGAL_ARGUMENT_ERROR;
return format(date, appendTo, posIter, status);
}
//----------------------------------------------------------------------
// Default implementation for backwards compatibility, subclasses should implement.
UnicodeString&
DateFormat::format(Calendar& /* unused cal */,
UnicodeString& appendTo,
FieldPositionIterator& /* unused posIter */,
UErrorCode& status) const {
if (U_SUCCESS(status)) {
status = U_UNSUPPORTED_ERROR;
}
return appendTo;
}
//----------------------------------------------------------------------
UnicodeString&
DateFormat::format(UDate date, UnicodeString& appendTo, FieldPosition& fieldPosition) const {
if (fCalendar != NULL) {
@ -156,6 +205,20 @@ DateFormat::format(UDate date, UnicodeString& appendTo, FieldPosition& fieldPosi
//----------------------------------------------------------------------
UnicodeString&
DateFormat::format(UDate date, UnicodeString& appendTo, FieldPositionIterator& posIter,
UErrorCode& status) const {
if (fCalendar != NULL) {
fCalendar->setTime(date, status);
if (U_SUCCESS(status)) {
return format(*fCalendar, appendTo, posIter, status);
}
}
return appendTo;
}
//----------------------------------------------------------------------
UnicodeString&
DateFormat::format(UDate date, UnicodeString& appendTo) const
{

View file

@ -41,6 +41,7 @@
#if !UCONFIG_NO_FORMATTING
#include "fphdlimp.h"
#include "unicode/decimfmt.h"
#include "unicode/choicfmt.h"
#include "unicode/ucurr.h"
@ -125,6 +126,8 @@ static void debugout(UnicodeString s) {
#define debug(x)
#endif
// *****************************************************************************
// class DecimalFormat
// *****************************************************************************
@ -983,6 +986,15 @@ DecimalFormat::format(int32_t number,
{
return format((int64_t)number, appendTo, fieldPosition);
}
UnicodeString&
DecimalFormat::format(int32_t number,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const
{
return format((int64_t)number, appendTo, posIter, status);
}
//------------------------------------------------------------------------------
@ -991,11 +1003,26 @@ DecimalFormat::format(int64_t number,
UnicodeString& appendTo,
FieldPosition& fieldPosition) const
{
DigitList digits;
FieldPositionOnlyHandler handler(fieldPosition);
return _format(number, appendTo, handler);
}
// Clears field positions.
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
UnicodeString&
DecimalFormat::format(int64_t number,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const
{
FieldPositionIteratorHandler handler(posIter, status);
return _format(number, appendTo, handler);
}
UnicodeString&
DecimalFormat::_format(int64_t number,
UnicodeString& appendTo,
FieldPositionHandler& handler) const
{
DigitList digits;
// If we are to do rounding, we need to move into the BigDecimal
// domain in order to do divide/multiply correctly.
@ -1020,7 +1047,7 @@ DecimalFormat::format(int64_t number,
digits.set(number * fMultiplier, precision(TRUE));
}
return subformat(appendTo, fieldPosition, digits, TRUE);
return subformat(appendTo, handler, digits, TRUE);
}
//------------------------------------------------------------------------------
@ -1030,23 +1057,35 @@ DecimalFormat::format( double number,
UnicodeString& appendTo,
FieldPosition& fieldPosition) const
{
// Clears field positions.
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
FieldPositionOnlyHandler handler(fieldPosition);
return _format(number, appendTo, handler);
}
UnicodeString&
DecimalFormat::format( double number,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const
{
FieldPositionIteratorHandler handler(posIter, status);
return _format(number, appendTo, handler);
}
UnicodeString&
DecimalFormat::_format( double number,
UnicodeString& appendTo,
FieldPositionHandler& handler) const
{
// Special case for NaN, sets the begin and end index to be the
// the string length of localized name of NaN.
if (uprv_isNaN(number))
{
if (fieldPosition.getField() == NumberFormat::kIntegerField)
fieldPosition.setBeginIndex(appendTo.length());
int begin = appendTo.length();
appendTo += getConstSymbol(DecimalFormatSymbols::kNaNSymbol);
if (fieldPosition.getField() == NumberFormat::kIntegerField)
fieldPosition.setEndIndex(appendTo.length());
handler.addAttribute(kIntegerField, begin, appendTo.length());
addPadding(appendTo, fieldPosition, 0, 0);
addPadding(appendTo, handler, 0, 0);
return appendTo;
}
@ -1082,19 +1121,16 @@ DecimalFormat::format( double number,
// Special case for INFINITE,
if (uprv_isInfinite(number))
{
int32_t prefixLen = appendAffix(appendTo, number, isNegative, TRUE);
if (fieldPosition.getField() == NumberFormat::kIntegerField)
fieldPosition.setBeginIndex(appendTo.length());
int32_t prefixLen = appendAffix(appendTo, number, handler, isNegative, TRUE);
int begin = appendTo.length();
appendTo += getConstSymbol(DecimalFormatSymbols::kInfinitySymbol);
if (fieldPosition.getField() == NumberFormat::kIntegerField)
fieldPosition.setEndIndex(appendTo.length());
handler.addAttribute(kIntegerField, begin, appendTo.length());
int32_t suffixLen = appendAffix(appendTo, number, isNegative, FALSE);
int32_t suffixLen = appendAffix(appendTo, number, handler, isNegative, FALSE);
addPadding(appendTo, fieldPosition, prefixLen, suffixLen);
addPadding(appendTo, handler, prefixLen, suffixLen);
return appendTo;
}
@ -1108,7 +1144,7 @@ DecimalFormat::format( double number,
digits.set(number, precision(FALSE),
!fUseExponentialNotation && !areSignificantDigitsUsed());
return subformat(appendTo, fieldPosition, digits, FALSE);
return subformat(appendTo, handler, digits, FALSE);
}
/**
@ -1179,16 +1215,15 @@ UBool DecimalFormat::isGroupingPosition(int32_t pos) const {
//------------------------------------------------------------------------------
/**
* Complete the formatting of a finite number. On entry, the fDigitList must
* be filled in with the correct digits.
*/
UnicodeString&
DecimalFormat::subformat(UnicodeString& appendTo,
FieldPosition& fieldPosition,
FieldPositionHandler& handler,
DigitList& digits,
UBool isInteger) const
UBool isInteger) const
{
// Gets the localized zero Unicode character.
UChar32 zero = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0);
@ -1222,20 +1257,14 @@ DecimalFormat::subformat(UnicodeString& appendTo,
// Appends the prefix.
double doubleValue = digits.getDouble();
int32_t prefixLen = appendAffix(appendTo, doubleValue, !digits.fIsPositive, TRUE);
int32_t prefixLen = appendAffix(appendTo, doubleValue, handler, !digits.fIsPositive, TRUE);
if (fUseExponentialNotation)
{
// Record field information for caller.
if (fieldPosition.getField() == NumberFormat::kIntegerField)
{
fieldPosition.setBeginIndex(appendTo.length());
fieldPosition.setEndIndex(-1);
}
else if (fieldPosition.getField() == NumberFormat::kFractionField)
{
fieldPosition.setBeginIndex(-1);
}
int currentLength = appendTo.length();
int intBegin = currentLength;
int intEnd = -1;
int fracBegin = -1;
int32_t minFracDig = 0;
if (useSigDig) {
@ -1299,15 +1328,13 @@ DecimalFormat::subformat(UnicodeString& appendTo,
{
if (i == integerDigits)
{
// Record field information for caller.
if (fieldPosition.getField() == NumberFormat::kIntegerField)
fieldPosition.setEndIndex(appendTo.length());
intEnd = appendTo.length();
handler.addAttribute(kIntegerField, intBegin, intEnd);
appendTo += *decimal;
// Record field information for caller.
if (fieldPosition.getField() == NumberFormat::kFractionField)
fieldPosition.setBeginIndex(appendTo.length());
fracBegin = appendTo.length();
handler.addAttribute(kDecimalSeparatorField, fracBegin - 1, fracBegin);
}
// Restores the digit character or pads the buffer with zeros.
UChar32 c = (UChar32)((i < digits.fCount) ?
@ -1316,17 +1343,13 @@ DecimalFormat::subformat(UnicodeString& appendTo,
appendTo += c;
}
// Record field information
if (fieldPosition.getField() == NumberFormat::kIntegerField)
{
if (fieldPosition.getEndIndex() < 0)
fieldPosition.setEndIndex(appendTo.length());
currentLength = appendTo.length();
if (intEnd < 0) {
handler.addAttribute(kIntegerField, intBegin, currentLength);
}
else if (fieldPosition.getField() == NumberFormat::kFractionField)
{
if (fieldPosition.getBeginIndex() < 0)
fieldPosition.setBeginIndex(appendTo.length());
fieldPosition.setEndIndex(appendTo.length());
if (fracBegin > 0) {
handler.addAttribute(kFractionField, fracBegin, currentLength);
}
// The exponent is output using the pattern-specified minimum
@ -1335,6 +1358,9 @@ DecimalFormat::subformat(UnicodeString& appendTo,
// unacceptable inaccuracy.
appendTo += getConstSymbol(DecimalFormatSymbols::kExponentialSymbol);
handler.addAttribute(kExponentSymbolField, currentLength, appendTo.length());
currentLength = appendTo.length();
// For zero values, we force the exponent to zero. We
// must do this here, and not earlier, because the value
// is used to determine integer digit count above.
@ -1343,10 +1369,14 @@ DecimalFormat::subformat(UnicodeString& appendTo,
if (exponent < 0) {
appendTo += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
handler.addAttribute(kExponentSignField, currentLength, appendTo.length());
} else if (fExponentSignAlwaysShown) {
appendTo += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol);
handler.addAttribute(kExponentSignField, currentLength, appendTo.length());
}
currentLength = appendTo.length();
DigitList expDigits;
expDigits.set(exponent);
{
@ -1363,12 +1393,13 @@ DecimalFormat::subformat(UnicodeString& appendTo,
(expDigits.fDigits[i] + zeroDelta) : zero);
appendTo += c;
}
handler.addAttribute(kExponentField, currentLength, appendTo.length());
}
else // Not using exponential notation
{
// Record field information for caller.
if (fieldPosition.getField() == NumberFormat::kIntegerField)
fieldPosition.setBeginIndex(appendTo.length());
int currentLength = appendTo.length();
int intBegin = currentLength;
int32_t sigCount = 0;
int32_t minSigDig = getMinimumSignificantDigits();
@ -1421,13 +1452,17 @@ DecimalFormat::subformat(UnicodeString& appendTo,
// Output grouping separator if necessary.
if (isGroupingPosition(i)) {
currentLength = appendTo.length();
appendTo.append(*grouping);
handler.addAttribute(kGroupingSeparatorField, currentLength, appendTo.length());
}
}
// TODO(dlf): this looks like it was a bug, we marked the int field as ending
// before the zero was generated.
// Record field information for caller.
if (fieldPosition.getField() == NumberFormat::kIntegerField)
fieldPosition.setEndIndex(appendTo.length());
// if (fieldPosition.getField() == NumberFormat::kIntegerField)
// fieldPosition.setEndIndex(appendTo.length());
// Determine whether or not there are any printable fractional
// digits. If we've used up the digits we know there aren't.
@ -1440,13 +1475,17 @@ DecimalFormat::subformat(UnicodeString& appendTo,
if (!fractionPresent && appendTo.length() == sizeBeforeIntegerPart)
appendTo += (zero);
// Output the decimal separator if we always do so.
if (fDecimalSeparatorAlwaysShown || fractionPresent)
appendTo += *decimal;
currentLength = appendTo.length();
handler.addAttribute(kIntegerField, intBegin, currentLength);
// Record field information for caller.
if (fieldPosition.getField() == NumberFormat::kFractionField)
fieldPosition.setBeginIndex(appendTo.length());
// Output the decimal separator if we always do so.
if (fDecimalSeparatorAlwaysShown || fractionPresent) {
appendTo += *decimal;
handler.addAttribute(kDecimalSeparatorField, currentLength, appendTo.length());
currentLength = appendTo.length();
}
int fracBegin = currentLength;
count = useSigDig ? INT32_MAX : getMaximumFractionDigits();
if (useSigDig && (sigCount == maxSigDig ||
@ -1495,14 +1534,12 @@ DecimalFormat::subformat(UnicodeString& appendTo,
}
}
// Record field information for caller.
if (fieldPosition.getField() == NumberFormat::kFractionField)
fieldPosition.setEndIndex(appendTo.length());
handler.addAttribute(kFractionField, fracBegin, appendTo.length());
}
int32_t suffixLen = appendAffix(appendTo, doubleValue, !digits.fIsPositive, FALSE);
int32_t suffixLen = appendAffix(appendTo, doubleValue, handler, !digits.fIsPositive, FALSE);
addPadding(appendTo, fieldPosition, prefixLen, suffixLen);
addPadding(appendTo, handler, prefixLen, suffixLen);
return appendTo;
}
@ -1511,7 +1548,7 @@ DecimalFormat::subformat(UnicodeString& appendTo,
* @param result the string to be padded
*/
void DecimalFormat::addPadding(UnicodeString& appendTo,
FieldPosition& fieldPosition,
FieldPositionHandler& handler,
int32_t prefixLen,
int32_t suffixLen) const
{
@ -1536,10 +1573,8 @@ void DecimalFormat::addPadding(UnicodeString& appendTo,
appendTo += padding;
break;
}
if (fPadPosition == kPadBeforePrefix ||
fPadPosition == kPadAfterPrefix) {
fieldPosition.setBeginIndex(len + fieldPosition.getBeginIndex());
fieldPosition.setEndIndex(len + fieldPosition.getEndIndex());
if (fPadPosition == kPadBeforePrefix || fPadPosition == kPadAfterPrefix) {
handler.shiftLast(len);
}
}
}
@ -2974,17 +3009,18 @@ DecimalFormat::toLocalizedPattern(UnicodeString& result) const
* In all other cases, the 'pluralCount' is null, which means it is not needed.
*/
void DecimalFormat::expandAffixes(const UnicodeString* pluralCount) {
FieldPositionHandler none;
if (fPosPrefixPattern != 0) {
expandAffix(*fPosPrefixPattern, fPositivePrefix, 0, FALSE, pluralCount);
expandAffix(*fPosPrefixPattern, fPositivePrefix, 0, none, FALSE, pluralCount);
}
if (fPosSuffixPattern != 0) {
expandAffix(*fPosSuffixPattern, fPositiveSuffix, 0, FALSE, pluralCount);
expandAffix(*fPosSuffixPattern, fPositiveSuffix, 0, none, FALSE, pluralCount);
}
if (fNegPrefixPattern != 0) {
expandAffix(*fNegPrefixPattern, fNegativePrefix, 0, FALSE, pluralCount);
expandAffix(*fNegPrefixPattern, fNegativePrefix, 0, none, FALSE, pluralCount);
}
if (fNegSuffixPattern != 0) {
expandAffix(*fNegSuffixPattern, fNegativeSuffix, 0, FALSE, pluralCount);
expandAffix(*fNegSuffixPattern, fNegativeSuffix, 0, none, FALSE, pluralCount);
}
#ifdef FMT_DEBUG
UnicodeString s;
@ -3041,6 +3077,7 @@ void DecimalFormat::expandAffixes(const UnicodeString* pluralCount) {
void DecimalFormat::expandAffix(const UnicodeString& pattern,
UnicodeString& affix,
double number,
FieldPositionHandler& handler,
UBool doFormat,
const UnicodeString* pluralCount) const {
affix.remove();
@ -3050,6 +3087,7 @@ void DecimalFormat::expandAffix(const UnicodeString& pattern,
if (c == kQuote) {
c = pattern.char32At(i);
i += U16_LENGTH(c);
int beginIdx = affix.length();
switch (c) {
case kCurrencySign: {
// As of ICU 2.2 we use the currency object, and
@ -3090,14 +3128,17 @@ void DecimalFormat::expandAffix(const UnicodeString& pattern,
Locale::getDefault().getName(), &isChoiceFormat,
pluralCountChar, &len, &ec);
affix += UnicodeString(s, len);
handler.addAttribute(kCurrencyField, beginIdx, affix.length());
} else if(intl) {
affix += currencyUChars;
handler.addAttribute(kCurrencyField, beginIdx, affix.length());
} else {
int32_t len;
UBool isChoiceFormat;
// If fSymbols is NULL, use default locale
const UChar* s = ucurr_getName(currencyUChars, fSymbols != NULL ? fSymbols->getLocale().getName() : Locale::getDefault().getName(),
UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec);
const UChar* s = ucurr_getName(currencyUChars,
fSymbols != NULL ? fSymbols->getLocale().getName() : Locale::getDefault().getName(),
UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec);
if (isChoiceFormat) {
// Two modes here: If doFormat is false, we set up
// currencyChoice. If doFormat is true, we use the
@ -3141,11 +3182,13 @@ void DecimalFormat::expandAffix(const UnicodeString& pattern,
// We only arrive here if the currency choice
// format in the locale data is INVALID.
affix += currencyUChars;
handler.addAttribute(kCurrencyField, beginIdx, affix.length());
}
}
continue;
}
affix += UnicodeString(s, len);
handler.addAttribute(kCurrencyField, beginIdx, affix.length());
}
} else {
if(intl) {
@ -3153,20 +3196,25 @@ void DecimalFormat::expandAffix(const UnicodeString& pattern,
} else {
affix += getConstSymbol(DecimalFormatSymbols::kCurrencySymbol);
}
handler.addAttribute(kCurrencyField, beginIdx, affix.length());
}
break;
}
case kPatternPercent:
affix += getConstSymbol(DecimalFormatSymbols::kPercentSymbol);
handler.addAttribute(kPercentField, beginIdx, affix.length());
break;
case kPatternPerMill:
affix += getConstSymbol(DecimalFormatSymbols::kPerMillSymbol);
handler.addAttribute(kPermillField, beginIdx, affix.length());
break;
case kPatternPlus:
affix += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol);
handler.addAttribute(kSignField, beginIdx, affix.length());
break;
case kPatternMinus:
affix += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
handler.addAttribute(kSignField, beginIdx, affix.length());
break;
default:
affix.append(c);
@ -3186,6 +3234,7 @@ void DecimalFormat::expandAffix(const UnicodeString& pattern,
* @param isPrefix
*/
int32_t DecimalFormat::appendAffix(UnicodeString& buf, double number,
FieldPositionHandler& handler,
UBool isNegative, UBool isPrefix) const {
// plural format precedes choice format
if (fCurrencyChoice != 0 &&
@ -3198,7 +3247,7 @@ int32_t DecimalFormat::appendAffix(UnicodeString& buf, double number,
}
if (affixPat) {
UnicodeString affixBuf;
expandAffix(*affixPat, affixBuf, number, TRUE, NULL);
expandAffix(*affixPat, affixBuf, number, handler, TRUE, NULL);
buf.append(affixBuf);
return affixBuf.length();
}
@ -3228,7 +3277,42 @@ int32_t DecimalFormat::appendAffix(UnicodeString& buf, double number,
affix = isNegative ? &fNegativeSuffix : &fPositiveSuffix;
}
}
int32_t begin = (int) buf.length();
buf.append(*affix);
if (handler.isRecording()) {
int32_t offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kCurrencySymbol));
if (offset > -1) {
UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kCurrencySymbol);
handler.addAttribute(kCurrencyField, begin + offset, begin + offset + aff.length());
}
offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol));
if (offset > -1) {
UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol);
handler.addAttribute(kCurrencyField, begin + offset, begin + offset + aff.length());
}
offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol));
if (offset > -1) {
UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
handler.addAttribute(kSignField, begin + offset, begin + offset + aff.length());
}
offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kPercentSymbol));
if (offset > -1) {
UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kPercentSymbol);
handler.addAttribute(kPercentField, begin + offset, begin + offset + aff.length());
}
offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kPerMillSymbol));
if (offset > -1) {
UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kPerMillSymbol);
handler.addAttribute(kPermillField, begin + offset, begin + offset + aff.length());
}
}
return affix->length();
}
@ -4568,7 +4652,6 @@ DecimalFormat::copyHashForAffix(const Hashtable* source,
}
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 1997-2007, International Business Machines Corporation and *
* Copyright (C) 1997-2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
@ -108,7 +108,23 @@ Format::format(const Formattable& obj,
return format(obj, toAppendTo, pos, status);
}
// -------------------------------------
// Default implementation sets unsupported error; subclasses should
// override.
UnicodeString&
Format::format(const Formattable& /* unused obj */,
UnicodeString& toAppendTo,
FieldPositionIterator& /* unused posIter */,
UErrorCode& status) const
{
if (!U_FAILURE(status)) {
status = U_UNSUPPORTED_ERROR;
}
return toAppendTo;
}
// -------------------------------------
// Parses the source string and create the corresponding
// result object. Checks the parse position for errors.

View file

@ -0,0 +1,110 @@
/*
*******************************************************************************
* Copyright (C) 2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
#if !UCONFIG_NO_FORMAT
#include "fphdlimp.h"
// utility FieldPositionHandler
// base class, null implementation
FieldPositionHandler::~FieldPositionHandler() {
}
void
FieldPositionHandler::addAttribute(int32_t, int32_t, int32_t) {
}
void
FieldPositionHandler::shiftLast(int32_t) {
}
UBool
FieldPositionHandler::isRecording(void) {
return FALSE;
}
// utility subclass FieldPositionOnlyHandler
FieldPositionOnlyHandler::FieldPositionOnlyHandler(FieldPosition& _pos)
: pos(_pos) {
}
FieldPositionOnlyHandler::~FieldPositionOnlyHandler() {
}
void
FieldPositionOnlyHandler::addAttribute(int32_t id, int32_t start, int32_t limit) {
if (pos.getField() == id) {
pos.setBeginIndex(start);
pos.setEndIndex(limit);
}
}
void
FieldPositionOnlyHandler::shiftLast(int32_t delta) {
if (delta != 0 && pos.getField() != FieldPosition::DONT_CARE && pos.getBeginIndex() != -1) {
pos.setBeginIndex(delta + pos.getBeginIndex());
pos.setEndIndex(delta + pos.getEndIndex());
}
}
UBool
FieldPositionOnlyHandler::isRecording(void) {
return pos.getField() != FieldPosition::DONT_CARE;
}
// utility subclass FieldPositionIteratorHandler
FieldPositionIteratorHandler::FieldPositionIteratorHandler(FieldPositionIterator& posIter,
UErrorCode& _status)
: iter(posIter), vec(NULL), status(_status) {
if (U_SUCCESS(status)) {
vec = new UVector32(status);
}
}
FieldPositionIteratorHandler::~FieldPositionIteratorHandler() {
// setData adopts the vec regardless of status, so it's safe to null it
iter.setData(vec, status);
vec = NULL;
}
void
FieldPositionIteratorHandler::addAttribute(int32_t id, int32_t start, int32_t limit) {
if (U_SUCCESS(status) && start < limit) {
int32_t size = vec->size();
vec->addElement(id, status);
vec->addElement(start, status);
vec->addElement(limit, status);
if (!U_SUCCESS(status)) {
vec->setSize(size);
}
}
}
void
FieldPositionIteratorHandler::shiftLast(int32_t delta) {
if (U_SUCCESS(status) && delta != 0) {
int32_t i = vec->size();
if (i > 0) {
--i;
vec->setElementAt(delta + vec->elementAti(i), i);
--i;
vec->setElementAt(delta + vec->elementAti(i), i);
}
}
}
UBool
FieldPositionIteratorHandler::isRecording(void) {
return U_SUCCESS(status);
}
#endif /* !UCONFIG_NO_FORMAT */

View file

@ -0,0 +1,72 @@
/*
*******************************************************************************
* Copyright (C) 2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
#ifndef FPHDLIMP_H
#define FPHDLIMP_H
#if !UCONFIG_NO_FORMAT
#include "unicode/fieldpos.h"
#include "unicode/fpositer.h"
U_NAMESPACE_BEGIN
// utility FieldPositionHandler
// base class, null implementation
class FieldPositionHandler {
public:
virtual ~FieldPositionHandler();
virtual void addAttribute(int32_t id, int32_t start, int32_t limit);
virtual void shiftLast(int32_t delta);
virtual UBool isRecording(void);
};
// utility subclass FieldPositionOnlyHandler
class FieldPositionOnlyHandler : public FieldPositionHandler {
FieldPosition& pos;
public:
FieldPositionOnlyHandler(FieldPosition& pos);
virtual ~FieldPositionOnlyHandler();
virtual void addAttribute(int32_t id, int32_t start, int32_t limit);
virtual void shiftLast(int32_t delta);
virtual UBool isRecording(void);
};
// utility subclass FieldPositionIteratorHandler
class FieldPositionIteratorHandler : public FieldPositionHandler {
FieldPositionIterator& iter;
UVector32* vec;
UErrorCode status;
// Note, we keep a reference to status, so if status is on the stack, we have
// to be destroyed before status goes out of scope. Easiest thing is to
// allocate us on the stack in the same (or narrower) scope as status has.
// This attempts to encourage that by blocking heap allocation.
void *operator new(size_t s);
void *operator new[](size_t s);
public:
FieldPositionIteratorHandler(FieldPositionIterator& posIter, UErrorCode& status);
~FieldPositionIteratorHandler();
virtual void addAttribute(int32_t id, int32_t start, int32_t limit);
virtual void shiftLast(int32_t delta);
virtual UBool isRecording(void);
};
U_NAMESPACE_END
#endif /* !UCONFIG_NO_FORMAT */
#endif /* FPHDLIMP_H */

View file

@ -0,0 +1,85 @@
/*
******************************************************************************
* Copyright (C) 2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
******************************************************************************
* Date Name Description
* 12/14/09 doug Creation.
******************************************************************************
*/
#include "unicode/fpositer.h"
#include "cmemory.h"
U_NAMESPACE_BEGIN
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FieldPositionIterator)
FieldPositionIterator::~FieldPositionIterator() {
delete data;
data = NULL;
pos = -1;
}
FieldPositionIterator::FieldPositionIterator()
: data(NULL), pos(-1) {
}
FieldPositionIterator::FieldPositionIterator(const FieldPositionIterator &rhs)
: UObject(rhs), data(NULL), pos(rhs.pos) {
if (rhs.data) {
UErrorCode status = U_ZERO_ERROR;
data = new UVector32(status);
data->assign(*rhs.data, status);
if (status != U_ZERO_ERROR) {
delete data;
data = NULL;
pos = -1;
}
}
}
UBool FieldPositionIterator::operator==(const FieldPositionIterator &rhs) const {
if (rhs == *this) {
return TRUE;
}
if (pos != rhs.pos) {
return FALSE;
}
if (!data) {
return rhs.data == NULL;
}
return rhs.data ? data->operator==(*rhs.data) : FALSE;
}
void FieldPositionIterator::setData(UVector32 *adopt, UErrorCode& status) {
// Verify that adopt has valid data, and update status if it doesn't.
if (U_SUCCESS(status)) {
if (adopt) {
if ((adopt->size() % 3) != 0) {
status = U_ILLEGAL_ARGUMENT_ERROR;
} else {
for (int i = 1; i < adopt->size(); i += 3) {
if (adopt->elementAti(i) >= adopt->elementAti(i+1)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
break;
}
}
}
}
}
// We own the data, even if status is in error, so we need to delete it now
// if we're not keeping track of it.
if (!U_SUCCESS(status)) {
delete adopt;
return;
}
delete data;
data = adopt;
pos = adopt == NULL ? -1 : 0;
}
U_NAMESPACE_END

View file

@ -1362,6 +1362,58 @@
/>
</FileConfiguration>
</File>
<File
RelativePath=".\fphdlimp.cpp"
>
</File>
<File
RelativePath=".\fphdlimp.h"
>
</File>
<File
RelativePath=".\fpositer.cpp"
>
</File>
<File
RelativePath=".\unicode\fpositer.h"
>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCustomBuildTool"
CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode&#x0D;&#x0A;"
Outputs="..\..\include\unicode\$(InputFileName)"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode&#x0D;&#x0A;"
Outputs="..\..\include\unicode\$(InputFileName)"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCustomBuildTool"
CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode&#x0D;&#x0A;"
Outputs="..\..\include\unicode\$(InputFileName)"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCustomBuildTool"
CommandLine="copy &quot;$(InputPath)&quot; ..\..\include\unicode&#x0D;&#x0A;"
Outputs="..\..\include\unicode\$(InputFileName)"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\decimfmt.cpp"
>

View file

@ -273,10 +273,105 @@ NumberFormat::operator==(const Format& that) const
u_strcmp(fCurrency, other->fCurrency) == 0)));
}
// -------------------------------------
// Default implementation sets unsupported error; subclasses should
// override.
UnicodeString&
NumberFormat::format(double /* unused number */,
UnicodeString& toAppendTo,
FieldPositionIterator& /* unused posIter */,
UErrorCode& status) const
{
if (!U_FAILURE(status)) {
status = U_UNSUPPORTED_ERROR;
}
return toAppendTo;
}
// -------------------------------------
// Default implementation sets unsupported error; subclasses should
// override.
UnicodeString&
NumberFormat::format(int32_t /* unused number */,
UnicodeString& toAppendTo,
FieldPositionIterator& /* unused posIter */,
UErrorCode& status) const
{
if (!U_FAILURE(status)) {
status = U_UNSUPPORTED_ERROR;
}
return toAppendTo;
}
// -------------------------------------
// Default implementation sets unsupported error; subclasses should
// override.
UnicodeString&
NumberFormat::format(int64_t /* unused number */,
UnicodeString& toAppendTo,
FieldPositionIterator& /* unused posIter */,
UErrorCode& status) const
{
if (!U_FAILURE(status)) {
status = U_UNSUPPORTED_ERROR;
}
return toAppendTo;
}
// -------------------------------------x
// Formats the number object and save the format
// result in the toAppendTo string buffer.
// utility to save/restore state, used in two overloads
// of format(const Formattable&...) below.
class ArgExtractor {
NumberFormat *ncnf;
const Formattable* num;
UBool setCurr;
UChar save[4];
public:
ArgExtractor(const NumberFormat& nf, const Formattable& obj, UErrorCode& status);
~ArgExtractor();
const Formattable* number(void) const;
};
inline const Formattable*
ArgExtractor::number(void) const {
return num;
}
ArgExtractor::ArgExtractor(const NumberFormat& nf, const Formattable& obj, UErrorCode& status)
: ncnf((NumberFormat*) &nf), num(&obj), setCurr(FALSE) {
const UObject* o = obj.getObject(); // most commonly o==NULL
if (o != NULL &&
o->getDynamicClassID() == CurrencyAmount::getStaticClassID()) {
// getISOCurrency() returns a pointer to internal storage, so we
// copy it to retain it across the call to setCurrency().
const CurrencyAmount* amt = (const CurrencyAmount*) o;
const UChar* curr = amt->getISOCurrency();
u_strcpy(save, nf.getCurrency());
setCurr = (u_strcmp(curr, save) != 0);
if (setCurr) {
ncnf->setCurrency(curr, status);
}
num = &amt->getNumber();
}
}
ArgExtractor::~ArgExtractor() {
if (setCurr) {
UErrorCode ok = U_ZERO_ERROR;
ncnf->setCurrency(save, ok); // always restore currency
}
}
UnicodeString&
NumberFormat::format(const Formattable& obj,
UnicodeString& appendTo,
@ -285,25 +380,8 @@ NumberFormat::format(const Formattable& obj,
{
if (U_FAILURE(status)) return appendTo;
NumberFormat* nonconst = (NumberFormat*) this;
const Formattable* n = &obj;
UChar save[4];
UBool setCurr = FALSE;
const UObject* o = obj.getObject(); // most commonly o==NULL
if (o != NULL &&
o->getDynamicClassID() == CurrencyAmount::getStaticClassID()) {
// getISOCurrency() returns a pointer to internal storage, so we
// copy it to retain it across the call to setCurrency().
const CurrencyAmount* amt = (const CurrencyAmount*) o;
const UChar* curr = amt->getISOCurrency();
u_strcpy(save, getCurrency());
setCurr = (u_strcmp(curr, save) != 0);
if (setCurr) {
nonconst->setCurrency(curr, status);
}
n = &amt->getNumber();
}
ArgExtractor arg(*this, obj, status);
const Formattable *n = arg.number();
switch (n->getType()) {
case Formattable::kDouble:
@ -320,10 +398,39 @@ NumberFormat::format(const Formattable& obj,
break;
}
if (setCurr) {
UErrorCode ok = U_ZERO_ERROR;
nonconst->setCurrency(save, ok); // always restore currency
return appendTo;
}
// -------------------------------------x
// Formats the number object and save the format
// result in the toAppendTo string buffer.
UnicodeString&
NumberFormat::format(const Formattable& obj,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const
{
if (U_FAILURE(status)) return appendTo;
ArgExtractor arg(*this, obj, status);
const Formattable *n = arg.number();
switch (n->getType()) {
case Formattable::kDouble:
format(n->getDouble(), appendTo, posIter, status);
break;
case Formattable::kLong:
format(n->getLong(), appendTo, posIter, status);
break;
case Formattable::kInt64:
format(n->getInt64(), appendTo, posIter, status);
break;
default:
status = U_INVALID_FORMAT_ERROR;
break;
}
return appendTo;
}

View file

@ -47,7 +47,8 @@
#include "unicode/vtzone.h"
#include "olsontz.h"
#include "util.h"
#include "gregoimp.h"
#include "fphdlimp.h"
#include "gregoimp.h"
#include "hebrwcal.h"
#include "cstring.h"
#include "uassert.h"
@ -741,10 +742,27 @@ void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& s
UnicodeString&
SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const
{
UErrorCode status = U_ZERO_ERROR;
pos.setBeginIndex(0);
pos.setEndIndex(0);
UErrorCode status = U_ZERO_ERROR;
FieldPositionOnlyHandler handler(pos);
return _format(cal, appendTo, handler, status);
}
//----------------------------------------------------------------------
UnicodeString&
SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo,
FieldPositionIterator& posIter, UErrorCode& status) const
{
FieldPositionIteratorHandler handler(posIter, status);
return _format(cal, appendTo, handler, status);
}
//----------------------------------------------------------------------
UnicodeString&
SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, FieldPositionHandler& handler,
UErrorCode& status) const
{
Calendar *workCal = &cal;
TimeZone *backupTZ = NULL;
if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) {
@ -769,7 +787,7 @@ SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition&
// 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, pos, *workCal, status);
subFormat(appendTo, prevCh, count, handler, *workCal, status);
count = 0;
}
if (ch == QUOTE) {
@ -797,7 +815,7 @@ SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition&
// Format the last item in the pattern, if any
if (count > 0) {
subFormat(appendTo, prevCh, count, pos, *workCal, status);
subFormat(appendTo, prevCh, count, handler, *workCal, status);
}
if (backupTZ != NULL) {
@ -805,30 +823,9 @@ SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition&
fCalendar->adoptTimeZone(backupTZ);
}
// and if something failed (e.g., an invalid format character), reset our FieldPosition
// to (0, 0) to show that
// {sfb} look at this later- are these being set correctly?
if (U_FAILURE(status)) {
pos.setBeginIndex(0);
pos.setEndIndex(0);
}
return appendTo;
}
UnicodeString&
SimpleDateFormat::format(const Formattable& obj,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const
{
// this is just here to get around the hiding problem
// (the previous format() override would hide the version of
// format() on DateFormat that this function correspond to, so we
// have to redefine it here)
return DateFormat::format(obj, appendTo, pos, status);
}
//----------------------------------------------------------------------
/* Map calendar field into calendar field level.
@ -1435,7 +1432,7 @@ void
SimpleDateFormat::subFormat(UnicodeString &appendTo,
UChar ch,
int32_t count,
FieldPosition& pos,
FieldPositionHandler& handler,
Calendar& cal,
UErrorCode& status) const
{
@ -1493,9 +1490,9 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
case UDAT_YEAR_FIELD:
case UDAT_YEAR_WOY_FIELD:
if(count == 2)
zeroPaddingNumber(currentNumberFormat,appendTo, value, 2, 2);
zeroPaddingNumber(currentNumberFormat, appendTo, value, 2, 2);
else
zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
zeroPaddingNumber(currentNumberFormat, appendTo, value, count, maxIntCount);
break;
// for "MMMM", write out the whole month name, for "MMM", write out the month
@ -1727,14 +1724,9 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
break;
}
// if the field we're formatting is the one the FieldPosition says it's interested
// in, fill in the FieldPosition with this field's positions
if (pos.getBeginIndex() == pos.getEndIndex() &&
pos.getField() == fgPatternIndexToDateFormatField[patternCharIndex]) {
pos.setBeginIndex(beginOffset);
pos.setEndIndex(appendTo.length());
}
handler.addAttribute(fgPatternIndexToDateFormatField[patternCharIndex], beginOffset, appendTo.length());
}
//----------------------------------------------------------------------
NumberFormat *
@ -1748,7 +1740,8 @@ SimpleDateFormat::getNumberFormat(UDateFormatField index) const {
//----------------------------------------------------------------------
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);

View file

@ -38,6 +38,50 @@ static void verifyIsSimpleDateFormat(const UDateFormat* fmt, UErrorCode *status)
}
}
// This mirrors the correspondence between the
// SimpleDateFormat::fgPatternIndexToDateFormatField and
// SimpleDateFormat::fgPatternIndexToCalendarField arrays.
static UCalendarDateFields gDateFieldMapping[] = {
UCAL_ERA, // UDAT_ERA_FIELD = 0
UCAL_YEAR, // UDAT_YEAR_FIELD = 1
UCAL_MONTH, // UDAT_MONTH_FIELD = 2
UCAL_DATE, // UDAT_DATE_FIELD = 3
UCAL_HOUR_OF_DAY, // UDAT_HOUR_OF_DAY1_FIELD = 4
UCAL_HOUR_OF_DAY, // UDAT_HOUR_OF_DAY0_FIELD = 5
UCAL_MINUTE, // UDAT_MINUTE_FIELD = 6
UCAL_SECOND, // UDAT_SECOND_FIELD = 7
UCAL_MILLISECOND, // UDAT_FRACTIONAL_SECOND_FIELD = 8
UCAL_DAY_OF_WEEK, // UDAT_DAY_OF_WEEK_FIELD = 9
UCAL_DAY_OF_YEAR, // UDAT_DAY_OF_YEAR_FIELD = 10
UCAL_DAY_OF_WEEK_IN_MONTH, // UDAT_DAY_OF_WEEK_IN_MONTH_FIELD = 11
UCAL_WEEK_OF_YEAR, // UDAT_WEEK_OF_YEAR_FIELD = 12
UCAL_WEEK_OF_MONTH, // UDAT_WEEK_OF_MONTH_FIELD = 13
UCAL_AM_PM, // UDAT_AM_PM_FIELD = 14
UCAL_HOUR, // UDAT_HOUR1_FIELD = 15
UCAL_HOUR, // UDAT_HOUR0_FIELD = 16
UCAL_ZONE_OFFSET, // UDAT_TIMEZONE_FIELD = 17
UCAL_YEAR_WOY, // UDAT_YEAR_WOY_FIELD = 18
UCAL_DOW_LOCAL, // UDAT_DOW_LOCAL_FIELD = 19
UCAL_EXTENDED_YEAR, // UDAT_EXTENDED_YEAR_FIELD = 20
UCAL_JULIAN_DAY, // UDAT_JULIAN_DAY_FIELD = 21
UCAL_MILLISECONDS_IN_DAY, // UDAT_MILLISECONDS_IN_DAY_FIELD = 22
UCAL_ZONE_OFFSET, // UDAT_TIMEZONE_RFC_FIELD = 23
// UCAL_DST_OFFSET also
UCAL_ZONE_OFFSET, // UDAT_TIMEZONE_GENERIC_FIELD = 24
UCAL_DOW_LOCAL, // UDAT_STANDALONE_DAY_FIELD = 25
UCAL_MONTH, // UDAT_STANDALONE_MONTH_FIELD = 26
UCAL_MONTH, // UDAT_QUARTER_FIELD = 27
UCAL_MONTH, // UDAT_STANDALONE_QUARTER_FIELD = 28
UCAL_ZONE_OFFSET, // UDAT_TIMEZONE_SPECIAL_FIELD = 29
UCAL_FIELD_COUNT, // UDAT_FIELD_COUNT = 30
// UCAL_IS_LEAP_MONTH is not the target of a mapping
};
U_CAPI UCalendarDateFields U_EXPORT2
udat_toCalendarDateField(UDateFormatField field) {
return gDateFieldMapping[field];
}
U_CAPI UDateFormat* U_EXPORT2
udat_open(UDateFormatStyle timeStyle,
UDateFormatStyle dateStyle,

View file

@ -230,6 +230,25 @@ public:
FieldPosition& pos,
UErrorCode& status) const;
/**
* Format an object to produce a string. This method handles Formattable
* objects with a UDate type. If a the Formattable object type is not a Date,
* then it returns a failing UErrorCode.
*
* @param obj The object to format. Must be a Date.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call. Field values
* are defined in UDateFormatField.
* @param status Output param filled with success/failure status.
* @return Reference to 'appendTo' parameter.
* @draft ICU 4.4
*/
virtual UnicodeString& format(const Formattable& obj,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Formats a date into a date/time string. This is an abstract method which
* concrete subclasses must implement.
@ -267,6 +286,28 @@ public:
UnicodeString& appendTo,
FieldPosition& fieldPosition) const = 0;
/**
* Formats a date into a date/time string. Subclasses should implement this method.
*
* @param cal Calendar set to the date and time to be formatted
* into a date/time string. When the calendar type is
* different from the internal calendar held by this
* DateFormat instance, the date and the time zone will
* be inherited from the input calendar, but other calendar
* field values will be calculated by the internal calendar.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call. Field values
* are defined in UDateFormatField.
* @param status error status.
* @return Reference to 'appendTo' parameter.
* @draft ICU 4.4
*/
virtual UnicodeString& format(Calendar& cal,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Formats a UDate into a date/time string.
* <P>
@ -298,6 +339,23 @@ public:
UnicodeString& appendTo,
FieldPosition& fieldPosition) const;
/**
* Formats a UDate into a date/time string.
*
* @param date UDate to be formatted into a date/time string.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call. Field values
* are defined in UDateFormatField.
* @param status error status.
* @return Reference to 'appendTo' parameter.
* @draft ICU 4.4
*/
UnicodeString& format(UDate date,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Formats a UDate into a date/time string. If there is a problem, you won't
* know, using this method. Use the overloaded format() method which takes a

View file

@ -35,6 +35,7 @@
#include "unicode/dcfmtsym.h"
#include "unicode/numfmt.h"
#include "unicode/locid.h"
#include "unicode/fpositer.h"
union UHashTok;
@ -58,6 +59,7 @@ class DigitList;
class ChoiceFormat;
class CurrencyPluralInfo;
class Hashtable;
class FieldPositionHandler;
/**
* DecimalFormat is a concrete subclass of NumberFormat that formats decimal
@ -848,6 +850,24 @@ public:
virtual UnicodeString& format(double number,
UnicodeString& appendTo,
FieldPosition& pos) const;
/**
* Format a double or long number using base-10 representation.
*
* @param number The value to be formatted.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call.
* @param status Output param filled with success/failure status.
* @return Reference to 'appendTo' parameter.
* @draft 4.4
*/
virtual UnicodeString& format(double number,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Format a long number using base-10 representation.
*
@ -862,6 +882,24 @@ public:
virtual UnicodeString& format(int32_t number,
UnicodeString& appendTo,
FieldPosition& pos) const;
/**
* Format a long number using base-10 representation.
*
* @param number The value to be formatted.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call.
* @param status Output param filled with success/failure status.
* @return Reference to 'appendTo' parameter.
* @draft 4.4
*/
virtual UnicodeString& format(int32_t number,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Format an int64 number using base-10 representation.
*
@ -877,6 +915,23 @@ public:
UnicodeString& appendTo,
FieldPosition& pos) const;
/**
* Format an int64 number using base-10 representation.
*
* @param number The value to be formatted.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call.
* @param status Output param filled with success/failure status.
* @return Reference to 'appendTo' parameter.
* @draft 4.4
*/
virtual UnicodeString& format(int64_t number,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Format a Formattable using base-10 representation.
*
@ -1804,16 +1859,15 @@ private:
*
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param fieldPosition On input: an alignment field, if desired.
* On output: the offsets of the alignment field.
* @param handler Records information about field positions.
* @param digits the digits to be formatted.
* @param isInteger if TRUE format the digits as Integer.
* @return Reference to 'appendTo' parameter.
*/
UnicodeString& subformat(UnicodeString& appendTo,
FieldPosition& fieldPosition,
DigitList& digits,
UBool isInteger) const;
FieldPositionHandler& handler,
DigitList& digits,
UBool isInteger) const;
void parse(const UnicodeString& text,
@ -1884,8 +1938,11 @@ private:
*/
inline const UnicodeString &getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol symbol) const;
int32_t appendAffix(UnicodeString& buf, double number,
UBool isNegative, UBool isPrefix) const;
int32_t appendAffix(UnicodeString& buf,
double number,
FieldPositionHandler& handler,
UBool isNegative,
UBool isPrefix) const;
/**
* Append an affix to the given UnicodeString, using quotes if
@ -1902,6 +1959,7 @@ private:
void expandAffix(const UnicodeString& pattern,
UnicodeString& affix,
double number,
FieldPositionHandler& handler,
UBool doFormat,
const UnicodeString* pluralCount) const;
@ -1910,7 +1968,7 @@ private:
static double round(double a, ERoundingMode mode, UBool isNegative);
void addPadding(UnicodeString& appendTo,
FieldPosition& fieldPosition,
FieldPositionHandler& handler,
int32_t prefixLen, int32_t suffixLen) const;
UBool isGroupingPosition(int32_t pos) const;
@ -1950,6 +2008,13 @@ private:
void copyHashForAffix(const Hashtable* source,
Hashtable* target, UErrorCode& status);
UnicodeString& _format(int64_t number,
UnicodeString& appendTo,
FieldPositionHandler& handler) const;
UnicodeString& _format(double number,
UnicodeString& appendTo,
FieldPositionHandler& handler) const;
// currency sign count
enum {
fgCurrencySignCountZero,

View file

@ -1,6 +1,6 @@
/*
********************************************************************************
* Copyright (C) 1997-2006, International Business Machines Corporation and others.
* Copyright (C) 1997-2009, International Business Machines Corporation and others.
* All Rights Reserved.
********************************************************************************
*
@ -34,6 +34,7 @@
#include "unicode/unistr.h"
#include "unicode/fmtable.h"
#include "unicode/fieldpos.h"
#include "unicode/fpositer.h"
#include "unicode/parsepos.h"
#include "unicode/parseerr.h"
#include "unicode/locid.h"
@ -159,6 +160,26 @@ public:
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const = 0;
/**
* Format an object to produce a string. Subclasses should override this
* method. This method allows polymorphic formatting of Formattable objects.
* If a subclass of Format receives a Formattable object type it doesn't
* handle (e.g., if a numeric Formattable is passed to a DateFormat object)
* then it returns a failing UErrorCode.
*
* @param obj The object to format.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call.
* @param status Output param filled with success/failure status.
* @return Reference to 'appendTo' parameter.
* @stable draft 4.4
*/
virtual UnicodeString& format(const Formattable& obj,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Parse a string to produce an object. This is a pure virtual

View file

@ -0,0 +1,142 @@
/*
********************************************************************************
* Copyright (C) 2009, International Business Machines
* Corporation and others. All Rights Reserved.
********************************************************************************
*
* File attiter.h
*
* Modification History:
*
* Date Name Description
* 12/15/2009 dougfelt Created
********************************************************************************
*/
#ifndef FPOSITER_H
#define FPOSITER_H
#include "unicode/utypes.h"
#include "unicode/uobject.h"
/**
* \file
* \brief C++ API: FieldPosition Iterator.
*/
#if UCONFIG_NO_FORMATTING
U_NAMESPACE_BEGIN
/*
* Allow the declaration of APIs with pointers to FieldPositionIterator
* even when formatting is removed from the build.
*/
class FieldPositionIterator;
U_NAMESPACE_END
#else
#include "unicode/fieldpos.h"
#include "unicode/umisc.h"
#include "uvectr32.h"
U_NAMESPACE_BEGIN
/**
* FieldPositionIterator returns the field ids and their start/limit positions generated
* by a call to Format::format. See Format, NumberFormat, DecimalFormat.
* @draft ICU 4.4
*/
class U_I18N_API FieldPositionIterator : public UObject {
public:
/**
* Destructor.
* @draft ICU 4.4
*/
~FieldPositionIterator();
/**
* Constructs a new, empty iterator.
* @draft ICU 4.4
*/
FieldPositionIterator(void);
/**
* Copy constructor. If the copy failed for some reason, the new iterator will
* be empty.
* @draft ICU 4.4
*/
FieldPositionIterator(const FieldPositionIterator&);
/**
* Return true if another object is semantically equal to this
* one.
* <p>
* Return true if this FieldPositionIterator is at the same position in an
* equal array of run values.
* @draft ICU 4.4
*/
UBool operator==(const FieldPositionIterator&) const;
/**
* Returns the complement of the result of operator==
* @param rhs The FieldPositionIterator to be compared for inequality
* @return the complement of the result of operator==
* @draft ICU 4.4
*/
UBool operator!=(const FieldPositionIterator& rhs) const { return !operator==(rhs); }
/**
* If the current position is valid, updates the FieldPosition values, advances the iterator,
* and returns TRUE, otherwise returns FALSE.
* @draft ICU 4.4
*/
UBool next(FieldPosition& fp);
/**
* ICU "poor man's RTTI", returns a UClassID for this class.
*/
static UClassID U_EXPORT2 getStaticClassID();
/**
* ICU "poor man's RTTI", returns a UClassID for the actual class.
*/
virtual UClassID getDynamicClassID() const;
private:
friend class FieldPositionIteratorHandler;
/**
* Sets the data used by the iterator, and resets the position.
* Returns U_ILLEGAL_ARGUMENT_ERROR in status if the data is not valid
* (length is not a multiple of 3, or start >= limit for any run).
*/
void setData(UVector32 *adopt, UErrorCode& status);
UVector32 *data;
int32_t pos;
};
inline UBool FieldPositionIterator::next(FieldPosition& fp) {
if (pos == -1) {
return FALSE;
}
fp.setField(data->elementAti(pos++));
fp.setBeginIndex(data->elementAti(pos++));
fp.setEndIndex(data->elementAti(pos++));
if (pos == data->size()) {
pos = -1;
}
return TRUE;
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif // FPOSITER_H

View file

@ -190,13 +190,25 @@ public:
* Signifies that the position of the integer part or fraction part of
* a formatted number should be returned.
*
* Note: as of ICU 4.4, the values in this enum have been extended to
* support identification of all number format fields, not just those
* pertaining to alignment.
*
* @see FieldPosition
* @stable ICU 2.0
*/
enum EAlignmentFields {
kIntegerField,
kFractionField,
kDecimalSeparatorField,
kExponentSymbolField,
kExponentSignField,
kExponentField,
kGroupingSeparatorField,
kCurrencyField,
kPercentField,
kPermillField,
kSignField,
/**
* These constants are provided for backwards compatibility only.
@ -241,6 +253,26 @@ public:
FieldPosition& pos,
UErrorCode& status) const;
/**
* Format an object to produce a string. This method handles
* Formattable objects with numeric types. If the Formattable
* object type is not a numeric type, then it returns a failing
* UErrorCode.
*
* @param obj The object to format.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call.
* @param status Output param filled with success/failure status.
* @return Reference to 'appendTo' parameter.
* @draft 4.4
*/
virtual UnicodeString& format(const Formattable& obj,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Parse a string to produce an object. This methods handles
* parsing of numeric strings into Formattable objects with numeric
@ -327,6 +359,23 @@ public:
virtual UnicodeString& format(double number,
UnicodeString& appendTo,
FieldPosition& pos) const = 0;
/**
* Format a double number. Subclasses must implement
* this method.
*
* @param number The value to be formatted.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call.
* @param status Output param filled with success/failure status.
* @return Reference to 'appendTo' parameter.
* @draft 4.4
*/
virtual UnicodeString& format(double number,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Format a long number. Concrete subclasses must implement
* these pure virtual methods.
@ -343,6 +392,23 @@ public:
UnicodeString& appendTo,
FieldPosition& pos) const = 0;
/**
* Format an int32 number. Subclasses must implement
* this method.
*
* @param number The value to be formatted.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call.
* @param status Output param filled with success/failure status.
* @return Reference to 'appendTo' parameter.
* @draft 4.4
*/
virtual UnicodeString& format(int32_t number,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Format an int64 number. (Not abstract to retain compatibility
* with earlier releases, however subclasses should override this
@ -359,6 +425,23 @@ public:
virtual UnicodeString& format(int64_t number,
UnicodeString& appendTo,
FieldPosition& pos) const;
/**
* Format an int64 number. Subclasses must implement
* this method.
*
* @param number The value to be formatted.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call.
* @param status Output param filled with success/failure status.
* @return Reference to 'appendTo' parameter.
* @draft 4.4
*/
virtual UnicodeString& format(int64_t number,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Redeclared Format method.
* @param obj The object to be formatted.

View file

@ -39,6 +39,7 @@ U_NAMESPACE_BEGIN
class DateFormatSymbols;
class DateFormat;
class MessageFormat;
class FieldPositionHandler;
/**
*
@ -385,6 +386,29 @@ public:
UnicodeString& appendTo,
FieldPosition& pos) const;
/**
* Format a date or time, which is the standard millis since 24:00 GMT, Jan
* 1, 1970. Overrides DateFormat pure virtual method.
* <P>
* Example: using the US locale: "yyyy.MM.dd e 'at' HH:mm:ss zzz" ->>
* 1996.07.10 AD at 15:08:56 PDT
*
* @param cal Calendar set to the date and time to be formatted
* into a date/time string.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call. Field values
* are defined in UDateFormatField.
* @param status Input/output param set to success/failure code.
* @return Reference to 'appendTo' parameter.
* @draft ICU 4.4
*/
virtual UnicodeString& format( Calendar& cal,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Format a date or time, which is the standard millis since 24:00 GMT, Jan
* 1, 1970. Overrides DateFormat pure virtual method.
@ -400,7 +424,7 @@ public:
* Result is appended to existing contents.
* @param pos The formatting position. On input: an alignment field,
* if desired. On output: the offsets of the alignment field.
* @param status Output param set to success/faulure code.
* @param status Input/output param set to success/failure code.
* @return Reference to 'appendTo' parameter.
* @stable ICU 2.0
*/
@ -409,6 +433,31 @@ public:
FieldPosition& pos,
UErrorCode& status) const;
/**
* Format a date or time, which is the standard millis since 24:00 GMT, Jan
* 1, 1970. Overrides DateFormat pure virtual method.
* <P>
* Example: using the US locale: "yyyy.MM.dd e 'at' HH:mm:ss zzz" ->>
* 1996.07.10 AD at 15:08:56 PDT
*
* @param obj A Formattable containing the date-time value to be formatted
* into a date-time string. If the type of the Formattable
* is a numeric type, it is treated as if it were an
* instance of Date.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call. Field values
* are defined in UDateFormatField.
* @param status Input/output param set to success/failure code.
* @return Reference to 'appendTo' parameter.
* @draft ICU 4.4
*/
virtual UnicodeString& format( const Formattable& obj,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Redeclared DateFormat method.
* @param date the Date value to be formatted.
@ -423,6 +472,23 @@ public:
UnicodeString& appendTo,
FieldPosition& fieldPosition) const;
/**
* Redeclared DateFormat method.
* @param date the Date value to be formatted.
* @param appendTo Output parameter to receive result.
* Result is appended to existing contents.
* @param posIter On return, can be used to iterate over positions
* of fields generated by this format call. Field values
* are defined in UDateFormatField.
* @param status Input/output param set to success/failure code.
* @return Reference to 'appendTo' parameter.
* @draft ICU 4.4
*/
UnicodeString& format(UDate date,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const;
/**
* Redeclared DateFormat method.
* @param obj Object to be formatted.
@ -724,6 +790,12 @@ private:
*/
SimpleDateFormat(const Locale& locale, UErrorCode& status); // Use default pattern
/**
* Hook called by format(... FieldPosition& ...) and format(...FieldPositionIterator&...)
*/
UnicodeString& _format(Calendar& cal, UnicodeString& appendTo, FieldPositionHandler& handler,
UErrorCode& status) const;
/**
* Called by format() to format a single field.
*
@ -733,19 +805,16 @@ private:
* @param count Number of characters in the current pattern symbol (e.g.,
* "yyyy" in the pattern would result in a call to this function
* with ch equal to 'y' and count equal to 4)
* @param pos The FieldPosition being filled in by the format() call. If
* this function is formatting the field specfied by pos, it
* will fill in pos with the beginning and ending offsets of the
* field.
* @param handler Records information about field positions.
* @param status Receives a status code, which will be U_ZERO_ERROR if the operation
* succeeds.
*/
void subFormat( UnicodeString &appendTo,
UChar ch,
int32_t count,
FieldPosition& pos,
Calendar& cal,
UErrorCode& status) const; // in case of illegal argument
void subFormat(UnicodeString &appendTo,
UChar ch,
int32_t count,
FieldPositionHandler& handler,
Calendar& cal,
UErrorCode& status) const; // in case of illegal argument
/**
* Used by subFormat() to format a numeric value.
@ -759,11 +828,11 @@ 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,
UnicodeString &appendTo,
int32_t value,
int32_t minDigits,
int32_t maxDigits) const;
void zeroPaddingNumber(NumberFormat *currentNumberFormat,
UnicodeString &appendTo,
int32_t value,
int32_t minDigits,
int32_t maxDigits) const;
/**
* Return true if the given format character, occuring count
@ -1081,6 +1150,28 @@ SimpleDateFormat::format(const Formattable& obj,
return DateFormat::format(obj, appendTo, status);
}
inline UnicodeString&
SimpleDateFormat::format(const Formattable& obj,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const
{
// Don't use Format:: - use immediate base class only,
// in case immediate base modifies behavior later.
return DateFormat::format(obj, appendTo, pos, status);
}
inline UnicodeString&
SimpleDateFormat::format(const Formattable& obj,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const
{
// Don't use Format:: - use immediate base class only,
// in case immediate base modifies behavior later.
return DateFormat::format(obj, appendTo, posIter, status);
}
inline UnicodeString&
SimpleDateFormat::format(UDate date,
UnicodeString& appendTo,
@ -1090,6 +1181,16 @@ SimpleDateFormat::format(UDate date,
return DateFormat::format(date, appendTo, fieldPosition);
}
inline UnicodeString&
SimpleDateFormat::format(UDate date,
UnicodeString& appendTo,
FieldPositionIterator& posIter,
UErrorCode& status) const {
// Don't use Format:: - use immediate base class only,
// in case immediate base modifies behavior later.
return DateFormat::format(date, appendTo, posIter, status);
}
inline UnicodeString&
SimpleDateFormat::format(UDate date, UnicodeString& appendTo) const {
return DateFormat::format(date, appendTo);

View file

@ -444,15 +444,15 @@ typedef enum UDateFormatField {
UDAT_TIMEZONE_GENERIC_FIELD = 24,
/**
* FieldPosition selector for 'c' field alignment,
* corresponding to the {@link #UCAL_DATE} field.
* corresponding to the {@link #UCAL_DOW_LOCAL} field.
* This displays the stand alone day name, if available.
* @stable ICU 3.4
*/
UDAT_STANDALONE_DAY_FIELD = 25,
/**
* FieldPosition selector for 'L' field alignment,
* corresponding to the {@link #UCAL_MONTH} field.
* corresponding to the {@link #UCAL_MONTH} field.
* This displays the stand alone month name, if available.
* @stable ICU 3.4
*/
@ -484,7 +484,7 @@ typedef enum UDateFormatField {
UDAT_TIMEZONE_SPECIAL_FIELD = 29,
/**
* Number of FieldPosition and UFieldPosition selectors for
* Number of FieldPosition and UFieldPosition selectors for
* DateFormat and UDateFormat.
* Valid selectors range from 0 to UDAT_FIELD_COUNT-1.
* This value is subject to change if new fields are defined
@ -495,6 +495,19 @@ typedef enum UDateFormatField {
} UDateFormatField;
/**
* Maps from a UDateFormatField to the corresponding UCalendarDateFields.
* Note: since the mapping is many-to-one, there is no inverse mapping.
* @param field the UDateFormatField.
* @return the UCalendarDateField. This will be UCAL_FIELD_COUNT in case
* of error (e.g., the input field is UDAT_FIELD_COUNT).
* @draft ICU 4.4
*/
U_DRAFT UCalendarDateFields U_EXPORT2
udat_toCalendarDateField(UDateFormatField field);
/**
* Open a new UDateFormat for formatting and parsing dates and times.
* A UDateFormat may be used to format dates in calls to {@link #udat_format },
@ -977,6 +990,7 @@ udat_applyPatternRelative(UDateFormat *format,
int32_t timePatternLength,
UErrorCode *status);
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif

View file

@ -407,6 +407,7 @@ void DateFormatTest::TestFieldPosition() {
}
logln((UnicodeString)" Result = " + df->format(aug13, buf.remove()));
int32_t expBase = exp; // save for later
for (i = 0; i < UDAT_FIELD_COUNT; ++i, ++exp) {
FieldPosition pos(i);
buf.remove();
@ -416,6 +417,24 @@ void DateFormatTest::TestFieldPosition() {
assertEquals((UnicodeString)"field #" + i + " " + DATEFORMAT_FIELD_NAMES[i],
ctou(EXPECTED[exp]), field);
}
// test FieldPositionIterator API
logln("FieldPositionIterator");
{
UErrorCode status = U_ZERO_ERROR;
FieldPositionIterator posIter;
FieldPosition fp;
buf.remove();
df->format(aug13, buf, posIter, status);
while (posIter.next(fp)) {
int32_t i = fp.getField();
UnicodeString field;
buf.extractBetween(fp.getBeginIndex(), fp.getEndIndex(), field);
assertEquals((UnicodeString)"field #" + i + " " + DATEFORMAT_FIELD_NAMES[i],
ctou(EXPECTED[expBase + i]), field);
}
}
}
for (i=0; i<COUNT; ++i) {

View file

@ -16,7 +16,7 @@
#include "caltztst.h"
/**
* Performs many different tests for DataeFormat and SimpleDateFormat
* Performs many different tests for DateFormat and SimpleDateFormat
**/
class DateFormatTest: public CalendarTimeZoneTest {
// IntlTest override

View file

@ -109,6 +109,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
CASE(42,TestCurrencyIsoPluralFormat);
CASE(43,TestCurrencyParsing);
CASE(44,TestParseCurrencyInUCurr);
CASE(45,TestFormatAttributes);
default: name = ""; break;
}
}
@ -5780,4 +5781,156 @@ NumberFormatTest::TestParseCurrencyInUCurr() {
}
}
const char* attrString(int32_t);
// UnicodeString s;
// std::string ss;
// std::cout << s.toUTF8String(ss)
void NumberFormatTest::expectPositions(FieldPositionIterator& iter, int32_t *values, int32_t tupleCount,
const UnicodeString& str) {
UBool found[10];
FieldPosition fp;
if (tupleCount > 10) {
assertTrue("internal error, tupleCount too large", FALSE);
} else {
for (int i = 0; i < tupleCount; ++i) {
found[i] = FALSE;
}
}
logln(str);
while (iter.next(fp)) {
UBool ok = FALSE;
int32_t id = fp.getField();
int32_t start = fp.getBeginIndex();
int32_t limit = fp.getEndIndex();
// is there a logln using printf?
char buf[128];
sprintf(buf, "%24s %3d %3d %3d", attrString(id), id, start, limit);
logln(buf);
for (int i = 0; i < tupleCount; ++i) {
if (found[i]) {
continue;
}
if (values[i*3] == id &&
values[i*3+1] == start &&
values[i*3+2] == limit) {
found[i] = ok = TRUE;
break;
}
}
assertTrue((UnicodeString)"found [" + id + "," + start + "," + limit + "]", ok);
}
// check that all were found
UBool ok = TRUE;
for (int i = 0; i < tupleCount; ++i) {
if (!found[i]) {
ok = FALSE;
assertTrue((UnicodeString) "missing [" + values[i*3] + "," + values[i*3+1] + "," + values[i*3+2] + "]", found[i]);
}
}
assertTrue("no expected values were missing", ok);
}
void NumberFormatTest::expectPosition(FieldPosition& pos, int32_t id, int32_t start, int32_t limit,
const UnicodeString& str) {
logln(str);
assertTrue((UnicodeString)"id " + id + " == " + pos.getField(), id == pos.getField());
assertTrue((UnicodeString)"begin " + start + " == " + pos.getBeginIndex(), start == pos.getBeginIndex());
assertTrue((UnicodeString)"end " + limit + " == " + pos.getEndIndex(), limit == pos.getEndIndex());
}
void NumberFormatTest::TestFormatAttributes() {
Locale locale("en_US");
UErrorCode status = U_ZERO_ERROR;
DecimalFormat *decFmt = (DecimalFormat *) NumberFormat::createInstance(locale, NumberFormat::kCurrencyStyle, status);
double val = 12345.67;
{
int32_t expected[] = {
NumberFormat::kCurrencyField, 0, 1,
NumberFormat::kGroupingSeparatorField, 3, 4,
NumberFormat::kIntegerField, 1, 7,
NumberFormat::kDecimalSeparatorField, 7, 8,
NumberFormat::kFractionField, 8, 10,
};
int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
FieldPositionIterator posIter;
UnicodeString result;
decFmt->format(val, result, posIter, status);
expectPositions(posIter, expected, tupleCount, result);
}
{
FieldPosition fp(NumberFormat::kIntegerField);
UnicodeString result;
decFmt->format(val, result, fp);
expectPosition(fp, NumberFormat::kIntegerField, 1, 7, result);
}
{
FieldPosition fp(NumberFormat::kFractionField);
UnicodeString result;
decFmt->format(val, result, fp);
expectPosition(fp, NumberFormat::kFractionField, 8, 10, result);
}
delete decFmt;
decFmt = (DecimalFormat *) NumberFormat::createInstance(locale, NumberFormat::kScientificStyle, status);
val = -0.0000123;
{
int32_t expected[] = {
NumberFormat::kSignField, 0, 1,
NumberFormat::kIntegerField, 1, 2,
NumberFormat::kDecimalSeparatorField, 2, 3,
NumberFormat::kFractionField, 3, 5,
NumberFormat::kExponentSymbolField, 5, 6,
NumberFormat::kExponentSignField, 6, 7,
NumberFormat::kExponentField, 7, 8
};
int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
FieldPositionIterator posIter;
UnicodeString result;
decFmt->format(val, result, posIter, status);
expectPositions(posIter, expected, tupleCount, result);
}
{
FieldPosition fp(NumberFormat::kIntegerField);
UnicodeString result;
decFmt->format(val, result, fp);
expectPosition(fp, NumberFormat::kIntegerField, 1, 2, result);
}
{
FieldPosition fp(NumberFormat::kFractionField);
UnicodeString result;
decFmt->format(val, result, fp);
expectPosition(fp, NumberFormat::kFractionField, 3, 5, result);
}
delete decFmt;
fflush(stderr);
}
const char* attrString(int32_t attrId) {
switch (attrId) {
case NumberFormat::kIntegerField: return "integer";
case NumberFormat::kFractionField: return "fraction";
case NumberFormat::kDecimalSeparatorField: return "decimal separator";
case NumberFormat::kExponentSymbolField: return "exponent symbol";
case NumberFormat::kExponentSignField: return "exponent sign";
case NumberFormat::kExponentField: return "exponent";
case NumberFormat::kGroupingSeparatorField: return "grouping separator";
case NumberFormat::kCurrencyField: return "currency";
case NumberFormat::kPercentField: return "percent";
case NumberFormat::kPermillField: return "permille";
case NumberFormat::kSignField: return "sign";
default: return "";
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -142,11 +142,18 @@ class NumberFormatTest: public CalendarTimeZoneTest {
void TestCurrencyIsoPluralFormat();
void TestCurrencyParsing();
void TestParseCurrencyInUCurr();
void TestFormatAttributes();
private:
static UBool equalValue(const Formattable& a, const Formattable& b);
void expectPositions(FieldPositionIterator& iter, int32_t *values, int32_t tupleCount,
const UnicodeString& str);
void expectPosition(FieldPosition& pos, int32_t id, int32_t start, int32_t limit,
const UnicodeString& str);
void expect2(NumberFormat& fmt, const Formattable& n, const UnicodeString& str);
void expect3(NumberFormat& fmt, const Formattable& n, const UnicodeString& str);