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     */
41class 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
55public:
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
110protected:
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
162const char TestReplaceable::fgClassID=0;
163
164const UChar TestReplaceable::NO_STYLE  = 0x005F;
165
166const UChar TestReplaceable::NO_STYLE_MARK = 0xFFFF;
167
168void
169ReplaceableTest::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 */
180class NoopReplaceable : public Replaceable {
181public:
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
209private:
210    static const char fgClassID;
211};
212
213const char NoopReplaceable::fgClassID=0;
214
215void 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
274void 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