+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/********************************************************************
* COPYRIGHT:
- * Copyright (c) 1997-2012, International Business Machines Corporation and
+ * Copyright (c) 1997-2016, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************
* File TMSGFMT.CPP
#if !UCONFIG_NO_FORMATTING
#include "tmsgfmt.h"
+#include "cmemory.h"
#include "unicode/format.h"
#include "unicode/decimfmt.h"
#include "unicode/messagepattern.h"
#include "unicode/selfmt.h"
#include "unicode/gregocal.h"
+#include "unicode/strenum.h"
#include <stdio.h>
-#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
-
void
TestMessageFormat::runIndexedTest(int32_t index, UBool exec,
const char* &name, char* /*par*/) {
TESTCASE_AUTO(testGetFormatNames);
TESTCASE_AUTO(TestTrimArgumentName);
TESTCASE_AUTO(TestSelectOrdinal);
+ TESTCASE_AUTO(TestDecimals);
+ TESTCASE_AUTO(TestArgIsPrefixOfAnother);
+ TESTCASE_AUTO(TestMessageFormatNumberSkeleton);
TESTCASE_AUTO_END;
}
continue;
}
Formattable result;
- FieldPosition pos(0);
+ FieldPosition pos(FieldPosition::DONT_CARE);
buffer.remove();
form->format(myNumber, buffer, pos);
success = U_ZERO_ERROR;
"1.0<=Arg<2.0",
"2.0<-Arg"};
ChoiceFormat *cf = new ChoiceFormat(limit, formats, 3);
- FieldPosition status(0);
+ FieldPosition status(FieldPosition::DONT_CARE);
UnicodeString toAppendTo;
cf->format((int32_t)1, toAppendTo, status);
if (toAppendTo != "1.0<=Arg<2.0") {
}
#if 0
-#if defined(_DEBUG) && U_IOSTREAM_SOURCE >= 199711
+#if defined(_DEBUG)
//----------------------------------------------------
// console I/O
//----------------------------------------------------
}
return stream;
}
-#endif /* defined(_DEBUG) && U_IOSTREAM_SOURCE >= 199711 */
+#endif /* defined(_DEBUG) */
#endif
void TestMessageFormat::PatternTest()
//it_out << "Pat out: " << form->toPattern(buffer));
UnicodeString result;
int32_t count = 4;
- FieldPosition fieldpos(0);
+ FieldPosition fieldpos(FieldPosition::DONT_CARE);
form->format(testArgs, count, result, fieldpos, success);
if (U_FAILURE(success)) {
dataerrln("MessageFormat failed test #3 - %s", u_errorName(success));
UnicodeString abc("abc");
UnicodeString def("def");
Formattable testArgs1[] = { abc, def };
- FieldPosition fieldpos(0);
+ FieldPosition fieldpos(FieldPosition::DONT_CARE);
assertEquals("format",
"There are abc files on def",
form->format(testArgs1, 2, buffer2, fieldpos, success));
delete mfNum;
delete mfAlpha;
- MessageFormat* mfNum2 = new MessageFormat(t3, Locale("ru"), err);
+ MessageFormat* mfNum2 = new MessageFormat(t3, Locale("uk"), err);
numResult1.remove();
Formattable testArgs2((int32_t)4);
mfNum2->format(&testArgs2, 1, numResult1, ignore, err);
- MessageFormat* mfAlpha2 = new MessageFormat(t4, Locale("ru"), err);
+ MessageFormat* mfAlpha2 = new MessageFormat(t4, Locale("uk"), err);
argNameResult.remove();
mfAlpha2->format(argName, &testArgs2, 1, argNameResult, err);
//Format with passed arguments
msgFmt->format( args , numOfArgs , result, ignore, status);
if (U_FAILURE(status)) {
- dataerrln( "%serror while formatting with ErrorCode as %s" ,errMsg, u_errorName(status) );
+ dataerrln( "%s error while formatting with ErrorCode as %s" ,errMsg, u_errorName(status) );
}
//Compare expected with obtained result
if ( result!= expected ) {
//Create the MessageFormat with simple SelectFormat
MessageFormat* msgFmt = new MessageFormat(pattern, locale, status);
if (U_FAILURE(status)) {
- dataerrln( "%serror while constructing with ErrorCode as %s" ,errMsg, u_errorName(status) );
+ dataerrln( "%s error while constructing with ErrorCode as %s" ,errMsg, u_errorName(status) );
logln(UnicodeString("TestMessageFormat::testMsgFormatSelect #1 with error code ")+(int32_t)status);
return NULL;
}
//Nested patterns with plural, number ,choice ,select format etc.
//Select Format with embedded number format
UnicodeString t4("{0} est {1, select, female {{2,number,integer} all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris.");
+ err = U_ZERO_ERROR;
//Create the MessageFormat with Select Format with embedded number format (nested pattern)
MessageFormat* msgFmt4 = internalCreate(t4.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t4");
if (!U_FAILURE(err)) {
}
delete msgFmt4;
- err = U_ZERO_ERROR;
//Plural format with embedded select format
UnicodeString t5("{0} {1, plural, one {est {2, select, female {all\\u00E9e} other {all\\u00E9}}} other {sont {2, select, female {all\\u00E9es} other {all\\u00E9s}}}} \\u00E0 Paris.");
err = U_ZERO_ERROR;
//Create the MessageFormat with Plural format with embedded select format(nested pattern)
MessageFormat* msgFmt5 = internalCreate(t5.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t5");
+ // with no data the above should fail but it seems to construct an invalid MessageFormat with no reported error. See #13079
if (!U_FAILURE(err)) {
//Arguments
Formattable testArgs10[] = {"Kirti",(int32_t)6,"female"};
MessageFormat msg( formatStr, err);
result = "";
- FieldPosition pos(0);
+ FieldPosition pos(FieldPosition::DONT_CARE);
result = msg.format(
arguments,
3,
{
Formattable( UDate(8.71068e+011), Formattable::kIsDate )
};
- const int32_t ft_cnt = sizeof(ftarray) / sizeof(Formattable);
+ const int32_t ft_cnt = UPRV_LENGTHOF(ftarray);
Formattable ft_arr( ftarray, ft_cnt );
Formattable* fmt = new Formattable(UDate(8.71068e+011), Formattable::kIsDate);
err = U_ZERO_ERROR;
MessageFormat msg( formatStr, err);
- FieldPosition fp(0);
+ FieldPosition fp(FieldPosition::DONT_CARE);
result = "";
fp = 0;
UnicodeString formatStr("Hello World on {0,date,full}", "");
UnicodeString resultStr(" ", "");
UnicodeString result;
- FieldPosition fp(0);
+ FieldPosition fp(FieldPosition::DONT_CARE);
UDate d = Calendar::getNow();
const Formattable fargs( d, Formattable::kIsDate );
Formattable("of course"),
Formattable("Horace"),
};
- const int32_t ARGS_LENGTH = sizeof(ARGS) / sizeof(ARGS[0]);
+ const int32_t ARGS_LENGTH = UPRV_LENGTHOF(ARGS);
Formattable ARGS_OBJ(ARGS, ARGS_LENGTH);
UnicodeString expected =
// do not always parse, so do not include them
"0", "1", "12", "100", "123", "1001", "123,456", "-17",
};
- int32_t values_count = sizeof(values)/sizeof(values[0]);
+ int32_t values_count = UPRV_LENGTHOF(values);
UnicodeString formats[] = {
"There are {0,spellout} files to search.",
"There are {0,spellout,%simplified} files to search.",
"The bogus spellout {0,spellout,%BOGUS} files behaves like the default.",
- "This is the {0,ordinal} file to search.", // TODO fix bug, ordinal does not parse
+ "This is the {0,ordinal} file to search.",
"Searching this file will take {0,duration} to complete.",
"Searching this file will take {0,duration,%with-words} to complete.",
};
- int32_t formats_count = sizeof(formats)/sizeof(formats[0]);
+ int32_t formats_count = UPRV_LENGTHOF(formats);
Formattable args[1];
if (U_FAILURE(ec)) {
errln((UnicodeString)"Failed to parse test argument " + values[j]);
} else {
- FieldPosition fp(0);
+ FieldPosition fp(FieldPosition::DONT_CARE);
UnicodeString result;
fmt->format(args, 1, result, fp, ec);
logln((UnicodeString)"value: " + toString(args[0]) + " --> " + result + UnicodeString(" ec: ") + u_errorName(ec));
- if (i != 3) { // TODO: fix this, for now skip ordinal parsing (format string at index 3)
- int32_t count = 0;
- Formattable* parseResult = fmt->parse(result, count, ec);
- if (count != 1) {
- errln((UnicodeString)"parse returned " + count + " args");
- } else if (parseResult[0] != args[0]) {
- errln((UnicodeString)"parsed argument " + toString(parseResult[0]) + " != " + toString(args[0]));
- }
- delete []parseResult;
+ int32_t count = 0;
+ Formattable* parseResult = fmt->parse(result, count, ec);
+ if (count != 1) {
+ errln((UnicodeString)"parse returned " + count + " args");
+ } else if (parseResult[0] != args[0]) {
+ errln((UnicodeString)"parsed argument " + toString(parseResult[0]) + " != " + toString(args[0]));
}
+ delete []parseResult;
}
}
delete fmt;
"I don't know", "I don't know", "I don''t know",
"I don't know", "I don''t know", "I don''t know"
};
- int32_t tuples_count = LENGTHOF(tuples);
+ int32_t tuples_count = UPRV_LENGTHOF(tuples);
for (int i = 0; i < tuples_count; i += 3) {
UnicodeString& desired = tuples[i];
}
Formattable zero0[] = { (int32_t)0 };
- FieldPosition fieldpos(0);
+ FieldPosition fieldpos(FieldPosition::DONT_CARE);
UnicodeString buffer1, buffer2;
assertEquals("incompatible ICU MessageFormat compatibility-apostrophe behavior",
"ab12'3'4''.yz",
"'} '{'}'", "'} '{'}''",
"'} {{{''", "'} {{{'''",
};
- int32_t pattern_count = sizeof(patterns)/sizeof(patterns[0]);
+ int32_t pattern_count = UPRV_LENGTHOF(patterns);
for (int i = 0; i < pattern_count; i += 2) {
UErrorCode status = U_ZERO_ERROR;
void TestMessageFormat::testGetFormatNames() {
IcuTestErrorCode errorCode(*this, "testGetFormatNames");
MessageFormat msgfmt("Hello, {alice,number} {oops,date,full} {zip,spellout} World.", Locale::getRoot(), errorCode);
- if(errorCode.logDataIfFailureAndReset("MessageFormat() failed")) {
+ if(errorCode.errDataIfFailureAndReset("MessageFormat() failed")) {
return;
}
LocalPointer<StringEnumeration> names(msgfmt.getFormatNames(errorCode));
- if(errorCode.logIfFailureAndReset("msgfmt.getFormatNames() failed")) {
+ if(errorCode.errIfFailureAndReset("msgfmt.getFormatNames() failed")) {
return;
}
const UnicodeString *name;
// ICU 4.8 allows and ignores white space around argument names and numbers.
IcuTestErrorCode errorCode(*this, "TestTrimArgumentName");
MessageFormat m("a { 0 , number , '#,#'#.0 } z", Locale::getEnglish(), errorCode);
- if (errorCode.logDataIfFailureAndReset("Unable to instantiate MessageFormat")) {
+ if (errorCode.errDataIfFailureAndReset("Unable to instantiate MessageFormat")) {
return;
}
Formattable args[1] = { (int32_t)2 };
- FieldPosition ignore(0);
+ FieldPosition ignore(FieldPosition::DONT_CARE);
UnicodeString result;
assertEquals("trim-numbered-arg format() failed", "a #,#2.0 z",
m.format(args, 1, result, ignore, errorCode));
"{0,plural,one{1 file}other{# files}}, "
"{0,selectordinal,one{#st file}two{#nd file}few{#rd file}other{#th file}}",
Locale::getEnglish(), errorCode);
- if (errorCode.logDataIfFailureAndReset("Unable to instantiate MessageFormat")) {
+ if (errorCode.errDataIfFailureAndReset("Unable to instantiate MessageFormat")) {
return;
}
Formattable args[1] = { (int32_t)21 };
- FieldPosition ignore(0);
+ FieldPosition ignore(FieldPosition::DONT_CARE);
UnicodeString result;
assertEquals("plural-and-ordinal format(21) failed", "21 files, 21st file",
m.format(args, 1, result, ignore, errorCode), TRUE);
assertEquals("plural-and-ordinal format(3) failed", "3 files, 3rd file",
m.format(args, 1, result.remove(), ignore, errorCode), TRUE);
- errorCode.logDataIfFailureAndReset("");
+ errorCode.errDataIfFailureAndReset("");
+}
+
+void TestMessageFormat::TestDecimals() {
+ IcuTestErrorCode errorCode(*this, "TestDecimals");
+ // Simple number replacement.
+ MessageFormat m(
+ "{0,plural,one{one meter}other{# meters}}",
+ Locale::getEnglish(), errorCode);
+ Formattable args[1] = { (int32_t)1 };
+ FieldPosition ignore;
+ UnicodeString result;
+ assertEquals("simple format(1)", "one meter",
+ m.format(args, 1, result, ignore, errorCode), TRUE);
+
+ args[0] = (double)1.5;
+ result.remove();
+ assertEquals("simple format(1.5)", "1.5 meters",
+ m.format(args, 1, result, ignore, errorCode), TRUE);
+
+ // Simple but explicit.
+ MessageFormat m0(
+ "{0,plural,one{one meter}other{{0} meters}}",
+ Locale::getEnglish(), errorCode);
+ args[0] = (int32_t)1;
+ result.remove();
+ assertEquals("explicit format(1)", "one meter",
+ m0.format(args, 1, result, ignore, errorCode), TRUE);
+
+ args[0] = (double)1.5;
+ result.remove();
+ assertEquals("explicit format(1.5)", "1.5 meters",
+ m0.format(args, 1, result, ignore, errorCode), TRUE);
+
+ // With offset and specific simple format with optional decimals.
+ MessageFormat m1(
+ "{0,plural,offset:1 one{another meter}other{{0,number,00.#} meters}}",
+ Locale::getEnglish(), errorCode);
+ args[0] = (int32_t)1;
+ result.remove();
+ assertEquals("offset format(1)", "01 meters",
+ m1.format(args, 1, result, ignore, errorCode), TRUE);
+
+ args[0] = (int32_t)2;
+ result.remove();
+ assertEquals("offset format(1)", "another meter",
+ m1.format(args, 1, result, ignore, errorCode), TRUE);
+
+ args[0] = (double)2.5;
+ result.remove();
+ assertEquals("offset format(1)", "02.5 meters",
+ m1.format(args, 1, result, ignore, errorCode), TRUE);
+
+ // With offset and specific simple format with forced decimals.
+ MessageFormat m2(
+ "{0,plural,offset:1 one{another meter}other{{0,number,0.0} meters}}",
+ Locale::getEnglish(), errorCode);
+ args[0] = (int32_t)1;
+ result.remove();
+ assertEquals("offset-decimals format(1)", "1.0 meters",
+ m2.format(args, 1, result, ignore, errorCode), TRUE);
+
+ args[0] = (int32_t)2;
+ result.remove();
+ assertEquals("offset-decimals format(1)", "2.0 meters",
+ m2.format(args, 1, result, ignore, errorCode), TRUE);
+
+ args[0] = (double)2.5;
+ result.remove();
+ assertEquals("offset-decimals format(1)", "2.5 meters",
+ m2.format(args, 1, result, ignore, errorCode), TRUE);
+ errorCode.reset();
+}
+
+void TestMessageFormat::TestArgIsPrefixOfAnother() {
+ IcuTestErrorCode errorCode(*this, "TestArgIsPrefixOfAnother");
+ // Ticket #11952
+ MessageFormat mf1("{0,select,a{A}ab{AB}abc{ABC}other{?}}", Locale::getEnglish(), errorCode);
+ Formattable args[3];
+ FieldPosition ignore;
+ UnicodeString result;
+ args[0].setString("a");
+ assertEquals("a", "A", mf1.format(args, 1, result, ignore, errorCode));
+ args[0].setString("ab");
+ assertEquals("ab", "AB", mf1.format(args, 1, result.remove(), ignore, errorCode));
+ args[0].setString("abc");
+ assertEquals("abc", "ABC", mf1.format(args, 1, result.remove(), ignore, errorCode));
+
+ // Ticket #12172
+ MessageFormat mf2("{a} {aa} {aaa}", Locale::getEnglish(), errorCode);
+ UnicodeString argNames[3] = { "a", "aa", "aaa" };
+ args[0].setString("A");
+ args[1].setString("AB");
+ args[2].setString("ABC");
+ assertEquals("a aa aaa", "A AB ABC", mf2.format(argNames, args, 3, result.remove(), errorCode));
+
+ // Ticket #12172
+ MessageFormat mf3("{aa} {aaa}", Locale::getEnglish(), errorCode);
+ assertEquals("aa aaa", "AB ABC", mf3.format(argNames + 1, args + 1, 2, result.remove(), errorCode));
+}
+
+void TestMessageFormat::TestMessageFormatNumberSkeleton() {
+ IcuTestErrorCode status(*this, "TestMessageFormatNumberSkeleton");
+
+ static const struct TestCase {
+ const char16_t* messagePattern;
+ const char* localeName;
+ double arg;
+ const char16_t* expected;
+ } cases[] = {
+ { u"{0,number,::percent}", "en", 50, u"50%" },
+ { u"{0,number,::percent scale/100}", "en", 0.5, u"50%" },
+ { u"{0,number, :: percent scale/100 }", "en", 0.5, u"50%" },
+ { u"{0,number,::currency/USD}", "en", 23, u"$23.00" },
+ { u"{0,number,::precision-integer}", "en", 514.23, u"514" },
+ { u"{0,number,::.000}", "en", 514.23, u"514.230" },
+ { u"{0,number,::.}", "en", 514.23, u"514" },
+ { u"{0,number,::}", "fr", 514.23, u"514,23" },
+ { u"Cost: {0,number,::currency/EUR}.", "en", 4.3, u"Cost: €4.30." },
+ { u"{0,number,'::'0.00}", "en", 50, u"::50.00" }, // pattern literal
+ };
+
+ for (auto& cas : cases) {
+ status.setScope(cas.messagePattern);
+ MessageFormat msgf(cas.messagePattern, cas.localeName, status);
+ UnicodeString sb;
+ FieldPosition fpos(0);
+ Formattable argsArray[] = {{cas.arg}};
+ Formattable args(argsArray, 1);
+ msgf.format(args, sb, status);
+
+ assertEquals(cas.messagePattern, cas.expected, sb);
+ }
}
#endif /* #if !UCONFIG_NO_FORMATTING */