mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-13 08:53:20 +00:00
ICU-13662 Improving NumberFormatter field position method names and behavior.
X-SVN-Rev: 41313
This commit is contained in:
parent
c035e5bd04
commit
0a0d99ebc8
22 changed files with 446 additions and 147 deletions
|
@ -428,7 +428,7 @@ UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, Fie
|
|||
}
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
FormattedNumber output = fields->formatter->formatDouble(number, localStatus);
|
||||
output.populateFieldPosition(pos, localStatus);
|
||||
fieldPositionHelper(output, pos, appendTo.length(), localStatus);
|
||||
auto appendable = UnicodeStringAppendable(appendTo);
|
||||
output.appendTo(appendable);
|
||||
return appendTo;
|
||||
|
@ -440,7 +440,7 @@ UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, Fie
|
|||
return appendTo;
|
||||
}
|
||||
FormattedNumber output = fields->formatter->formatDouble(number, status);
|
||||
output.populateFieldPosition(pos, status);
|
||||
fieldPositionHelper(output, pos, appendTo.length(), status);
|
||||
auto appendable = UnicodeStringAppendable(appendTo);
|
||||
output.appendTo(appendable);
|
||||
return appendTo;
|
||||
|
@ -482,7 +482,7 @@ UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, Fi
|
|||
}
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
FormattedNumber output = fields->formatter->formatInt(number, localStatus);
|
||||
output.populateFieldPosition(pos, localStatus);
|
||||
fieldPositionHelper(output, pos, appendTo.length(), localStatus);
|
||||
auto appendable = UnicodeStringAppendable(appendTo);
|
||||
output.appendTo(appendable);
|
||||
return appendTo;
|
||||
|
@ -494,7 +494,7 @@ UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, Fi
|
|||
return appendTo;
|
||||
}
|
||||
FormattedNumber output = fields->formatter->formatInt(number, status);
|
||||
output.populateFieldPosition(pos, status);
|
||||
fieldPositionHelper(output, pos, appendTo.length(), status);
|
||||
auto appendable = UnicodeStringAppendable(appendTo);
|
||||
output.appendTo(appendable);
|
||||
return appendTo;
|
||||
|
@ -542,7 +542,7 @@ UnicodeString&
|
|||
DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPosition& pos,
|
||||
UErrorCode& status) const {
|
||||
FormattedNumber output = fields->formatter->formatDecimalQuantity(number, status);
|
||||
output.populateFieldPosition(pos, status);
|
||||
fieldPositionHelper(output, pos, appendTo.length(), status);
|
||||
auto appendable = UnicodeStringAppendable(appendTo);
|
||||
output.appendTo(appendable);
|
||||
return appendTo;
|
||||
|
@ -1227,6 +1227,17 @@ const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorC
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
DecimalFormat::fieldPositionHelper(const number::FormattedNumber& formatted, FieldPosition& fieldPosition,
|
||||
int32_t offset, UErrorCode& status) {
|
||||
fieldPosition.setEndIndex(0); // always return first occurrence
|
||||
bool found = formatted.nextFieldPosition(fieldPosition, status);
|
||||
if (found && offset != 0) {
|
||||
fieldPosition.setBeginIndex(fieldPosition.getBeginIndex() + offset);
|
||||
fieldPosition.setEndIndex(fieldPosition.getEndIndex() + offset);
|
||||
}
|
||||
}
|
||||
|
||||
// To debug fast-format, change void(x) to printf(x)
|
||||
#define trace(x) void(x)
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
// © 2018 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
#ifndef __SOURCE_FIELDPOSUTIL_H__
|
||||
#define __SOURCE_FIELDPOSUTIL_H__
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* Wraps a UFieldPosition and makes it usable as a FieldPosition. Example:
|
||||
*
|
||||
* <pre>
|
||||
* UFieldPositionWrapper wrapper(myUFPos);
|
||||
* u_favorite_function_taking_ufpos(wrapper);
|
||||
* // when destructed, the wrapper saves the data back into myUFPos
|
||||
* </pre>
|
||||
*/
|
||||
class UFieldPositionWrapper : public UMemory {
|
||||
public:
|
||||
explicit UFieldPositionWrapper(UFieldPosition& ufpos)
|
||||
: _ufpos(ufpos) {
|
||||
_fpos.setField(_ufpos.field);
|
||||
_fpos.setBeginIndex(_ufpos.beginIndex);
|
||||
_fpos.setEndIndex(_ufpos.endIndex);
|
||||
}
|
||||
|
||||
/** When destructed, copies the information from the fpos into the ufpos. */
|
||||
~UFieldPositionWrapper() {
|
||||
_ufpos.field = _fpos.getField();
|
||||
_ufpos.beginIndex = _fpos.getBeginIndex();
|
||||
_ufpos.endIndex = _fpos.getEndIndex();
|
||||
}
|
||||
|
||||
/** Conversion operator to FieldPosition */
|
||||
operator FieldPosition&() {
|
||||
return _fpos;
|
||||
}
|
||||
|
||||
private:
|
||||
FieldPosition _fpos;
|
||||
UFieldPosition& _ufpos;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif //__SOURCE_FIELDPOSUTIL_H__
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
|
@ -534,7 +534,6 @@
|
|||
<ClInclude Include="number_multiplier.h" />
|
||||
<ClInclude Include="number_currencysymbols.h" />
|
||||
<ClInclude Include="number_skeletons.h" />
|
||||
<ClInclude Include="fieldposutil.h" />
|
||||
<ClInclude Include="numparse_stringsegment.h" />
|
||||
<ClInclude Include="numparse_impl.h" />
|
||||
<ClInclude Include="numparse_symbols.h" />
|
||||
|
|
|
@ -857,9 +857,6 @@
|
|||
<ClInclude Include="number_skeletons.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="fieldposutil.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="numparse_stringsegment.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -639,7 +639,6 @@
|
|||
<ClInclude Include="number_multiplier.h" />
|
||||
<ClInclude Include="number_currencysymbols.h" />
|
||||
<ClInclude Include="number_skeletons.h" />
|
||||
<ClInclude Include="fieldposutil.h" />
|
||||
<ClInclude Include="numparse_stringsegment.h" />
|
||||
<ClInclude Include="numparse_impl.h" />
|
||||
<ClInclude Include="numparse_symbols.h" />
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "number_utypes.h"
|
||||
#include "unicode/numberformatter.h"
|
||||
#include "unicode/unumberformatter.h"
|
||||
#include "fieldposutil.h"
|
||||
|
||||
using namespace icu;
|
||||
using namespace icu::number;
|
||||
|
@ -158,23 +157,30 @@ unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t buf
|
|||
return result->string.toTempUnicodeString().extract(buffer, bufferCapacity, *ec);
|
||||
}
|
||||
|
||||
U_CAPI void U_EXPORT2
|
||||
unumf_resultGetField(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec) {
|
||||
U_CAPI UBool U_EXPORT2
|
||||
unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec) {
|
||||
if (ufpos == nullptr) {
|
||||
*ec = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec);
|
||||
if (U_FAILURE(*ec)) { return; }
|
||||
if (U_FAILURE(*ec)) { return FALSE; }
|
||||
|
||||
UFieldPositionWrapper helper(*ufpos);
|
||||
result->string.populateFieldPosition(helper, 0, *ec);
|
||||
FieldPosition fp;
|
||||
fp.setField(ufpos->field);
|
||||
fp.setBeginIndex(ufpos->beginIndex);
|
||||
fp.setEndIndex(ufpos->endIndex);
|
||||
bool retval = result->string.nextFieldPosition(fp, *ec);
|
||||
ufpos->beginIndex = fp.getBeginIndex();
|
||||
ufpos->endIndex = fp.getEndIndex();
|
||||
// NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
|
||||
return retval ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
U_CAPI void U_EXPORT2
|
||||
unumf_resultGetAllFields(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
|
||||
UErrorCode* ec) {
|
||||
unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
|
||||
UErrorCode* ec) {
|
||||
if (ufpositer == nullptr) {
|
||||
*ec = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
|
@ -184,7 +190,7 @@ unumf_resultGetAllFields(const UFormattedNumber* uresult, UFieldPositionIterator
|
|||
if (U_FAILURE(*ec)) { return; }
|
||||
|
||||
auto* helper = reinterpret_cast<FieldPositionIterator*>(ufpositer);
|
||||
result->string.populateFieldPositionIterator(*helper, *ec);
|
||||
result->string.getAllFieldPositions(*helper, *ec);
|
||||
}
|
||||
|
||||
U_CAPI void U_EXPORT2
|
||||
|
|
|
@ -776,7 +776,21 @@ void FormattedNumber::populateFieldPosition(FieldPosition& fieldPosition, UError
|
|||
status = fErrorCode;
|
||||
return;
|
||||
}
|
||||
fResults->string.populateFieldPosition(fieldPosition, 0, status);
|
||||
// in case any users were depending on the old behavior:
|
||||
fieldPosition.setEndIndex(0);
|
||||
fResults->string.nextFieldPosition(fieldPosition, status);
|
||||
}
|
||||
|
||||
UBool FormattedNumber::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
if (fResults == nullptr) {
|
||||
status = fErrorCode;
|
||||
return FALSE;
|
||||
}
|
||||
// NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
|
||||
return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
void FormattedNumber::populateFieldPositionIterator(FieldPositionIterator& iterator, UErrorCode& status) {
|
||||
|
@ -787,7 +801,18 @@ void FormattedNumber::populateFieldPositionIterator(FieldPositionIterator& itera
|
|||
status = fErrorCode;
|
||||
return;
|
||||
}
|
||||
fResults->string.populateFieldPositionIterator(iterator, status);
|
||||
fResults->string.getAllFieldPositions(iterator, status);
|
||||
}
|
||||
|
||||
void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
if (fResults == nullptr) {
|
||||
status = fErrorCode;
|
||||
return;
|
||||
}
|
||||
fResults->string.getAllFieldPositions(iterator, status);
|
||||
}
|
||||
|
||||
void FormattedNumber::getDecimalQuantity(DecimalQuantity& output, UErrorCode& status) const {
|
||||
|
|
|
@ -413,23 +413,24 @@ bool NumberStringBuilder::contentEquals(const NumberStringBuilder &other) const
|
|||
return true;
|
||||
}
|
||||
|
||||
void NumberStringBuilder::populateFieldPosition(FieldPosition &fp, int32_t offset, UErrorCode &status) const {
|
||||
bool NumberStringBuilder::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const {
|
||||
int32_t rawField = fp.getField();
|
||||
|
||||
if (rawField == FieldPosition::DONT_CARE) {
|
||||
return;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
auto field = static_cast<Field>(rawField);
|
||||
|
||||
bool seenStart = false;
|
||||
int32_t fractionStart = -1;
|
||||
for (int i = fZero; i <= fZero + fLength; i++) {
|
||||
int32_t startIndex = fp.getEndIndex();
|
||||
for (int i = fZero + startIndex; i <= fZero + fLength; i++) {
|
||||
Field _field = UNUM_FIELD_COUNT;
|
||||
if (i < fZero + fLength) {
|
||||
_field = getFieldPtr()[i];
|
||||
|
@ -439,10 +440,10 @@ void NumberStringBuilder::populateFieldPosition(FieldPosition &fp, int32_t offse
|
|||
if (field == UNUM_INTEGER_FIELD && _field == UNUM_GROUPING_SEPARATOR_FIELD) {
|
||||
continue;
|
||||
}
|
||||
fp.setEndIndex(i - fZero + offset);
|
||||
fp.setEndIndex(i - fZero);
|
||||
break;
|
||||
} else if (!seenStart && field == _field) {
|
||||
fp.setBeginIndex(i - fZero + offset);
|
||||
fp.setBeginIndex(i - fZero);
|
||||
seenStart = true;
|
||||
}
|
||||
if (_field == UNUM_INTEGER_FIELD || _field == UNUM_DECIMAL_SEPARATOR_FIELD) {
|
||||
|
@ -450,14 +451,17 @@ void NumberStringBuilder::populateFieldPosition(FieldPosition &fp, int32_t offse
|
|||
}
|
||||
}
|
||||
|
||||
// Backwards compatibility: FRACTION needs to start after INTEGER if empty
|
||||
if (field == UNUM_FRACTION_FIELD && !seenStart) {
|
||||
fp.setBeginIndex(fractionStart + offset);
|
||||
fp.setEndIndex(fractionStart + offset);
|
||||
// Backwards compatibility: FRACTION needs to start after INTEGER if empty.
|
||||
// Do not return that a field was found, though, since there is not actually a fraction part.
|
||||
if (field == UNUM_FRACTION_FIELD && !seenStart && fractionStart != -1) {
|
||||
fp.setBeginIndex(fractionStart);
|
||||
fp.setEndIndex(fractionStart);
|
||||
}
|
||||
|
||||
return seenStart;
|
||||
}
|
||||
|
||||
void NumberStringBuilder::populateFieldPositionIterator(FieldPositionIterator &fpi, UErrorCode &status) const {
|
||||
void NumberStringBuilder::getAllFieldPositions(FieldPositionIterator& fpi, UErrorCode& status) const {
|
||||
// TODO: Set an initial capacity on uvec?
|
||||
LocalPointer <UVector32> uvec(new UVector32(status), status);
|
||||
if (U_FAILURE(status)) {
|
||||
|
|
|
@ -101,9 +101,9 @@ class U_I18N_API NumberStringBuilder : public UMemory {
|
|||
|
||||
bool contentEquals(const NumberStringBuilder &other) const;
|
||||
|
||||
void populateFieldPosition(FieldPosition &fp, int32_t offset, UErrorCode &status) const;
|
||||
bool nextFieldPosition(FieldPosition& fp, UErrorCode& status) const;
|
||||
|
||||
void populateFieldPositionIterator(FieldPositionIterator &fpi, UErrorCode &status) const;
|
||||
void getAllFieldPositions(FieldPositionIterator& fpi, UErrorCode& status) const;
|
||||
|
||||
private:
|
||||
bool fUsingHeap = false;
|
||||
|
|
|
@ -60,6 +60,7 @@ class CompactDecimalFormat;
|
|||
|
||||
namespace number {
|
||||
class LocalizedNumberFormatter;
|
||||
class FormattedNumber;
|
||||
namespace impl {
|
||||
class DecimalQuantity;
|
||||
struct DecimalFormatFields;
|
||||
|
@ -2111,6 +2112,9 @@ class U_I18N_API DecimalFormat : public NumberFormat {
|
|||
|
||||
const numparse::impl::NumberParserImpl* getCurrencyParser(UErrorCode& status) const;
|
||||
|
||||
static void fieldPositionHelper(const number::FormattedNumber& formatted, FieldPosition& fieldPosition,
|
||||
int32_t offset, UErrorCode& status);
|
||||
|
||||
void setupFastFormat();
|
||||
|
||||
bool fastFormatDouble(double input, UnicodeString& output) const;
|
||||
|
|
|
@ -2378,11 +2378,45 @@ class U_I18N_API FormattedNumber : public UMemory {
|
|||
* The FieldPosition to populate with the start and end indices of the desired field.
|
||||
* @param status
|
||||
* Set if an error occurs while populating the FieldPosition.
|
||||
* @draft ICU 60
|
||||
* @deprecated ICU 62 Use {@link #toCharacterIterator} instead. This method will be removed in a future
|
||||
* release. See http://bugs.icu-project.org/trac/ticket/13746
|
||||
* @see UNumberFormatFields
|
||||
*/
|
||||
void populateFieldPosition(FieldPosition &fieldPosition, UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Determines the start and end indices of the next occurrence of the given <em>field</em> in the
|
||||
* output string. This allows you to determine the locations of, for example, the integer part,
|
||||
* fraction part, or symbols.
|
||||
*
|
||||
* If a field occurs just once, calling this method will find that occurrence and return it. If a
|
||||
* field occurs multiple times, this method may be called repeatedly with the following pattern:
|
||||
*
|
||||
* <pre>
|
||||
* FieldPosition fpos(UNUM_GROUPING_SEPARATOR_FIELD);
|
||||
* while (formattedNumber.nextFieldPosition(fpos, status)) {
|
||||
* // do something with fpos.
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* This method is useful if you know which field to query. If you want all available field position
|
||||
* information, use #getAllFields().
|
||||
*
|
||||
* @param fieldPosition
|
||||
* Input+output variable. On input, the "field" property determines which field to look up,
|
||||
* and the "endIndex" property determines where to begin the search. On output, the
|
||||
* "beginIndex" field is set to the beginning of the first occurrence of the field after the
|
||||
* input "endIndex", and "endIndex" is set to the end of that occurrence of the field
|
||||
* (exclusive index). If a field position is not found, the FieldPosition is not changed and
|
||||
* the method returns FALSE.
|
||||
* @param status
|
||||
* Set if an error occurs while populating the FieldPosition.
|
||||
* @return TRUE if a new occurrence of the field was found; FALSE otherwise.
|
||||
* @draft ICU 62
|
||||
* @see UNumberFormatFields
|
||||
*/
|
||||
UBool nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Export the formatted number to a FieldPositionIterator. This allows you to determine which characters in
|
||||
* the output string correspond to which <em>fields</em>, such as the integer part, fraction part, and sign.
|
||||
|
@ -2394,11 +2428,27 @@ class U_I18N_API FormattedNumber : public UMemory {
|
|||
* The FieldPositionIterator to populate with all of the fields present in the formatted number.
|
||||
* @param status
|
||||
* Set if an error occurs while populating the FieldPositionIterator.
|
||||
* @draft ICU 60
|
||||
* @deprecated ICU 62 Use {@link #getAllFieldPositions} instead. This method will be removed in a
|
||||
* future release. See http://bugs.icu-project.org/trac/ticket/13746
|
||||
* @see UNumberFormatFields
|
||||
*/
|
||||
void populateFieldPositionIterator(FieldPositionIterator &iterator, UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Export the formatted number to a FieldPositionIterator. This allows you to determine which characters in
|
||||
* the output string correspond to which <em>fields</em>, such as the integer part, fraction part, and sign.
|
||||
*
|
||||
* If information on only one field is needed, use #nextFieldPosition().
|
||||
*
|
||||
* @param iterator
|
||||
* The FieldPositionIterator to populate with all of the fields present in the formatted number.
|
||||
* @param status
|
||||
* Set if an error occurs while populating the FieldPositionIterator.
|
||||
* @draft ICU 62
|
||||
* @see UNumberFormatFields
|
||||
*/
|
||||
void getAllFieldPositions(FieldPositionIterator &iterator, UErrorCode &status) const;
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
|
||||
/**
|
||||
|
|
|
@ -532,30 +532,44 @@ unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t buf
|
|||
|
||||
|
||||
/**
|
||||
* Determines the start and end indices of the first occurrence of the given field in the output string.
|
||||
* This allows you to determine the locations of the integer part, fraction part, and sign.
|
||||
* Determines the start and end indices of the next occurrence of the given <em>field</em> in the
|
||||
* output string. This allows you to determine the locations of, for example, the integer part,
|
||||
* fraction part, or symbols.
|
||||
*
|
||||
* If a field occurs multiple times in an output string, such as a grouping separator, this method will
|
||||
* only ever return the first occurrence. Use unumf_resultGetAllFields() to access all occurrences of an
|
||||
* attribute.
|
||||
* If a field occurs just once, calling this method will find that occurrence and return it. If a
|
||||
* field occurs multiple times, this method may be called repeatedly with the following pattern:
|
||||
*
|
||||
* @param uresult The object containing the formatted number.
|
||||
* @param fpos
|
||||
* A pointer to a UFieldPosition. On input, position->field is read. On output,
|
||||
* position->beginIndex and position->endIndex indicate the beginning and ending indices of field
|
||||
* number position->field, if such a field exists.
|
||||
* <pre>
|
||||
* UFieldPosition ufpos = {UNUM_GROUPING_SEPARATOR_FIELD, 0, 0};
|
||||
* while (unumf_resultNextFieldPosition(uresult, ufpos, &ec)) {
|
||||
* // do something with ufpos.
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* This method is useful if you know which field to query. If you want all available field position
|
||||
* information, use unumf_resultGetAllFieldPositions().
|
||||
*
|
||||
* NOTE: All fields of the UFieldPosition must be initialized before calling this method.
|
||||
*
|
||||
* @param fieldPosition
|
||||
* Input+output variable. On input, the "field" property determines which field to look up,
|
||||
* and the "endIndex" property determines where to begin the search. On output, the
|
||||
* "beginIndex" field is set to the beginning of the first occurrence of the field after the
|
||||
* input "endIndex", and "endIndex" is set to the end of that occurrence of the field
|
||||
* (exclusive index). If a field position is not found, the FieldPosition is not changed and
|
||||
* the method returns FALSE.
|
||||
* @param ec Set if an error occurs.
|
||||
* @draft ICU 62
|
||||
*/
|
||||
U_DRAFT void U_EXPORT2
|
||||
unumf_resultGetField(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec);
|
||||
U_DRAFT UBool U_EXPORT2
|
||||
unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec);
|
||||
|
||||
|
||||
/**
|
||||
* Populates the given iterator with all fields in the formatted output string. This allows you to
|
||||
* determine the locations of the integer part, fraction part, and sign.
|
||||
*
|
||||
* If you need information on only one field, consider using unumf_resultGetField().
|
||||
* If you need information on only one field, use unumf_resultNextFieldPosition().
|
||||
*
|
||||
* @param uresult The object containing the formatted number.
|
||||
* @param fpositer
|
||||
|
@ -570,8 +584,8 @@ unumf_resultGetField(const UFormattedNumber* uresult, UFieldPosition* ufpos, UEr
|
|||
* @draft ICU 62
|
||||
*/
|
||||
U_DRAFT void U_EXPORT2
|
||||
unumf_resultGetAllFields(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
|
||||
UErrorCode* ec);
|
||||
unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
|
||||
UErrorCode* ec);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -83,14 +83,14 @@ static void TestSkeletonFormatToFields() {
|
|||
|
||||
// field position test:
|
||||
UFieldPosition ufpos = {UNUM_DECIMAL_SEPARATOR_FIELD};
|
||||
unumf_resultGetField(uresult, &ufpos, &ec);
|
||||
unumf_resultNextFieldPosition(uresult, &ufpos, &ec);
|
||||
assertIntEquals("Field position should be correct", 14, ufpos.beginIndex);
|
||||
assertIntEquals("Field position should be correct", 15, ufpos.endIndex);
|
||||
|
||||
// field position iterator test:
|
||||
UFieldPositionIterator* ufpositer = ufieldpositer_open(&ec);
|
||||
assertSuccess("Should create iterator without error", &ec);
|
||||
unumf_resultGetAllFields(uresult, ufpositer, &ec);
|
||||
unumf_resultGetAllFieldPositions(uresult, ufpositer, &ec);
|
||||
static const UFieldPosition expectedFields[] = {
|
||||
// Field, begin index, end index
|
||||
{UNUM_SIGN_FIELD, 0, 1},
|
||||
|
@ -128,6 +128,18 @@ static void TestSkeletonFormatToFields() {
|
|||
actual.field = ufieldpositer_next(ufpositer, &actual.beginIndex, &actual.endIndex);
|
||||
assertTrue("No more fields; should return a negative index", actual.field < 0);
|
||||
|
||||
// next field iteration:
|
||||
actual.field = UNUM_GROUPING_SEPARATOR_FIELD;
|
||||
actual.beginIndex = 0;
|
||||
actual.endIndex = 0;
|
||||
int32_t i = 1;
|
||||
while (unumf_resultNextFieldPosition(uresult, &actual, &ec)) {
|
||||
UFieldPosition expected = expectedFields[i++];
|
||||
assertIntEquals("Grouping separator begin index", expected.beginIndex, actual.beginIndex);
|
||||
assertIntEquals("Grouping separator end index", expected.endIndex, actual.endIndex);
|
||||
}
|
||||
assertIntEquals("Should have seen all grouping separators", 4, i);
|
||||
|
||||
// cleanup:
|
||||
unumf_closeResult(uresult);
|
||||
unumf_close(uformatter);
|
||||
|
|
|
@ -67,6 +67,7 @@ class NumberFormatterApiTest : public IntlTest {
|
|||
void scale();
|
||||
void locale();
|
||||
void formatTypes();
|
||||
void fieldPosition();
|
||||
void errors();
|
||||
void validRanges();
|
||||
void copyMove();
|
||||
|
|
|
@ -81,6 +81,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
|
|||
TESTCASE_AUTO(scale);
|
||||
TESTCASE_AUTO(locale);
|
||||
TESTCASE_AUTO(formatTypes);
|
||||
TESTCASE_AUTO(fieldPosition);
|
||||
TESTCASE_AUTO(errors);
|
||||
TESTCASE_AUTO(validRanges);
|
||||
TESTCASE_AUTO(copyMove);
|
||||
|
@ -2066,6 +2067,94 @@ void NumberFormatterApiTest::formatTypes() {
|
|||
assertEquals("Format decNumber to 40 digits", str, actual);
|
||||
}
|
||||
|
||||
void NumberFormatterApiTest::fieldPosition() {
|
||||
IcuTestErrorCode status(*this, "fieldPosition");
|
||||
FormattedNumber fmtd = NumberFormatter::withLocale("en").formatDouble(-9876543210.12, status);
|
||||
assertEquals("Should have expected format output", u"-9,876,543,210.12", fmtd.toString(status));
|
||||
|
||||
static const UFieldPosition expectedFieldPositions[] = {
|
||||
// field, begin index, end index
|
||||
{UNUM_SIGN_FIELD, 0, 1},
|
||||
{UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
|
||||
{UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
|
||||
{UNUM_GROUPING_SEPARATOR_FIELD, 10, 11},
|
||||
{UNUM_INTEGER_FIELD, 1, 14},
|
||||
{UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15},
|
||||
{UNUM_FRACTION_FIELD, 15, 17}};
|
||||
|
||||
FieldPositionIterator fpi;
|
||||
fmtd.getAllFieldPositions(fpi, status);
|
||||
int32_t i = 0;
|
||||
FieldPosition actual;
|
||||
while (fpi.next(actual)) {
|
||||
UFieldPosition expected = expectedFieldPositions[i++];
|
||||
assertEquals(
|
||||
UnicodeString(u"Field, case #") + Int64ToUnicodeString(i),
|
||||
expected.field,
|
||||
actual.getField());
|
||||
assertEquals(
|
||||
UnicodeString(u"Iterator, begin index, case #") + Int64ToUnicodeString(i),
|
||||
expected.beginIndex,
|
||||
actual.getBeginIndex());
|
||||
assertEquals(
|
||||
UnicodeString(u"Iterator, end index, case #") + Int64ToUnicodeString(i),
|
||||
expected.endIndex,
|
||||
actual.getEndIndex());
|
||||
|
||||
// Check for the first location of the field
|
||||
if (expected.field != UNUM_GROUPING_SEPARATOR_FIELD) {
|
||||
FieldPosition actual2(expected.field);
|
||||
UBool found = fmtd.nextFieldPosition(actual2, status);
|
||||
assertEquals(
|
||||
UnicodeString(u"Next, found first time, case #") + Int64ToUnicodeString(i),
|
||||
(UBool) TRUE,
|
||||
found);
|
||||
assertEquals(
|
||||
UnicodeString(u"Next, begin index, case #") + Int64ToUnicodeString(i),
|
||||
expected.beginIndex,
|
||||
actual2.getBeginIndex());
|
||||
assertEquals(
|
||||
UnicodeString(u"Next, end index, case #") + Int64ToUnicodeString(i),
|
||||
expected.endIndex,
|
||||
actual2.getEndIndex());
|
||||
found = fmtd.nextFieldPosition(actual2, status);
|
||||
assertEquals(
|
||||
UnicodeString(u"Next, found second time, case #") + Int64ToUnicodeString(i),
|
||||
(UBool) FALSE,
|
||||
found);
|
||||
}
|
||||
}
|
||||
assertEquals(
|
||||
"Should have seen every field position",
|
||||
sizeof(expectedFieldPositions) / sizeof(*expectedFieldPositions),
|
||||
i);
|
||||
|
||||
// Test the iteration functionality of nextFieldPosition
|
||||
actual = {UNUM_GROUPING_SEPARATOR_FIELD};
|
||||
i = 1;
|
||||
while (fmtd.nextFieldPosition(actual, status)) {
|
||||
UFieldPosition expected = expectedFieldPositions[i++];
|
||||
assertEquals(
|
||||
UnicodeString(u"Next for grouping, field, case #") + Int64ToUnicodeString(i),
|
||||
expected.field,
|
||||
actual.getField());
|
||||
assertEquals(
|
||||
UnicodeString(u"Next for grouping, begin index, case #") + Int64ToUnicodeString(i),
|
||||
expected.beginIndex,
|
||||
actual.getBeginIndex());
|
||||
assertEquals(
|
||||
UnicodeString(u"Next for grouping, end index, case #") + Int64ToUnicodeString(i),
|
||||
expected.endIndex,
|
||||
actual.getEndIndex());
|
||||
}
|
||||
assertEquals(u"Should have seen all grouping separators", 4, i);
|
||||
|
||||
// Make sure strings without fraction do not contain fraction field
|
||||
actual = {UNUM_FRACTION_FIELD};
|
||||
fmtd = NumberFormatter::withLocale("en").formatInt(5, status);
|
||||
assertFalse(u"No fraction part in an integer", fmtd.nextFieldPosition(actual, status));
|
||||
}
|
||||
|
||||
void NumberFormatterApiTest::errors() {
|
||||
LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).rounding(
|
||||
Rounder::fixedFraction(
|
||||
|
@ -2293,7 +2382,7 @@ void NumberFormatterApiTest::localPointerCAPI() {
|
|||
|
||||
// Get the location of the percent sign:
|
||||
UFieldPosition ufpos = {UNUM_PERCENT_FIELD, 0, 0};
|
||||
unumf_resultGetField(uresult.getAlias(), &ufpos, &ec);
|
||||
unumf_resultNextFieldPosition(uresult.getAlias(), &ufpos, &ec);
|
||||
assertEquals("Percent sign location within '0.00987%'", 7, ufpos.beginIndex);
|
||||
assertEquals("Percent sign location within '0.00987%'", 8, ufpos.endIndex);
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ void NumberStringBuilderTest::testFields() {
|
|||
// Very basic FieldPosition test. More robust tests happen in NumberFormatTest.
|
||||
// Let NumberFormatTest also take care of FieldPositionIterator material.
|
||||
FieldPosition fp(UNUM_CURRENCY_FIELD);
|
||||
sb.populateFieldPosition(fp, 0, status);
|
||||
sb.nextFieldPosition(fp, status);
|
||||
assertSuccess("Populating the FieldPosition", status);
|
||||
assertEquals("Currency start position", str.length(), fp.getBeginIndex());
|
||||
assertEquals("Currency end position", str.length() * 2, fp.getEndIndex());
|
||||
|
|
|
@ -486,10 +486,9 @@ public class NumberStringBuilder implements CharSequence {
|
|||
*
|
||||
* @param fp
|
||||
* The FieldPosition to populate.
|
||||
* @param offset
|
||||
* An offset to add to the field position index; can be zero.
|
||||
* @return true if the field was found; false if it was not found.
|
||||
*/
|
||||
public void populateFieldPosition(FieldPosition fp, int offset) {
|
||||
public boolean nextFieldPosition(FieldPosition fp) {
|
||||
java.text.Format.Field rawField = fp.getFieldAttribute();
|
||||
|
||||
if (rawField == null) {
|
||||
|
@ -500,21 +499,22 @@ public class NumberStringBuilder implements CharSequence {
|
|||
rawField = NumberFormat.Field.FRACTION;
|
||||
} else {
|
||||
// No field is set
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(rawField instanceof com.ibm.icu.text.NumberFormat.Field)) {
|
||||
if (!(rawField instanceof NumberFormat.Field)) {
|
||||
throw new IllegalArgumentException(
|
||||
"You must pass an instance of com.ibm.icu.text.NumberFormat.Field as your FieldPosition attribute. You passed: "
|
||||
+ rawField.getClass().toString());
|
||||
}
|
||||
|
||||
/* com.ibm.icu.text.NumberFormat. */ Field field = (Field) rawField;
|
||||
NumberFormat.Field field = (NumberFormat.Field) rawField;
|
||||
|
||||
boolean seenStart = false;
|
||||
int fractionStart = -1;
|
||||
for (int i = zero; i <= zero + length; i++) {
|
||||
int startIndex = fp.getEndIndex();
|
||||
for (int i = zero + startIndex; i <= zero + length; i++) {
|
||||
Field _field = (i < zero + length) ? fields[i] : null;
|
||||
if (seenStart && field != _field) {
|
||||
// Special case: GROUPING_SEPARATOR counts as an INTEGER.
|
||||
|
@ -522,10 +522,10 @@ public class NumberStringBuilder implements CharSequence {
|
|||
&& _field == NumberFormat.Field.GROUPING_SEPARATOR) {
|
||||
continue;
|
||||
}
|
||||
fp.setEndIndex(i - zero + offset);
|
||||
fp.setEndIndex(i - zero);
|
||||
break;
|
||||
} else if (!seenStart && field == _field) {
|
||||
fp.setBeginIndex(i - zero + offset);
|
||||
fp.setBeginIndex(i - zero);
|
||||
seenStart = true;
|
||||
}
|
||||
if (_field == NumberFormat.Field.INTEGER || _field == NumberFormat.Field.DECIMAL_SEPARATOR) {
|
||||
|
@ -533,14 +533,17 @@ public class NumberStringBuilder implements CharSequence {
|
|||
}
|
||||
}
|
||||
|
||||
// Backwards compatibility: FRACTION needs to start after INTEGER if empty
|
||||
if (field == NumberFormat.Field.FRACTION && !seenStart) {
|
||||
fp.setBeginIndex(fractionStart + offset);
|
||||
fp.setEndIndex(fractionStart + offset);
|
||||
// Backwards compatibility: FRACTION needs to start after INTEGER if empty.
|
||||
// Do not return that a field was found, though, since there is not actually a fraction part.
|
||||
if (field == NumberFormat.Field.FRACTION && !seenStart && fractionStart != -1) {
|
||||
fp.setBeginIndex(fractionStart);
|
||||
fp.setEndIndex(fractionStart);
|
||||
}
|
||||
|
||||
return seenStart;
|
||||
}
|
||||
|
||||
public AttributedCharacterIterator getIterator() {
|
||||
public AttributedCharacterIterator toCharacterIterator() {
|
||||
AttributedString as = new AttributedString(toString());
|
||||
Field current = null;
|
||||
int currentStart = -1;
|
||||
|
|
|
@ -85,43 +85,91 @@ public class FormattedNumber {
|
|||
*
|
||||
* @param fieldPosition
|
||||
* The FieldPosition to populate with the start and end indices of the desired field.
|
||||
* @draft ICU 60
|
||||
* @deprecated ICU 62 Use {@link #nextFieldPosition} instead. This method will be removed in a future
|
||||
* release. See http://bugs.icu-project.org/trac/ticket/13746
|
||||
* @see com.ibm.icu.text.NumberFormat.Field
|
||||
* @see NumberFormatter
|
||||
*/
|
||||
@Deprecated
|
||||
public void populateFieldPosition(FieldPosition fieldPosition) {
|
||||
// in case any users were depending on the old behavior:
|
||||
fieldPosition.setEndIndex(0);
|
||||
nextFieldPosition(fieldPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the start and end indices of the next occurrence of the given <em>field</em> in the
|
||||
* output string. This allows you to determine the locations of, for example, the integer part,
|
||||
* fraction part, or symbols.
|
||||
* <p>
|
||||
* If a field occurs just once, calling this method will find that occurrence and return it. If a
|
||||
* field occurs multiple times, this method may be called repeatedly with the following pattern:
|
||||
* <p>
|
||||
* <pre>
|
||||
* FieldPosition fpos = new FieldPosition(NumberFormat.Field.GROUPING_SEPARATOR);
|
||||
* while (formattedNumber.nextFieldPosition(fpos, status)) {
|
||||
* // do something with fpos.
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* This method is useful if you know which field to query. If you want all available field position
|
||||
* information, use #getAllFields().
|
||||
*
|
||||
* @param fieldPosition
|
||||
* Input+output variable. On input, the "field" property determines which field to look up,
|
||||
* and the "endIndex" property determines where to begin the search. On output, the
|
||||
* "beginIndex" field is set to the beginning of the first occurrence of the field after the
|
||||
* input "endIndex", and "endIndex" is set to the end of that occurrence of the field
|
||||
* (exclusive index). If a field position is not found, the FieldPosition is not changed and
|
||||
* the method returns false.
|
||||
* @return true if a new occurrence of the field was found; false otherwise.
|
||||
* @draft ICU 62
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
* @see com.ibm.icu.text.NumberFormat.Field
|
||||
* @see NumberFormatter
|
||||
*/
|
||||
public void populateFieldPosition(FieldPosition fieldPosition) {
|
||||
populateFieldPosition(fieldPosition, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public void populateFieldPosition(FieldPosition fieldPosition, int offset) {
|
||||
nsb.populateFieldPosition(fieldPosition, offset);
|
||||
public boolean nextFieldPosition(FieldPosition fieldPosition) {
|
||||
fq.populateUFieldPosition(fieldPosition);
|
||||
return nsb.nextFieldPosition(fieldPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the formatted number as an AttributedCharacterIterator. This allows you to determine which
|
||||
* characters in the output string correspond to which <em>fields</em>, such as the integer part,
|
||||
* fraction part, and sign.
|
||||
*
|
||||
* <p>
|
||||
* If information on only one field is needed, consider using populateFieldPosition() instead.
|
||||
*
|
||||
* @return An AttributedCharacterIterator, containing information on the field attributes of the
|
||||
* number string.
|
||||
* @draft ICU 60
|
||||
* @deprecated ICU 62 Use {@link #toCharacterIterator} instead. This method will be removed in a future
|
||||
* release. See http://bugs.icu-project.org/trac/ticket/13746
|
||||
* @see com.ibm.icu.text.NumberFormat.Field
|
||||
* @see AttributedCharacterIterator
|
||||
* @see NumberFormatter
|
||||
*/
|
||||
@Deprecated
|
||||
public AttributedCharacterIterator getFieldIterator() {
|
||||
return nsb.toCharacterIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the formatted number as an AttributedCharacterIterator. This allows you to determine which
|
||||
* characters in the output string correspond to which <em>fields</em>, such as the integer part,
|
||||
* fraction part, and sign.
|
||||
* <p>
|
||||
* If information on only one field is needed, consider using populateFieldPosition() instead.
|
||||
*
|
||||
* @return An AttributedCharacterIterator, containing information on the field attributes of the
|
||||
* number string.
|
||||
* @draft ICU 62
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
* @see com.ibm.icu.text.NumberFormat.Field
|
||||
* @see AttributedCharacterIterator
|
||||
* @see NumberFormatter
|
||||
*/
|
||||
public AttributedCharacterIterator getFieldIterator() {
|
||||
return nsb.getIterator();
|
||||
public AttributedCharacterIterator toCharacterIterator() {
|
||||
return nsb.toCharacterIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -709,7 +709,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
@Override
|
||||
public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
|
||||
FormattedNumber output = formatter.format(number);
|
||||
output.populateFieldPosition(fieldPosition, result.length());
|
||||
fieldPositionHelper(output, fieldPosition, result.length());
|
||||
output.appendTo(result);
|
||||
return result;
|
||||
}
|
||||
|
@ -722,7 +722,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
@Override
|
||||
public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) {
|
||||
FormattedNumber output = formatter.format(number);
|
||||
output.populateFieldPosition(fieldPosition, result.length());
|
||||
fieldPositionHelper(output, fieldPosition, result.length());
|
||||
output.appendTo(result);
|
||||
return result;
|
||||
}
|
||||
|
@ -735,7 +735,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
@Override
|
||||
public StringBuffer format(BigInteger number, StringBuffer result, FieldPosition fieldPosition) {
|
||||
FormattedNumber output = formatter.format(number);
|
||||
output.populateFieldPosition(fieldPosition, result.length());
|
||||
fieldPositionHelper(output, fieldPosition, result.length());
|
||||
output.appendTo(result);
|
||||
return result;
|
||||
}
|
||||
|
@ -749,7 +749,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
public StringBuffer format(
|
||||
java.math.BigDecimal number, StringBuffer result, FieldPosition fieldPosition) {
|
||||
FormattedNumber output = formatter.format(number);
|
||||
output.populateFieldPosition(fieldPosition, result.length());
|
||||
fieldPositionHelper(output, fieldPosition, result.length());
|
||||
output.appendTo(result);
|
||||
return result;
|
||||
}
|
||||
|
@ -762,7 +762,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
@Override
|
||||
public StringBuffer format(BigDecimal number, StringBuffer result, FieldPosition fieldPosition) {
|
||||
FormattedNumber output = formatter.format(number);
|
||||
output.populateFieldPosition(fieldPosition, result.length());
|
||||
fieldPositionHelper(output, fieldPosition, result.length());
|
||||
output.appendTo(result);
|
||||
return result;
|
||||
}
|
||||
|
@ -786,11 +786,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
* @stable ICU 3.0
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(CurrencyAmount currAmt, StringBuffer toAppendTo, FieldPosition pos) {
|
||||
public StringBuffer format(CurrencyAmount currAmt, StringBuffer result, FieldPosition fieldPosition) {
|
||||
FormattedNumber output = formatter.format(currAmt);
|
||||
output.populateFieldPosition(pos, toAppendTo.length());
|
||||
output.appendTo(toAppendTo);
|
||||
return toAppendTo;
|
||||
fieldPositionHelper(output, fieldPosition, result.length());
|
||||
output.appendTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2566,6 +2566,15 @@ public class DecimalFormat extends NumberFormat {
|
|||
PatternStringParser.parseToExistingProperties(pattern, properties, ignoreRounding);
|
||||
}
|
||||
|
||||
static void fieldPositionHelper(FormattedNumber formatted, FieldPosition fieldPosition, int offset) {
|
||||
fieldPosition.setEndIndex(0); // always return first occurrence
|
||||
boolean found = formatted.nextFieldPosition(fieldPosition);
|
||||
if (found && offset != 0) {
|
||||
fieldPosition.setBeginIndex(fieldPosition.getBeginIndex() + offset);
|
||||
fieldPosition.setEndIndex(fieldPosition.getEndIndex() + offset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
|
|
|
@ -388,7 +388,7 @@ public class MeasureFormat extends UFormat {
|
|||
FormattedNumber result = getUnitFormatterFromCache(NUMBER_FORMATTER_STANDARD,
|
||||
measure.getUnit(),
|
||||
perUnit).format(measure.getNumber());
|
||||
result.populateFieldPosition(pos, appendTo.length());
|
||||
DecimalFormat.fieldPositionHelper(result, pos, appendTo.length());
|
||||
result.appendTo(appendTo);
|
||||
return appendTo;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
package com.ibm.icu.dev.test.number;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
@ -11,6 +12,8 @@ import java.lang.reflect.InvocationTargetException;
|
|||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.FieldPosition;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
|
@ -26,6 +29,7 @@ import com.ibm.icu.impl.number.Padder;
|
|||
import com.ibm.icu.impl.number.Padder.PadPosition;
|
||||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.number.CompactNotation;
|
||||
import com.ibm.icu.number.FormattedNumber;
|
||||
import com.ibm.icu.number.FractionRounder;
|
||||
import com.ibm.icu.number.IntegerWidth;
|
||||
import com.ibm.icu.number.LocalizedNumberFormatter;
|
||||
|
@ -40,6 +44,7 @@ import com.ibm.icu.number.Scale;
|
|||
import com.ibm.icu.number.ScientificNotation;
|
||||
import com.ibm.icu.number.UnlocalizedNumberFormatter;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.text.NumberFormat;
|
||||
import com.ibm.icu.text.NumberingSystem;
|
||||
import com.ibm.icu.util.Currency;
|
||||
import com.ibm.icu.util.Currency.CurrencyUsage;
|
||||
|
@ -2040,6 +2045,79 @@ public class NumberFormatterApiTest {
|
|||
.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fieldPosition() {
|
||||
FormattedNumber fmtd = NumberFormatter.withLocale(ULocale.ENGLISH).format(-9876543210.12);
|
||||
assertEquals("Should have expected format output", "-9,876,543,210.12", fmtd.toString());
|
||||
|
||||
Object[][] expectedFieldPositions = new Object[][]{
|
||||
{NumberFormat.Field.SIGN, 0, 1},
|
||||
{NumberFormat.Field.GROUPING_SEPARATOR, 2, 3},
|
||||
{NumberFormat.Field.GROUPING_SEPARATOR, 6, 7},
|
||||
{NumberFormat.Field.GROUPING_SEPARATOR, 10, 11},
|
||||
{NumberFormat.Field.INTEGER, 1, 14},
|
||||
{NumberFormat.Field.DECIMAL_SEPARATOR, 14, 15},
|
||||
{NumberFormat.Field.FRACTION, 15, 17}};
|
||||
|
||||
AttributedCharacterIterator fpi = fmtd.getFieldIterator();
|
||||
Set<AttributedCharacterIterator.Attribute> allAttributes = fpi.getAllAttributeKeys();
|
||||
assertEquals("All known fields should be in the iterator", 5, allAttributes.size());
|
||||
assertEquals("Iterator should have length of string output", 17, fpi.getEndIndex());
|
||||
int i = 0;
|
||||
for (char c = fpi.first(); c != AttributedCharacterIterator.DONE; c = fpi.next(), i++) {
|
||||
Set<AttributedCharacterIterator.Attribute> currentAttributes = fpi.getAttributes().keySet();
|
||||
int attributesRemaining = currentAttributes.size();
|
||||
for (Object[] cas : expectedFieldPositions) {
|
||||
NumberFormat.Field expectedField = (NumberFormat.Field) cas[0];
|
||||
int expectedBeginIndex = (Integer) cas[1];
|
||||
int expectedEndIndex = (Integer) cas[2];
|
||||
if (expectedBeginIndex > i || expectedEndIndex <= i) {
|
||||
// Field position does not overlap with the current character
|
||||
continue;
|
||||
}
|
||||
|
||||
assertTrue("Current character should have expected field", currentAttributes.contains(expectedField));
|
||||
assertTrue("Field should be a known attribute", allAttributes.contains(expectedField));
|
||||
int actualBeginIndex = fpi.getRunStart(expectedField);
|
||||
int actualEndIndex = fpi.getRunLimit(expectedField);
|
||||
assertEquals(expectedField + " begin index @" + i, expectedBeginIndex, actualBeginIndex);
|
||||
assertEquals(expectedField + " end index @" + i, expectedEndIndex, actualEndIndex);
|
||||
attributesRemaining--;
|
||||
}
|
||||
assertEquals("Should have looked at every field", 0, attributesRemaining);
|
||||
}
|
||||
assertEquals("Should have looked at every character", 17, i);
|
||||
|
||||
// Test the iteration functionality of nextFieldPosition
|
||||
FieldPosition actual = new FieldPosition(NumberFormat.Field.GROUPING_SEPARATOR);
|
||||
i = 1;
|
||||
while (fmtd.nextFieldPosition(actual)) {
|
||||
Object[] cas = expectedFieldPositions[i++];
|
||||
NumberFormat.Field expectedField = (NumberFormat.Field) cas[0];
|
||||
int expectedBeginIndex = (Integer) cas[1];
|
||||
int expectedEndIndex = (Integer) cas[2];
|
||||
|
||||
assertEquals(
|
||||
"Next for grouping, field, case #" + i,
|
||||
expectedField,
|
||||
actual.getFieldAttribute());
|
||||
assertEquals(
|
||||
"Next for grouping, begin index, case #" + i,
|
||||
expectedBeginIndex,
|
||||
actual.getBeginIndex());
|
||||
assertEquals(
|
||||
"Next for grouping, end index, case #" + i,
|
||||
expectedEndIndex,
|
||||
actual.getEndIndex());
|
||||
}
|
||||
assertEquals("Should have seen all grouping separators", 4, i);
|
||||
|
||||
// Make sure strings without fraction do not contain fraction field
|
||||
actual = new FieldPosition(NumberFormat.Field.FRACTION);
|
||||
fmtd = NumberFormatter.withLocale(ULocale.ENGLISH).format(5);
|
||||
assertFalse("No fraction part in an integer", fmtd.nextFieldPosition(actual));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void plurals() {
|
||||
// TODO: Expand this test.
|
||||
|
|
|
@ -170,7 +170,7 @@ public class NumberStringBuilderTest {
|
|||
// Very basic FieldPosition test. More robust tests happen in NumberFormatTest.
|
||||
// Let NumberFormatTest also take care of AttributedCharacterIterator material.
|
||||
FieldPosition fp = new FieldPosition(NumberFormat.Field.CURRENCY);
|
||||
sb.populateFieldPosition(fp, 0);
|
||||
sb.nextFieldPosition(fp);
|
||||
assertEquals(str.length(), fp.getBeginIndex());
|
||||
assertEquals(str.length() * 2, fp.getEndIndex());
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue