1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
4 * COPYRIGHT:
5 * Copyright (c) 2001-2010, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************/
8 /************************************************************************
9 * This test program is intended for testing Replaceable class.
10 *
11 * Date Name Description
12 * 11/28/2001 hshih Ported back from Java.
13 *
14 ************************************************************************/
15
16 #include "unicode/utypes.h"
17
18 #if !UCONFIG_NO_TRANSLITERATION
19
20 #include "ittrans.h"
21 #include <string.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include "unicode/rep.h"
25 #include "reptest.h"
26
27 //---------------------------------------------
28 // runIndexedTest
29 //---------------------------------------------
30
31 /**
32 * This is a test class that simulates styled text.
33 * It associates a style number (0..65535) with each character,
34 * and maintains that style in the normal fashion:
35 * When setting text from raw string or characters,<br>
36 * Set the styles to the style of the first character replaced.<br>
37 * If no characters are replaced, use the style of the previous character.<br>
38 * If at start, use the following character<br>
39 * Otherwise use NO_STYLE.
40 */
41 class TestReplaceable : public Replaceable {
42 UnicodeString chars;
43 UnicodeString styles;
44
45 static const UChar NO_STYLE;
46
47 static const UChar NO_STYLE_MARK;
48
49 /**
50 * The address of this static class variable serves as this class's ID
51 * for ICU "poor man's RTTI".
52 */
53 static const char fgClassID;
54
55 public:
TestReplaceable(const UnicodeString & text,const UnicodeString & newStyles)56 TestReplaceable (const UnicodeString& text,
57 const UnicodeString& newStyles) {
58 chars = text;
59 UnicodeString s;
60 for (int i = 0; i < text.length(); ++i) {
61 if (i < newStyles.length()) {
62 s.append(newStyles.charAt(i));
63 } else {
64 if (text.charAt(i) == NO_STYLE_MARK) {
65 s.append(NO_STYLE);
66 } else {
67 s.append((UChar)(i + 0x0031));
68 }
69 }
70 }
71 this->styles = s;
72 }
73
clone() const74 virtual Replaceable *clone() const {
75 return new TestReplaceable(chars, styles);
76 }
77
~TestReplaceable(void)78 ~TestReplaceable(void) {}
79
getStyles()80 UnicodeString getStyles() {
81 return styles;
82 }
83
toString()84 UnicodeString toString() {
85 UnicodeString s = chars;
86 s.append("{");
87 s.append(styles);
88 s.append("}");
89 return s;
90 }
91
extractBetween(int32_t start,int32_t limit,UnicodeString & result) const92 void extractBetween(int32_t start, int32_t limit, UnicodeString& result) const {
93 chars.extractBetween(start, limit, result);
94 }
95
96 /**
97 * ICU "poor man's RTTI", returns a UClassID for this class.
98 *
99 * @draft ICU 2.2
100 */
getStaticClassID()101 static inline UClassID getStaticClassID() { return (UClassID)&fgClassID; }
102
103 /**
104 * ICU "poor man's RTTI", returns a UClassID for the actual class.
105 *
106 * @draft ICU 2.2
107 */
getDynamicClassID() const108 virtual inline UClassID getDynamicClassID() const { return getStaticClassID(); }
109
110 protected:
getLength() const111 virtual int32_t getLength() const {
112 return chars.length();
113 }
114
getCharAt(int32_t offset) const115 virtual UChar getCharAt(int32_t offset) const{
116 return chars.charAt(offset);
117 }
118
getChar32At(int32_t offset) const119 virtual UChar32 getChar32At(int32_t offset) const{
120 return chars.char32At(offset);
121 }
122
fixStyles(int32_t start,int32_t limit,int32_t newLen)123 void fixStyles(int32_t start, int32_t limit, int32_t newLen) {
124 UChar newStyle = NO_STYLE;
125 if (start != limit && styles.charAt(start) != NO_STYLE) {
126 newStyle = styles.charAt(start);
127 } else if (start > 0 && getCharAt(start-1) != NO_STYLE_MARK) {
128 newStyle = styles.charAt(start-1);
129 } else if (limit < styles.length()) {
130 newStyle = styles.charAt(limit);
131 }
132 // dumb implementation for now.
133 UnicodeString s;
134 for (int i = 0; i < newLen; ++i) {
135 // this doesn't really handle an embedded NO_STYLE_MARK
136 // in the middle of a long run of characters right -- but
137 // that case shouldn't happen anyway
138 if (getCharAt(start+i) == NO_STYLE_MARK) {
139 s.append(NO_STYLE);
140 } else {
141 s.append(newStyle);
142 }
143 }
144 styles.replaceBetween(start, limit, s);
145 }
146
handleReplaceBetween(int32_t start,int32_t limit,const UnicodeString & text)147 virtual void handleReplaceBetween(int32_t start, int32_t limit, const UnicodeString& text) {
148 UnicodeString s;
149 this->extractBetween(start, limit, s);
150 if (s == text) return; // NO ACTION!
151 this->chars.replaceBetween(start, limit, text);
152 fixStyles(start, limit, text.length());
153 }
154
155
copy(int32_t start,int32_t limit,int32_t dest)156 virtual void copy(int32_t start, int32_t limit, int32_t dest) {
157 chars.copy(start, limit, dest);
158 styles.copy(start, limit, dest);
159 }
160 };
161
162 const char TestReplaceable::fgClassID=0;
163
164 const UChar TestReplaceable::NO_STYLE = 0x005F;
165
166 const UChar TestReplaceable::NO_STYLE_MARK = 0xFFFF;
167
168 void
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)169 ReplaceableTest::runIndexedTest(int32_t index, UBool exec,
170 const char* &name, char* /*par*/) {
171 switch (index) {
172 TESTCASE(0,TestReplaceableClass);
173 default: name = ""; break;
174 }
175 }
176
177 /*
178 * Dummy Replaceable implementation for better API/code coverage.
179 */
180 class NoopReplaceable : public Replaceable {
181 public:
getLength() const182 virtual int32_t getLength() const {
183 return 0;
184 }
185
getCharAt(int32_t) const186 virtual UChar getCharAt(int32_t /*offset*/) const{
187 return 0xffff;
188 }
189
getChar32At(int32_t) const190 virtual UChar32 getChar32At(int32_t /*offset*/) const{
191 return 0xffff;
192 }
193
extractBetween(int32_t,int32_t,UnicodeString & result) const194 void extractBetween(int32_t /*start*/, int32_t /*limit*/, UnicodeString& result) const {
195 result.remove();
196 }
197
handleReplaceBetween(int32_t,int32_t,const UnicodeString &)198 virtual void handleReplaceBetween(int32_t /*start*/, int32_t /*limit*/, const UnicodeString &/*text*/) {
199 /* do nothing */
200 }
201
copy(int32_t,int32_t,int32_t)202 virtual void copy(int32_t /*start*/, int32_t /*limit*/, int32_t /*dest*/) {
203 /* do nothing */
204 }
205
getStaticClassID()206 static inline UClassID getStaticClassID() { return (UClassID)&fgClassID; }
getDynamicClassID() const207 virtual inline UClassID getDynamicClassID() const { return getStaticClassID(); }
208
209 private:
210 static const char fgClassID;
211 };
212
213 const char NoopReplaceable::fgClassID=0;
214
TestReplaceableClass(void)215 void ReplaceableTest::TestReplaceableClass(void) {
216 UChar rawTestArray[][6] = {
217 {0x0041, 0x0042, 0x0043, 0x0044, 0x0000, 0x0000}, // ABCD
218 {0x0061, 0x0062, 0x0063, 0x0064, 0x00DF, 0x0000}, // abcd\u00DF
219 {0x0061, 0x0042, 0x0043, 0x0044, 0x0000, 0x0000}, // aBCD
220 {0x0041, 0x0300, 0x0045, 0x0300, 0x0000, 0x0000}, // A\u0300E\u0300
221 {0x00C0, 0x00C8, 0x0000, 0x0000, 0x0000, 0x0000}, // \u00C0\u00C8
222 {0x0077, 0x0078, 0x0079, 0x0000, 0x0000, 0x0000}, /* "wxy" */
223 {0x0077, 0x0078, 0x0079, 0x007A, 0x0000, 0x0000}, /* "wxyz" */
224 {0x0077, 0x0078, 0x0079, 0x007A, 0x0075, 0x0000}, /* "wxyzu" */
225 {0x0078, 0x0079, 0x007A, 0x0000, 0x0000, 0x0000}, /* "xyz" */
226 {0x0077, 0x0078, 0x0079, 0x0000, 0x0000, 0x0000}, /* "wxy" */
227 {0xFFFF, 0x0078, 0x0079, 0x0000, 0x0000, 0x0000}, /* "*xy" */
228 {0xFFFF, 0x0078, 0x0079, 0x0000, 0x0000, 0x0000}, /* "*xy" */
229 };
230 check("Lower", rawTestArray[0], "1234");
231 check("Upper", rawTestArray[1], "123455"); // must map 00DF to SS
232 check("Title", rawTestArray[2], "1234");
233 check("NFC", rawTestArray[3], "13");
234 check("NFD", rawTestArray[4], "1122");
235 check("*(x) > A $1 B", rawTestArray[5], "11223");
236 check("*(x)(y) > A $2 B $1 C $2 D", rawTestArray[6], "113322334");
237 check("*(x)(y)(z) > A $3 B $2 C $1 D", rawTestArray[7], "114433225");
238 // Disabled for 2.4. TODO Revisit in 2.6 or later.
239 //check("*x > a", rawTestArray[8], "223"); // expect "123"?
240 //check("*x > a", rawTestArray[9], "113"); // expect "123"?
241 //check("*x > a", rawTestArray[10], "_33"); // expect "_23"?
242 //check("*(x) > A $1 B", rawTestArray[11], "__223");
243
244 // improve API/code coverage
245 NoopReplaceable noop;
246 Replaceable *p;
247 if((p=noop.clone())!=NULL) {
248 errln("Replaceable::clone() does not return NULL");
249 delete p;
250 }
251
252 if(!noop.hasMetaData()) {
253 errln("Replaceable::hasMetaData() does not return TRUE");
254 }
255
256 // try to call the compiler-provided
257 // UMemory/UObject/Replaceable assignment operators
258 NoopReplaceable noop2;
259 noop2=noop;
260 if((p=noop2.clone())!=NULL) {
261 errln("noop2.Replaceable::clone() does not return NULL");
262 delete p;
263 }
264
265 // try to call the compiler-provided
266 // UMemory/UObject/Replaceable copy constructors
267 NoopReplaceable noop3(noop);
268 if((p=noop3.clone())!=NULL) {
269 errln("noop3.Replaceable::clone() does not return NULL");
270 delete p;
271 }
272 }
273
check(const UnicodeString & transliteratorName,const UnicodeString & test,const UnicodeString & shouldProduceStyles)274 void ReplaceableTest::check(const UnicodeString& transliteratorName,
275 const UnicodeString& test,
276 const UnicodeString& shouldProduceStyles)
277 {
278 UErrorCode status = U_ZERO_ERROR;
279 TestReplaceable *tr = new TestReplaceable(test, "");
280 UnicodeString expectedStyles = shouldProduceStyles;
281 UnicodeString original = tr->toString();
282
283 Transliterator* t;
284 if (transliteratorName.charAt(0) == 0x2A /*'*'*/) {
285 UnicodeString rules(transliteratorName);
286 rules.remove(0,1);
287 UParseError pe;
288 t = Transliterator::createFromRules("test", rules, UTRANS_FORWARD,
289 pe, status);
290
291 // test clone()
292 TestReplaceable *tr2 = (TestReplaceable *)tr->clone();
293 if(tr2 != NULL) {
294 delete tr;
295 tr = tr2;
296 }
297 } else {
298 t = Transliterator::createInstance(transliteratorName, UTRANS_FORWARD, status);
299 }
300 if (U_FAILURE(status)) {
301 dataerrln("FAIL: failed to create the " + transliteratorName + " transliterator");
302 delete tr;
303 return;
304 }
305 t->transliterate(*tr);
306 UnicodeString newStyles = tr->getStyles();
307 if (newStyles != expectedStyles) {
308 errln("FAIL Styles: " + transliteratorName + "{" + original + "} => "
309 + tr->toString() + "; should be {" + expectedStyles + "}!");
310 } else {
311 log("OK: ");
312 log(transliteratorName);
313 log("(");
314 log(original);
315 log(") => ");
316 logln(tr->toString());
317 }
318 delete tr;
319 delete t;
320 }
321
322 #endif /* #if !UCONFIG_NO_TRANSLITERATION */
323