1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/*
4*******************************************************************************
5*   Copyright (C) 2001-2009, International Business Machines
6*   Corporation and others.  All Rights Reserved.
7*******************************************************************************
8*/
9
10package com.ibm.icu.dev.test.shaping;
11
12import org.junit.Ignore;
13import org.junit.Test;
14
15import com.ibm.icu.text.ArabicShaping;
16import com.ibm.icu.text.ArabicShapingException;
17
18/**
19 * Interactive test for Arabic shaping.
20 * Invoke from a command line passing args and strings.  Use '-help' to see description of arguments.
21 */
22// TODO(junit): wasn't running before - needs to be fixed
23public class ArabicShapingTest{
24    private static final int COPY = 0;
25    private static final int INPLACE = 1;
26    private static final int STRING = 2;
27
28    // TODO(junit): marked with a test to keep from failing during ant run
29    @Ignore
30    @Test
31    public void dummyTest() {
32    }
33
34    public static final void main(String[] args) {
35        int testtype = COPY;
36        int options = 0;
37        int ss = 0;
38        int sl = -1;
39        int ds = 0;
40        int dl = -1;
41        String text = "$22.4 test 123 \ufef6\u0644\u0622 456 \u0664\u0665\u0666!";
42
43        for (int i = 0; i < args.length; ++i) {
44            String arg = args[i];
45            if (arg.charAt(0) == '-') {
46                String opt = arg.substring(1);
47                String val = opt;
48                int index = arg.indexOf(':');
49                if (index != -1) {
50                    opt = opt.substring(0, Math.min(index, 3));
51                    val = arg.substring(index + 1);
52                }
53
54                if (opt.equalsIgnoreCase("len")) {
55                    options &= ~ArabicShaping.LENGTH_MASK;
56                    if (val.equalsIgnoreCase("gs")) {
57                        options |= ArabicShaping.LENGTH_GROW_SHRINK;
58                    } else if (val.equalsIgnoreCase("sn")) {
59                        options |= ArabicShaping.LENGTH_FIXED_SPACES_NEAR;
60                    } else if (val.equalsIgnoreCase("se")) {
61                        options |= ArabicShaping.LENGTH_FIXED_SPACES_AT_END;
62                    } else if (val.equalsIgnoreCase("sb")) {
63                        options |= ArabicShaping.LENGTH_FIXED_SPACES_AT_BEGINNING;
64                    } else {
65                        throwValError(opt, val);
66                    }
67                } else if (opt.equalsIgnoreCase("dir")) {
68                    options &= ~ArabicShaping.TEXT_DIRECTION_MASK;
69                    if (val.equalsIgnoreCase("log")) {
70                        options |= ArabicShaping.TEXT_DIRECTION_LOGICAL;
71                    } else if (val.equalsIgnoreCase("vis")) {
72                        options |= ArabicShaping.TEXT_DIRECTION_VISUAL_LTR;
73                    } else {
74                        throwValError(opt, val);
75                    }
76                } else if (opt.equalsIgnoreCase("let")) {
77                    options &= ~ArabicShaping.LETTERS_MASK;
78                    if (val.equalsIgnoreCase("no")) {
79                        options |= ArabicShaping.LETTERS_NOOP;
80                    } else if (val.equalsIgnoreCase("sh")) {
81                        options |= ArabicShaping.LETTERS_SHAPE;
82                    } else if (val.equalsIgnoreCase("un")) {
83                        options |= ArabicShaping.LETTERS_UNSHAPE;
84                    } else if (val.equalsIgnoreCase("ta")) {
85                        options |= ArabicShaping.LETTERS_SHAPE_TASHKEEL_ISOLATED;
86                    } else {
87                        throwValError(opt, val);
88                    }
89                } else if (opt.equalsIgnoreCase("dig")) {
90                    options &= ~ArabicShaping.DIGITS_MASK;
91                    if (val.equalsIgnoreCase("no")) {
92                        options |= ArabicShaping.DIGITS_NOOP;
93                    } else if (val.equalsIgnoreCase("ea")) {
94                        options |= ArabicShaping.DIGITS_EN2AN;
95                    } else if (val.equalsIgnoreCase("ae")) {
96                        options |= ArabicShaping.DIGITS_AN2EN;
97                    } else if (val.equalsIgnoreCase("lr")) {
98                        options |= ArabicShaping.DIGITS_EN2AN_INIT_LR;
99                    } else if (val.equalsIgnoreCase("al")) {
100                        options |= ArabicShaping.DIGITS_EN2AN_INIT_AL;
101                    } else {
102                        throwValError(opt, val);
103                    }
104                } else if (opt.equalsIgnoreCase("typ")) {
105                    options &= ~ArabicShaping.DIGIT_TYPE_MASK;
106                    if (val.equalsIgnoreCase("an")) {
107                        options |= ArabicShaping.DIGIT_TYPE_AN;
108                    } else if (val.equalsIgnoreCase("ex")) {
109                        options |= ArabicShaping.DIGIT_TYPE_AN_EXTENDED;
110                    } else {
111                        throwValError(opt, val);
112                    }
113                } else if (opt.equalsIgnoreCase("dst")) {
114                    try {
115                        ds = Integer.parseInt(val);
116                    }
117                    catch (Exception e) {
118                        throwValError(opt, val);
119                    }
120                } else if (opt.equalsIgnoreCase("dln")) {
121                    try {
122                        dl = Integer.parseInt(val);
123                    }
124                    catch (Exception e) {
125                        throwValError(opt, val);
126                    }
127                } else if (opt.equalsIgnoreCase("sst")) {
128                    try {
129                        ss = Integer.parseInt(val);
130                    }
131                    catch (Exception e) {
132                        throwValError(opt, val);
133                    }
134                } else if (opt.equalsIgnoreCase("sln")) {
135                    try {
136                        sl = Integer.parseInt(val);
137                    }
138                    catch (Exception e) {
139                        throwValError(opt, val);
140                    }
141                } else if (opt.equalsIgnoreCase("tes")) {
142                    if (val.equalsIgnoreCase("cp")) {
143                        testtype = COPY;
144                    } else if (val.equalsIgnoreCase("ip")) {
145                        testtype = INPLACE;
146                    } else if (val.equalsIgnoreCase("st")) {
147                        testtype = STRING;
148                    } else {
149                        throwValError(opt, val);
150                    }
151                } else if (opt.equalsIgnoreCase("help")) {
152                    System.out.println(usage);
153                } else {
154                    throwOptError(opt);
155                }
156            } else {
157                // assume text
158                text = parseText(arg);
159            }
160        }
161
162        if (sl < 0) {
163            sl = text.length() - ss;
164            System.out.println("sl defaulting to " + sl);
165        }
166        if (dl < 0) {
167            dl = 2 * sl;
168            System.out.println("dl defaulting to " + dl);
169        }
170
171        ArabicShaping shaper = new ArabicShaping(options);
172        System.out.println("shaper: " + shaper);
173
174        char[] src = text.toCharArray();
175        System.out.println(" input: '" + escapedText(src, ss, sl) + "'");
176        if (testtype != STRING) {
177            System.out.println("start: " + ss + " length: " + sl + " total length: " + src.length);
178        }
179
180        int result = -1;
181        char[] dest = null;
182
183        try {
184            switch (testtype) {
185            case COPY:
186                dest = new char[ds + dl];
187                result = shaper.shape(src, ss, sl, dest, ds, dl);
188                break;
189
190            case INPLACE:
191                shaper.shape(src, ss, sl);
192                ds = ss;
193                result = sl;
194                dest = src;
195                break;
196
197            case STRING:
198                dest = shaper.shape(text).toCharArray();
199                ds = 0;
200                result = dest.length;
201                break;
202            }
203
204            System.out.println("output: '" + escapedText(dest, ds, result) + "'");
205            System.out.println("length: " + result);
206            if (ds != 0 || result != dest.length) {
207                System.out.println("full output: '" + escapedText(dest, 0, dest.length) + "'");
208            }
209        }
210        catch (ArabicShapingException e) {
211            System.out.println("Caught ArabicShapingException");
212            System.out.println(e);
213        }
214        catch (Exception e) {
215            System.out.println("Caught Exception");
216            System.out.println(e);
217        }
218    }
219
220    private static void throwOptError(String opt) {
221        throwUsageError("unknown option: " + opt);
222    }
223
224    private static void throwValError(String opt, String val) {
225        throwUsageError("unknown value: " + val + " for option: " + opt);
226    }
227
228    private static void throwUsageError(String message) {
229        StringBuffer buf = new StringBuffer("*** usage error ***\n");
230        buf.append(message);
231        buf.append("\n");
232        buf.append(usage);
233        throw new Error(buf.toString());
234    }
235
236    private static final String usage =
237        "Usage: [option]* [text]\n" +
238        "  where option is in the format '-opt[:val]'\n" +
239        "  options are:\n" +
240        "    -len:[gs|sn|se|sb]    (length: grow/shrink, spaces near, spaces end, spaces beginning)\n" +
241        "    -dir:[log|vis]        (direction: logical, visual)\n" +
242        "    -let:[no|sh|un|ta]    (letters: noop, shape, unshape, tashkeel)\n" +
243        // "    -let:[no|sh|un]       (letters: noop, shape, unshape)\n" +
244        "    -dig:[no|ea|ae|lr|al] (digits: noop, en2an, an2en, en2an_lr, en2an_al)\n" +
245        "    -typ:[an|ex]          (digit type: arabic, arabic extended)\n" +
246        "    -dst:#                (dest start: [integer])\n" +
247        "    -dln:#                (dest length (max size): [integer])\n" +
248        "    -sst:#                (source start: [integer])\n" +
249        "    -sln:#                (source length: [integer])\n" +
250        "    -tes:[cp|ip|st]       (test type: copy, in place, string)\n" +
251        "    -help                 (print this help message)\n" +
252        "  text can contain unicode escape values in the format '\\uXXXX' only\n";
253
254    private static String escapedText(char[] text, int start, int length) {
255        StringBuffer buf = new StringBuffer();
256        for (int i = start, e = start + length; i < e; ++i) {
257            char ch = text[i];
258            if (ch < 0x20 || ch > 0x7e) {
259                buf.append("\\u");
260                if (ch < 0x1000) {
261                    buf.append('0');
262                }
263                if (ch < 0x100) {
264                    buf.append('0');
265                }
266                if (ch < 0x10) {
267                    buf.append('0');
268                }
269                buf.append(Integer.toHexString(ch));
270            } else {
271                buf.append(ch);
272            }
273        }
274        return buf.toString();
275    }
276
277    private static String parseText(String text) {
278        // process unicode escapes (only)
279        StringBuffer buf = new StringBuffer();
280        char[] chars = text.toCharArray();
281        for (int i = 0; i < chars.length; ++i) {
282            char ch = chars[i];
283            if (ch == '\\') {
284                if ((i < chars.length - 1) &&
285                    (chars[i+1] == 'u')) {
286                    int val = Integer.parseInt(text.substring(i+2, i+6), 16);
287                    buf.append((char)val);
288                    i += 5;
289                } else {
290                    buf.append('\\');
291                }
292            } else {
293                buf.append(ch);
294            }
295        }
296        return buf.toString();
297    }
298}
299