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