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