ICU-11303 Bring MeasureFormat per unit formatting out of tech preview.

X-SVN-Rev: 36813
This commit is contained in:
Travis Keep 2014-12-05 20:52:28 +00:00
parent 3d248f9875
commit 79964e6bc7
10 changed files with 789 additions and 394 deletions

View File

@ -248,9 +248,10 @@ ListFormatter::~ListFormatter() {
* On entry offset is an offset into first or -1 if offset unspecified.
* On exit offset is offset of second in result if recordOffset was set
* Otherwise if it was >=0 it is set to point into result where it used
* to point into first.
* to point into first. On exit, result is the join of first and second
* according to pat. Any previous value of result gets replaced.
*/
static void joinStrings(
static void joinStringsAndReplace(
const SimplePatternFormatter& pat,
const UnicodeString& first,
const UnicodeString& second,
@ -263,7 +264,7 @@ static void joinStrings(
}
const UnicodeString *params[2] = {&first, &second};
int32_t offsets[2];
pat.format(
pat.formatAndReplace(
params,
UPRV_LENGTHOF(params),
result,
@ -319,69 +320,43 @@ UnicodeString& ListFormatter::format(
appendTo.append(items[0]);
return appendTo;
}
if (nItems == 2) {
UnicodeString result(items[0]);
if (index == 0) {
offset = 0;
}
joinStrings(
data->twoPattern,
items[0],
joinStringsAndReplace(
nItems == 2 ? data->twoPattern : data->startPattern,
result,
items[1],
appendTo,
result,
index == 1,
offset,
errorCode);
return appendTo;
}
UnicodeString temp[2];
if (index == 0) {
offset = 0;
}
joinStrings(
data->startPattern,
items[0],
items[1],
temp[0],
index == 1,
offset,
errorCode);
int32_t i;
int32_t pos = 0;
int32_t npos = 0;
UBool startsWithZeroPlaceholder =
data->middlePattern.startsWithPlaceholder(0);
for (i = 2; i < nItems - 1; ++i) {
if (!startsWithZeroPlaceholder) {
npos = (pos + 1) & 1;
temp[npos].remove();
}
joinStrings(
if (nItems > 2) {
for (int32_t i = 2; i < nItems - 1; ++i) {
joinStringsAndReplace(
data->middlePattern,
temp[pos],
result,
items[i],
temp[npos],
result,
index == i,
offset,
errorCode);
pos = npos;
}
if (!data->endPattern.startsWithPlaceholder(0)) {
npos = (pos + 1) & 1;
temp[npos].remove();
}
joinStrings(
joinStringsAndReplace(
data->endPattern,
temp[pos],
result,
items[nItems - 1],
temp[npos],
result,
index == nItems - 1,
offset,
errorCode);
}
if (U_SUCCESS(errorCode)) {
if (offset >= 0) {
offset += appendTo.length();
}
appendTo += temp[npos];
appendTo += result;
}
return appendTo;
}

View File

@ -11,20 +11,38 @@
U_NAMESPACE_BEGIN
static UBool isInvalidArray(const void *array, int32_t size) {
return (size < 0 || (size > 0 && array == NULL));
}
typedef enum SimplePatternFormatterCompileState {
INIT,
APOSTROPHE,
PLACEHOLDER
} SimplePatternFormatterCompileState;
// Handles parsing placeholders in the pattern string, e.g {4} or {35}
class SimplePatternFormatterIdBuilder {
public:
SimplePatternFormatterIdBuilder() : id(0), idLen(0) { }
~SimplePatternFormatterIdBuilder() { }
// Resets so that this object has seen no placeholder ID.
void reset() { id = 0; idLen = 0; }
// Returns the numeric placeholder ID parsed so far
int32_t getId() const { return id; }
// Appends the numeric placeholder ID parsed so far back to a
// UChar buffer. Used to recover if parser using this object finds
// no closing curly brace.
void appendTo(UChar *buffer, int32_t *len) const;
// Returns true if this object has seen a placeholder ID.
UBool isValid() const { return (idLen > 0); }
// Processes a single digit character. Pattern string parser calls this
// as it processes digits after an opening curly brace.
void add(UChar ch);
private:
int32_t id;
@ -52,18 +70,81 @@ void SimplePatternFormatterIdBuilder::add(UChar ch) {
idLen++;
}
// Represents placeholder values.
class SimplePatternFormatterPlaceholderValues : public UMemory {
public:
SimplePatternFormatterPlaceholderValues(
const UnicodeString * const *values,
int32_t valuesCount);
// Returns TRUE if appendTo value is at any index besides exceptIndex.
UBool isAppendToInAnyIndexExcept(
const UnicodeString &appendTo, int32_t exceptIndex) const;
// For each appendTo value, stores the snapshot of it in its place.
void snapshotAppendTo(const UnicodeString &appendTo);
// Returns the placeholder value at index. No range checking performed.
// Returned reference is valid for as long as this object exists.
const UnicodeString &get(int32_t index) const;
private:
const UnicodeString * const *fValues;
int32_t fValuesCount;
const UnicodeString *fAppendTo;
UnicodeString fAppendToCopy;
SimplePatternFormatterPlaceholderValues(
const SimplePatternFormatterPlaceholderValues &);
SimplePatternFormatterPlaceholderValues &operator=(
const SimplePatternFormatterPlaceholderValues &);
};
SimplePatternFormatterPlaceholderValues::SimplePatternFormatterPlaceholderValues(
const UnicodeString * const *values,
int32_t valuesCount)
: fValues(values),
fValuesCount(valuesCount),
fAppendTo(NULL),
fAppendToCopy() {
}
UBool SimplePatternFormatterPlaceholderValues::isAppendToInAnyIndexExcept(
const UnicodeString &appendTo, int32_t exceptIndex) const {
for (int32_t i = 0; i < fValuesCount; ++i) {
if (i != exceptIndex && fValues[i] == &appendTo) {
return TRUE;
}
}
return FALSE;
}
void SimplePatternFormatterPlaceholderValues::snapshotAppendTo(
const UnicodeString &appendTo) {
fAppendTo = &appendTo;
fAppendToCopy = appendTo;
}
const UnicodeString &SimplePatternFormatterPlaceholderValues::get(
int32_t index) const {
if (fAppendTo == NULL || fAppendTo != fValues[index]) {
return *fValues[index];
}
return fAppendToCopy;
}
SimplePatternFormatter::SimplePatternFormatter() :
noPlaceholders(),
placeholders(),
placeholderSize(0),
placeholderCount(0) {
placeholderCount(0),
firstPlaceholderReused(FALSE) {
}
SimplePatternFormatter::SimplePatternFormatter(const UnicodeString &pattern) :
noPlaceholders(),
placeholders(),
placeholderSize(0),
placeholderCount(0) {
placeholderCount(0),
firstPlaceholderReused(FALSE) {
UErrorCode status = U_ZERO_ERROR;
compile(pattern, status);
}
@ -73,7 +154,8 @@ SimplePatternFormatter::SimplePatternFormatter(
noPlaceholders(other.noPlaceholders),
placeholders(),
placeholderSize(0),
placeholderCount(other.placeholderCount) {
placeholderCount(other.placeholderCount),
firstPlaceholderReused(other.firstPlaceholderReused) {
placeholderSize = ensureCapacity(other.placeholderSize);
uprv_memcpy(
placeholders.getAlias(),
@ -89,6 +171,7 @@ SimplePatternFormatter &SimplePatternFormatter::operator=(
noPlaceholders = other.noPlaceholders;
placeholderSize = ensureCapacity(other.placeholderSize);
placeholderCount = other.placeholderCount;
firstPlaceholderReused = other.firstPlaceholderReused;
uprv_memcpy(
placeholders.getAlias(),
other.placeholders.getAlias(),
@ -175,19 +258,12 @@ UBool SimplePatternFormatter::compile(
return TRUE;
}
UBool SimplePatternFormatter::startsWithPlaceholder(int32_t id) const {
if (placeholderSize == 0) {
return FALSE;
}
return (placeholders[0].offset == 0 && placeholders[0].id == id);
}
UnicodeString& SimplePatternFormatter::format(
const UnicodeString &arg0,
UnicodeString &appendTo,
UErrorCode &status) const {
const UnicodeString *params[] = {&arg0};
return format(
return formatAndAppend(
params,
UPRV_LENGTHOF(params),
appendTo,
@ -202,7 +278,7 @@ UnicodeString& SimplePatternFormatter::format(
UnicodeString &appendTo,
UErrorCode &status) const {
const UnicodeString *params[] = {&arg0, &arg1};
return format(
return formatAndAppend(
params,
UPRV_LENGTHOF(params),
appendTo,
@ -218,7 +294,7 @@ UnicodeString& SimplePatternFormatter::format(
UnicodeString &appendTo,
UErrorCode &status) const {
const UnicodeString *params[] = {&arg0, &arg1, &arg2};
return format(
return formatAndAppend(
params,
UPRV_LENGTHOF(params),
appendTo,
@ -242,10 +318,14 @@ static void appendRange(
int32_t start,
int32_t end,
UnicodeString &dest) {
// This check improves performance significantly.
if (start == end) {
return;
}
dest.append(src, start, end - start);
}
UnicodeString& SimplePatternFormatter::format(
UnicodeString& SimplePatternFormatter::formatAndAppend(
const UnicodeString * const *placeholderValues,
int32_t placeholderValueCount,
UnicodeString &appendTo,
@ -255,10 +335,102 @@ UnicodeString& SimplePatternFormatter::format(
if (U_FAILURE(status)) {
return appendTo;
}
if (isInvalidArray(placeholderValues, placeholderValueCount)
|| isInvalidArray(offsetArray, offsetArrayLength)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return appendTo;
}
if (placeholderValueCount < placeholderCount) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return appendTo;
}
// Since we are disallowing parameter values that are the same as
// appendTo, we have to check all placeholderValues as opposed to
// the first placeholderCount placeholder values.
SimplePatternFormatterPlaceholderValues values(
placeholderValues, placeholderValueCount);
if (values.isAppendToInAnyIndexExcept(appendTo, -1)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return appendTo;
}
return formatAndAppend(
values,
appendTo,
offsetArray,
offsetArrayLength);
}
UnicodeString& SimplePatternFormatter::formatAndReplace(
const UnicodeString * const *placeholderValues,
int32_t placeholderValueCount,
UnicodeString &result,
int32_t *offsetArray,
int32_t offsetArrayLength,
UErrorCode &status) const {
if (U_FAILURE(status)) {
return result;
}
if (isInvalidArray(placeholderValues, placeholderValueCount)
|| isInvalidArray(offsetArray, offsetArrayLength)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return result;
}
if (placeholderValueCount < placeholderCount) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return result;
}
SimplePatternFormatterPlaceholderValues values(
placeholderValues, placeholderCount);
int32_t placeholderAtStart = getUniquePlaceholderAtStart();
// If pattern starts with a unique placeholder and that placeholder
// value is result, we may be able to optimize by just appending to result.
if (placeholderAtStart >= 0
&& placeholderValues[placeholderAtStart] == &result) {
// If result is the value for other placeholders, call off optimization.
if (values.isAppendToInAnyIndexExcept(result, placeholderAtStart)) {
values.snapshotAppendTo(result);
result.remove();
return formatAndAppend(
values,
result,
offsetArray,
offsetArrayLength);
}
// Otherwise we can optimize
formatAndAppend(
values,
result,
offsetArray,
offsetArrayLength);
// We have to make the offset for the placeholderAtStart
// placeholder be 0. Otherwise it would be the length of the
// previous value of result.
if (offsetArrayLength > placeholderAtStart) {
offsetArray[placeholderAtStart] = 0;
}
return result;
}
if (values.isAppendToInAnyIndexExcept(result, -1)) {
values.snapshotAppendTo(result);
}
result.remove();
return formatAndAppend(
values,
result,
offsetArray,
offsetArrayLength);
}
UnicodeString& SimplePatternFormatter::formatAndAppend(
const SimplePatternFormatterPlaceholderValues &values,
UnicodeString &appendTo,
int32_t *offsetArray,
int32_t offsetArrayLength) const {
for (int32_t i = 0; i < offsetArrayLength; ++i) {
offsetArray[i] = -1;
}
@ -266,8 +438,6 @@ UnicodeString& SimplePatternFormatter::format(
appendTo.append(noPlaceholders);
return appendTo;
}
if (placeholders[0].offset > 0 ||
placeholderValues[placeholders[0].id] != &appendTo) {
appendRange(
noPlaceholders,
0,
@ -278,13 +448,9 @@ UnicodeString& SimplePatternFormatter::format(
appendTo.length(),
offsetArray,
offsetArrayLength);
appendTo.append(*placeholderValues[placeholders[0].id]);
} else {
updatePlaceholderOffset(
placeholders[0].id,
0,
offsetArray,
offsetArrayLength);
const UnicodeString *placeholderValue = &values.get(placeholders[0].id);
if (placeholderValue != &appendTo) {
appendTo.append(*placeholderValue);
}
for (int32_t i = 1; i < placeholderSize; ++i) {
appendRange(
@ -297,7 +463,10 @@ UnicodeString& SimplePatternFormatter::format(
appendTo.length(),
offsetArray,
offsetArrayLength);
appendTo.append(*placeholderValues[placeholders[i].id]);
placeholderValue = &values.get(placeholders[i].id);
if (placeholderValue != &appendTo) {
appendTo.append(*placeholderValue);
}
}
appendRange(
noPlaceholders,
@ -307,6 +476,14 @@ UnicodeString& SimplePatternFormatter::format(
return appendTo;
}
int32_t SimplePatternFormatter::getUniquePlaceholderAtStart() const {
if (placeholderSize == 0
|| firstPlaceholderReused || placeholders[0].offset != 0) {
return -1;
}
return placeholders[0].id;
}
int32_t SimplePatternFormatter::ensureCapacity(
int32_t desiredCapacity, int32_t allocationSize) {
if (allocationSize < desiredCapacity) {
@ -333,6 +510,10 @@ UBool SimplePatternFormatter::addPlaceholder(int32_t id, int32_t offset) {
if (id >= placeholderCount) {
placeholderCount = id + 1;
}
if (placeholderSize > 1
&& placeholders[placeholderSize - 1].id == placeholders[0].id) {
firstPlaceholderReused = TRUE;
}
return TRUE;
}

View File

@ -17,6 +17,8 @@
U_NAMESPACE_BEGIN
class SimplePatternFormatterPlaceholderValues;
struct PlaceholderInfo {
int32_t id;
int32_t offset;
@ -39,7 +41,7 @@ struct PlaceholderInfo {
* UnicodeString result;
* UErrorCode status = U_ZERO_ERROR;
* // Evaluates to: "paul {born} in england"
* fmt.format("englad", "paul", result, status);
* fmt.format("england", "paul", result, status);
* </pre>
*/
class U_COMMON_API SimplePatternFormatter : public UMemory {
@ -90,12 +92,6 @@ public:
return placeholderCount;
}
/**
* Returns true if the pattern this object represents starts with
* placeholder id; otherwise, returns false.
*/
UBool startsWithPlaceholder(int32_t id) const;
/**
* Returns this pattern with none of the placeholders.
*/
@ -104,7 +100,7 @@ public:
}
/**
* Formats given value.
* Formats given value. arg0 cannot be appendTo.
*/
UnicodeString &format(
const UnicodeString &args0,
@ -112,7 +108,7 @@ public:
UErrorCode &status) const;
/**
* Formats given values.
* Formats given values. Neither arg0 nor arg1 can be appendTo.
*/
UnicodeString &format(
const UnicodeString &args0,
@ -121,7 +117,7 @@ public:
UErrorCode &status) const;
/**
* Formats given values.
* Formats given values. Neither arg0, arg1, nor arg2 can be appendTo.
*/
UnicodeString &format(
const UnicodeString &args0,
@ -135,37 +131,82 @@ public:
*
* The caller retains ownership of all pointers.
* @param placeholderValues 1st one corresponds to {0}; 2nd to {1};
* 3rd to {2} etc.
* 3rd to {2} etc. If any of these point to appendTo, this method
* sets status to U_ILLEGAL_ARGUMENT_ERROR.
* @param placeholderValueCount the number of placeholder values
* must be at least large enough to provide values for all placeholders
* in this object. Otherwise status set to U_ILLEGAL_ARGUMENT_ERROR.
* @param appendTo resulting string appended here. Optimization: If
* the pattern this object represents starts with a placeholder AND
* appendTo references the value of that same placeholder, then that
* placeholder value is not copied to appendTo (Its already there).
* If the value of the starting placeholder is a very large string,
* this optimization can offer huge savings.
* @param appendTo resulting string appended here.
* @param offsetArray The offset of each placeholder value in appendTo
* stored here. The first value gets the offset of the value for {0};
* the 2nd for {1}; the 3rd for {2} etc. -1 means that the corresponding
* placeholder does not exist in this object. If caller is not
* interested in offsets, it may pass NULL and 0 for the length.
* @param offsetArrayLength the size of offsetArray may be less than
* placeholderValueCount.
* @param offsetArrayLength the size of offsetArray. If less than
* placeholderValueCount only the first offsets get recorded. If
* greater than placeholderValueCount, then extra values in offset
* array are set to -1.
* @param status any error stored here.
*/
UnicodeString &format(
UnicodeString &formatAndAppend(
const UnicodeString * const *placeholderValues,
int32_t placeholderValueCount,
UnicodeString &appendTo,
int32_t *offsetArray,
int32_t offsetArrayLength,
UErrorCode &status) const;
/**
* Formats given values.
*
* The caller retains ownership of all pointers.
* @param placeholderValues 1st one corresponds to {0}; 2nd to {1};
* 3rd to {2} etc. May include pointer to result in which case
* the previous value of result is used for the corresponding
* placeholder.
* @param placeholderValueCount the number of placeholder values
* must be at least large enough to provide values for all placeholders
* in this object. Otherwise status set to U_ILLEGAL_ARGUMENT_ERROR.
* @param result resulting string stored here overwriting any previous
* value.
* @param offsetArray The offset of each placeholder value in result
* stored here. The first value gets the offset of the value for {0};
* the 2nd for {1}; the 3rd for {2} etc. -1 means that the corresponding
* placeholder does not exist in this object. If caller is not
* interested in offsets, it may pass NULL and 0 for the length.
* @param offsetArrayLength the size of offsetArray. If less than
* placeholderValueCount only the first offsets get recorded. If
* greater than placeholderValueCount, then extra values in offset
* array are set to -1.
* @param status any error stored here.
*/
UnicodeString &formatAndReplace(
const UnicodeString * const *placeholderValues,
int32_t placeholderValueCount,
UnicodeString &result,
int32_t *offsetArray,
int32_t offsetArrayLength,
UErrorCode &status) const;
private:
UnicodeString noPlaceholders;
MaybeStackArray<PlaceholderInfo, 3> placeholders;
int32_t placeholderSize;
int32_t placeholderCount;
UBool firstPlaceholderReused;
// A Placeholder value that is the same as appendTo is treated as the
// empty string.
UnicodeString &formatAndAppend(
const SimplePatternFormatterPlaceholderValues &placeholderValues,
UnicodeString &appendTo,
int32_t *offsetArray,
int32_t offsetArrayLength) const;
// Returns the placeholder at the beginning of this pattern
// (e.g 3 for placeholder {3}). Returns -1 if the beginning of pattern
// is text or if the placeholder at the beginning of this pattern
// is used again in the middle of the pattern.
int32_t getUniquePlaceholderAtStart() const;
// ensureCapacity ensures that the capacity of the placeholders array
// is desiredCapacity. If ensureCapacity must resize the placeholders

View File

@ -590,18 +590,27 @@ void MeasureFormat::parseObject(
return;
}
UnicodeString &MeasureFormat::formatMeasuresPer(
const Measure *measures,
int32_t measureCount,
UnicodeString &MeasureFormat::formatMeasurePerUnit(
const Measure &measure,
const MeasureUnit &perUnit,
UnicodeString &appendTo,
FieldPosition &pos,
UErrorCode &status) const {
if (U_FAILURE(status)) {
return appendTo;
}
MeasureUnit *resolvedUnit =
MeasureUnit::resolveUnitPerUnit(measure.getUnit(), perUnit);
if (resolvedUnit != NULL) {
Measure newMeasure(measure.getNumber(), resolvedUnit, status);
return formatMeasure(
newMeasure, **numberFormat, appendTo, pos, status);
}
FieldPosition fpos(pos.getField());
UnicodeString measuresString;
int32_t offset = withPerUnit(
formatMeasures(
measures, measureCount, measuresString, fpos, status),
UnicodeString result;
int32_t offset = withPerUnitAndAppend(
formatMeasure(
measure, **numberFormat, result, fpos, status),
perUnit,
appendTo,
status);
@ -979,7 +988,7 @@ static void getPerUnitString(
result.trim();
}
int32_t MeasureFormat::withPerUnit(
int32_t MeasureFormat::withPerUnitAndAppend(
const UnicodeString &formatted,
const MeasureUnit &perUnit,
UnicodeString &appendTo,
@ -992,7 +1001,7 @@ int32_t MeasureFormat::withPerUnit(
perUnit.getIndex(), widthToIndex(width));
if (perUnitFormatter != NULL) {
const UnicodeString *params[] = {&formatted};
perUnitFormatter->format(
perUnitFormatter->formatAndAppend(
params,
UPRV_LENGTHOF(params),
appendTo,
@ -1011,7 +1020,7 @@ int32_t MeasureFormat::withPerUnit(
UnicodeString perUnitString;
getPerUnitString(*qf, perUnitString);
const UnicodeString *params[] = {&formatted, &perUnitString};
perFormatter->format(
perFormatter->formatAndAppend(
params,
UPRV_LENGTHOF(params),
appendTo,

View File

@ -27,7 +27,7 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureUnit)
// the "End generated code" comment is auto generated code
// and must not be edited manually. For instructions on how to correctly
// update this code, refer to:
// https://sites.google.com/site/icusite/design/formatting/measureformat/updating-measure-unit
// http://site.icu-project.org/design/formatting/measureformat/updating-measure-unit
//
// Start generated code
@ -77,6 +77,7 @@ static const int32_t gIndexes[] = {
121
};
// Must be sorted alphabetically.
static const char * const gTypes[] = {
"acceleration",
"angle",
@ -99,6 +100,7 @@ static const char * const gTypes[] = {
"volume"
};
// Must be grouped by type and sorted alphabetically within each type.
static const char * const gSubTypes[] = {
"g-force",
"meter-per-second-squared",
@ -483,6 +485,16 @@ static const char * const gSubTypes[] = {
"teaspoon"
};
// Must be sorted by first value and then second value.
static int32_t unitPerUnitToSingleUnit[][4] = {
{318, 288, 16, 0},
{320, 294, 16, 1},
{322, 288, 16, 2},
{322, 372, 3, 1},
{338, 10, 14, 4},
{374, 318, 3, 0}
};
MeasureUnit *MeasureUnit::createGForce(UErrorCode &status) {
return MeasureUnit::create(0, 0, status);
}
@ -1100,6 +1112,34 @@ int32_t MeasureUnit::getIndexCount() {
return gIndexes[UPRV_LENGTHOF(gIndexes) - 1];
}
MeasureUnit *MeasureUnit::resolveUnitPerUnit(
const MeasureUnit &unit, const MeasureUnit &perUnit) {
int32_t unitOffset = unit.getOffset();
int32_t perUnitOffset = perUnit.getOffset();
// binary search for (unitOffset, perUnitOffset)
int32_t start = 0;
int32_t end = UPRV_LENGTHOF(unitPerUnitToSingleUnit);
while (start < end) {
int32_t mid = (start + end) / 2;
int32_t *midRow = unitPerUnitToSingleUnit[mid];
if (unitOffset < midRow[0]) {
end = mid;
} else if (unitOffset > midRow[0]) {
start = mid + 1;
} else if (perUnitOffset < midRow[1]) {
end = mid;
} else if (perUnitOffset > midRow[1]) {
start = mid + 1;
} else {
// We found a resolution for our unit / per-unit combo
// return it.
return new MeasureUnit(midRow[2], midRow[3]);
}
}
return NULL;
}
MeasureUnit *MeasureUnit::create(int typeId, int subTypeId, UErrorCode &status) {
if (U_FAILURE(status)) {
return NULL;

View File

@ -170,7 +170,13 @@ UnicodeString &QuantityFormatter::format(
fmt.format(quantity, formattedNumber, fpos, status);
const UnicodeString *params[1] = {&formattedNumber};
int32_t offsets[1];
pattern->format(params, UPRV_LENGTHOF(params), appendTo, offsets, UPRV_LENGTHOF(offsets), status);
pattern->formatAndAppend(
params,
UPRV_LENGTHOF(params),
appendTo,
offsets,
UPRV_LENGTHOF(offsets),
status);
if (offsets[0] != -1) {
if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
pos.setBeginIndex(fpos.getBeginIndex() + offsets[0]);

View File

@ -190,31 +190,28 @@ class U_I18N_API MeasureFormat : public Format {
UnicodeString &appendTo,
FieldPosition &pos,
UErrorCode &status) const;
#endif /* U_HIDE_DRAFT_API */
#ifndef U_HIDE_INTERNAL_API
/**
* Works like formatMeasures but adds a per unit. An example of such a
* formatted string is 3 meters, 3.5 centimeters per second.
* @param measures array of measure objects.
* @param measureCount the number of measure objects.
* @param perUnit The per unit. In the example formatted string,
* it is *MeasureUnit::createSecond(status).
* Formats a single measure per unit. An example of such a
* formatted string is 3.5 meters per second.
* @param measure The measure object. In above example, 3.5 meters.
* @param perUnit The per unit. In above example, it is
* *MeasureUnit::createSecond(status).
* @param appendTo formatted string appended here.
* @param pos the field position.
* @param status the error.
* @return appendTo reference
*
* @internal Technology preview
* @draft ICU 55
*/
UnicodeString &formatMeasuresPer(
const Measure *measures,
int32_t measureCount,
UnicodeString &formatMeasurePerUnit(
const Measure &measure,
const MeasureUnit &perUnit,
UnicodeString &appendTo,
FieldPosition &pos,
UErrorCode &status) const;
#endif /* U_HIDE_INTERNAL_API */
#endif /* U_HIDE_DRAFT_API */
/**
* Return a formatter for CurrencyAmount objects in the given
@ -347,7 +344,7 @@ class U_I18N_API MeasureFormat : public Format {
int32_t widthIndex,
UErrorCode &status) const;
int32_t withPerUnit(
int32_t withPerUnitAndAppend(
const UnicodeString &formatted,
const MeasureUnit &perUnit,
UnicodeString &appendTo,

View File

@ -184,13 +184,20 @@ class U_I18N_API MeasureUnit: public UObject {
* @internal
*/
static int32_t getIndexCount();
/**
* ICU use only.
* @internal
*/
static MeasureUnit *resolveUnitPerUnit(
const MeasureUnit &unit, const MeasureUnit &perUnit);
#endif /* U_HIDE_INTERNAL_API */
// All code between the "Start generated createXXX methods" comment and
// the "End generated createXXX methods" comment is auto generated code
// and must not be edited manually. For instructions on how to correctly
// update this code, refer to:
// https://sites.google.com/site/icusite/design/formatting/measureformat/updating-measure-unit
// http://site.icu-project.org/design/formatting/measureformat/updating-measure-unit
//
// Start generated createXXX methods

View File

@ -47,7 +47,6 @@ private:
void TestGreek();
void TestFormatSingleArg();
void TestFormatMeasuresZeroArg();
void TestMultiplesWithPer();
void TestSimplePer();
void TestNumeratorPlurals();
void TestMultiples();
@ -55,11 +54,11 @@ private:
void TestCurrencies();
void TestFieldPosition();
void TestFieldPositionMultiple();
void TestFieldPositionMultipleWithPer();
void TestBadArg();
void TestEquality();
void TestGroupingSeparator();
void TestDoubleZero();
void TestUnitPerUnitResolution();
void verifyFormat(
const char *description,
const MeasureFormat &fmt,
@ -85,11 +84,16 @@ private:
const MeasureUnit &unit,
const MeasureUnit &perUnit,
const char *expected);
void helperTestMultiplesWithPer(
void helperTestSimplePer(
const Locale &locale,
UMeasureFormatWidth width,
double value,
const MeasureUnit &unit,
const char *expected);
const MeasureUnit &perUnit,
const char *expected,
int32_t field,
int32_t expected_start,
int32_t expected_end);
void helperTestMultiples(
const Locale &locale,
UMeasureFormatWidth width,
@ -103,16 +107,6 @@ private:
NumberFormat::EAlignmentFields field,
int32_t start,
int32_t end);
void verifyFieldPositionWithPer(
const char *description,
const MeasureFormat &fmt,
const UnicodeString &prefix,
const Measure *measures,
int32_t measureCount,
const MeasureUnit &perUnit,
NumberFormat::EAlignmentFields field,
int32_t start,
int32_t end);
};
void MeasureFormatTest::runIndexedTest(
@ -131,7 +125,6 @@ void MeasureFormatTest::runIndexedTest(
TESTCASE_AUTO(TestGreek);
TESTCASE_AUTO(TestFormatSingleArg);
TESTCASE_AUTO(TestFormatMeasuresZeroArg);
TESTCASE_AUTO(TestMultiplesWithPer);
TESTCASE_AUTO(TestSimplePer);
TESTCASE_AUTO(TestNumeratorPlurals);
TESTCASE_AUTO(TestMultiples);
@ -139,11 +132,11 @@ void MeasureFormatTest::runIndexedTest(
TESTCASE_AUTO(TestCurrencies);
TESTCASE_AUTO(TestFieldPosition);
TESTCASE_AUTO(TestFieldPositionMultiple);
TESTCASE_AUTO(TestFieldPositionMultipleWithPer);
TESTCASE_AUTO(TestBadArg);
TESTCASE_AUTO(TestEquality);
TESTCASE_AUTO(TestGroupingSeparator);
TESTCASE_AUTO(TestDoubleZero);
TESTCASE_AUTO(TestUnitPerUnitResolution);
TESTCASE_AUTO_END;
}
@ -871,32 +864,6 @@ void MeasureFormatTest::TestFormatMeasuresZeroArg() {
verifyFormat("TestFormatMeasuresZeroArg", fmt, NULL, 0, "");
}
void MeasureFormatTest::TestMultiplesWithPer() {
Locale en("en");
UErrorCode status = U_ZERO_ERROR;
LocalPointer<MeasureUnit> second(MeasureUnit::createSecond(status));
LocalPointer<MeasureUnit> minute(MeasureUnit::createMinute(status));
if (!assertSuccess("", status)) {
return;
}
// Per unit test.
helperTestMultiplesWithPer(
en, UMEASFMT_WIDTH_WIDE, *second, "2 miles, 1 foot, 2.3 inches per second");
helperTestMultiplesWithPer(
en, UMEASFMT_WIDTH_SHORT, *second, "2 mi, 1 ft, 2.3 inps");
helperTestMultiplesWithPer(
en, UMEASFMT_WIDTH_NARROW, *second, "2mi 1\\u2032 2.3\\u2033/s");
// Fallback compound per test
helperTestMultiplesWithPer(
en, UMEASFMT_WIDTH_WIDE, *minute, "2 miles, 1 foot, 2.3 inches per minute");
helperTestMultiplesWithPer(
en, UMEASFMT_WIDTH_SHORT, *minute, "2 mi, 1 ft, 2.3 in/min");
helperTestMultiplesWithPer(
en, UMEASFMT_WIDTH_NARROW, *minute, "2mi 1\\u2032 2.3\\u2033/m");
}
void MeasureFormatTest::TestSimplePer() {
Locale en("en");
UErrorCode status = U_ZERO_ERROR;
@ -907,6 +874,19 @@ void MeasureFormatTest::TestSimplePer() {
return;
}
helperTestSimplePer(
en, UMEASFMT_WIDTH_WIDE,
1.0, *pound, *second, "1 pound per second");
helperTestSimplePer(
en, UMEASFMT_WIDTH_WIDE,
2.0, *pound, *second, "2 pounds per second");
helperTestSimplePer(
en, UMEASFMT_WIDTH_WIDE,
1.0, *pound, *minute, "1 pound per minute");
helperTestSimplePer(
en, UMEASFMT_WIDTH_WIDE,
2.0, *pound, *minute, "2 pounds per minute");
helperTestSimplePer(
en, UMEASFMT_WIDTH_SHORT, 1.0, *pound, *second, "1 lbps");
helperTestSimplePer(
@ -915,6 +895,39 @@ void MeasureFormatTest::TestSimplePer() {
en, UMEASFMT_WIDTH_SHORT, 1.0, *pound, *minute, "1 lb/min");
helperTestSimplePer(
en, UMEASFMT_WIDTH_SHORT, 2.0, *pound, *minute, "2 lb/min");
helperTestSimplePer(
en, UMEASFMT_WIDTH_NARROW, 1.0, *pound, *second, "1#/s");
helperTestSimplePer(
en, UMEASFMT_WIDTH_NARROW, 2.0, *pound, *second, "2#/s");
helperTestSimplePer(
en, UMEASFMT_WIDTH_NARROW, 1.0, *pound, *minute, "1#/m");
helperTestSimplePer(
en, UMEASFMT_WIDTH_NARROW, 2.0, *pound, *minute, "2#/m");
helperTestSimplePer(
en, UMEASFMT_WIDTH_SHORT,
23.3, *pound, *second, "23.3 lbps",
NumberFormat::kDecimalSeparatorField,
2, 3);
helperTestSimplePer(
en, UMEASFMT_WIDTH_SHORT,
23.3, *pound, *second, "23.3 lbps",
NumberFormat::kIntegerField,
0, 2);
helperTestSimplePer(
en, UMEASFMT_WIDTH_SHORT,
23.3, *pound, *minute, "23.3 lb/min",
NumberFormat::kDecimalSeparatorField,
2, 3);
helperTestSimplePer(
en, UMEASFMT_WIDTH_SHORT,
23.3, *pound, *minute, "23.3 lb/min",
NumberFormat::kIntegerField,
0, 2);
}
void MeasureFormatTest::TestNumeratorPlurals() {
@ -927,13 +940,20 @@ void MeasureFormatTest::TestNumeratorPlurals() {
}
helperTestSimplePer(
pl, UMEASFMT_WIDTH_WIDE, 1.0, *foot, *second, "1 stopa na sekund\\u0119");
pl,
UMEASFMT_WIDTH_WIDE,
1.0, *foot, *second, "1 stopa na sekund\\u0119");
helperTestSimplePer(
pl, UMEASFMT_WIDTH_WIDE, 2.0, *foot, *second, "2 stopy na sekund\\u0119");
pl,
UMEASFMT_WIDTH_WIDE,
2.0, *foot, *second, "2 stopy na sekund\\u0119");
helperTestSimplePer(
pl, UMEASFMT_WIDTH_WIDE, 5.0, *foot, *second, "5 st\\u00f3p na sekund\\u0119");
pl,
UMEASFMT_WIDTH_WIDE,
5.0, *foot, *second, "5 st\\u00f3p na sekund\\u0119");
helperTestSimplePer(
pl, UMEASFMT_WIDTH_WIDE, 1.5, *foot, *second, "1,5 stopy na sekund\\u0119");
pl, UMEASFMT_WIDTH_WIDE,
1.5, *foot, *second, "1,5 stopy na sekund\\u0119");
}
void MeasureFormatTest::helperTestSimplePer(
@ -943,8 +963,30 @@ void MeasureFormatTest::helperTestSimplePer(
const MeasureUnit &unit,
const MeasureUnit &perUnit,
const char *expected) {
helperTestSimplePer(
locale,
width,
value,
unit,
perUnit,
expected,
FieldPosition::DONT_CARE,
0,
0);
}
void MeasureFormatTest::helperTestSimplePer(
const Locale &locale,
UMeasureFormatWidth width,
double value,
const MeasureUnit &unit,
const MeasureUnit &perUnit,
const char *expected,
int32_t field,
int32_t expected_start,
int32_t expected_end) {
UErrorCode status = U_ZERO_ERROR;
FieldPosition pos(0);
FieldPosition pos(field);
MeasureFormat fmt(locale, width, status);
if (!assertSuccess("Error creating format object", status)) {
return;
@ -953,10 +995,10 @@ void MeasureFormatTest::helperTestSimplePer(
if (!assertSuccess("Error creating measure object", status)) {
return;
}
UnicodeString buffer;
fmt.formatMeasuresPer(
&measure,
1,
UnicodeString prefix("prefix: ");
UnicodeString buffer(prefix);
fmt.formatMeasurePerUnit(
measure,
perUnit,
buffer,
pos,
@ -964,45 +1006,18 @@ void MeasureFormatTest::helperTestSimplePer(
if (!assertSuccess("Error formatting measures with per", status)) {
return;
}
UnicodeString uexpected(expected);
uexpected = prefix + uexpected;
assertEquals(
"TestSimplePer",
UnicodeString(expected).unescape(),
uexpected.unescape(),
buffer);
}
void MeasureFormatTest::helperTestMultiplesWithPer(
const Locale &locale,
UMeasureFormatWidth width,
const MeasureUnit &perUnit,
const char *expected) {
UErrorCode status = U_ZERO_ERROR;
FieldPosition pos(0);
MeasureFormat fmt(locale, width, status);
if (!assertSuccess("Error creating format object", status)) {
return;
}
Measure measures[] = {
Measure(2, MeasureUnit::createMile(status), status),
Measure(1, MeasureUnit::createFoot(status), status),
Measure(2.3, MeasureUnit::createInch(status), status)};
if (!assertSuccess("Error creating measures", status)) {
return;
}
UnicodeString buffer;
fmt.formatMeasuresPer(
measures,
UPRV_LENGTHOF(measures),
perUnit,
buffer,
pos,
status);
if (!assertSuccess("Error formatting measures with per", status)) {
return;
}
if (field != FieldPosition::DONT_CARE) {
assertEquals(
"TestMultiplesWithPer",
UnicodeString(expected).unescape(),
buffer);
"Start", expected_start, pos.getBeginIndex() - prefix.length());
assertEquals(
"End", expected_end, pos.getEndIndex() - prefix.length());
}
}
void MeasureFormatTest::TestMultiples() {
@ -1181,99 +1196,6 @@ void MeasureFormatTest::TestFieldPositionMultiple() {
0);
}
void MeasureFormatTest::TestFieldPositionMultipleWithPer() {
UErrorCode status = U_ZERO_ERROR;
MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);
if (!assertSuccess("Error creating format object", status)) {
return;
}
Measure first[] = {
Measure(354, MeasureUnit::createMeter(status), status),
Measure(23, MeasureUnit::createCentimeter(status), status)};
Measure second[] = {
Measure(354, MeasureUnit::createMeter(status), status),
Measure(23, MeasureUnit::createCentimeter(status), status),
Measure(5.4, MeasureUnit::createMillimeter(status), status)};
Measure third[] = {
Measure(3, MeasureUnit::createMeter(status), status),
Measure(23, MeasureUnit::createCentimeter(status), status),
Measure(5, MeasureUnit::createMillimeter(status), status)};
if (!assertSuccess("Error creating measure objects", status)) {
return;
}
UnicodeString prefix("123456: ");
LocalPointer<MeasureUnit> secondUnit(MeasureUnit::createSecond(status));
LocalPointer<MeasureUnit> minuteUnit(MeasureUnit::createMinute(status));
if (!assertSuccess("Error creating format object", status)) {
return;
}
// per unit test
verifyFieldPositionWithPer(
"Integer",
fmt,
prefix,
first,
UPRV_LENGTHOF(first),
*secondUnit,
NumberFormat::kIntegerField,
8,
11);
verifyFieldPositionWithPer(
"Decimal separator",
fmt,
prefix,
second,
UPRV_LENGTHOF(second),
*secondUnit,
NumberFormat::kDecimalSeparatorField,
23,
24);
verifyFieldPositionWithPer(
"no decimal separator",
fmt,
prefix,
third,
UPRV_LENGTHOF(third),
*secondUnit,
NumberFormat::kDecimalSeparatorField,
0,
0);
// Fallback to compound per test
verifyFieldPositionWithPer(
"Integer",
fmt,
prefix,
first,
UPRV_LENGTHOF(first),
*minuteUnit,
NumberFormat::kIntegerField,
8,
11);
verifyFieldPositionWithPer(
"Decimal separator",
fmt,
prefix,
second,
UPRV_LENGTHOF(second),
*minuteUnit,
NumberFormat::kDecimalSeparatorField,
23,
24);
verifyFieldPositionWithPer(
"no decimal separator",
fmt,
prefix,
third,
UPRV_LENGTHOF(third),
*minuteUnit,
NumberFormat::kDecimalSeparatorField,
0,
0);
}
void MeasureFormatTest::TestBadArg() {
UErrorCode status = U_ZERO_ERROR;
MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);
@ -1379,6 +1301,26 @@ void MeasureFormatTest::TestDoubleZero() {
appendTo);
}
void MeasureFormatTest::TestUnitPerUnitResolution() {
UErrorCode status = U_ZERO_ERROR;
Locale en("en");
MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);
Measure measure(50, MeasureUnit::createPound(status), status);
LocalPointer<MeasureUnit> sqInch(MeasureUnit::createSquareInch(status));
if (!assertSuccess("Create of format unit and per unit", status)) {
return;
}
FieldPosition pos(0);
UnicodeString actual;
fmt.formatMeasurePerUnit(
measure,
*sqInch,
actual,
pos,
status);
assertEquals("", "50 psi", actual);
}
void MeasureFormatTest::verifyFieldPosition(
const char *description,
const MeasureFormat &fmt,
@ -1407,40 +1349,6 @@ void MeasureFormatTest::verifyFieldPosition(
assertEquals(endIndex.data(), end, pos.getEndIndex());
}
void MeasureFormatTest::verifyFieldPositionWithPer(
const char *description,
const MeasureFormat &fmt,
const UnicodeString &prefix,
const Measure *measures,
int32_t measureCount,
const MeasureUnit &perUnit,
NumberFormat::EAlignmentFields field,
int32_t start,
int32_t end) {
UnicodeString result(prefix);
FieldPosition pos(field);
UErrorCode status = U_ZERO_ERROR;
CharString ch;
const char *descPrefix = ch.append(description, status)
.append(": ", status).data();
CharString beginIndex;
beginIndex.append(descPrefix, status).append("beginIndex", status);
CharString endIndex;
endIndex.append(descPrefix, status).append("endIndex", status);
fmt.formatMeasuresPer(
measures,
measureCount,
perUnit,
result,
pos,
status);
if (!assertSuccess("Error formatting", status)) {
return;
}
assertEquals(beginIndex.data(), start, pos.getBeginIndex());
assertEquals(endIndex.data(), end, pos.getEndIndex());
}
void MeasureFormatTest::verifyFormat(
const char *description,
const MeasureFormat &fmt,

View File

@ -19,10 +19,21 @@ public:
void TestNoPlaceholders();
void TestOnePlaceholder();
void TestManyPlaceholders();
void TestTooFewPlaceholderValues();
void TestBadArguments();
void TestGetPatternWithNoPlaceholders();
void TestOptimization();
void TestFormatReplaceNoOptimization();
void TestFormatReplaceNoOptimizationLeadingText();
void TestFormatReplaceOptimization();
void TestFormatReplaceNoOptimizationLeadingPlaceholderUsedTwice();
void TestFormatReplaceOptimizationNoOffsets();
void TestFormatReplaceNoOptimizationNoOffsets();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
private:
void verifyOffsets(
const int32_t *expected,
const int32_t *actual,
int32_t count);
};
void SimplePatternFormatterTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) {
@ -30,8 +41,15 @@ void SimplePatternFormatterTest::runIndexedTest(int32_t index, UBool exec, const
TESTCASE_AUTO(TestNoPlaceholders);
TESTCASE_AUTO(TestOnePlaceholder);
TESTCASE_AUTO(TestManyPlaceholders);
TESTCASE_AUTO(TestTooFewPlaceholderValues);
TESTCASE_AUTO(TestBadArguments);
TESTCASE_AUTO(TestGetPatternWithNoPlaceholders);
TESTCASE_AUTO(TestOptimization);
TESTCASE_AUTO(TestFormatReplaceNoOptimization);
TESTCASE_AUTO(TestFormatReplaceNoOptimizationLeadingText);
TESTCASE_AUTO(TestFormatReplaceOptimization);
TESTCASE_AUTO(TestFormatReplaceNoOptimizationLeadingPlaceholderUsedTwice);
TESTCASE_AUTO(TestFormatReplaceOptimizationNoOffsets);
TESTCASE_AUTO(TestFormatReplaceNoOptimizationNoOffsets);
TESTCASE_AUTO_END;
}
@ -58,13 +76,15 @@ void SimplePatternFormatterTest::TestOnePlaceholder() {
UErrorCode status = U_ZERO_ERROR;
SimplePatternFormatter fmt;
fmt.compile("{0} meter", status);
if (!assertSuccess("Status", status)) {
return;
}
assertEquals("PlaceholderCount", 1, fmt.getPlaceholderCount());
UnicodeString appendTo;
assertEquals(
"format",
"1 meter",
fmt.format("1", appendTo, status));
assertSuccess("Status", status);
// assignment
SimplePatternFormatter s;
@ -90,8 +110,9 @@ void SimplePatternFormatterTest::TestManyPlaceholders() {
SimplePatternFormatter fmt;
fmt.compile(
"Templates {2}{1}{5} and {4} are out of order.", status);
assertSuccess("Status", status);
assertFalse("startsWithPlaceholder", fmt.startsWithPlaceholder(2));
if (!assertSuccess("Status", status)) {
return;
}
assertEquals("PlaceholderCount", 6, fmt.getPlaceholderCount());
UnicodeString values[] = {
"freddy", "tommy", "frog", "billy", "leg", "{0}"};
@ -103,38 +124,24 @@ void SimplePatternFormatterTest::TestManyPlaceholders() {
assertEquals(
"format",
"Prefix: Templates frogtommy{0} and leg are out of order.",
fmt.format(
fmt.formatAndAppend(
params,
UPRV_LENGTHOF(params),
appendTo,
offsets,
UPRV_LENGTHOF(offsets),
status));
assertSuccess("Status", status);
for (int32_t i = 0; i < UPRV_LENGTHOF(expectedOffsets); ++i) {
if (expectedOffsets[i] != offsets[i]) {
errln("Expected %d, got %d", expectedOffsets[i], offsets[i]);
}
if (!assertSuccess("Status", status)) {
return;
}
verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets));
appendTo.remove();
// Not having enough placeholder params results in error.
fmt.format(
params,
UPRV_LENGTHOF(params) - 1,
appendTo,
offsets,
UPRV_LENGTHOF(offsets),
status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
}
// Ensure we don't write to offsets array beyond its length.
status = U_ZERO_ERROR;
offsets[UPRV_LENGTHOF(offsets) - 1] = 289;
appendTo.remove();
fmt.format(
fmt.formatAndAppend(
params,
UPRV_LENGTHOF(params),
appendTo,
@ -150,7 +157,7 @@ void SimplePatternFormatterTest::TestManyPlaceholders() {
assertEquals(
"Assignment",
"Templates frogtommy{0} and leg are out of order.",
s.format(
s.formatAndAppend(
params,
UPRV_LENGTHOF(params),
appendTo,
@ -164,7 +171,7 @@ void SimplePatternFormatterTest::TestManyPlaceholders() {
assertEquals(
"Copy constructor",
"Templates frogtommy{0} and leg are out of order.",
r.format(
r.formatAndAppend(
params,
UPRV_LENGTHOF(params),
appendTo,
@ -195,42 +202,265 @@ void SimplePatternFormatterTest::TestManyPlaceholders() {
assertSuccess("Status", status);
}
void SimplePatternFormatterTest::TestTooFewPlaceholderValues() {
SimplePatternFormatter fmt("{0} and {1}");
UnicodeString appendTo;
UnicodeString firstValue;
UnicodeString *params[] = {&firstValue};
UErrorCode status = U_ZERO_ERROR;
fmt.format(
firstValue, appendTo, status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
}
status = U_ZERO_ERROR;
fmt.formatAndAppend(
params, UPRV_LENGTHOF(params), appendTo, NULL, 0, status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
}
status = U_ZERO_ERROR;
fmt.formatAndReplace(
params, UPRV_LENGTHOF(params), appendTo, NULL, 0, status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
}
}
void SimplePatternFormatterTest::TestBadArguments() {
SimplePatternFormatter fmt("pickle");
UnicodeString appendTo;
UErrorCode status = U_ZERO_ERROR;
int32_t offsets[1];
// These succeed
fmt.formatAndAppend(
NULL, 0, appendTo, NULL, 0, status);
fmt.formatAndReplace(
NULL, 0, appendTo, NULL, 0, status);
assertSuccess("", status);
status = U_ZERO_ERROR;
// fails
fmt.formatAndAppend(
NULL, 1, appendTo, NULL, 0, status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
}
status = U_ZERO_ERROR;
// fails
fmt.formatAndAppend(
NULL, 0, appendTo, NULL, 1, status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
}
status = U_ZERO_ERROR;
// fails because appendTo used as a parameter value
const UnicodeString *params[] = {&appendTo};
fmt.formatAndAppend(
params, UPRV_LENGTHOF(params), appendTo, NULL, 0, status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
}
status = U_ZERO_ERROR;
// fails
fmt.formatAndReplace(
NULL, 1, appendTo, NULL, 0, status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
}
status = U_ZERO_ERROR;
// fails
fmt.formatAndReplace(
NULL, 0, appendTo, NULL, 1, status);
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
}
}
void SimplePatternFormatterTest::TestGetPatternWithNoPlaceholders() {
SimplePatternFormatter fmt("{0} has no {1} placeholders.");
assertEquals(
"", " has no placeholders.", fmt.getPatternWithNoPlaceholders());
}
void SimplePatternFormatterTest::TestOptimization() {
void SimplePatternFormatterTest::TestFormatReplaceNoOptimization() {
UErrorCode status = U_ZERO_ERROR;
SimplePatternFormatter fmt;
fmt.compile("{2}, {0}, {1} and {3}", status);
assertSuccess("Status", status);
assertTrue("startsWithPlaceholder", fmt.startsWithPlaceholder(2));
assertFalse("startsWithPlaceholder", fmt.startsWithPlaceholder(0));
UnicodeString values[] = {
"freddy", "frog", "leg", "by"};
UnicodeString *params[] = {
&values[0], &values[1], &values[2], &values[3]};
int32_t offsets[4];
int32_t expectedOffsets[4] = {5, 13, 0, 22};
// The pattern starts with {2}, so format should append the result of
// the rest of the pattern to values[2], the value for {2}.
if (!assertSuccess("Status", status)) {
return;
}
UnicodeString result("original");
int offsets[4];
UnicodeString freddy("freddy");
UnicodeString frog("frog");
UnicodeString by("by");
const UnicodeString *params[] = {&result, &freddy, &frog, &by};
assertEquals(
"format",
"leg, freddy, frog and by",
fmt.format(
"",
"frog, original, freddy and by",
fmt.formatAndReplace(
params,
UPRV_LENGTHOF(params),
values[2],
result,
offsets,
UPRV_LENGTHOF(offsets),
status));
if (!assertSuccess("Status", status)) {
return;
}
int32_t expectedOffsets[] = {6, 16, 0, 27};
verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets));
}
void SimplePatternFormatterTest::TestFormatReplaceNoOptimizationLeadingText() {
UErrorCode status = U_ZERO_ERROR;
SimplePatternFormatter fmt;
fmt.compile("boo {2}, {0}, {1} and {3}", status);
if (!assertSuccess("Status", status)) {
return;
}
UnicodeString result("original");
int offsets[4];
UnicodeString freddy("freddy");
UnicodeString frog("frog");
UnicodeString by("by");
const UnicodeString *params[] = {&freddy, &frog, &result, &by};
assertEquals(
"",
"boo original, freddy, frog and by",
fmt.formatAndReplace(
params,
UPRV_LENGTHOF(params),
result,
offsets,
UPRV_LENGTHOF(offsets),
status));
if (!assertSuccess("Status", status)) {
return;
}
int32_t expectedOffsets[] = {14, 22, 4, 31};
verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets));
}
void SimplePatternFormatterTest::TestFormatReplaceOptimization() {
UErrorCode status = U_ZERO_ERROR;
SimplePatternFormatter fmt;
fmt.compile("{2}, {0}, {1} and {3}", status);
if (!assertSuccess("Status", status)) {
return;
}
UnicodeString result("original");
int offsets[4];
UnicodeString freddy("freddy");
UnicodeString frog("frog");
UnicodeString by("by");
const UnicodeString *params[] = {&freddy, &frog, &result, &by};
assertEquals(
"",
"original, freddy, frog and by",
fmt.formatAndReplace(
params,
UPRV_LENGTHOF(params),
result,
offsets,
UPRV_LENGTHOF(offsets),
status));
if (!assertSuccess("Status", status)) {
return;
}
int32_t expectedOffsets[] = {10, 18, 0, 27};
verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets));
}
void SimplePatternFormatterTest::TestFormatReplaceNoOptimizationLeadingPlaceholderUsedTwice() {
UErrorCode status = U_ZERO_ERROR;
SimplePatternFormatter fmt;
fmt.compile("{2}, {0}, {1} and {3} {2}", status);
if (!assertSuccess("Status", status)) {
return;
}
UnicodeString result("original");
int offsets[4];
UnicodeString freddy("freddy");
UnicodeString frog("frog");
UnicodeString by("by");
const UnicodeString *params[] = {&freddy, &frog, &result, &by};
assertEquals(
"",
"original, freddy, frog and by original",
fmt.formatAndReplace(
params,
UPRV_LENGTHOF(params),
result,
offsets,
UPRV_LENGTHOF(offsets),
status));
if (!assertSuccess("Status", status)) {
return;
}
int32_t expectedOffsets[] = {10, 18, 30, 27};
verifyOffsets(expectedOffsets, offsets, UPRV_LENGTHOF(expectedOffsets));
}
void SimplePatternFormatterTest::TestFormatReplaceOptimizationNoOffsets() {
UErrorCode status = U_ZERO_ERROR;
SimplePatternFormatter fmt;
fmt.compile("{2}, {0}, {1} and {3}", status);
if (!assertSuccess("Status", status)) {
return;
}
UnicodeString result("original");
UnicodeString freddy("freddy");
UnicodeString frog("frog");
UnicodeString by("by");
const UnicodeString *params[] = {&freddy, &frog, &result, &by};
assertEquals(
"",
"original, freddy, frog and by",
fmt.formatAndReplace(
params,
UPRV_LENGTHOF(params),
result,
NULL,
0,
status));
assertSuccess("Status", status);
for (int32_t i = 0; i < UPRV_LENGTHOF(expectedOffsets); ++i) {
if (expectedOffsets[i] != offsets[i]) {
errln("Expected %d, got %d", expectedOffsets[i], offsets[i]);
}
void SimplePatternFormatterTest::TestFormatReplaceNoOptimizationNoOffsets() {
UErrorCode status = U_ZERO_ERROR;
SimplePatternFormatter fmt("Placeholders {0} and {1}");
UnicodeString result("previous:");
UnicodeString frog("frog");
const UnicodeString *params[] = {&result, &frog};
assertEquals(
"",
"Placeholders previous: and frog",
fmt.formatAndReplace(
params,
UPRV_LENGTHOF(params),
result,
NULL,
0,
status));
assertSuccess("Status", status);
}
void SimplePatternFormatterTest::verifyOffsets(
const int32_t *expected, const int32_t *actual, int32_t count) {
for (int32_t i = 0; i < count; ++i) {
if (expected[i] != actual[i]) {
errln("Expected %d, got %d", expected[i], actual[i]);
}
}
}
@ -238,3 +468,4 @@ void SimplePatternFormatterTest::TestOptimization() {
extern IntlTest *createSimplePatternFormatterTest() {
return new SimplePatternFormatterTest();
}