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