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: 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 74 virtual Replaceable *clone() const { 75 return new TestReplaceable(chars, styles); 76 } 77 78 ~TestReplaceable(void) {} 79 80 UnicodeString getStyles() { 81 return styles; 82 } 83 84 UnicodeString toString() { 85 UnicodeString s = chars; 86 s.append("{"); 87 s.append(styles); 88 s.append("}"); 89 return s; 90 } 91 92 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 */ 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 */ 108 virtual inline UClassID getDynamicClassID() const { return getStaticClassID(); } 109 110 protected: 111 virtual int32_t getLength() const { 112 return chars.length(); 113 } 114 115 virtual UChar getCharAt(int32_t offset) const{ 116 return chars.charAt(offset); 117 } 118 119 virtual UChar32 getChar32At(int32_t offset) const{ 120 return chars.char32At(offset); 121 } 122 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 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 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 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: 182 virtual int32_t getLength() const { 183 return 0; 184 } 185 186 virtual UChar getCharAt(int32_t /*offset*/) const{ 187 return 0xffff; 188 } 189 190 virtual UChar32 getChar32At(int32_t /*offset*/) const{ 191 return 0xffff; 192 } 193 194 void extractBetween(int32_t /*start*/, int32_t /*limit*/, UnicodeString& result) const { 195 result.remove(); 196 } 197 198 virtual void handleReplaceBetween(int32_t /*start*/, int32_t /*limit*/, const UnicodeString &/*text*/) { 199 /* do nothing */ 200 } 201 202 virtual void copy(int32_t /*start*/, int32_t /*limit*/, int32_t /*dest*/) { 203 /* do nothing */ 204 } 205 206 static inline UClassID getStaticClassID() { return (UClassID)&fgClassID; } 207 virtual inline UClassID getDynamicClassID() const { return getStaticClassID(); } 208 209 private: 210 static const char fgClassID; 211 }; 212 213 const char NoopReplaceable::fgClassID=0; 214 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 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