|
|
// © 2017 and later: Unicode, Inc. and others.
|
|
|
// License & terms of use: http://www.unicode.org/copyright.html
|
|
|
|
|
|
#include "unicode/utypes.h"
|
|
|
|
|
|
#if !UCONFIG_NO_FORMATTING
|
|
|
|
|
|
#include "putilimp.h"
|
|
|
#include "unicode/dcfmtsym.h"
|
|
|
#include "numbertest.h"
|
|
|
#include "number_utils.h"
|
|
|
|
|
|
using namespace icu::number::impl;
|
|
|
|
|
|
class DefaultSymbolProvider : public SymbolProvider {
|
|
|
DecimalFormatSymbols fSymbols;
|
|
|
|
|
|
public:
|
|
|
DefaultSymbolProvider(UErrorCode &status) : fSymbols(Locale("ar_SA"), status) {}
|
|
|
|
|
|
UnicodeString getSymbol(AffixPatternType type) const U_OVERRIDE {
|
|
|
switch (type) {
|
|
|
case TYPE_MINUS_SIGN:
|
|
|
return u"−";
|
|
|
case TYPE_PLUS_SIGN:
|
|
|
return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol);
|
|
|
case TYPE_PERCENT:
|
|
|
return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol);
|
|
|
case TYPE_PERMILLE:
|
|
|
return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol);
|
|
|
case TYPE_CURRENCY_SINGLE:
|
|
|
return u"$";
|
|
|
case TYPE_CURRENCY_DOUBLE:
|
|
|
return u"XXX";
|
|
|
case TYPE_CURRENCY_TRIPLE:
|
|
|
return u"long name";
|
|
|
case TYPE_CURRENCY_QUAD:
|
|
|
return u"\uFFFD";
|
|
|
case TYPE_CURRENCY_QUINT:
|
|
|
// TODO: Add support for narrow currency symbols here.
|
|
|
return u"\uFFFD";
|
|
|
case TYPE_CURRENCY_OVERFLOW:
|
|
|
return u"\uFFFD";
|
|
|
default:
|
|
|
UPRV_UNREACHABLE;
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
void AffixUtilsTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) {
|
|
|
if (exec) {
|
|
|
logln("TestSuite AffixUtilsTest: ");
|
|
|
}
|
|
|
TESTCASE_AUTO_BEGIN;
|
|
|
TESTCASE_AUTO(testEscape);
|
|
|
TESTCASE_AUTO(testUnescape);
|
|
|
TESTCASE_AUTO(testContainsReplaceType);
|
|
|
TESTCASE_AUTO(testInvalid);
|
|
|
TESTCASE_AUTO(testUnescapeWithSymbolProvider);
|
|
|
TESTCASE_AUTO_END;
|
|
|
}
|
|
|
|
|
|
void AffixUtilsTest::testEscape() {
|
|
|
static const char16_t *cases[][2] = {{u"", u""},
|
|
|
{u"abc", u"abc"},
|
|
|
{u"-", u"'-'"},
|
|
|
{u"-!", u"'-'!"},
|
|
|
{u"−", u"−"},
|
|
|
{u"---", u"'---'"},
|
|
|
{u"-%-", u"'-%-'"},
|
|
|
{u"'", u"''"},
|
|
|
{u"-'", u"'-'''"},
|
|
|
{u"-'-", u"'-''-'"},
|
|
|
{u"a-'-", u"a'-''-'"}};
|
|
|
|
|
|
for (auto &cas : cases) {
|
|
|
UnicodeString input(cas[0]);
|
|
|
UnicodeString expected(cas[1]);
|
|
|
UnicodeString result = AffixUtils::escape(input);
|
|
|
assertEquals(input, expected, result);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AffixUtilsTest::testUnescape() {
|
|
|
static struct TestCase {
|
|
|
const char16_t *input;
|
|
|
bool currency;
|
|
|
int32_t expectedLength;
|
|
|
const char16_t *output;
|
|
|
} cases[] = {{u"", false, 0, u""},
|
|
|
{u"abc", false, 3, u"abc"},
|
|
|
{u"-", false, 1, u"−"},
|
|
|
{u"-!", false, 2, u"−!"},
|
|
|
{u"+", false, 1, u"\u061C+"},
|
|
|
{u"+!", false, 2, u"\u061C+!"},
|
|
|
{u"‰", false, 1, u"؉"},
|
|
|
{u"‰!", false, 2, u"؉!"},
|
|
|
{u"-x", false, 2, u"−x"},
|
|
|
{u"'-'x", false, 2, u"-x"},
|
|
|
{u"'--''-'-x", false, 6, u"--'-−x"},
|
|
|
{u"''", false, 1, u"'"},
|
|
|
{u"''''", false, 2, u"''"},
|
|
|
{u"''''''", false, 3, u"'''"},
|
|
|
{u"''x''", false, 3, u"'x'"},
|
|
|
{u"¤", true, 1, u"$"},
|
|
|
{u"¤¤", true, 2, u"XXX"},
|
|
|
{u"¤¤¤", true, 3, u"long name"},
|
|
|
{u"¤¤¤¤", true, 4, u"\uFFFD"},
|
|
|
{u"¤¤¤¤¤", true, 5, u"\uFFFD"},
|
|
|
{u"¤¤¤¤¤¤", true, 6, u"\uFFFD"},
|
|
|
{u"¤¤¤a¤¤¤¤", true, 8, u"long namea\uFFFD"},
|
|
|
{u"a¤¤¤¤b¤¤¤¤¤c", true, 12, u"a\uFFFDb\uFFFDc"},
|
|
|
{u"¤!", true, 2, u"$!"},
|
|
|
{u"¤¤!", true, 3, u"XXX!"},
|
|
|
{u"¤¤¤!", true, 4, u"long name!"},
|
|
|
{u"-¤¤", true, 3, u"−XXX"},
|
|
|
{u"¤¤-", true, 3, u"XXX−"},
|
|
|
{u"'¤'", false, 1, u"¤"},
|
|
|
{u"%", false, 1, u"٪\u061C"},
|
|
|
{u"'%'", false, 1, u"%"},
|
|
|
{u"¤'-'%", true, 3, u"$-٪\u061C"},
|
|
|
{u"#0#@#*#;#", false, 9, u"#0#@#*#;#"}};
|
|
|
|
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
|
DefaultSymbolProvider defaultProvider(status);
|
|
|
assertSuccess("Constructing DefaultSymbolProvider", status);
|
|
|
|
|
|
for (TestCase cas : cases) {
|
|
|
UnicodeString input(cas.input);
|
|
|
UnicodeString output(cas.output);
|
|
|
|
|
|
assertEquals(input, cas.currency, AffixUtils::hasCurrencySymbols(input, status));
|
|
|
assertSuccess("Spot 1", status);
|
|
|
assertEquals(input, cas.expectedLength, AffixUtils::estimateLength(input, status));
|
|
|
assertSuccess("Spot 2", status);
|
|
|
|
|
|
UnicodeString actual = unescapeWithDefaults(defaultProvider, input, status);
|
|
|
assertSuccess("Spot 3", status);
|
|
|
assertEquals(input, output, actual);
|
|
|
|
|
|
int32_t ulength = AffixUtils::unescapedCodePointCount(input, defaultProvider, status);
|
|
|
assertSuccess("Spot 4", status);
|
|
|
assertEquals(input, output.countChar32(), ulength);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AffixUtilsTest::testContainsReplaceType() {
|
|
|
static struct TestCase {
|
|
|
const char16_t *input;
|
|
|
bool hasMinusSign;
|
|
|
const char16_t *output;
|
|
|
} cases[] = {{u"", false, u""},
|
|
|
{u"-", true, u"+"},
|
|
|
{u"-a", true, u"+a"},
|
|
|
{u"a-", true, u"a+"},
|
|
|
{u"a-b", true, u"a+b"},
|
|
|
{u"--", true, u"++"},
|
|
|
{u"x", false, u"x"}};
|
|
|
|
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
|
for (TestCase cas : cases) {
|
|
|
UnicodeString input(cas.input);
|
|
|
bool hasMinusSign = cas.hasMinusSign;
|
|
|
UnicodeString output(cas.output);
|
|
|
|
|
|
assertEquals(
|
|
|
input, hasMinusSign, AffixUtils::containsType(input, TYPE_MINUS_SIGN, status));
|
|
|
assertSuccess("Spot 1", status);
|
|
|
assertEquals(
|
|
|
input, output, AffixUtils::replaceType(input, TYPE_MINUS_SIGN, u'+', status));
|
|
|
assertSuccess("Spot 2", status);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void AffixUtilsTest::testInvalid() {
|
|
|
static const char16_t *invalidExamples[] = {
|
|
|
u"'", u"x'", u"'x", u"'x''", u"''x'"};
|
|
|
|
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
|
DefaultSymbolProvider defaultProvider(status);
|
|
|
assertSuccess("Constructing DefaultSymbolProvider", status);
|
|
|
|
|
|
for (const char16_t *strPtr : invalidExamples) {
|
|
|
UnicodeString str(strPtr);
|
|
|
|
|
|
status = U_ZERO_ERROR;
|
|
|
AffixUtils::hasCurrencySymbols(str, status);
|
|
|
assertEquals("Should set error code spot 1", status, U_ILLEGAL_ARGUMENT_ERROR);
|
|
|
|
|
|
status = U_ZERO_ERROR;
|
|
|
AffixUtils::estimateLength(str, status);
|
|
|
assertEquals("Should set error code spot 2", status, U_ILLEGAL_ARGUMENT_ERROR);
|
|
|
|
|
|
status = U_ZERO_ERROR;
|
|
|
unescapeWithDefaults(defaultProvider, str, status);
|
|
|
assertEquals("Should set error code spot 3", status, U_ILLEGAL_ARGUMENT_ERROR);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class NumericSymbolProvider : public SymbolProvider {
|
|
|
public:
|
|
|
virtual UnicodeString getSymbol(AffixPatternType type) const {
|
|
|
return Int64ToUnicodeString(type < 0 ? -type : type);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
void AffixUtilsTest::testUnescapeWithSymbolProvider() {
|
|
|
static const char16_t* cases[][2] = {
|
|
|
{u"", u""},
|
|
|
{u"-", u"1"},
|
|
|
{u"'-'", u"-"},
|
|
|
{u"- + % ‰ ¤ ¤¤ ¤¤¤ ¤¤¤¤ ¤¤¤¤¤", u"1 2 3 4 5 6 7 8 9"},
|
|
|
{u"'¤¤¤¤¤¤'", u"¤¤¤¤¤¤"},
|
|
|
{u"¤¤¤¤¤¤", u"\uFFFD"}
|
|
|
};
|
|
|
|
|
|
NumericSymbolProvider provider;
|
|
|
|
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
|
FormattedStringBuilder sb;
|
|
|
for (auto& cas : cases) {
|
|
|
UnicodeString input(cas[0]);
|
|
|
UnicodeString expected(cas[1]);
|
|
|
sb.clear();
|
|
|
AffixUtils::unescape(input, sb, 0, provider, kUndefinedField, status);
|
|
|
assertSuccess("Spot 1", status);
|
|
|
assertEquals(input, expected, sb.toUnicodeString());
|
|
|
assertEquals(input, expected, sb.toTempUnicodeString());
|
|
|
}
|
|
|
|
|
|
// Test insertion position
|
|
|
sb.clear();
|
|
|
sb.append(u"abcdefg", kUndefinedField, status);
|
|
|
assertSuccess("Spot 2", status);
|
|
|
AffixUtils::unescape(u"-+%", sb, 4, provider, kUndefinedField, status);
|
|
|
assertSuccess("Spot 3", status);
|
|
|
assertEquals(u"Symbol provider into middle", u"abcd123efg", sb.toUnicodeString());
|
|
|
}
|
|
|
|
|
|
UnicodeString AffixUtilsTest::unescapeWithDefaults(const SymbolProvider &defaultProvider,
|
|
|
UnicodeString input, UErrorCode &status) {
|
|
|
FormattedStringBuilder nsb;
|
|
|
int32_t length = AffixUtils::unescape(input, nsb, 0, defaultProvider, kUndefinedField, status);
|
|
|
assertEquals("Return value of unescape", nsb.length(), length);
|
|
|
return nsb.toUnicodeString();
|
|
|
}
|
|
|
|
|
|
#endif /* #if !UCONFIG_NO_FORMATTING */
|