1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/**
18 * @author Ilya S. Okomin
19 * @version $Revision$
20 */
21package java.awt.font;
22
23import java.io.IOException;
24import java.io.Serializable;
25
26// BEGIN android-deleted
27//import org.apache.harmony.awt.internal.nls.Messages;
28// END android-deleted
29import org.apache.harmony.misc.HashCode;
30
31
32/**
33 * The Class NumericShaper provides methods to convert latin character codes
34 * to unicode character codes.
35 * For tables of the character codes used,
36 * see <a href="http://www.unicode.org/Public/UNIDATA/">unicode.org</a>.
37 */
38public final class NumericShaper implements Serializable {
39
40    /** The Constant serialVersionUID. */
41    private static final long serialVersionUID = -8022764705923730308L;
42
43    /**
44     * The Constant EUROPEAN indicates the latin and extended range,
45     * and latin decimal base.
46     */
47    public static final int EUROPEAN = 1;
48
49    /** The Constant ARABIC indicates the ARABIC range and decimal base. */
50    public static final int ARABIC = 2;
51
52    /**
53     * The Constant EASTERN_ARABIC indicates the ARABIC range and
54     * ARABIC_EXTENDED decimal base.
55     */
56    public static final int EASTERN_ARABIC = 4;
57
58    /**
59     * The Constant DEVANAGARI indicates the DEVANAGARI range and
60     * decimal base.
61     */
62    public static final int DEVANAGARI = 8;
63
64    /** The Constant BENGALI indicates the BENGALI range and decimal base. */
65    public static final int BENGALI = 16;
66
67    /** The Constant GURMUKHI indicates the GURMUKHI range and decimal base. */
68    public static final int GURMUKHI = 32;
69
70    /** The Constant GUJARATI indicates the GUJARATI range and decimal base. */
71    public static final int GUJARATI = 64;
72
73    /** The Constant ORIYA indicates the ORIYA range and decimal base. */
74    public static final int ORIYA = 128;
75
76    /** The Constant TAMIL indicates the TAMIL range and decimal base. */
77    public static final int TAMIL = 256;
78
79    /** The Constant TELUGU indicates the TELUGU range and decimal base. */
80    public static final int TELUGU = 512;
81
82    /** The Constant KANNADA indicates the KANNADA range and decimal base. */
83    public static final int KANNADA = 1024;
84
85    /**
86     * The Constant MALAYALAM indicates the MALAYALAM range and decimal base.
87     */
88    public static final int MALAYALAM = 2048;
89
90    /** The Constant THAI indicates the THAI range and decimal base. */
91    public static final int THAI = 4096;
92
93    /** The Constant LAO indicates the LAO range and decimal base. */
94    public static final int LAO = 8192;
95
96    /** The Constant TIBETAN indicates the TIBETAN range and decimal base. */
97    public static final int TIBETAN = 16384;
98
99    /** The Constant MYANMAR indicates the MYANMAR range and decimal base. */
100    public static final int MYANMAR = 32768;
101
102    /**
103     * The Constant ETHIOPIC indicates the ETHIOPIC range and decimal base.
104     */
105    public static final int ETHIOPIC = 65536;
106
107    /** The Constant KHMER indicates the KHMER range and decimal base. */
108    public static final int KHMER = 131072;
109
110    /**
111     * The Constant MONGOLIAN indicates the MONGOLIAN range and
112     * decimal base.
113     */
114    public static final int MONGOLIAN = 262144;
115
116    /** The Constant ALL_RANGES indicates all ranges. */
117    public static final int ALL_RANGES = 524287;
118
119    /* Further one can find the set of script indices.
120     * Index is the power you need the 2 to raise to to get corresponding
121     * range constant value. Also script ranges, context names and digits low
122     * ranges are indexed with these indices.
123     */
124
125    // Index of the EUROPEAN range
126    /** The Constant INDEX_EUROPEAN. */
127    private static final int INDEX_EUROPEAN = 0;
128
129    // Index of the ARABIC range
130    /** The Constant INDEX_ARABIC. */
131    private static final int INDEX_ARABIC = 1;
132
133    // Index of the EASTERN_ARABIC range
134    /** The Constant INDEX_EASTERN_ARABIC. */
135    private static final int INDEX_EASTERN_ARABIC = 2;
136
137    // Index of the DEVANAGARI range
138    /** The Constant INDEX_DEVANAGARI. */
139    private static final int INDEX_DEVANAGARI = 3;
140
141    // Index of the BENGALI range
142    /** The Constant INDEX_BENGALI. */
143    private static final int INDEX_BENGALI = 4;
144
145    // Index of the GURMUKHI range
146    /** The Constant INDEX_GURMUKHI. */
147    private static final int INDEX_GURMUKHI = 5;
148
149    // Index of the GUJARTI range
150    /** The Constant INDEX_GUJARATI. */
151    private static final int INDEX_GUJARATI = 6;
152
153    // Index of the ORIYA range
154    /** The Constant INDEX_ORIYA. */
155    private static final int INDEX_ORIYA = 7;
156
157    // Index of the TAMIL range
158    /** The Constant INDEX_TAMIL. */
159    private static final int INDEX_TAMIL = 8;
160
161    // Index of the TELUGU range
162    /** The Constant INDEX_TELUGU. */
163    private static final int INDEX_TELUGU = 9;
164
165    // Index of the KANNADA range
166    /** The Constant INDEX_KANNADA. */
167    private static final int INDEX_KANNADA = 10;
168
169    // Index of the MALAYALAM range
170    /** The Constant INDEX_MALAYALAM. */
171    private static final int INDEX_MALAYALAM = 11;
172
173    // Index of the THAI range
174    /** The Constant INDEX_THAI. */
175    private static final int INDEX_THAI = 12;
176
177    // Index of the LAO range
178    /** The Constant INDEX_LAO. */
179    private static final int INDEX_LAO = 13;
180
181    // Index of the TIBETAN range
182    /** The Constant INDEX_TIBETAN. */
183    private static final int INDEX_TIBETAN = 14;
184
185    // Index of the MYANMAR range
186    /** The Constant INDEX_MYANMAR. */
187    private static final int INDEX_MYANMAR = 15;
188
189    // Index of the ETHIOPIC range
190    /** The Constant INDEX_ETHIOPIC. */
191    private static final int INDEX_ETHIOPIC = 16;
192
193    // Index of the KHMER range
194    /** The Constant INDEX_KHMER. */
195    private static final int INDEX_KHMER = 17;
196
197    // Index of the MONGOLIAN range
198    /** The Constant INDEX_MONGOLIAN. */
199    private static final int INDEX_MONGOLIAN = 18;
200
201    // Maximum index that range can't exceed
202    /** The Constant MAX_INDEX. */
203    private static final int MAX_INDEX = 19;
204
205    /*
206     * Scripts ranges array. Array represents ranges as pairs of
207     * lowest and highest range bounds.
208     * Data is taken from the UnicodeData.txt file from
209     * http://www.unicode.org/Public/UNIDATA/
210     */
211    /** The scripts ranges. */
212    private final int[] scriptsRanges = {
213            0x0000, 0x024F,     // EUROPEAN (basic latin + latin-1 + extended)
214            0x0600, 0x06FF,     // ARABIC
215            0x0600, 0x06FF,     // EASTERN_ARABIC (XXX: diff with ARABIC ? )
216            0x0900, 0x097F,     // DEVANAGARI
217            0x0980, 0x09FF,     // BENGALI
218            0x0A00, 0x0A7F,     // GURMUKHI
219            0x0A80, 0x0AFF,     // GUJARATI
220            0x0B00, 0x0B7F,     // ORIYA
221            0x0B80, 0x0BFF,     // TAMIL
222            0x0C00, 0x0C7F,     // TELUGU
223            0x0C80, 0x0CFF,     // KANNADA
224            0x0D00, 0x0D7F,     // MALAYALAM
225            0x0E00, 0x0E7F,     // THAI
226            0x0E80, 0x0EFF,     // LAO
227            0x0F00, 0x0FFF,     // TIBETAN
228            0x1000, 0x109F,     // MYANMAR
229            0x1200, 0x137F,     // ETHIOPIC
230            0x1780, 0x17FF,     // KHMER
231            0x1800, 0x18AF      // MONGOLIAN
232    };
233
234    /*
235     * Digit low ranges values decreased by 0x0030. Each low range
236     * value decreased by 0x0030 for easy obtaing unicode value of the
237     * context dependent digit. European digits starts from 0x0030 hence
238     * context dependent unicode digit value equals to
239     *      digitsLowRanges[script index] + european digit char unicode value.
240     * !! the only exception is ETHIOPIC script where there is no '0' digit
241     * Data is taken from the UnicodeData.txt file from
242     * http://www.unicode.org/Public/UNIDATA/
243     */
244    /** The digits low ranges. */
245    private final int[] digitsLowRanges = {
246            0x0000,             // EUROPEAN
247            0x0630,             // ARABIC
248            0x0630,             // EASTERN_ARABIC
249            0x0936,             // DEVANAGARI
250            0x09B6,             // BENGALI
251            0x0A36,             // GURMUKHI
252            0x0AB6,             // GUJARATI
253            0x0B36,             // ORIYA
254            0x0BB6,             // TAMIL
255            0x0C36,             // TELUGU
256            0x0CB6,             // KANNADA
257            0x0D36,             // MALAYALAM
258            0x0E20,             // THAI
259            0x0EA0,             // LAO
260            0x0EF0,             // TIBETAN
261            0x1010,             // MYANMAR
262            0x1338,             // ETHIOPIC - (low range-1) no ETHIOPIC '0' DIGIT!
263            0x17B0,             // KHMER
264            0x17E0              // MONGOLIAN
265    };
266
267    // Set of context names used in toString method
268    /** The contexts. */
269    private final String[] contexts = {
270            "EUROPEAN", //$NON-NLS-1$
271            "ARABIC", //$NON-NLS-1$
272            "EASTERN_ARABIC", //$NON-NLS-1$
273            "DEVANAGARI", //$NON-NLS-1$
274            "BENGALI", //$NON-NLS-1$
275            "GURMUKHI", //$NON-NLS-1$
276            "GUJARATI", //$NON-NLS-1$
277            "ORIYA", //$NON-NLS-1$
278            "TAMIL", //$NON-NLS-1$
279            "TELUGU", //$NON-NLS-1$
280            "KANNADA", //$NON-NLS-1$
281            "MALAYALAM", //$NON-NLS-1$
282            "THAI", //$NON-NLS-1$
283            "LAO", //$NON-NLS-1$
284            "TIBETAN", //$NON-NLS-1$
285            "MYANMAR", //$NON-NLS-1$
286            "ETHIOPIC", //$NON-NLS-1$
287            "KHMER", //$NON-NLS-1$
288            "MONGOLIAN" //$NON-NLS-1$
289    };
290
291    /*
292     * Strong characters flags array is to determine if the
293     * unicode bidirectional category of the character is strong,
294     * according to Unicode specification. If the bit with index equals to
295     * character's unicode value is 1 - the character is strong.
296     * This array was generated using UnicodeData.txt file from
297     * http://www.unicode.org/Public/UNIDATA/
298     */
299
300    /** The Constant STRONG_TEXT_FLAGS. */
301    private static final int[] STRONG_TEXT_FLAGS = { 0, 0, 134217726, 134217726,
302            0, 69207040, -8388609, -8388609, -1, -1, -1, -1, -1, -1, -1, -1,
303            -1, -1, -65533, -1, -1, -100663297, 196611, 16415, 0, 0, 0,
304            67108864, -10432, -5, -32769, -4194305, -1, -1, -1, -1, -1017, -1,
305            -32769, 67108863, 65535, -131072, -25165825, -2, 767, 1073741824,
306            -65463, 2033663, -939513841, 134217726, 2047, -73728, -1, -1,
307            541065215, -67059616, -180225, 65535, -8192, 16383, -1, 131135, 0,
308            0, 0, 0, 0, 0, 0, 0, 0, 0, -8, -469762049, -16703999, 537001971,
309            -417812, -473563649, -1333765759, 133431235, -423960, -1016201729,
310            1577058305, 1900480, -278552, -470942209, 72193, 65475, -417812,
311            1676541439, -1333782143, 262083, -700594200, -1006647528, 8396230,
312            524224, -139282, 66059775, 30, 65475, -139284, -470811137,
313            1080036831, 65475, -139284, -1006633473, 8396225, 65475, -58720276,
314            805044223, -16547713, 1835008, -2, 917503, 268402815, 0, -17816170,
315            537783470, 872349791, 0, -50331649, -1050673153, -257, -2147481601,
316            3872, -1073741824, 237503, 0, -1, 16914171, 16777215, 0, 0, -1,
317            -65473, 536870911, -1, -1, -2080374785, -1, -1, -249, -1, 67108863,
318            -1, -1, 1031749119, -1, -49665, 2134769663, -8388803, -1,
319            -12713985, -1, 134217727, 536870911, 65535, -1, -1, 2097151, -2,
320            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
321            -1, 8388607, 134217726, -1, -1, 131071, 253951, 6553599, 262143,
322            122879, -1, -1065353217, 401605055, 1023, 67043328, -1, -1,
323            16777215, -1, 511, 0, 0, 536870911, 33226872, -64, 2047999, -1,
324            -64513, 67044351, 0, -830472193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
325            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0,
326            -1, -1, -1, -1, 268435455, -1, -1, 67108863, 1061158911, -1,
327            -1426112705, 1073741823, -1, 1608515583, 265232348, 534519807,
328            49152, 27648, 0, -2147352576, 2031616, 0, 0, 0, 1043332228,
329            -201605808, 992, -1, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
330            -4194304, -1, 134217727, 2097152, 0, 0, 0, 0, 0, 0, 0, -268435456,
331            -1, -1, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4096, 0, 0, 0,
332            0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0,
333            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1,
334            -32769, 2147483647, 0, -1, -1, -1, 31, -1, -65473, -1, 32831,
335            8388607, 2139062143, 2139062143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
336            0, 0, 0, 0, 0, 0, 224, 524157950, -2, -1, -528482305, -2, -1,
337            -134217729, -32, -122881, -1, -1, -32769, 16777215, 0, -65536,
338            536870911, -1, 15, -1879048193, -1, 131071, -61441, 2147483647, -1,
339            -1, -1, -125829121, -1, -1, 1073741823, 2147483647, 1, 0, 0, 0, 0,
340            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
341            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
342            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
343            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
344            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
345            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
346            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
347            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
348            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
349            0, 0, 2097152, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
350            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
351            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
352            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
353            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
354            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
355            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
356            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
358            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
359            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
360            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
361            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
362            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
363            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
364            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
365            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
366            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
367            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
368            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
369            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
370            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
371            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
372            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
373            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
374            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
375            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
376            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
377            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
378            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
379            134217728, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
380            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
381            -1, -1, -1, -1, -1, -1, -1, 8191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
382            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2117, 159, 0, 0,
383            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
384            0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
385            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
386            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
387            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
388            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
389            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
390            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
391            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
392            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
393            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
394            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
395            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
396            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
397            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
398            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
399            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
400            0, 0, 0, 8, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
401            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2147483648, 1, 0, 0, -2147483648,
402            1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
403            0, 0, 0, 0, 0, 0, 0, 0, 0, -2147483648, 1, 0, 0, 0, 0, 0, 0, 0, 0,
404            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
405            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
406            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
407            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
408            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
409            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
410            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
411            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
412            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2147483648, -1, -1, -1,
413            -1, -1, -1, -1, -1, -1, -49153, -1, -63489, -1, -1, 67108863, 0,
414            -1594359681, 1602223615, -37, -1, -1, 262143, -524288, -1, -1, -1,
415            -1, -1, -1, -1, -1, -1, -1, 1073741823, -65536, -1, -196609, -1,
416            255, 536805376, 0, 0, 0, -2162688, -1, -1, -1, 536870911, 0,
417            134217726, 134217726, -64, -1, 2147483647, 486341884, 0
418
419    };
420
421    // index of context range (Serialization support)
422    /** The key. */
423    private int key;
424
425    // flag, true if shaping contextual (Serialization support)
426    /** The mask. */
427    private int mask;
428
429    // ranges to be shaped
430    /** The ranges. */
431    private int fRanges;
432
433    // index of the default context
434    /** The default context index. */
435    private int fDefaultContextIndex;
436
437    // flag if NumericShaper shapes contextually
438    /** The contextual. */
439    private boolean fContextual;
440
441    // uses for non-context dependent case only
442    /** The single range index. */
443    private int fSingleRangeIndex;
444
445    /**
446     * Creates NumericShaper with specified parameters.
447     *
448     * @param ranges specified ranges to be shaped
449     * @param defaultContext default context range
450     * @param isContextual specifies if the instance is contextual
451     */
452    private NumericShaper(int ranges, int defaultContext, boolean isContextual){
453        this.fRanges = ranges;
454        this.fDefaultContextIndex = getIndexFromRange(defaultContext);
455        this.fContextual = isContextual;
456
457        if (!fContextual){
458            fSingleRangeIndex = getIndexFromRange(ranges);
459        }
460    }
461
462    /**
463     * Returns script index of the specified context range.
464     *
465     * @param range specified range
466     *
467     * @return one of the script indices according to the specified range.
468     */
469    private int getIndexFromRange(int range){
470        if (range == 0){
471            // BEGIN android-changed
472            throwRange(range);
473            // END android-changed
474        }
475
476        int index = 0;
477        while (index < MAX_INDEX){
478            if (range == (1 << index)){
479                return index;
480            }
481            index++;
482        }
483
484        // BEGIN android-changed
485        throwRange(range);
486        return -1; // Never executed; quiets the compiler.
487        // END android-changed
488    }
489
490    /**
491     * Returns range corresponding to the specified script index.
492     *
493     * @param index specified script index
494     *
495     * @return one of the range constants according to the specified script index.
496     */
497    private int getRangeFromIndex(int index){
498        if (index < 0 || index >= MAX_INDEX){
499            // BEGIN android-changed
500            throwRange(index);
501            // END android-changed
502        }
503
504        return 1 << index;
505    }
506
507    // BEGIN android-added
508    /**
509     * Throws a standard "out of range" exception.
510     *
511     * @param value the bogus value
512     */
513    private static void throwRange(int value) {
514        throw new IllegalArgumentException(
515                "Illegal range argument value: " + value);
516    }
517    // END android-added
518
519    /**
520     * Returns a hash code of this NumericShaper.
521     *
522     * @return a hash code of this NumericShaper.
523     */
524    @Override
525    public int hashCode() {
526        HashCode hash = new HashCode();
527
528        hash.append(fRanges);
529        hash.append(fDefaultContextIndex);
530        hash.append(fContextual);
531
532        return hash.hashCode();
533
534    }
535
536    /**
537     * Compares this NumericShaper object with the specified Object.
538     *
539     * @param obj the Object to be compared.
540     *
541     * @return true, if this NumericShaper object is equal to
542     * the specified Object, false otherwise.
543     */
544    @Override
545    public boolean equals(Object obj) {
546        if (obj == null) {
547            return false;
548        }
549
550        if (obj == this) {
551            return true;
552        }
553
554        try {
555            NumericShaper ns = (NumericShaper)obj;
556            return (fRanges == ns.fRanges &&
557                    fDefaultContextIndex == ns.fDefaultContextIndex &&
558                    fContextual == ns.fContextual);
559        } catch (ClassCastException e){
560        }
561
562        return false;
563    }
564
565    /**
566     * Returns a string representation of this NumericShaper.
567     *
568     * @return the string representation of this NumericShaper.
569     */
570    @Override
571    public String toString() {
572        /* !! There is no description in the documentation what this method must
573         * return. Thus format of toString method is based on 1.5 release
574         * behavior and can be obtained using next test sample:
575         *
576         * // Simple shapers toString format
577         * System.out.println(NumericShaper.getShaper(NumericShaper.EASTERN_ARABIC));
578         *
579         * // Context shapers with default context toString format
580         * System.out.println(NumericShaper.getContextualShaper(
581         *      NumericShaper.ARABIC | NumericShaper.TAMIL));
582         *
583         * // Context shapers with context
584         * System.out.println(NumericShaper.getContextualShaper(
585         *      NumericShaper.ARABIC | NumericShaper.TAMIL,
586         *      NumericShaper.EASTERN_ARABIC));
587         */
588        StringBuffer sb = new StringBuffer(super.toString());
589
590        sb.append("[contextual:"); //$NON-NLS-1$
591        sb.append(fContextual);
592
593        if (fContextual){
594            sb.append(", context:"); //$NON-NLS-1$
595            sb.append(contexts[fDefaultContextIndex]);
596        }
597
598        sb.append(", range(s): "); //$NON-NLS-1$
599        if (fContextual) {
600            int index = 0;
601            boolean isFirst = true;
602            while (index < MAX_INDEX){
603                if ((fRanges & (1 << index)) != 0){
604                    if (isFirst){
605                        isFirst = false;
606                    } else {
607                        sb.append(", "); //$NON-NLS-1$
608                    }
609                    sb.append(contexts[index]);
610                }
611                index++;
612            }
613        } else {
614            sb.append(contexts[fSingleRangeIndex]);
615        }
616        sb.append("]"); //$NON-NLS-1$
617
618        return sb.toString();
619    }
620
621    /**
622     * Gets the NumericShaper for the specified unicode ranges
623     * and default unicode range. The defaultContext parameter
624     * is used as the starting context (which indicates the
625     * language/script being used). The OR logical operation
626     * should be used for multiple ranges:
627     * NumericShaper.DEVANAGARI | NumericShaper.BENGALI.
628     * The NumericShaper returned by this method is contextual
629     * in that it supports multiple character ranges, depending
630     * on the context.
631     *
632     * @param ranges the unicode ranges.
633     * @param defaultContext the default, starting context.
634     *
635     * @return the NumericShaper for the specified ranges.
636     */
637    public static NumericShaper getContextualShaper(int ranges,
638            int defaultContext) {
639        ranges &= ALL_RANGES;
640        defaultContext &= ALL_RANGES;
641        return new NumericShaper(ranges, defaultContext, true);
642    }
643
644    /**
645     * Gets the NumericShaper for the specified unicode ranges.
646     * The OR logical operation should be used for multiple ranges:
647     * NumericShaper.DEVANAGARI | NumericShaper.BENGALI.
648     * The NumericShaper returned by this method is contextual
649     * in that it supports multiple character ranges, depending
650     * on the context.
651     *
652     * @param ranges the unicode ranges.
653     *
654     * @return the NumericShaper for the specified ranges.
655     */
656    public static NumericShaper getContextualShaper(int ranges) {
657        ranges &= ALL_RANGES;
658        return new NumericShaper(ranges, EUROPEAN, true);
659    }
660
661    /**
662     * Gets the masks for all of the ranges supported by this NumericShaper,
663     * packed into an int value using the logical OR logical operation
664     * for multiple ranges:
665     * NumericShaper.DEVANAGARI | NumericShaper.BENGALI.
666     *
667     * @return all ranges of this NumericShaper.
668     */
669    public int getRanges() {
670        return fRanges;
671    }
672
673    /**
674     * Gets a NumericShaper for the specified unicode range.
675     * The NumericShaper supports only a single range and
676     * hence is not contextual.
677     *
678     * @param singleRange the specified unicode single range.
679     *
680     * @return the NumericShaper for the specified unicode range.
681     */
682    public static NumericShaper getShaper(int singleRange) {
683        singleRange &= ALL_RANGES;
684        return new NumericShaper(singleRange, EUROPEAN, false);
685    }
686
687    /**
688     * Checks if this NumericShaper is contextual (supporting
689     * multiple script ranges) or not.
690     *
691     * @return true, if this NumericShaper is contextual, false otherwise.
692     */
693    public boolean isContextual() {
694        return fContextual;
695    }
696
697    /**
698     * Transforms the encoding of the text, starting from the character
699     * at index start and transforming count characters,
700     * using the specified context.
701     *
702     * @param text the text to be shaped.
703     * @param start the start offset of the text.
704     * @param count the number of characters to be shaped.
705     * @param context the context to be used for shaping.
706     */
707    public void shape(char[] text, int start, int count, int context) {
708        if (isContextual()){
709            contextualShape(text, start, count, getIndexFromRange(context));
710        } else {
711            nonContextualShape(text, start, count);
712        }
713    }
714
715    /**
716     * Transforms the encoding of the text, starting from the character
717     * at index start and transforming count characters.
718     *
719     * @param text the text to be shaped.
720     * @param start the start offset of the text.
721     * @param count the number of characters to be shaped.
722     */
723    public void shape(char[] text, int start, int count) {
724        if (isContextual()){
725            contextualShape(text, start, count, fDefaultContextIndex);
726        } else {
727            nonContextualShape(text, start, count);
728        }
729    }
730
731    /**
732     * Converts count of digits of the given array of characters from the start
733     * index using specified context. This method is applied for the contextual
734     * shaping, if the shaper instance is not contextual use nonContextualShape
735     * method.
736     *
737     * @param text an array of chars
738     * @param start index of the first character to convert
739     * @param count a number of characters to convert
740     * @param contextIndex index of the script index to use in shaper
741     */
742    private void contextualShape(char[] text, int start, int count,
743            int contextIndex){
744        char maxDigit = (char)0x0039;
745        char minDigit = (char)0x0030;
746
747        int currIndex;
748        if (((1 << contextIndex) & fRanges) == 0 ){
749            currIndex = INDEX_EUROPEAN;
750        } else {
751            currIndex = contextIndex;
752        }
753
754        for (int ind = start; ind < start + count; ind++){
755            if (minDigit <= text[ind] && text[ind] <= maxDigit){
756                if (currIndex != INDEX_ETHIOPIC || text[ind] != '0'){
757                    text[ind] = (char)(digitsLowRanges[currIndex] + text[ind]);
758                }
759            } else {
760                if(isCharStrong(text[ind])){
761                    int index = getCharIndex(text[ind]);
762                    if (currIndex != index){
763                        if (((1 << index) & fRanges) != 0){
764                            currIndex = index;
765                        } else {
766                            currIndex = INDEX_EUROPEAN;
767                        }
768                    }
769                }
770            }
771        }
772
773    }
774
775    /**
776     * Converts count of digits of the given array of characters from the start
777     * index. Method is applied for non-contextual shaper.
778     *
779     * @param text an array of chars
780     * @param start index of the first character to convert
781     * @param count a number of characters to convert
782     */
783    private void nonContextualShape(char[] text, int start, int count){
784        char maxDigit = (char)0x0039;
785        char minDigit = (char)((fRanges == ETHIOPIC) ? 0x0031 : 0x0030);
786        for (int ind = start; ind < start + count; ind++){
787            if (minDigit <= text[ind] && text[ind] <= maxDigit){
788                    text[ind] = (char)(digitsLowRanges[fSingleRangeIndex] + text[ind]);
789            }
790        }
791
792    }
793
794    /**
795     * Returns the index of the script of the specified char.
796     *
797     * @param ch specified unicode character
798     *
799     * @return script index corresponding to the given char
800     */
801    private int getCharIndex(char ch){
802        int index = INDEX_EUROPEAN;
803        for (int i=0; i < MAX_INDEX; i++){
804            int j = i * 2;
805            if (scriptsRanges[j] <= ch && ch <= scriptsRanges[j+1]){
806                return i;
807            }
808        }
809
810        return index;
811    }
812
813    /**
814     * Returns true if the bidirectional category of the character
815     * is strong.
816     *
817     * @param chr the chr
818     *
819     * @return true, if the character is strong, false otherwise
820     */
821    private boolean isCharStrong(int chr) {
822        return (STRONG_TEXT_FLAGS[chr >> 5] & (1 << (chr % 32))) != 0;
823    }
824
825    /**
826     * Updates all private serialized fields for object to be correctly serialized
827     * according to the serialized form of this class mentioned in the
828     * documentation.
829     */
830     private void updateRangesFields(){
831        fRanges = (mask & ~(1 << 31));
832        fContextual = ((mask &(1 << 31)) != 0);
833        if (fContextual){
834            fRanges = (mask & ~(1 << 31));
835            fDefaultContextIndex = key;
836        } else {
837            fRanges = mask;
838            fSingleRangeIndex = key;
839        }
840    }
841
842    /**
843     * Updates private fields for object after deserialization
844     * according to the serialized form of this class mentioned in the
845     * documentation.
846     */
847    private void updateKeyMaskFields(){
848        mask = fRanges;
849        if (fContextual){
850            mask |= (1 << 31);
851            key = fDefaultContextIndex;
852        } else{
853            key = fSingleRangeIndex;
854        }
855    }
856
857    /**
858     * Write object.
859     *
860     * @param out the out
861     *
862     * @throws IOException Signals that an I/O exception has occurred.
863     */
864    private void writeObject(java.io.ObjectOutputStream out)
865                                throws IOException{
866        updateKeyMaskFields();
867        out.defaultWriteObject();
868    }
869
870    /**
871     * Read object.
872     *
873     * @param in the in
874     *
875     * @throws IOException Signals that an I/O exception has occurred.
876     * @throws ClassNotFoundException the class not found exception
877     */
878    private void readObject(java.io.ObjectInputStream in)
879                                throws IOException, ClassNotFoundException{
880        in.defaultReadObject();
881        updateRangesFields();
882    }
883
884}
885
886