001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Ilya S. Okomin
019: * @version $Revision$
020: */package java.awt.font;
021:
022: import java.io.IOException;
023: import java.io.Serializable;
024:
025: import org.apache.harmony.awt.internal.nls.Messages;
026: import org.apache.harmony.misc.HashCode;
027:
028: public final class NumericShaper implements Serializable {
029: private static final long serialVersionUID = -8022764705923730308L;
030:
031: public static final int EUROPEAN = 1;
032:
033: public static final int ARABIC = 2;
034:
035: public static final int EASTERN_ARABIC = 4;
036:
037: public static final int DEVANAGARI = 8;
038:
039: public static final int BENGALI = 16;
040:
041: public static final int GURMUKHI = 32;
042:
043: public static final int GUJARATI = 64;
044:
045: public static final int ORIYA = 128;
046:
047: public static final int TAMIL = 256;
048:
049: public static final int TELUGU = 512;
050:
051: public static final int KANNADA = 1024;
052:
053: public static final int MALAYALAM = 2048;
054:
055: public static final int THAI = 4096;
056:
057: public static final int LAO = 8192;
058:
059: public static final int TIBETAN = 16384;
060:
061: public static final int MYANMAR = 32768;
062:
063: public static final int ETHIOPIC = 65536;
064:
065: public static final int KHMER = 131072;
066:
067: public static final int MONGOLIAN = 262144;
068:
069: public static final int ALL_RANGES = 524287;
070:
071: /* Further one can find the set of script indices.
072: * Index is the power you need the 2 to raise to to get corresponding
073: * range constant value. Also script ranges, context names and digits low
074: * ranges are indexed with these indices.
075: */
076:
077: // Index of the EUROPEAN range
078: private static final int INDEX_EUROPEAN = 0;
079:
080: // Index of the ARABIC range
081: private static final int INDEX_ARABIC = 1;
082:
083: // Index of the EASTERN_ARABIC range
084: private static final int INDEX_EASTERN_ARABIC = 2;
085:
086: // Index of the DEVANAGARI range
087: private static final int INDEX_DEVANAGARI = 3;
088:
089: // Index of the BENGALI range
090: private static final int INDEX_BENGALI = 4;
091:
092: // Index of the GURMUKHI range
093: private static final int INDEX_GURMUKHI = 5;
094:
095: // Index of the GUJARTI range
096: private static final int INDEX_GUJARATI = 6;
097:
098: // Index of the ORIYA range
099: private static final int INDEX_ORIYA = 7;
100:
101: // Index of the TAMIL range
102: private static final int INDEX_TAMIL = 8;
103:
104: // Index of the TELUGU range
105: private static final int INDEX_TELUGU = 9;
106:
107: // Index of the KANNADA range
108: private static final int INDEX_KANNADA = 10;
109:
110: // Index of the MALAYALAM range
111: private static final int INDEX_MALAYALAM = 11;
112:
113: // Index of the THAI range
114: private static final int INDEX_THAI = 12;
115:
116: // Index of the LAO range
117: private static final int INDEX_LAO = 13;
118:
119: // Index of the TIBETAN range
120: private static final int INDEX_TIBETAN = 14;
121:
122: // Index of the MYANMAR range
123: private static final int INDEX_MYANMAR = 15;
124:
125: // Index of the ETHIOPIC range
126: private static final int INDEX_ETHIOPIC = 16;
127:
128: // Index of the KHMER range
129: private static final int INDEX_KHMER = 17;
130:
131: // Index of the MONGOLIAN range
132: private static final int INDEX_MONGOLIAN = 18;
133:
134: // Maximum index that range can't exceed
135: private static final int MAX_INDEX = 19;
136:
137: /*
138: * Scripts ranges array. Array represents ranges as pairs of
139: * lowest and highest range bounds.
140: * Data is taken from the UnicodeData.txt file from
141: * http://www.unicode.org/Public/UNIDATA/
142: */
143: private final int[] scriptsRanges = { 0x0000, 0x024F, // EUROPEAN (basic latin + latin-1 + extended)
144: 0x0600, 0x06FF, // ARABIC
145: 0x0600, 0x06FF, // EASTERN_ARABIC (XXX: diff with ARABIC ? )
146: 0x0900, 0x097F, // DEVANAGARI
147: 0x0980, 0x09FF, // BENGALI
148: 0x0A00, 0x0A7F, // GURMUKHI
149: 0x0A80, 0x0AFF, // GUJARATI
150: 0x0B00, 0x0B7F, // ORIYA
151: 0x0B80, 0x0BFF, // TAMIL
152: 0x0C00, 0x0C7F, // TELUGU
153: 0x0C80, 0x0CFF, // KANNADA
154: 0x0D00, 0x0D7F, // MALAYALAM
155: 0x0E00, 0x0E7F, // THAI
156: 0x0E80, 0x0EFF, // LAO
157: 0x0F00, 0x0FFF, // TIBETAN
158: 0x1000, 0x109F, // MYANMAR
159: 0x1200, 0x137F, // ETHIOPIC
160: 0x1780, 0x17FF, // KHMER
161: 0x1800, 0x18AF // MONGOLIAN
162: };
163:
164: /*
165: * Digit low ranges values decreased by 0x0030. Each low range
166: * value decreased by 0x0030 for easy obtaing unicode value of the
167: * context dependent digit. European digits starts from 0x0030 hence
168: * context dependent unicode digit value equals to
169: * digitsLowRanges[script index] + european digit char unicode value.
170: * !! the only exception is ETHIOPIC script where there is no '0' digit
171: * Data is taken from the UnicodeData.txt file from
172: * http://www.unicode.org/Public/UNIDATA/
173: */
174: private final int[] digitsLowRanges = { 0x0000, // EUROPEAN
175: 0x0630, // ARABIC
176: 0x0630, // EASTERN_ARABIC
177: 0x0936, // DEVANAGARI
178: 0x09B6, // BENGALI
179: 0x0A36, // GURMUKHI
180: 0x0AB6, // GUJARATI
181: 0x0B36, // ORIYA
182: 0x0BB6, // TAMIL
183: 0x0C36, // TELUGU
184: 0x0CB6, // KANNADA
185: 0x0D36, // MALAYALAM
186: 0x0E20, // THAI
187: 0x0EA0, // LAO
188: 0x0EF0, // TIBETAN
189: 0x1010, // MYANMAR
190: 0x1338, // ETHIOPIC - (low range-1) no ETHIOPIC '0' DIGIT!
191: 0x17B0, // KHMER
192: 0x17E0 // MONGOLIAN
193: };
194:
195: // Set of context names used in toString method
196: private final String[] contexts = { "EUROPEAN", //$NON-NLS-1$
197: "ARABIC", //$NON-NLS-1$
198: "EASTERN_ARABIC", //$NON-NLS-1$
199: "DEVANAGARI", //$NON-NLS-1$
200: "BENGALI", //$NON-NLS-1$
201: "GURMUKHI", //$NON-NLS-1$
202: "GUJARATI", //$NON-NLS-1$
203: "ORIYA", //$NON-NLS-1$
204: "TAMIL", //$NON-NLS-1$
205: "TELUGU", //$NON-NLS-1$
206: "KANNADA", //$NON-NLS-1$
207: "MALAYALAM", //$NON-NLS-1$
208: "THAI", //$NON-NLS-1$
209: "LAO", //$NON-NLS-1$
210: "TIBETAN", //$NON-NLS-1$
211: "MYANMAR", //$NON-NLS-1$
212: "ETHIOPIC", //$NON-NLS-1$
213: "KHMER", //$NON-NLS-1$
214: "MONGOLIAN" //$NON-NLS-1$
215: };
216:
217: /*
218: * Strong characters flags array is to determine if the
219: * unicode bidirectional category of the character is strong,
220: * according to Unicode specification. If the bit with index equals to
221: * character's unicode value is 1 - the character is strong.
222: * This array was generated using UnicodeData.txt file from
223: * http://www.unicode.org/Public/UNIDATA/
224: */
225:
226: private static final int[] STRONG_TEXT_FLAGS = { 0, 0, 134217726,
227: 134217726, 0, 69207040, -8388609, -8388609, -1, -1, -1, -1,
228: -1, -1, -1, -1, -1, -1, -65533, -1, -1, -100663297, 196611,
229: 16415, 0, 0, 0, 67108864, -10432, -5, -32769, -4194305, -1,
230: -1, -1, -1, -1017, -1, -32769, 67108863, 65535, -131072,
231: -25165825, -2, 767, 1073741824, -65463, 2033663,
232: -939513841, 134217726, 2047, -73728, -1, -1, 541065215,
233: -67059616, -180225, 65535, -8192, 16383, -1, 131135, 0, 0,
234: 0, 0, 0, 0, 0, 0, 0, 0, -8, -469762049, -16703999,
235: 537001971, -417812, -473563649, -1333765759, 133431235,
236: -423960, -1016201729, 1577058305, 1900480, -278552,
237: -470942209, 72193, 65475, -417812, 1676541439, -1333782143,
238: 262083, -700594200, -1006647528, 8396230, 524224, -139282,
239: 66059775, 30, 65475, -139284, -470811137, 1080036831,
240: 65475, -139284, -1006633473, 8396225, 65475, -58720276,
241: 805044223, -16547713, 1835008, -2, 917503, 268402815, 0,
242: -17816170, 537783470, 872349791, 0, -50331649, -1050673153,
243: -257, -2147481601, 3872, -1073741824, 237503, 0, -1,
244: 16914171, 16777215, 0, 0, -1, -65473, 536870911, -1, -1,
245: -2080374785, -1, -1, -249, -1, 67108863, -1, -1,
246: 1031749119, -1, -49665, 2134769663, -8388803, -1,
247: -12713985, -1, 134217727, 536870911, 65535, -1, -1,
248: 2097151, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
249: -1, -1, -1, -1, -1, -1, -1, 8388607, 134217726, -1, -1,
250: 131071, 253951, 6553599, 262143, 122879, -1, -1065353217,
251: 401605055, 1023, 67043328, -1, -1, 16777215, -1, 511, 0, 0,
252: 536870911, 33226872, -64, 2047999, -1, -64513, 67044351, 0,
253: -830472193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
254: 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0, -1, -1,
255: -1, -1, 268435455, -1, -1, 67108863, 1061158911, -1,
256: -1426112705, 1073741823, -1, 1608515583, 265232348,
257: 534519807, 49152, 27648, 0, -2147352576, 2031616, 0, 0, 0,
258: 1043332228, -201605808, 992, -1, 15, 0, 0, 0, 0, 0, 0, 0,
259: 0, 0, 0, 0, 0, -4194304, -1, 134217727, 2097152, 0, 0, 0,
260: 0, 0, 0, 0, -268435456, -1, -1, 1023, 0, 0, 0, 0, 0, 0, 0,
261: 0, 0, 0, 0, 0, 0, 4096, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1,
262: -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
263: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -32769,
264: 2147483647, 0, -1, -1, -1, 31, -1, -65473, -1, 32831,
265: 8388607, 2139062143, 2139062143, 0, 0, 0, 0, 0, 0, 0, 0, 0,
266: 0, 0, 0, 0, 0, 0, 0, 0, 224, 524157950, -2, -1, -528482305,
267: -2, -1, -134217729, -32, -122881, -1, -1, -32769, 16777215,
268: 0, -65536, 536870911, -1, 15, -1879048193, -1, 131071,
269: -61441, 2147483647, -1, -1, -1, -125829121, -1, -1,
270: 1073741823, 2147483647, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
271: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
272: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
273: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
274: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
275: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
276: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
277: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
278: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
279: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
280: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2097152, 0, 0, 1, 0,
281: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
282: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
283: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
284: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
285: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
286: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
287: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
288: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
289: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
290: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
291: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
292: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
293: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
294: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
295: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
296: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
297: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
298: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
299: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
300: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
301: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
302: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
303: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
304: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
305: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
306: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
307: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
308: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
309: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
310: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
311: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
312: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
313: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134217728, 0, 0, -1, -1,
314: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
315: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
316: -1, -1, -1, -1, 8191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
317: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2117, 159, 0,
318: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
319: 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
320: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
321: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
322: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
323: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
324: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
325: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
326: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
327: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
328: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
329: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
330: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
331: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
332: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0,
334: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
335: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
336: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0,
337: 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
338: 0, 0, 0, 0, 0, 0, 0, 0, -2147483648, 1, 0, 0, -2147483648,
339: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
340: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2147483648, 1, 0, 0, 0,
341: 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,
343: 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,
345: 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,
347: 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,
349: 0, 0, 0, 0, 0, 0, 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, -2147483648,
351: -1, -1, -1, -1, -1, -1, -1, -1, -1, -49153, -1, -63489, -1,
352: -1, 67108863, 0, -1594359681, 1602223615, -37, -1, -1,
353: 262143, -524288, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
354: 1073741823, -65536, -1, -196609, -1, 255, 536805376, 0, 0,
355: 0, -2162688, -1, -1, -1, 536870911, 0, 134217726,
356: 134217726, -64, -1, 2147483647, 486341884, 0
357:
358: };
359:
360: // index of context range (Serialization support)
361: private int key;
362:
363: // flag, true if shaping contextual (Serialization support)
364: private int mask;
365:
366: // ranges to be shaped
367: private int fRanges;
368:
369: // index of the default context
370: private int fDefaultContextIndex;
371:
372: // flag if NumericShaper shapes contextually
373: private boolean fContextual;
374:
375: // uses for non-context dependent case only
376: private int fSingleRangeIndex;
377:
378: /**
379: * Creates NumericShaper with specified parameters.
380: *
381: * @param ranges specified ranges to be shaped
382: * @param defaultContext default context range
383: * @param isContextual specifies if the instance is contextual
384: */
385: private NumericShaper(int ranges, int defaultContext,
386: boolean isContextual) {
387: this .fRanges = ranges;
388: this .fDefaultContextIndex = getIndexFromRange(defaultContext);
389: this .fContextual = isContextual;
390:
391: if (!fContextual) {
392: fSingleRangeIndex = getIndexFromRange(ranges);
393: }
394: }
395:
396: /**
397: * Returns script index of the specified context range.
398: *
399: * @param range specified range
400: * @return one of the script indices according to the specified range.
401: */
402: private int getIndexFromRange(int range) {
403: if (range == 0) {
404: // awt.199=Illegal range argument value: {0}
405: throw new IllegalArgumentException(Messages.getString(
406: "awt.199", //$NON-NLS-1$
407: range));
408: }
409:
410: int index = 0;
411: while (index < MAX_INDEX) {
412: if (range == (1 << index)) {
413: return index;
414: }
415: index++;
416: }
417:
418: // awt.199=Illegal range argument value: {0}
419: throw new IllegalArgumentException(Messages.getString(
420: "awt.199", //$NON-NLS-1$
421: range));
422:
423: }
424:
425: /**
426: * Returns range corresponding to the specified script index.
427: *
428: * @param index specified script index
429: * @return one of the range constants according to the specified script index.
430: */
431: private int getRangeFromIndex(int index) {
432: if (index < 0 || index >= MAX_INDEX) {
433: // awt.199=Illegal range argument value: {0}
434: throw new IllegalArgumentException(Messages.getString(
435: "awt.199", //$NON-NLS-1$
436: index));
437: }
438:
439: return 1 << index;
440: }
441:
442: @Override
443: public int hashCode() {
444: HashCode hash = new HashCode();
445:
446: hash.append(fRanges);
447: hash.append(fDefaultContextIndex);
448: hash.append(fContextual);
449:
450: return hash.hashCode();
451:
452: }
453:
454: @Override
455: public boolean equals(Object obj) {
456: if (obj == null) {
457: return false;
458: }
459:
460: if (obj == this ) {
461: return true;
462: }
463:
464: try {
465: NumericShaper ns = (NumericShaper) obj;
466: return (fRanges == ns.fRanges
467: && fDefaultContextIndex == ns.fDefaultContextIndex && fContextual == ns.fContextual);
468: } catch (ClassCastException e) {
469: }
470:
471: return false;
472: }
473:
474: @Override
475: public String toString() {
476: /* !! There is no description in the documentation what this method must
477: * return. Thus format of toString method is based on 1.5 release
478: * behavior and can be obtained using next test sample:
479: *
480: * // Simple shapers toString format
481: * System.out.println(NumericShaper.getShaper(NumericShaper.EASTERN_ARABIC));
482: *
483: * // Context shapers with default context toString format
484: * System.out.println(NumericShaper.getContextualShaper(
485: * NumericShaper.ARABIC | NumericShaper.TAMIL));
486: *
487: * // Context shapers with context
488: * System.out.println(NumericShaper.getContextualShaper(
489: * NumericShaper.ARABIC | NumericShaper.TAMIL,
490: * NumericShaper.EASTERN_ARABIC));
491: */
492: StringBuffer sb = new StringBuffer(super .toString());
493:
494: sb.append("[contextual:"); //$NON-NLS-1$
495: sb.append(fContextual);
496:
497: if (fContextual) {
498: sb.append(", context:"); //$NON-NLS-1$
499: sb.append(contexts[fDefaultContextIndex]);
500: }
501:
502: sb.append(", range(s): "); //$NON-NLS-1$
503: if (fContextual) {
504: int index = 0;
505: boolean isFirst = true;
506: while (index < MAX_INDEX) {
507: if ((fRanges & (1 << index)) != 0) {
508: if (isFirst) {
509: isFirst = false;
510: } else {
511: sb.append(", "); //$NON-NLS-1$
512: }
513: sb.append(contexts[index]);
514: }
515: index++;
516: }
517: } else {
518: sb.append(contexts[fSingleRangeIndex]);
519: }
520: sb.append("]"); //$NON-NLS-1$
521:
522: return sb.toString();
523: }
524:
525: public static NumericShaper getContextualShaper(int ranges,
526: int defaultContext) {
527: ranges &= ALL_RANGES;
528: defaultContext &= ALL_RANGES;
529: return new NumericShaper(ranges, defaultContext, true);
530: }
531:
532: public static NumericShaper getContextualShaper(int ranges) {
533: ranges &= ALL_RANGES;
534: return new NumericShaper(ranges, EUROPEAN, true);
535: }
536:
537: public int getRanges() {
538: return fRanges;
539: }
540:
541: public static NumericShaper getShaper(int singleRange) {
542: singleRange &= ALL_RANGES;
543: return new NumericShaper(singleRange, EUROPEAN, false);
544: }
545:
546: public boolean isContextual() {
547: return fContextual;
548: }
549:
550: public void shape(char[] text, int start, int count, int context) {
551: if (isContextual()) {
552: contextualShape(text, start, count,
553: getIndexFromRange(context));
554: } else {
555: nonContextualShape(text, start, count);
556: }
557: }
558:
559: public void shape(char[] text, int start, int count) {
560: if (isContextual()) {
561: contextualShape(text, start, count, fDefaultContextIndex);
562: } else {
563: nonContextualShape(text, start, count);
564: }
565: }
566:
567: /**
568: * Converts count of digits of the given array of characters from the start
569: * index using specified context. This method is applied for the contextual
570: * shaping, if the shaper instance is not contextual use nonContextualShape
571: * method.
572: *
573: * @param text an array of chars
574: * @param start index of the first character to convert
575: * @param count a number of characters to convert
576: * @param contextIndex index of the script index to use in shaper
577: */
578: private void contextualShape(char[] text, int start, int count,
579: int contextIndex) {
580: char maxDigit = (char) 0x0039;
581: char minDigit = (char) 0x0030;
582:
583: int currIndex;
584: if (((1 << contextIndex) & fRanges) == 0) {
585: currIndex = INDEX_EUROPEAN;
586: } else {
587: currIndex = contextIndex;
588: }
589:
590: for (int ind = start; ind < start + count; ind++) {
591: if (minDigit <= text[ind] && text[ind] <= maxDigit) {
592: if (currIndex != INDEX_ETHIOPIC || text[ind] != '0') {
593: text[ind] = (char) (digitsLowRanges[currIndex] + text[ind]);
594: }
595: } else {
596: if (isCharStrong(text[ind])) {
597: int index = getCharIndex(text[ind]);
598: if (currIndex != index) {
599: if (((1 << index) & fRanges) != 0) {
600: currIndex = index;
601: } else {
602: currIndex = INDEX_EUROPEAN;
603: }
604: }
605: }
606: }
607: }
608:
609: }
610:
611: /**
612: * Converts count of digits of the given array of characters from the start
613: * index. Method is applied for non-contextual shaper.
614: *
615: * @param text an array of chars
616: * @param start index of the first character to convert
617: * @param count a number of characters to convert
618: */
619: private void nonContextualShape(char[] text, int start, int count) {
620: char maxDigit = (char) 0x0039;
621: char minDigit = (char) ((fRanges == ETHIOPIC) ? 0x0031 : 0x0030);
622: for (int ind = start; ind < start + count; ind++) {
623: if (minDigit <= text[ind] && text[ind] <= maxDigit) {
624: text[ind] = (char) (digitsLowRanges[fSingleRangeIndex] + text[ind]);
625: }
626: }
627:
628: }
629:
630: /**
631: * Returns the index of the script of the specified char.
632: *
633: * @param ch specified unicode character
634: * @return script index corresponding to the given char
635: */
636: private int getCharIndex(char ch) {
637: int index = INDEX_EUROPEAN;
638: for (int i = 0; i < MAX_INDEX; i++) {
639: int j = i * 2;
640: if (scriptsRanges[j] <= ch && ch <= scriptsRanges[j + 1]) {
641: return i;
642: }
643: }
644:
645: return index;
646: }
647:
648: /**
649: * Returns true if the bidirectional category of the character
650: * is strong.
651: *
652: * @param ch specified unicode character
653: * @return true, if the character is strong, false otherwise
654: */
655: private boolean isCharStrong(int chr) {
656: return (STRONG_TEXT_FLAGS[chr >> 5] & (1 << (chr % 32))) != 0;
657: }
658:
659: /**
660: * Updates all private serialized fields for object to be correctly serialized
661: * according to the serialized form of this class mentioned in the
662: * documentation.
663: */
664: private void updateRangesFields() {
665: fRanges = (mask & ~(1 << 31));
666: fContextual = ((mask & (1 << 31)) != 0);
667: if (fContextual) {
668: fRanges = (mask & ~(1 << 31));
669: fDefaultContextIndex = key;
670: } else {
671: fRanges = mask;
672: fSingleRangeIndex = key;
673: }
674: }
675:
676: /**
677: * Updates private fields for object after deserialization
678: * according to the serialized form of this class mentioned in the
679: * documentation.
680: */
681: private void updateKeyMaskFields() {
682: mask = fRanges;
683: if (fContextual) {
684: mask |= (1 << 31);
685: key = fDefaultContextIndex;
686: } else {
687: key = fSingleRangeIndex;
688: }
689: }
690:
691: private void writeObject(java.io.ObjectOutputStream out)
692: throws IOException {
693: updateKeyMaskFields();
694: out.defaultWriteObject();
695: }
696:
697: private void readObject(java.io.ObjectInputStream in)
698: throws IOException, ClassNotFoundException {
699: in.defaultReadObject();
700: updateRangesFields();
701: }
702:
703: }
|