Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 1 | // © 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 TestReplaceable *clone() const override { |
| 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 override { |
| 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 override { return getStaticClassID(); } |
| 109 | |
| 110 | protected: |
| 111 | virtual int32_t getLength() const override { |
| 112 | return chars.length(); |
| 113 | } |
| 114 | |
| 115 | virtual UChar getCharAt(int32_t offset) const override { |
| 116 | return chars.charAt(offset); |
| 117 | } |
| 118 | |
| 119 | virtual UChar32 getChar32At(int32_t offset) const override { |
| 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) override { |
| 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) override { |
| 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 override { |
| 183 | return 0; |
| 184 | } |
| 185 | |
| 186 | virtual UChar getCharAt(int32_t /*offset*/) const override { |
| 187 | return 0xffff; |
| 188 | } |
| 189 | |
| 190 | virtual UChar32 getChar32At(int32_t /*offset*/) const override { |
| 191 | return 0xffff; |
| 192 | } |
| 193 | |
| 194 | void extractBetween(int32_t /*start*/, int32_t /*limit*/, UnicodeString& result) const override { |
| 195 | result.remove(); |
| 196 | } |
| 197 | |
| 198 | virtual void handleReplaceBetween(int32_t /*start*/, int32_t /*limit*/, const UnicodeString &/*text*/) override { |
| 199 | /* do nothing */ |
| 200 | } |
| 201 | |
| 202 | virtual void copy(int32_t /*start*/, int32_t /*limit*/, int32_t /*dest*/) override { |
| 203 | /* do nothing */ |
| 204 | } |
| 205 | |
| 206 | static inline UClassID getStaticClassID() { return (UClassID)&fgClassID; } |
| 207 | virtual inline UClassID getDynamicClassID() const override { 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 = 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 */ |