• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING
7 
8 #include "putilimp.h"
9 #include "unicode/dcfmtsym.h"
10 #include "numbertest.h"
11 #include "number_utils.h"
12 
13 using namespace icu::number::impl;
14 
15 class DefaultSymbolProvider : public SymbolProvider {
16     DecimalFormatSymbols fSymbols;
17 
18   public:
DefaultSymbolProvider(UErrorCode & status)19     DefaultSymbolProvider(UErrorCode &status) : fSymbols(Locale("ar_SA"), status) {}
20 
getSymbol(AffixPatternType type) const21     UnicodeString getSymbol(AffixPatternType type) const U_OVERRIDE {
22         switch (type) {
23             case TYPE_MINUS_SIGN:
24                 return u"−";
25             case TYPE_PLUS_SIGN:
26                 return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol);
27             case TYPE_PERCENT:
28                 return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol);
29             case TYPE_PERMILLE:
30                 return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol);
31             case TYPE_CURRENCY_SINGLE:
32                 return u"$";
33             case TYPE_CURRENCY_DOUBLE:
34                 return u"XXX";
35             case TYPE_CURRENCY_TRIPLE:
36                 return u"long name";
37             case TYPE_CURRENCY_QUAD:
38                 return u"\uFFFD";
39             case TYPE_CURRENCY_QUINT:
40                 // TODO: Add support for narrow currency symbols here.
41                 return u"\uFFFD";
42             case TYPE_CURRENCY_OVERFLOW:
43                 return u"\uFFFD";
44             default:
45                 UPRV_UNREACHABLE;
46         }
47     }
48 };
49 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)50 void AffixUtilsTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) {
51     if (exec) {
52         logln("TestSuite AffixUtilsTest: ");
53     }
54     TESTCASE_AUTO_BEGIN;
55         TESTCASE_AUTO(testEscape);
56         TESTCASE_AUTO(testUnescape);
57         TESTCASE_AUTO(testContainsReplaceType);
58         TESTCASE_AUTO(testInvalid);
59         TESTCASE_AUTO(testUnescapeWithSymbolProvider);
60     TESTCASE_AUTO_END;
61 }
62 
testEscape()63 void AffixUtilsTest::testEscape() {
64     static const char16_t *cases[][2] = {{u"", u""},
65                                          {u"abc", u"abc"},
66                                          {u"-", u"'-'"},
67                                          {u"-!", u"'-'!"},
68                                          {u"−", u"−"},
69                                          {u"---", u"'---'"},
70                                          {u"-%-", u"'-%-'"},
71                                          {u"'", u"''"},
72                                          {u"-'", u"'-'''"},
73                                          {u"-'-", u"'-''-'"},
74                                          {u"a-'-", u"a'-''-'"}};
75 
76     for (auto &cas : cases) {
77         UnicodeString input(cas[0]);
78         UnicodeString expected(cas[1]);
79         UnicodeString result = AffixUtils::escape(input);
80         assertEquals(input, expected, result);
81     }
82 }
83 
testUnescape()84 void AffixUtilsTest::testUnescape() {
85     static struct TestCase {
86         const char16_t *input;
87         bool currency;
88         int32_t expectedLength;
89         const char16_t *output;
90     } cases[] = {{u"", false, 0, u""},
91                  {u"abc", false, 3, u"abc"},
92                  {u"-", false, 1, u"−"},
93                  {u"-!", false, 2, u"−!"},
94                  {u"+", false, 1, u"\u061C+"},
95                  {u"+!", false, 2, u"\u061C+!"},
96                  {u"‰", false, 1, u"؉"},
97                  {u"‰!", false, 2, u"؉!"},
98                  {u"-x", false, 2, u"−x"},
99                  {u"'-'x", false, 2, u"-x"},
100                  {u"'--''-'-x", false, 6, u"--'-−x"},
101                  {u"''", false, 1, u"'"},
102                  {u"''''", false, 2, u"''"},
103                  {u"''''''", false, 3, u"'''"},
104                  {u"''x''", false, 3, u"'x'"},
105                  {u"¤", true, 1, u"$"},
106                  {u"¤¤", true, 2, u"XXX"},
107                  {u"¤¤¤", true, 3, u"long name"},
108                  {u"¤¤¤¤", true, 4, u"\uFFFD"},
109                  {u"¤¤¤¤¤", true, 5, u"\uFFFD"},
110                  {u"¤¤¤¤¤¤", true, 6, u"\uFFFD"},
111                  {u"¤¤¤a¤¤¤¤", true, 8, u"long namea\uFFFD"},
112                  {u"a¤¤¤¤b¤¤¤¤¤c", true, 12, u"a\uFFFDb\uFFFDc"},
113                  {u"¤!", true, 2, u"$!"},
114                  {u"¤¤!", true, 3, u"XXX!"},
115                  {u"¤¤¤!", true, 4, u"long name!"},
116                  {u"-¤¤", true, 3, u"−XXX"},
117                  {u"¤¤-", true, 3, u"XXX−"},
118                  {u"'¤'", false, 1, u"¤"},
119                  {u"%", false, 1, u"٪\u061C"},
120                  {u"'%'", false, 1, u"%"},
121                  {u"¤'-'%", true, 3, u"$-٪\u061C"},
122                  {u"#0#@#*#;#", false, 9, u"#0#@#*#;#"}};
123 
124     UErrorCode status = U_ZERO_ERROR;
125     DefaultSymbolProvider defaultProvider(status);
126     assertSuccess("Constructing DefaultSymbolProvider", status);
127 
128     for (TestCase cas : cases) {
129         UnicodeString input(cas.input);
130         UnicodeString output(cas.output);
131 
132         assertEquals(input, cas.currency, AffixUtils::hasCurrencySymbols(input, status));
133         assertSuccess("Spot 1", status);
134         assertEquals(input, cas.expectedLength, AffixUtils::estimateLength(input, status));
135         assertSuccess("Spot 2", status);
136 
137         UnicodeString actual = unescapeWithDefaults(defaultProvider, input, status);
138         assertSuccess("Spot 3", status);
139         assertEquals(input, output, actual);
140 
141         int32_t ulength = AffixUtils::unescapedCodePointCount(input, defaultProvider, status);
142         assertSuccess("Spot 4", status);
143         assertEquals(input, output.countChar32(), ulength);
144     }
145 }
146 
testContainsReplaceType()147 void AffixUtilsTest::testContainsReplaceType() {
148     static struct TestCase {
149         const char16_t *input;
150         bool hasMinusSign;
151         const char16_t *output;
152     } cases[] = {{u"", false, u""},
153                  {u"-", true, u"+"},
154                  {u"-a", true, u"+a"},
155                  {u"a-", true, u"a+"},
156                  {u"a-b", true, u"a+b"},
157                  {u"--", true, u"++"},
158                  {u"x", false, u"x"}};
159 
160     UErrorCode status = U_ZERO_ERROR;
161     for (TestCase cas : cases) {
162         UnicodeString input(cas.input);
163         bool hasMinusSign = cas.hasMinusSign;
164         UnicodeString output(cas.output);
165 
166         assertEquals(
167                 input, hasMinusSign, AffixUtils::containsType(input, TYPE_MINUS_SIGN, status));
168         assertSuccess("Spot 1", status);
169         assertEquals(
170                 input, output, AffixUtils::replaceType(input, TYPE_MINUS_SIGN, u'+', status));
171         assertSuccess("Spot 2", status);
172     }
173 }
174 
testInvalid()175 void AffixUtilsTest::testInvalid() {
176     static const char16_t *invalidExamples[] = {
177             u"'", u"x'", u"'x", u"'x''", u"''x'"};
178 
179     UErrorCode status = U_ZERO_ERROR;
180     DefaultSymbolProvider defaultProvider(status);
181     assertSuccess("Constructing DefaultSymbolProvider", status);
182 
183     for (const char16_t *strPtr : invalidExamples) {
184         UnicodeString str(strPtr);
185 
186         status = U_ZERO_ERROR;
187         AffixUtils::hasCurrencySymbols(str, status);
188         assertEquals("Should set error code spot 1", status, U_ILLEGAL_ARGUMENT_ERROR);
189 
190         status = U_ZERO_ERROR;
191         AffixUtils::estimateLength(str, status);
192         assertEquals("Should set error code spot 2", status, U_ILLEGAL_ARGUMENT_ERROR);
193 
194         status = U_ZERO_ERROR;
195         unescapeWithDefaults(defaultProvider, str, status);
196         assertEquals("Should set error code spot 3", status, U_ILLEGAL_ARGUMENT_ERROR);
197     }
198 }
199 
200 class NumericSymbolProvider : public SymbolProvider {
201   public:
getSymbol(AffixPatternType type) const202     virtual UnicodeString getSymbol(AffixPatternType type) const {
203         return Int64ToUnicodeString(type < 0 ? -type : type);
204     }
205 };
206 
testUnescapeWithSymbolProvider()207 void AffixUtilsTest::testUnescapeWithSymbolProvider() {
208     static const char16_t* cases[][2] = {
209             {u"", u""},
210             {u"-", u"1"},
211             {u"'-'", u"-"},
212             {u"- + % ‰ ¤ ¤¤ ¤¤¤ ¤¤¤¤ ¤¤¤¤¤", u"1 2 3 4 5 6 7 8 9"},
213             {u"'¤¤¤¤¤¤'", u"¤¤¤¤¤¤"},
214             {u"¤¤¤¤¤¤", u"\uFFFD"}
215     };
216 
217     NumericSymbolProvider provider;
218 
219     UErrorCode status = U_ZERO_ERROR;
220     FormattedStringBuilder sb;
221     for (auto& cas : cases) {
222         UnicodeString input(cas[0]);
223         UnicodeString expected(cas[1]);
224         sb.clear();
225         AffixUtils::unescape(input, sb, 0, provider, kUndefinedField, status);
226         assertSuccess("Spot 1", status);
227         assertEquals(input, expected, sb.toUnicodeString());
228         assertEquals(input, expected, sb.toTempUnicodeString());
229     }
230 
231     // Test insertion position
232     sb.clear();
233     sb.append(u"abcdefg", kUndefinedField, status);
234     assertSuccess("Spot 2", status);
235     AffixUtils::unescape(u"-+%", sb, 4, provider, kUndefinedField, status);
236     assertSuccess("Spot 3", status);
237     assertEquals(u"Symbol provider into middle", u"abcd123efg", sb.toUnicodeString());
238 }
239 
unescapeWithDefaults(const SymbolProvider & defaultProvider,UnicodeString input,UErrorCode & status)240 UnicodeString AffixUtilsTest::unescapeWithDefaults(const SymbolProvider &defaultProvider,
241                                                           UnicodeString input, UErrorCode &status) {
242     FormattedStringBuilder nsb;
243     int32_t length = AffixUtils::unescape(input, nsb, 0, defaultProvider, kUndefinedField, status);
244     assertEquals("Return value of unescape", nsb.length(), length);
245     return nsb.toUnicodeString();
246 }
247 
248 #endif /* #if !UCONFIG_NO_FORMATTING */
249