]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/text/ArabicShaping.java
go
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / text / ArabicShaping.java
1 /*\r
2 *******************************************************************************\r
3 *   Copyright (C) 2001-2009, International Business Machines\r
4 *   Corporation and others.  All Rights Reserved.\r
5 *******************************************************************************\r
6 */\r
7 \r
8 package com.ibm.icu.text;\r
9 \r
10 import java.io.IOException;\r
11 import java.util.MissingResourceException;\r
12 \r
13 import com.ibm.icu.impl.UBiDiProps;\r
14 \r
15 import com.ibm.icu.lang.*;\r
16 \r
17 /**\r
18  * Shape Arabic text on a character basis.\r
19  *\r
20  * <p>ArabicShaping performs basic operations for "shaping" Arabic text. It is most\r
21  * useful for use with legacy data formats and legacy display technology\r
22  * (simple terminals). All operations are performed on Unicode characters.</p>\r
23  *\r
24  * <p>Text-based shaping means that some character code points in the text are\r
25  * replaced by others depending on the context. It transforms one kind of text\r
26  * into another. In comparison, modern displays for Arabic text select\r
27  * appropriate, context-dependent font glyphs for each text element, which means\r
28  * that they transform text into a glyph vector.</p>\r
29  *\r
30  * <p>Text transformations are necessary when modern display technology is not\r
31  * available or when text needs to be transformed to or from legacy formats that\r
32  * use "shaped" characters. Since the Arabic script is cursive, connecting\r
33  * adjacent letters to each other, computers select images for each letter based\r
34  * on the surrounding letters. This usually results in four images per Arabic\r
35  * letter: initial, middle, final, and isolated forms. In Unicode, on the other\r
36  * hand, letters are normally stored abstract, and a display system is expected\r
37  * to select the necessary glyphs. (This makes searching and other text\r
38  * processing easier because the same letter has only one code.) It is possible\r
39  * to mimic this with text transformations because there are characters in\r
40  * Unicode that are rendered as letters with a specific shape\r
41  * (or cursive connectivity). They were included for interoperability with\r
42  * legacy systems and codepages, and for unsophisticated display systems.</p>\r
43  *\r
44  * <p>A second kind of text transformations is supported for Arabic digits:\r
45  * For compatibility with legacy codepages that only include European digits,\r
46  * it is possible to replace one set of digits by another, changing the\r
47  * character code points. These operations can be performed for either\r
48  * Arabic-Indic Digits (U+0660...U+0669) or Eastern (Extended) Arabic-Indic\r
49  * digits (U+06f0...U+06f9).</p>\r
50  *\r
51  * <p>Some replacements may result in more or fewer characters (code points).\r
52  * By default, this means that the destination buffer may receive text with a\r
53  * length different from the source length. Some legacy systems rely on the\r
54  * length of the text to be constant. They expect extra spaces to be added\r
55  * or consumed either next to the affected character or at the end of the\r
56  * text.</p>\r
57  * @stable ICU 2.0\r
58  */\r
59 public final class ArabicShaping {\r
60     private final int options;\r
61     private boolean isLogical; // convenience\r
62     private boolean spacesRelativeToTextBeginEnd;\r
63     private char tailChar;\r
64 \r
65     /**\r
66      * Convert a range of text in the source array, putting the result \r
67      * into a range of text in the destination array, and return the number\r
68      * of characters written.\r
69      *\r
70      * @param source An array containing the input text\r
71      * @param sourceStart The start of the range of text to convert\r
72      * @param sourceLength The length of the range of text to convert\r
73      * @param dest The destination array that will receive the result.\r
74      *   It may be <code>NULL</code> only if  <code>destSize</code> is 0.  \r
75      * @param destStart The start of the range of the destination buffer to use.\r
76      * @param destSize The size (capacity) of the destination buffer.\r
77      *   If <code>destSize</code> is 0, then no output is produced,\r
78      *   but the necessary buffer size is returned ("preflighting").  This\r
79      *   does not validate the text against the options, for example, \r
80      *   if letters are being unshaped, and spaces are being consumed\r
81      *   following lamalef, this will not detect a lamalef without a \r
82      *   corresponding space.  An error will be thrown when the actual\r
83      *   conversion is attempted.\r
84      * @return The number of chars written to the destination buffer.\r
85      *   If an error occurs, then no output was written, or it may be\r
86      *   incomplete.\r
87      * @throws ArabicShapingException if the text cannot be converted according to the options.\r
88      * @stable ICU 2.0\r
89      */\r
90     public int shape(char[] source, int sourceStart, int sourceLength,\r
91                      char[] dest, int destStart, int destSize) throws ArabicShapingException {\r
92         if (source == null) {\r
93             throw new IllegalArgumentException("source can not be null");\r
94         }\r
95         if (sourceStart < 0 || sourceLength < 0 || sourceStart + sourceLength > source.length) {\r
96             throw new IllegalArgumentException("bad source start (" + sourceStart +\r
97                                                ") or length (" + sourceLength +\r
98                                                ") for buffer of length " + source.length);\r
99         }\r
100         if (dest == null && destSize != 0) {\r
101             throw new IllegalArgumentException("null dest requires destSize == 0");\r
102         }\r
103         if ((destSize != 0) &&\r
104             (destStart < 0 || destSize < 0 || destStart + destSize > dest.length)) {\r
105             throw new IllegalArgumentException("bad dest start (" + destStart + \r
106                                                ") or size (" + destSize + \r
107                                                ") for buffer of length " + dest.length);\r
108         }\r
109         /* Validate input options */\r
110         if ( ((options&TASHKEEL_MASK) > 0) &&\r
111              !(((options & TASHKEEL_MASK)==TASHKEEL_BEGIN)  ||\r
112                ((options & TASHKEEL_MASK)==TASHKEEL_END )   ||\r
113                ((options & TASHKEEL_MASK)==TASHKEEL_RESIZE )||\r
114                ((options & TASHKEEL_MASK)==TASHKEEL_REPLACE_BY_TATWEEL)) ){\r
115             throw new IllegalArgumentException("Wrong Tashkeel argument");\r
116         }\r
117 \r
118        if(((options&LAMALEF_MASK) > 0)&&\r
119               !(((options & LAMALEF_MASK)==LAMALEF_BEGIN)  ||\r
120                 ((options & LAMALEF_MASK)==LAMALEF_END )   ||\r
121                 ((options & LAMALEF_MASK)==LAMALEF_RESIZE )||\r
122                  ((options & LAMALEF_MASK)==LAMALEF_AUTO)  ||\r
123                  ((options & LAMALEF_MASK)==LAMALEF_NEAR))){\r
124            \r
125            throw new IllegalArgumentException("Wrong Lam Alef argument");\r
126        }\r
127        /* Validate Tashkeel (Tashkeel replacement options should be enabled in shaping mode only)*/\r
128        if(((options&TASHKEEL_MASK) > 0) && (options&LETTERS_MASK) == LETTERS_UNSHAPE) {\r
129             throw new IllegalArgumentException("Tashkeel replacement should not be enabled in deshaping mode ");\r
130        }\r
131        return internalShape(source, sourceStart, sourceLength, dest, destStart, destSize);\r
132     }\r
133 \r
134     /**\r
135      * Convert a range of text in place.  This may only be used if the Length option\r
136      * does not grow or shrink the text.\r
137      *\r
138      * @param source An array containing the input text\r
139      * @param start The start of the range of text to convert\r
140      * @param length The length of the range of text to convert\r
141      * @throws ArabicShapingException if the text cannot be converted according to the options.\r
142      * @stable ICU 2.0\r
143      */\r
144     public void shape(char[] source, int start, int length) throws ArabicShapingException {\r
145         if ((options & LAMALEF_MASK) == LAMALEF_RESIZE) {\r
146             throw new ArabicShapingException("Cannot shape in place with length option resize.");\r
147         }\r
148         shape(source, start, length, source, start, length);\r
149     }\r
150 \r
151     /**\r
152      * Convert a string, returning the new string.\r
153      *\r
154      * @param text the string to convert\r
155      * @return the converted string\r
156      * @throws ArabicShapingException if the string cannot be converted according to the options.\r
157      * @stable ICU 2.0\r
158      */\r
159     public String shape(String text) throws ArabicShapingException {\r
160         char[] src = text.toCharArray();\r
161         char[] dest = src;\r
162         if (((options & LAMALEF_MASK) == LAMALEF_RESIZE) &&\r
163             ((options & LETTERS_MASK) == LETTERS_UNSHAPE)) {\r
164 \r
165             dest = new char[src.length * 2]; // max\r
166         }\r
167         int len = shape(src, 0, src.length, dest, 0, dest.length);\r
168 \r
169         return new String(dest, 0, len);\r
170     }\r
171 \r
172     /**\r
173      * Construct ArabicShaping using the options flags.\r
174      * The flags are as follows:<br>\r
175      * 'LENGTH' flags control whether the text can change size, and if not,\r
176      * how to maintain the size of the text when LamAlef ligatures are \r
177      * formed or broken.<br>\r
178      * 'TEXT_DIRECTION' flags control whether the text is read and written\r
179      * in visual order or in logical order.<br>\r
180      * 'LETTERS_SHAPE' flags control whether conversion is to or from\r
181      * presentation forms.<br>\r
182      * 'DIGITS' flags control whether digits are shaped, and whether from\r
183      * European to Arabic-Indic or vice-versa.<br>\r
184      * 'DIGIT_TYPE' flags control whether standard or extended Arabic-Indic\r
185      * digits are used when performing digit conversion.\r
186      * @stable ICU 2.0\r
187      */\r
188     public ArabicShaping(int options) {\r
189         this.options = options;\r
190         if ((options & DIGITS_MASK) > 0x80) {\r
191             throw new IllegalArgumentException("bad DIGITS options");\r
192         }\r
193         \r
194         isLogical = ( (options & TEXT_DIRECTION_MASK) == TEXT_DIRECTION_LOGICAL );\r
195         /* Validate options */\r
196         spacesRelativeToTextBeginEnd = ( (options & SPACES_RELATIVE_TO_TEXT_MASK) == SPACES_RELATIVE_TO_TEXT_BEGIN_END );\r
197         if ( (options&SHAPE_TAIL_TYPE_MASK) == SHAPE_TAIL_NEW_UNICODE){\r
198             tailChar = NEW_TAIL_CHAR;\r
199         } else {\r
200             tailChar = OLD_TAIL_CHAR;\r
201         }\r
202     }\r
203     \r
204     /* Seen Tail options */ \r
205     /**\r
206      * Memory option: the result must have the same length as the source.\r
207      * Shaping mode: The SEEN family character will expand into two characters using space near \r
208      *               the SEEN family character(i.e. the space after the character).\r
209      *               if there are no spaces found, ArabicShapingException will be thrown\r
210      *\r
211      * De-shaping mode: Any Seen character followed by Tail character will be\r
212      *                  replaced by one cell Seen and a space will replace the Tail.\r
213      * Affects: Seen options\r
214      */\r
215     public static final int SEEN_TWOCELL_NEAR = 0x200000;\r
216 \r
217     /** Bit mask for Seen memory options. */\r
218     public static final int SEEN_MASK = 0x700000;\r
219 \r
220     /* YehHamza options */ \r
221     /**\r
222      * Memory option: the result must have the same length as the source.\r
223      * Shaping mode: The YEHHAMZA character will expand into two characters using space near it \r
224      *              (i.e. the space after the character)\r
225      *               if there are no spaces found, ArabicShapingException will be thrown\r
226      *\r
227      * De-shaping mode: Any Yeh (final or isolated) character followed by Hamza character will be\r
228      *                  replaced by one cell YehHamza and space will replace the Hamza.\r
229      * Affects: YehHamza options\r
230      */\r
231     public static final int YEHHAMZA_TWOCELL_NEAR  = 0x1000000;\r
232 \r
233 \r
234     /** Bit mask for YehHamza memory options. */\r
235     public static final int YEHHAMZA_MASK = 0x3800000;\r
236 \r
237     /* New Tashkeel options */ \r
238     /**\r
239      * Memory option: the result must have the same length as the source.\r
240      * Shaping mode: Tashkeel characters will be replaced by spaces. \r
241      *               Spaces will be placed at beginning of the buffer\r
242      *\r
243      * De-shaping mode: N/A\r
244      * Affects: Tashkeel options\r
245      */\r
246     public static final int TASHKEEL_BEGIN = 0x40000;\r
247 \r
248     /**\r
249      * Memory option: the result must have the same length as the source.\r
250      * Shaping mode: Tashkeel characters will be replaced by spaces. \r
251      *               Spaces will be placed at end of the buffer\r
252      *\r
253      * De-shaping mode: N/A\r
254      * Affects: Tashkeel options\r
255      */\r
256     public static final int TASHKEEL_END = 0x60000;\r
257 \r
258     /**\r
259      * Memory option: allow the result to have a different length than the source.\r
260      * Shaping mode: Tashkeel characters will be removed, buffer length will shrink. \r
261      * De-shaping mode: N/A \r
262      *\r
263      * Affects: Tashkeel options\r
264      */\r
265     public static final int TASHKEEL_RESIZE = 0x80000;\r
266 \r
267     /**\r
268      * Memory option: the result must have the same length as the source.\r
269      * Shaping mode: Tashkeel characters will be replaced by Tatweel if it is connected to adjacent\r
270      *               characters (i.e. shaped on Tatweel) or replaced by space if it is not connected.\r
271      *\r
272      * De-shaping mode: N/A\r
273      * Affects: YehHamza options\r
274      */\r
275     public static final int TASHKEEL_REPLACE_BY_TATWEEL = 0xC0000;\r
276 \r
277     /** Bit mask for Tashkeel replacement with Space or Tatweel memory options. */\r
278     public static final int TASHKEEL_MASK  = 0xE0000;\r
279     \r
280     /* Space location Control options */ \r
281     /**\r
282      * This option effects the meaning of BEGIN and END options. if this option is not used the default\r
283      * for BEGIN and END will be as following: \r
284      * The Default (for both Visual LTR, Visual RTL and Logical Text)\r
285      *           1. BEGIN always refers to the start address of physical memory.\r
286      *           2. END always refers to the end address of physical memory.\r
287      *\r
288      * If this option is used it will swap the meaning of BEGIN and END only for Visual LTR text. \r
289      *\r
290      * The affect on BEGIN and END Memory Options will be as following:\r
291      *    A. BEGIN For Visual LTR text: This will be the beginning (right side) of the visual text \r
292      *       (corresponding to the physical memory address end, same as END in default behavior)\r
293      *    B. BEGIN For Logical text: Same as BEGIN in default behavior. \r
294      *    C. END For Visual LTR text: This will be the end (left side) of the visual text. (corresponding to  \r
295      *      the physical memory address beginning, same as BEGIN in default behavior) \r
296      *    D. END For Logical text: Same as END in default behavior. \r
297      * Affects: All LamAlef BEGIN, END and AUTO options.\r
298      */\r
299     public static final int SPACES_RELATIVE_TO_TEXT_BEGIN_END = 0x4000000;\r
300 \r
301     /** Bit mask for swapping BEGIN and END for Visual LTR text */\r
302     public static final int SPACES_RELATIVE_TO_TEXT_MASK = 0x4000000;\r
303     \r
304     /**\r
305      * If this option is used, shaping will use the new Unicode code point for TAIL (i.e. 0xFE73). \r
306      * If this option is not specified (Default), old unofficial Unicode TAIL code point is used (i.e. 0x200B)\r
307      * De-shaping will not use this option as it will always search for both the new Unicode code point for the \r
308      * TAIL (i.e. 0xFE73) or the old unofficial Unicode TAIL code point (i.e. 0x200B) and de-shape the\r
309      * Seen-Family letter accordingly.\r
310      *\r
311      * Shaping Mode: Only shaping.\r
312      * De-shaping Mode: N/A.\r
313      * Affects: All Seen options\r
314      */\r
315     public static final int SHAPE_TAIL_NEW_UNICODE = 0x8000000;\r
316 \r
317     /** Bit mask for new Unicode Tail option */\r
318     public static final int SHAPE_TAIL_TYPE_MASK = 0x8000000;\r
319 \r
320     /**\r
321      * Memory option: allow the result to have a different length than the source.\r
322      * @stable ICU 2.0\r
323      */\r
324     public static final int LENGTH_GROW_SHRINK = 0;\r
325 \r
326     /**\r
327      * Memory option: allow the result to have a different length than the source.\r
328      * Affects: LamAlef options\r
329      * This option is an alias to LENGTH_GROW_SHRINK\r
330      */\r
331     public static final int LAMALEF_RESIZE   = 0;\r
332     \r
333     /**\r
334      * Memory option: the result must have the same length as the source.\r
335      * If more room is necessary, then try to consume spaces next to modified characters.\r
336      * @stable ICU 2.0\r
337      */\r
338     public static final int LENGTH_FIXED_SPACES_NEAR = 1;\r
339 \r
340     /**\r
341      * Memory option: the result must have the same length as the source.\r
342      * If more room is necessary, then try to consume spaces next to modified characters.\r
343      * Affects: LamAlef options\r
344      * This option is an alias to LENGTH_FIXED_SPACES_NEAR\r
345      */\r
346     public static final int LAMALEF_NEAR = 1 ;\r
347         \r
348     /**\r
349      * Memory option: the result must have the same length as the source.\r
350      * If more room is necessary, then try to consume spaces at the end of the text.\r
351      * @stable ICU 2.0\r
352      */\r
353     public static final int LENGTH_FIXED_SPACES_AT_END = 2;\r
354 \r
355 \r
356     /**\r
357      * Memory option: the result must have the same length as the source.\r
358      * If more room is necessary, then try to consume spaces at the end of the text.\r
359      * Affects: LamAlef options\r
360      * This option is an alias to LENGTH_FIXED_SPACES_AT_END\r
361      */\r
362     public static final int LAMALEF_END = 2;\r
363     \r
364     /**\r
365      * Memory option: the result must have the same length as the source.\r
366      * If more room is necessary, then try to consume spaces at the beginning of the text.\r
367      * @stable ICU 2.0\r
368      */\r
369     public static final int LENGTH_FIXED_SPACES_AT_BEGINNING = 3;\r
370 \r
371     /**\r
372      * Memory option: the result must have the same length as the source.\r
373      * If more room is necessary, then try to consume spaces at the beginning of the text.\r
374      * Affects: LamAlef options\r
375      * This option is an alias to LENGTH_FIXED_SPACES_AT_BEGINNING\r
376      */\r
377     public static final int LAMALEF_BEGIN = 3; \r
378 \r
379     /**\r
380      * Memory option: the result must have the same length as the source.\r
381      * Shaping Mode: For each LAMALEF character found, expand LAMALEF using space at end.\r
382      *               If there is no space at end, use spaces at beginning of the buffer. If there\r
383      *               is no space at beginning of the buffer, use spaces at the near (i.e. the space\r
384      *               after the LAMALEF character).\r
385      *\r
386      * Deshaping Mode: Perform the same function as the flag equals LAMALEF_END. \r
387      * Affects: LamAlef options\r
388      */\r
389     public static final int LAMALEF_AUTO  = 0x10000; \r
390     \r
391     /** \r
392      * Bit mask for memory options. \r
393      * @stable ICU 2.0\r
394      */\r
395     public static final int LENGTH_MASK = 0x10003;\r
396 \r
397     /** Bit mask for LamAlef memory options. */\r
398 \r
399     public static final int LAMALEF_MASK  = 0x10003;\r
400 \r
401     /** \r
402      * Direction indicator: the source is in logical (keyboard) order. \r
403      * @stable ICU 2.0\r
404      */\r
405     public static final int TEXT_DIRECTION_LOGICAL = 0;\r
406 \r
407     /**\r
408      * Direction indicator:the source is in visual RTL order,\r
409      * the rightmost displayed character stored first.\r
410      * This option is an alias to U_SHAPE_TEXT_DIRECTION_LOGICAL\r
411      */\r
412     public static final int TEXT_DIRECTION_VISUAL_RTL = 0;\r
413     \r
414     /** \r
415      * Direction indicator: the source is in visual (display) order, that is,\r
416      * the leftmost displayed character is stored first.\r
417      * @stable ICU 2.0\r
418      */\r
419     public static final int TEXT_DIRECTION_VISUAL_LTR = 4;\r
420 \r
421     /** \r
422      * Bit mask for direction indicators. \r
423      * @stable ICU 2.0\r
424      */\r
425     public static final int TEXT_DIRECTION_MASK = 4;\r
426 \r
427 \r
428     /**\r
429      * Letter shaping option: do not perform letter shaping. \r
430      * @stable ICU 2.0\r
431      */\r
432     public static final int LETTERS_NOOP = 0;\r
433 \r
434     /** \r
435      * Letter shaping option: replace normative letter characters in the U+0600 (Arabic) block,\r
436      * by shaped ones in the U+FE70 (Presentation Forms B) block. Performs Lam-Alef ligature\r
437      * substitution.\r
438      * @stable ICU 2.0\r
439      */\r
440     public static final int LETTERS_SHAPE = 8;\r
441 \r
442     /** \r
443      * Letter shaping option: replace shaped letter characters in the U+FE70 (Presentation Forms B) block\r
444      * by normative ones in the U+0600 (Arabic) block.  Converts Lam-Alef ligatures to pairs of Lam and\r
445      * Alef characters, consuming spaces if required.\r
446      * @stable ICU 2.0\r
447      */\r
448     public static final int LETTERS_UNSHAPE = 0x10;\r
449 \r
450     /**\r
451      * Letter shaping option: replace normative letter characters in the U+0600 (Arabic) block,\r
452      * except for the TASHKEEL characters at U+064B...U+0652, by shaped ones in the U+Fe70\r
453      * (Presentation Forms B) block.  The TASHKEEL characters will always be converted to\r
454      * the isolated forms rather than to their correct shape.\r
455      * @stable ICU 2.0\r
456      */\r
457     public static final int LETTERS_SHAPE_TASHKEEL_ISOLATED = 0x18;\r
458 \r
459     /** \r
460      * Bit mask for letter shaping options. \r
461      * @stable ICU 2.0\r
462      */\r
463     public static final int LETTERS_MASK = 0x18;\r
464 \r
465 \r
466     /** \r
467      * Digit shaping option: do not perform digit shaping. \r
468      * @stable ICU 2.0\r
469      */\r
470     public static final int DIGITS_NOOP = 0;\r
471 \r
472     /**\r
473      * Digit shaping option: Replace European digits (U+0030...U+0039) by Arabic-Indic digits.\r
474      * @stable ICU 2.0\r
475      */\r
476     public static final int DIGITS_EN2AN = 0x20;\r
477 \r
478     /**\r
479      * Digit shaping option: Replace Arabic-Indic digits by European digits (U+0030...U+0039).\r
480      * @stable ICU 2.0\r
481      */\r
482     public static final int DIGITS_AN2EN = 0x40;\r
483 \r
484     /**\r
485      * Digit shaping option:\r
486      * Replace European digits (U+0030...U+0039) by Arabic-Indic digits\r
487      * if the most recent strongly directional character\r
488      * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC). \r
489      * The initial state at the start of the text is assumed to be not an Arabic,\r
490      * letter, so European digits at the start of the text will not change.\r
491      * Compare to DIGITS_ALEN2AN_INIT_AL.\r
492      * @stable ICU 2.0\r
493      */\r
494     public static final int DIGITS_EN2AN_INIT_LR = 0x60;\r
495 \r
496     /**\r
497      * Digit shaping option:\r
498      * Replace European digits (U+0030...U+0039) by Arabic-Indic digits\r
499      * if the most recent strongly directional character\r
500      * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC). \r
501      * The initial state at the start of the text is assumed to be an Arabic,\r
502      * letter, so European digits at the start of the text will change.\r
503      * Compare to DIGITS_ALEN2AN_INT_LR.\r
504      * @stable ICU 2.0\r
505      */\r
506     public static final int DIGITS_EN2AN_INIT_AL = 0x80;\r
507 \r
508     /** Not a valid option value. */\r
509     //private static final int DIGITS_RESERVED = 0xa0;\r
510 \r
511     /** \r
512      * Bit mask for digit shaping options. \r
513      * @stable ICU 2.0\r
514      */\r
515     public static final int DIGITS_MASK = 0xe0;\r
516 \r
517     /** \r
518      * Digit type option: Use Arabic-Indic digits (U+0660...U+0669). \r
519      * @stable ICU 2.0\r
520      */\r
521     public static final int DIGIT_TYPE_AN = 0;\r
522 \r
523     /** \r
524      * Digit type option: Use Eastern (Extended) Arabic-Indic digits (U+06f0...U+06f9). \r
525      * @stable ICU 2.0\r
526      */\r
527     public static final int DIGIT_TYPE_AN_EXTENDED = 0x100;\r
528 \r
529     /** \r
530      * Bit mask for digit type options. \r
531      * @stable ICU 2.0\r
532      */\r
533     public static final int DIGIT_TYPE_MASK = 0x0100; // 0x3f00?\r
534 \r
535     /**\r
536      * some constants\r
537      */\r
538     private static final char HAMZAFE_CHAR       = '\ufe80';\r
539     private static final char HAMZA06_CHAR       = '\u0621';\r
540     private static final char YEH_HAMZA_CHAR     = '\u0626';\r
541     private static final char YEH_HAMZAFE_CHAR   = '\uFE89';\r
542     private static final char LAMALEF_SPACE_SUB  = '\uffff';\r
543     private static final char TASHKEEL_SPACE_SUB = '\ufffe';\r
544     private static final char LAM_CHAR      = '\u0644';\r
545     private static final char SPACE_CHAR    = '\u0020';\r
546     private static final char SHADDA_CHAR   = '\uFE7C';\r
547     private static final char TATWEEL_CHAR  = '\u0640';\r
548     private static final char SHADDA_TATWEEL_CHAR = '\uFE7D';\r
549     private static final char NEW_TAIL_CHAR = '\uFE73';\r
550     private static final char OLD_TAIL_CHAR = '\u200B';\r
551     private static final int SHAPE_MODE      = 0;\r
552     private static final int DESHAPE_MODE    = 1;\r
553 \r
554     /**\r
555      * @stable ICU 2.0\r
556      */\r
557     public boolean equals(Object rhs) {\r
558         return rhs != null && \r
559             rhs.getClass() == ArabicShaping.class && \r
560             options == ((ArabicShaping)rhs).options;\r
561     }\r
562 \r
563     /**\r
564      * @stable ICU 2.0\r
565      */\r
566      ///CLOVER:OFF\r
567     public int hashCode() {\r
568         return options;\r
569     }\r
570 \r
571     /**\r
572      * @stable ICU 2.0\r
573      */\r
574     public String toString() {\r
575         StringBuffer buf = new StringBuffer(super.toString());\r
576         buf.append('[');\r
577 \r
578         switch (options & LAMALEF_MASK) {\r
579         case LAMALEF_RESIZE: buf.append("LamAlef resize"); break;\r
580         case LAMALEF_NEAR: buf.append("LamAlef spaces at near"); break;\r
581         case LAMALEF_BEGIN: buf.append("LamAlef spaces at begin"); break;\r
582         case LAMALEF_END: buf.append("LamAlef spaces at end"); break;\r
583         case LAMALEF_AUTO: buf.append("lamAlef auto"); break;\r
584         }\r
585         switch (options & TEXT_DIRECTION_MASK) {\r
586         case TEXT_DIRECTION_LOGICAL: buf.append(", logical"); break;\r
587         case TEXT_DIRECTION_VISUAL_LTR: buf.append(", visual"); break;\r
588         }\r
589         switch (options & LETTERS_MASK) {\r
590         case LETTERS_NOOP: buf.append(", no letter shaping"); break;\r
591         case LETTERS_SHAPE: buf.append(", shape letters"); break;\r
592         case LETTERS_SHAPE_TASHKEEL_ISOLATED: buf.append(", shape letters tashkeel isolated"); break;\r
593         case LETTERS_UNSHAPE: buf.append(", unshape letters"); break;\r
594         }\r
595         switch (options & SEEN_MASK) {\r
596         case SEEN_TWOCELL_NEAR: buf.append(", Seen at near"); break;\r
597         }\r
598         switch (options & YEHHAMZA_MASK) {\r
599         case YEHHAMZA_TWOCELL_NEAR: buf.append(", Yeh Hamza at near"); break;\r
600         }\r
601         switch (options & TASHKEEL_MASK) {\r
602         case TASHKEEL_BEGIN: buf.append(", Tashkeel at begin"); break;\r
603         case TASHKEEL_END: buf.append(", Tashkeel at end"); break;\r
604         case TASHKEEL_REPLACE_BY_TATWEEL: buf.append(", Tashkeel replace with tatweel"); break;\r
605         case TASHKEEL_RESIZE: buf.append(", Tashkeel resize"); break;\r
606         }\r
607 \r
608         switch (options & DIGITS_MASK) {\r
609         case DIGITS_NOOP: buf.append(", no digit shaping"); break;\r
610         case DIGITS_EN2AN: buf.append(", shape digits to AN"); break;\r
611         case DIGITS_AN2EN: buf.append(", shape digits to EN"); break;\r
612         case DIGITS_EN2AN_INIT_LR: buf.append(", shape digits to AN contextually: default EN"); break;\r
613         case DIGITS_EN2AN_INIT_AL: buf.append(", shape digits to AN contextually: default AL"); break;\r
614         }\r
615         switch (options & DIGIT_TYPE_MASK) {\r
616         case DIGIT_TYPE_AN: buf.append(", standard Arabic-Indic digits"); break;\r
617         case DIGIT_TYPE_AN_EXTENDED: buf.append(", extended Arabic-Indic digits"); break;\r
618         }\r
619         buf.append("]");\r
620 \r
621         return buf.toString();\r
622     }\r
623     ///CLOVER:ON\r
624 \r
625     //\r
626     // ported api\r
627     //\r
628 \r
629     private static final int IRRELEVANT = 4;\r
630     private static final int LAMTYPE = 16;\r
631     private static final int ALEFTYPE = 32;\r
632 \r
633     private static final int LINKR = 1;\r
634     private static final int LINKL = 2;\r
635     private static final int LINK_MASK = 3;\r
636 \r
637     private static final int irrelevantPos[] = { \r
638         0x0, 0x2, 0x4, 0x6, 0x8, 0xA, 0xC, 0xE \r
639     };\r
640 \r
641 /*\r
642     private static final char convertLamAlef[] =  {\r
643         '\u0622', // FEF5 \r
644         '\u0622', // FEF6\r
645         '\u0623', // FEF7\r
646         '\u0623', // FEF8\r
647         '\u0625', // FEF9\r
648         '\u0625', // FEFA\r
649         '\u0627', // FEFB\r
650         '\u0627'  // FEFC \r
651     };\r
652 */\r
653     \r
654     private static final int tailFamilyIsolatedFinal[] = {\r
655         /* FEB1 */ 1,\r
656         /* FEB2 */ 1,\r
657         /* FEB3 */ 0,\r
658         /* FEB4 */ 0,\r
659         /* FEB5 */ 1,\r
660         /* FEB6 */ 1,\r
661         /* FEB7 */ 0,\r
662         /* FEB8 */ 0,\r
663         /* FEB9 */ 1,\r
664         /* FEBA */ 1,\r
665         /* FEBB */ 0,\r
666         /* FEBC */ 0,\r
667         /* FEBD */ 1,\r
668         /* FEBE */ 1\r
669     };\r
670 \r
671     private static final int tashkeelMedial[] = {\r
672         /* FE70 */ 0,\r
673         /* FE71 */ 1,\r
674         /* FE72 */ 0,\r
675         /* FE73 */ 0,\r
676         /* FE74 */ 0,\r
677         /* FE75 */ 0,\r
678         /* FE76 */ 0,\r
679         /* FE77 */ 1,\r
680         /* FE78 */ 0,\r
681         /* FE79 */ 1,\r
682         /* FE7A */ 0,\r
683         /* FE7B */ 1,\r
684         /* FE7C */ 0,\r
685         /* FE7D */ 1,\r
686         /* FE7E */ 0,\r
687         /* FE7F */ 1\r
688     };\r
689 \r
690     private static final char yehHamzaToYeh[] =\r
691     {\r
692     /* isolated*/ 0xFEEF,\r
693     /* final   */ 0xFEF0\r
694     };\r
695 \r
696     private static final char convertNormalizedLamAlef[] = {\r
697         '\u0622', // 065C\r
698         '\u0623', // 065D\r
699         '\u0625', // 065E\r
700         '\u0627', // 065F\r
701     };\r
702 \r
703     private static final int[] araLink = {\r
704         1           + 32 + 256 * 0x11,  /*0x0622*/\r
705         1           + 32 + 256 * 0x13,  /*0x0623*/\r
706         1                + 256 * 0x15,  /*0x0624*/\r
707         1           + 32 + 256 * 0x17,  /*0x0625*/\r
708         1 + 2            + 256 * 0x19,  /*0x0626*/\r
709         1           + 32 + 256 * 0x1D,  /*0x0627*/\r
710         1 + 2            + 256 * 0x1F,  /*0x0628*/\r
711         1                + 256 * 0x23,  /*0x0629*/\r
712         1 + 2            + 256 * 0x25,  /*0x062A*/\r
713         1 + 2            + 256 * 0x29,  /*0x062B*/\r
714         1 + 2            + 256 * 0x2D,  /*0x062C*/\r
715         1 + 2            + 256 * 0x31,  /*0x062D*/\r
716         1 + 2            + 256 * 0x35,  /*0x062E*/\r
717         1                + 256 * 0x39,  /*0x062F*/\r
718         1                + 256 * 0x3B,  /*0x0630*/\r
719         1                + 256 * 0x3D,  /*0x0631*/\r
720         1                + 256 * 0x3F,  /*0x0632*/\r
721         1 + 2            + 256 * 0x41,  /*0x0633*/\r
722         1 + 2            + 256 * 0x45,  /*0x0634*/\r
723         1 + 2            + 256 * 0x49,  /*0x0635*/\r
724         1 + 2            + 256 * 0x4D,  /*0x0636*/\r
725         1 + 2            + 256 * 0x51,  /*0x0637*/\r
726         1 + 2            + 256 * 0x55,  /*0x0638*/\r
727         1 + 2            + 256 * 0x59,  /*0x0639*/\r
728         1 + 2            + 256 * 0x5D,  /*0x063A*/\r
729         0, 0, 0, 0, 0,                  /*0x063B-0x063F*/\r
730         1 + 2,                          /*0x0640*/\r
731         1 + 2            + 256 * 0x61,  /*0x0641*/\r
732         1 + 2            + 256 * 0x65,  /*0x0642*/\r
733         1 + 2            + 256 * 0x69,  /*0x0643*/\r
734         1 + 2       + 16 + 256 * 0x6D,  /*0x0644*/\r
735         1 + 2            + 256 * 0x71,  /*0x0645*/\r
736         1 + 2            + 256 * 0x75,  /*0x0646*/\r
737         1 + 2            + 256 * 0x79,  /*0x0647*/\r
738         1                + 256 * 0x7D,  /*0x0648*/\r
739         1                + 256 * 0x7F,  /*0x0649*/\r
740         1 + 2            + 256 * 0x81,  /*0x064A*/\r
741         4, 4, 4, 4,                     /*0x064B-0x064E*/\r
742         4, 4, 4, 4,                     /*0x064F-0x0652*/\r
743         4, 4, 4, 0, 0,                  /*0x0653-0x0657*/\r
744         0, 0, 0, 0,                     /*0x0658-0x065B*/\r
745         1                + 256 * 0x85,  /*0x065C*/\r
746         1                + 256 * 0x87,  /*0x065D*/\r
747         1                + 256 * 0x89,  /*0x065E*/\r
748         1                + 256 * 0x8B,  /*0x065F*/\r
749         0, 0, 0, 0, 0,                  /*0x0660-0x0664*/\r
750         0, 0, 0, 0, 0,                  /*0x0665-0x0669*/\r
751         0, 0, 0, 0, 0, 0,               /*0x066A-0x066F*/\r
752         4,                              /*0x0670*/\r
753         0,                              /*0x0671*/\r
754         1           + 32,               /*0x0672*/\r
755         1           + 32,               /*0x0673*/\r
756         0,                              /*0x0674*/\r
757         1           + 32,               /*0x0675*/\r
758         1, 1,                           /*0x0676-0x0677*/\r
759         1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x0678-0x067D*/\r
760         1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x067E-0x0683*/\r
761         1+2, 1+2, 1+2, 1+2,             /*0x0684-0x0687*/\r
762         1, 1, 1, 1, 1, 1, 1, 1, 1, 1,   /*0x0688-0x0691*/\r
763         1, 1, 1, 1, 1, 1, 1, 1,         /*0x0692-0x0699*/\r
764         1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x069A-0x06A3*/\r
765         1+2, 1+2, 1+2, 1+2,             /*0x069A-0x06A3*/\r
766         1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x06A4-0x06AD*/\r
767         1+2, 1+2, 1+2, 1+2,             /*0x06A4-0x06AD*/\r
768         1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x06AE-0x06B7*/\r
769         1+2, 1+2, 1+2, 1+2,             /*0x06AE-0x06B7*/\r
770         1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x06B8-0x06BF*/\r
771         1+2, 1+2,                       /*0x06B8-0x06BF*/\r
772         1,                              /*0x06C0*/\r
773         1+2,                            /*0x06C1*/\r
774         1, 1, 1, 1, 1, 1, 1, 1, 1, 1,   /*0x06C2-0x06CB*/\r
775         1+2,                            /*0x06CC*/\r
776         1,                              /*0x06CD*/\r
777         1+2, 1+2, 1+2, 1+2,             /*0x06CE-0x06D1*/\r
778         1, 1                            /*0x06D2-0x06D3*/\r
779     };\r
780 \r
781     private static final int[] presLink = {\r
782         1 + 2,                        /*0xFE70*/\r
783         1 + 2,                        /*0xFE71*/\r
784         1 + 2, 0, 1+ 2, 0, 1+ 2,      /*0xFE72-0xFE76*/\r
785         1 + 2,                        /*0xFE77*/\r
786         1+ 2, 1 + 2, 1+2, 1 + 2,      /*0xFE78-0xFE81*/\r
787         1+ 2, 1 + 2, 1+2, 1 + 2,      /*0xFE82-0xFE85*/\r
788         0, 0 + 32, 1 + 32, 0 + 32,    /*0xFE86-0xFE89*/\r
789         1 + 32, 0, 1,  0 + 32,        /*0xFE8A-0xFE8D*/\r
790         1 + 32, 0, 2,  1 + 2,         /*0xFE8E-0xFE91*/\r
791         1, 0 + 32, 1 + 32, 0,         /*0xFE92-0xFE95*/\r
792         2, 1 + 2, 1, 0,               /*0xFE96-0xFE99*/\r
793         1, 0, 2, 1 + 2,               /*0xFE9A-0xFE9D*/\r
794         1, 0, 2, 1 + 2,               /*0xFE9E-0xFEA1*/\r
795         1, 0, 2, 1 + 2,               /*0xFEA2-0xFEA5*/\r
796         1, 0, 2, 1 + 2,               /*0xFEA6-0xFEA9*/\r
797         1, 0, 2, 1 + 2,               /*0xFEAA-0xFEAD*/\r
798         1, 0, 1, 0,                   /*0xFEAE-0xFEB1*/\r
799         1, 0, 1, 0,                   /*0xFEB2-0xFEB5*/\r
800         1, 0, 2, 1+2,                 /*0xFEB6-0xFEB9*/\r
801         1, 0, 2, 1+2,                 /*0xFEBA-0xFEBD*/\r
802         1, 0, 2, 1+2,                 /*0xFEBE-0xFEC1*/\r
803         1, 0, 2, 1+2,                 /*0xFEC2-0xFEC5*/\r
804         1, 0, 2, 1+2,                 /*0xFEC6-0xFEC9*/\r
805         1, 0, 2, 1+2,                 /*0xFECA-0xFECD*/\r
806         1, 0, 2, 1+2,                 /*0xFECE-0xFED1*/\r
807         1, 0, 2, 1+2,                 /*0xFED2-0xFED5*/\r
808         1, 0, 2, 1+2,                 /*0xFED6-0xFED9*/\r
809         1, 0, 2, 1+2,                 /*0xFEDA-0xFEDD*/\r
810         1, 0, 2, 1+2,                 /*0xFEDE-0xFEE1*/\r
811         1, 0 + 16, 2 + 16, 1 + 2 +16, /*0xFEE2-0xFEE5*/\r
812         1 + 16, 0, 2, 1+2,            /*0xFEE6-0xFEE9*/\r
813         1, 0, 2, 1+2,                 /*0xFEEA-0xFEED*/\r
814         1, 0, 2, 1+2,                 /*0xFEEE-0xFEF1*/\r
815         1, 0, 1, 0,                   /*0xFEF2-0xFEF5*/\r
816         1, 0, 2, 1+2,                 /*0xFEF6-0xFEF9*/\r
817         1, 0, 1, 0,                   /*0xFEFA-0xFEFD*/\r
818         1, 0, 1, 0,\r
819         1\r
820     };\r
821 \r
822     private static int[] convertFEto06 = {\r
823         /***********0******1******2******3******4******5******6******7******8******9******A******B******C******D******E******F***/\r
824         /*FE7*/   0x64B, 0x64B, 0x64C, 0x64C, 0x64D, 0x64D, 0x64E, 0x64E, 0x64F, 0x64F, 0x650, 0x650, 0x651, 0x651, 0x652, 0x652,\r
825         /*FE8*/   0x621, 0x622, 0x622, 0x623, 0x623, 0x624, 0x624, 0x625, 0x625, 0x626, 0x626, 0x626, 0x626, 0x627, 0x627, 0x628,\r
826         /*FE9*/   0x628, 0x628, 0x628, 0x629, 0x629, 0x62A, 0x62A, 0x62A, 0x62A, 0x62B, 0x62B, 0x62B, 0x62B, 0x62C, 0x62C, 0x62C,\r
827         /*FEA*/   0x62C, 0x62D, 0x62D, 0x62D, 0x62D, 0x62E, 0x62E, 0x62E, 0x62E, 0x62F, 0x62F, 0x630, 0x630, 0x631, 0x631, 0x632,\r
828         /*FEB*/   0x632, 0x633, 0x633, 0x633, 0x633, 0x634, 0x634, 0x634, 0x634, 0x635, 0x635, 0x635, 0x635, 0x636, 0x636, 0x636,\r
829         /*FEC*/   0x636, 0x637, 0x637, 0x637, 0x637, 0x638, 0x638, 0x638, 0x638, 0x639, 0x639, 0x639, 0x639, 0x63A, 0x63A, 0x63A,\r
830         /*FED*/   0x63A, 0x641, 0x641, 0x641, 0x641, 0x642, 0x642, 0x642, 0x642, 0x643, 0x643, 0x643, 0x643, 0x644, 0x644, 0x644,\r
831         /*FEE*/   0x644, 0x645, 0x645, 0x645, 0x645, 0x646, 0x646, 0x646, 0x646, 0x647, 0x647, 0x647, 0x647, 0x648, 0x648, 0x649,\r
832         /*FEF*/   0x649, 0x64A, 0x64A, 0x64A, 0x64A, 0x65C, 0x65C, 0x65D, 0x65D, 0x65E, 0x65E, 0x65F, 0x65F\r
833     };\r
834 \r
835     private static final int shapeTable[][][] = {\r
836         { {0,0,0,0}, {0,0,0,0}, {0,1,0,3}, {0,1,0,1} },\r
837         { {0,0,2,2}, {0,0,1,2}, {0,1,1,2}, {0,1,1,3} },\r
838         { {0,0,0,0}, {0,0,0,0}, {0,1,0,3}, {0,1,0,3} },\r
839         { {0,0,1,2}, {0,0,1,2}, {0,1,1,2}, {0,1,1,3} }\r
840     };\r
841 \r
842     /*\r
843      * This function shapes European digits to Arabic-Indic digits\r
844      * in-place, writing over the input characters.  Data is in visual\r
845      * order.\r
846      */\r
847     private void shapeToArabicDigitsWithContext(char[] dest,\r
848                                                 int start,\r
849                                                 int length,\r
850                                                 char digitBase,\r
851                                                 boolean lastStrongWasAL) {\r
852         UBiDiProps bdp;\r
853         try {\r
854             bdp=UBiDiProps.getSingleton();\r
855         } catch (IOException e) {\r
856             throw new MissingResourceException(e.getMessage(), "(BidiProps)", "");\r
857         }\r
858         digitBase -= '0'; // move common adjustment out of loop\r
859 \r
860         for(int i = start + length; --i >= start;) {\r
861             char ch = dest[i];\r
862             switch (bdp.getClass(ch)) {\r
863             case UCharacterDirection.LEFT_TO_RIGHT:\r
864             case UCharacterDirection.RIGHT_TO_LEFT:\r
865                 lastStrongWasAL = false;\r
866                 break;\r
867             case UCharacterDirection.RIGHT_TO_LEFT_ARABIC:\r
868                 lastStrongWasAL = true;\r
869                 break;\r
870             case UCharacterDirection.EUROPEAN_NUMBER:\r
871                 if (lastStrongWasAL && ch <= '\u0039') {\r
872                     dest[i] = (char)(ch + digitBase);\r
873                 }\r
874                 break;\r
875             default:\r
876                 break;\r
877             }\r
878         }\r
879     }\r
880 \r
881     /*\r
882      * Name    : invertBuffer\r
883      * Function: This function inverts the buffer, it's used\r
884      *           in case the user specifies the buffer to be\r
885      *           TEXT_DIRECTION_LOGICAL\r
886      */\r
887     private static void invertBuffer(char[] buffer,\r
888                                      int start,\r
889                                      int length) {\r
890 \r
891         for(int i = start, j = start + length - 1; i < j; i++, --j) {\r
892             char temp = buffer[i];\r
893             buffer[i] = buffer[j];\r
894             buffer[j] = temp;\r
895         }\r
896     }\r
897 \r
898     /*\r
899      * Name    : changeLamAlef\r
900      * Function: Converts the Alef characters into an equivalent\r
901      *           LamAlef location in the 0x06xx Range, this is an\r
902      *           intermediate stage in the operation of the program\r
903      *           later it'll be converted into the 0xFExx LamAlefs \r
904      *           in the shaping function.\r
905      */\r
906     private static char changeLamAlef(char ch) {\r
907         switch(ch) {\r
908         case '\u0622': return '\u065C';\r
909         case '\u0623': return '\u065D';\r
910         case '\u0625': return '\u065E';\r
911         case '\u0627': return '\u065F';\r
912         default:  return '\u0000'; // not a lamalef\r
913         }\r
914     }\r
915 \r
916     /*\r
917      * Name    : specialChar\r
918      * Function: Special Arabic characters need special handling in the shapeUnicode\r
919      *           function, this function returns 1 or 2 for these special characters\r
920      */\r
921     private static int specialChar(char ch) {\r
922         if ((ch > '\u0621' && ch < '\u0626') || \r
923             (ch == '\u0627') ||\r
924             (ch > '\u062E' && ch < '\u0633') ||\r
925             (ch > '\u0647' && ch < '\u064A') ||\r
926             (ch == '\u0629')) {\r
927             return 1;\r
928         } else if (ch >= '\u064B' && ch<= '\u0652') {\r
929             return 2;\r
930         } else if (ch >= 0x0653 && ch <= 0x0655 || \r
931                    ch == 0x0670 ||\r
932                    ch >= 0xFE70 && ch <= 0xFE7F) {\r
933             return 3;\r
934         } else {\r
935             return 0;\r
936         }\r
937     }\r
938     \r
939     /*\r
940      * Name    : getLink\r
941      * Function: Resolves the link between the characters as \r
942      *           Arabic characters have four forms :\r
943      *           Isolated, Initial, Middle and Final Form\r
944      */\r
945     private static int getLink(char ch) {\r
946         if (ch >= '\u0622' && ch <= '\u06D3') {\r
947             return araLink[ch - '\u0622'];\r
948         } else if (ch == '\u200D') {\r
949             return 3;\r
950         } else if (ch >= '\u206D' && ch <= '\u206F') {\r
951             return 4;\r
952         } else if (ch >= '\uFE70' && ch <= '\uFEFC') {\r
953             return presLink[ch - '\uFE70'];\r
954         } else {\r
955             return 0;\r
956         }\r
957     }\r
958 \r
959     /*\r
960      * Name    : countSpaces\r
961      * Function: Counts the number of spaces\r
962      *           at each end of the logical buffer\r
963      */\r
964     private static int countSpacesLeft(char[] dest, \r
965                                        int start,\r
966                                        int count) {\r
967         for (int i = start, e = start + count; i < e; ++i) {\r
968             if (dest[i] != SPACE_CHAR) {\r
969                 return i - start;\r
970             }\r
971         }\r
972         return count;\r
973     }\r
974 \r
975     private static int countSpacesRight(char[] dest,\r
976                                         int start,\r
977                                         int count) {\r
978 \r
979         for (int i = start + count; --i >= start;) {\r
980             if (dest[i] != SPACE_CHAR) {\r
981                 return start + count - 1 - i;\r
982             }\r
983         }\r
984         return count;\r
985     }\r
986 \r
987     /*\r
988      * Name    : isTashkeelChar\r
989      * Function: Returns true for Tashkeel characters else return false\r
990      */\r
991     private static boolean isTashkeelChar(char ch) {\r
992         return ( ch >='\u064B' && ch <= '\u0652' );\r
993     }\r
994 \r
995     /*\r
996      *Name     : isSeenTailFamilyChar\r
997      *Function : returns 1 if the character is a seen family isolated character \r
998      *           in the FE range otherwise returns 0\r
999      */\r
1000 \r
1001     private static int isSeenTailFamilyChar(char ch) {\r
1002         if (ch >= 0xfeb1 && ch < 0xfebf){\r
1003              return tailFamilyIsolatedFinal [ch - 0xFEB1];\r
1004         } else {\r
1005              return 0;\r
1006         }\r
1007     }\r
1008 \r
1009      /* Name     : isSeenFamilyChar\r
1010       * Function : returns 1 if the character is a seen family character in the Unicode\r
1011       *            06 range otherwise returns 0\r
1012      */\r
1013 \r
1014     private static int isSeenFamilyChar(char  ch){\r
1015         if (ch >= 0x633 && ch <= 0x636){\r
1016             return 1;\r
1017         }else {\r
1018             return 0;\r
1019         }\r
1020     }\r
1021 \r
1022     /*\r
1023      *Name     : isTailChar\r
1024      *Function : returns true if the character matches one of the tail characters \r
1025      *           (0xfe73 or 0x200b) otherwise returns false\r
1026      */\r
1027 \r
1028     private static boolean isTailChar(char ch) {\r
1029         if(ch == OLD_TAIL_CHAR || ch == NEW_TAIL_CHAR){\r
1030                 return true;\r
1031         }else{\r
1032                 return false;\r
1033         }\r
1034     }\r
1035     \r
1036     /*\r
1037      *Name     : isAlefMaksouraChar\r
1038      *Function : returns true if the character is a Alef Maksoura Final or isolated \r
1039      *           otherwise returns false\r
1040      */\r
1041     private static boolean isAlefMaksouraChar(char ch) {\r
1042         return ( (ch == 0xFEEF) || ( ch == 0xFEF0) || (ch == 0x0649));\r
1043     } \r
1044 \r
1045     /*\r
1046      * Name     : isYehHamzaChar\r
1047      * Function : returns true if the character is a yehHamza isolated or yehhamza\r
1048      *            final is found otherwise returns false\r
1049      */\r
1050     private static boolean isYehHamzaChar(char ch) {\r
1051         if((ch==0xFE89)||(ch==0xFE8A)){\r
1052             return true;\r
1053         }else{\r
1054             return false;\r
1055         }\r
1056     } \r
1057 \r
1058     /*\r
1059      *Name     : isTashkeelCharFE\r
1060      *Function : Returns true for Tashkeel characters in FE range else return false\r
1061      */\r
1062         \r
1063     private static boolean isTashkeelCharFE(char ch) {\r
1064         return ( ch!=0xFE75 &&(ch>=0xFE70 && ch<= 0xFE7F) );\r
1065     }\r
1066 \r
1067     /* \r
1068      * Name: isTashkeelOnTatweelChar\r
1069      * Function: Checks if the Tashkeel Character is on Tatweel or not,if the \r
1070      *           Tashkeel on tatweel (FE range), it returns 1 else if the \r
1071      *           Tashkeel with shadda on tatweel (FC range)return 2 otherwise \r
1072      *           returns 0\r
1073      */\r
1074     private static int isTashkeelOnTatweelChar(char ch){\r
1075         if (ch >= 0xfe70 && ch <= 0xfe7f && ch != NEW_TAIL_CHAR && ch != 0xFE75 && ch != SHADDA_TATWEEL_CHAR)\r
1076         {\r
1077             return tashkeelMedial [ch - 0xFE70];\r
1078         } else if( (ch >= 0xfcf2 && ch <= 0xfcf4) || (ch == SHADDA_TATWEEL_CHAR)) {\r
1079             return 2;\r
1080         } else {\r
1081             return 0;\r
1082         }\r
1083     }\r
1084     \r
1085     /*\r
1086      * Name: isIsolatedTashkeelChar\r
1087      * Function: Checks if the Tashkeel Character is in the isolated form \r
1088      *           (i.e. Unicode FE range) returns 1 else if the Tashkeel \r
1089      *           with shadda is in the isolated form (i.e. Unicode FC range)\r
1090      *           returns 1 otherwise returns 0\r
1091      */\r
1092     private static int isIsolatedTashkeelChar(char ch){\r
1093         if (ch >= 0xfe70 && ch <= 0xfe7f && ch != NEW_TAIL_CHAR && ch != 0xFE75){\r
1094             return (1 - tashkeelMedial [ch - 0xFE70]);\r
1095         } else if(ch >= 0xfc5e && ch <= 0xfc63){\r
1096             return 1;\r
1097         } else{\r
1098             return 0;\r
1099         }\r
1100     }\r
1101    \r
1102     /*\r
1103      * Name    : isAlefChar\r
1104      * Function: Returns 1 for Alef characters else return 0\r
1105      */\r
1106     private static boolean isAlefChar(char ch) {\r
1107         return ch == '\u0622' || ch == '\u0623' || ch == '\u0625' || ch == '\u0627';\r
1108     }\r
1109      \r
1110     /*\r
1111      * Name    : isLamAlefChar\r
1112      * Function: Returns true for LamAlef characters else return false\r
1113      */\r
1114     private static boolean isLamAlefChar(char ch) {\r
1115         return ch >= '\uFEF5' && ch <= '\uFEFC';\r
1116     }\r
1117 \r
1118     private static boolean isNormalizedLamAlefChar(char ch) {\r
1119         return ch >= '\u065C' && ch <= '\u065F';\r
1120     }\r
1121 \r
1122     /*\r
1123      * Name    : calculateSize\r
1124      * Function: This function calculates the destSize to be used in preflighting\r
1125      *           when the destSize is equal to 0\r
1126      */\r
1127     private int calculateSize(char[] source,\r
1128                               int sourceStart,\r
1129                               int sourceLength) {\r
1130     \r
1131         int destSize = sourceLength;\r
1132         \r
1133         switch (options & LETTERS_MASK) {\r
1134         case LETTERS_SHAPE:\r
1135         case LETTERS_SHAPE_TASHKEEL_ISOLATED:\r
1136             if (isLogical) {\r
1137                 for (int i = sourceStart, e = sourceStart + sourceLength - 1; i < e; ++i) {\r
1138                     if ((source[i] == LAM_CHAR && isAlefChar(source[i+1])) || isTashkeelCharFE(source[i])){\r
1139                         --destSize;\r
1140                     }\r
1141                 }\r
1142             } else { // visual\r
1143                 for(int i = sourceStart + 1, e = sourceStart + sourceLength; i < e; ++i) {\r
1144                     if ((source[i] == LAM_CHAR && isAlefChar(source[i-1])) || isTashkeelCharFE(source[i])) {\r
1145                         --destSize;\r
1146                     }\r
1147                 }\r
1148             }\r
1149             break;\r
1150 \r
1151         case LETTERS_UNSHAPE:\r
1152             for(int i = sourceStart, e = sourceStart + sourceLength; i < e; ++i) {\r
1153                 if (isLamAlefChar(source[i])) {\r
1154                     destSize++;\r
1155                 }\r
1156             }\r
1157             break;\r
1158 \r
1159         default:\r
1160             break;\r
1161         }\r
1162 \r
1163         return destSize;\r
1164     }\r
1165     \r
1166     \r
1167     /*\r
1168      * Name    : countSpaceSub\r
1169      * Function: Counts number of times the subChar appears in the array\r
1170      */\r
1171     public static int countSpaceSub(char [] dest,int length, char subChar){\r
1172         int i = 0;\r
1173         int count = 0;\r
1174         while (i < length) {\r
1175           if (dest[i] == subChar) {\r
1176               count++;\r
1177               }\r
1178           i++;\r
1179         } \r
1180         return count;  \r
1181     }\r
1182     \r
1183     /*\r
1184      * Name    : shiftArray\r
1185      * Function: Shifts characters to replace space sub characters\r
1186      */\r
1187     public static void shiftArray(char [] dest,int start, int e, char subChar){\r
1188         int w = e;\r
1189         int r = e;\r
1190         while (--r >= start) {\r
1191           char ch = dest[r];\r
1192           if (ch != subChar) {\r
1193             --w;\r
1194             if (w != r) {\r
1195               dest[w] = ch;\r
1196             }\r
1197           }\r
1198         }\r
1199    }\r
1200 \r
1201     /*\r
1202      * Name    : flipArray\r
1203      * Function: inverts array, so that start becomes end and vice versa\r
1204      */\r
1205       public static int flipArray(char [] dest, int start, int e, int w){\r
1206         int r;\r
1207         if (w > start) {\r
1208         // shift, assume small buffer size so don't use arraycopy\r
1209           r = w;\r
1210           w = start;\r
1211           while (r < e) {\r
1212             dest[w++] = dest[r++];\r
1213            }\r
1214          } else {\r
1215              w = e;\r
1216          }\r
1217         return w;\r
1218       }\r
1219     \r
1220     /*\r
1221      * Name     : handleTashkeelWithTatweel\r
1222      * Function : Replaces Tashkeel as following: \r
1223      *            Case 1 :if the Tashkeel on tatweel, replace it with Tatweel.   \r
1224      *            Case 2 :if the Tashkeel aggregated with Shadda on Tatweel, replace \r
1225      *                   it with Shadda on Tatweel.\r
1226      *            Case 3: if the Tashkeel is isolated replace it with Space.\r
1227      *\r
1228      */\r
1229     private static int handleTashkeelWithTatweel(char[] dest, int sourceLength) {\r
1230                      int i;\r
1231                      for(i = 0; i < sourceLength; i++){\r
1232                          if((isTashkeelOnTatweelChar(dest[i]) == 1)){\r
1233                              dest[i] = TATWEEL_CHAR;\r
1234                         }else if((isTashkeelOnTatweelChar(dest[i]) == 2)){\r
1235                              dest[i] = SHADDA_TATWEEL_CHAR;\r
1236                         }else if((isIsolatedTashkeelChar(dest[i])==1) && dest[i] != SHADDA_CHAR){\r
1237                              dest[i] = SPACE_CHAR;\r
1238                         }\r
1239                      }\r
1240                      return sourceLength;\r
1241     }\r
1242 \r
1243     /*\r
1244      *Name     : handleGeneratedSpaces\r
1245      *Function : The shapeUnicode function converts Lam + Alef into LamAlef + space,\r
1246      *           and Tashkeel to space. \r
1247      *           handleGeneratedSpaces function puts these generated spaces \r
1248      *           according to the options the user specifies. LamAlef and Tashkeel\r
1249      *           spaces can be replaced at begin, at end, at near or decrease the \r
1250      *           buffer size.\r
1251      *\r
1252      *           There is also Auto option for LamAlef and tashkeel, which will put\r
1253      *           the spaces at end of the buffer (or end of text if the user used \r
1254      *           the option SPACES_RELATIVE_TO_TEXT_BEGIN_END).\r
1255      *\r
1256      *           If the text type was visual_LTR and the option \r
1257      *           SPACES_RELATIVE_TO_TEXT_BEGIN_END was selected the END \r
1258      *           option will place the space at the beginning of the buffer and\r
1259      *           BEGIN will place the space at the end of the buffer. \r
1260      */\r
1261   private int handleGeneratedSpaces(char[] dest, \r
1262             int start,\r
1263             int length) {\r
1264       \r
1265       int lenOptionsLamAlef = options & LAMALEF_MASK;\r
1266       int lenOptionsTashkeel = options & TASHKEEL_MASK;\r
1267       boolean lamAlefOn = false;\r
1268       boolean tashkeelOn = false;\r
1269       \r
1270       if (!isLogical & !spacesRelativeToTextBeginEnd) {\r
1271           switch (lenOptionsLamAlef) {\r
1272           case LAMALEF_BEGIN: lenOptionsLamAlef = LAMALEF_END; break;\r
1273           case LAMALEF_END: lenOptionsLamAlef = LAMALEF_BEGIN; break;\r
1274           default: break;\r
1275          }   \r
1276           switch (lenOptionsTashkeel){\r
1277           case TASHKEEL_BEGIN: lenOptionsTashkeel = TASHKEEL_END; break;\r
1278           case TASHKEEL_END: lenOptionsTashkeel = TASHKEEL_BEGIN; break;\r
1279           default: break;\r
1280           }\r
1281         }\r
1282       \r
1283      \r
1284       if (lenOptionsLamAlef == LAMALEF_NEAR) {\r
1285           for (int i = start, e = i + length; i < e; ++i) {\r
1286               if (dest[i] == LAMALEF_SPACE_SUB) {\r
1287                   dest[i] = SPACE_CHAR;\r
1288               }\r
1289           }\r
1290           \r
1291       } else {\r
1292           \r
1293           final int e = start + length;\r
1294           int wL = countSpaceSub(dest, length, LAMALEF_SPACE_SUB);\r
1295           int wT = countSpaceSub(dest, length, TASHKEEL_SPACE_SUB);\r
1296 \r
1297           if (lenOptionsLamAlef == LAMALEF_END){\r
1298             lamAlefOn = true;\r
1299           }\r
1300           if (lenOptionsTashkeel == TASHKEEL_END){\r
1301             tashkeelOn = true;\r
1302           }\r
1303 \r
1304 \r
1305           if (lamAlefOn && (lenOptionsLamAlef == LAMALEF_END)) { \r
1306             shiftArray(dest, start, e, LAMALEF_SPACE_SUB);\r
1307             while (wL > start) {\r
1308                 dest[--wL] = SPACE_CHAR;\r
1309             } \r
1310           }\r
1311 \r
1312           if (tashkeelOn && (lenOptionsTashkeel == TASHKEEL_END)){\r
1313             shiftArray(dest, start, e, TASHKEEL_SPACE_SUB);\r
1314             while (wT > start) {\r
1315                  dest[--wT] = SPACE_CHAR;\r
1316             }\r
1317           }\r
1318             \r
1319           lamAlefOn = false; \r
1320           tashkeelOn = false;\r
1321         \r
1322           if (lenOptionsLamAlef == LAMALEF_RESIZE){\r
1323             lamAlefOn = true;\r
1324           }\r
1325           if (lenOptionsTashkeel == TASHKEEL_RESIZE){\r
1326             tashkeelOn = true;\r
1327           }\r
1328         \r
1329           if (lamAlefOn && (lenOptionsLamAlef == LAMALEF_RESIZE)){\r
1330               shiftArray(dest, start, e, LAMALEF_SPACE_SUB);\r
1331               wL = flipArray(dest,start,e, wL);\r
1332               length = wL - start;\r
1333           }\r
1334           if (tashkeelOn && (lenOptionsTashkeel == TASHKEEL_RESIZE)) { \r
1335               shiftArray(dest, start, e, TASHKEEL_SPACE_SUB);\r
1336               wT = flipArray(dest,start,e, wT);\r
1337               length = wT - start;\r
1338           } \r
1339 \r
1340           lamAlefOn = false; \r
1341           tashkeelOn = false;          \r
1342         \r
1343           if ((lenOptionsLamAlef == LAMALEF_BEGIN) || \r
1344               (lenOptionsLamAlef == LAMALEF_AUTO)){\r
1345                 lamAlefOn = true;\r
1346           }\r
1347           if (lenOptionsTashkeel == TASHKEEL_BEGIN){\r
1348                 tashkeelOn = true;\r
1349           }\r
1350 \r
1351           if (lamAlefOn && ((lenOptionsLamAlef == LAMALEF_BEGIN)||\r
1352                             (lenOptionsLamAlef == LAMALEF_AUTO))) { // spaces at beginning\r
1353               shiftArray(dest, start, e, LAMALEF_SPACE_SUB);\r
1354                wL = flipArray(dest,start,e, wL);\r
1355                   while (wL < e) {\r
1356                       dest[wL++] = SPACE_CHAR;\r
1357                   }\r
1358               }\r
1359               if(tashkeelOn && (lenOptionsTashkeel == TASHKEEL_BEGIN)){\r
1360                shiftArray(dest, start, e, TASHKEEL_SPACE_SUB);\r
1361                wT = flipArray(dest,start,e, wT);\r
1362                   while (wT < e) {\r
1363                       dest[wT++] = SPACE_CHAR;\r
1364                   }\r
1365               }\r
1366            }\r
1367       \r
1368       return length;\r
1369   }\r
1370   \r
1371   \r
1372   /*\r
1373    *Name     :expandCompositCharAtBegin\r
1374    *Function :Expands the LamAlef character to Lam and Alef consuming the required\r
1375    *         space from beginning of the buffer. If the text type was visual_LTR \r
1376    *         and the option SPACES_RELATIVE_TO_TEXT_BEGIN_END was selected\r
1377    *         the spaces will be located at end of buffer.\r
1378    *         If there are no spaces to expand the LamAlef, an exception is thrown.\r
1379 */\r
1380  private boolean expandCompositCharAtBegin(char[] dest,int start, int length,\r
1381                             int lacount) {\r
1382      boolean spaceNotFound = false;\r
1383      \r
1384      if (lacount > countSpacesRight(dest, start, length)) {\r
1385          spaceNotFound = true;\r
1386          return spaceNotFound;\r
1387      }\r
1388      for (int r = start + length - lacount, w = start + length; --r >= start;) {\r
1389          char ch = dest[r];\r
1390          if (isNormalizedLamAlefChar(ch)) {\r
1391              dest[--w] = LAM_CHAR;\r
1392              dest[--w] = convertNormalizedLamAlef[ch - '\u065C'];\r
1393          } else {\r
1394              dest[--w] = ch;\r
1395          }\r
1396      }\r
1397      return spaceNotFound;\r
1398  \r
1399   }\r
1400 \r
1401   /*\r
1402    *Name     : expandCompositCharAtEnd\r
1403    *Function : Expands the LamAlef character to Lam and Alef consuming the \r
1404    *           required space from end of the buffer. If the text type was\r
1405    *           Visual LTR and the option SPACES_RELATIVE_TO_TEXT_BEGIN_END\r
1406    *           was used, the spaces will be consumed from begin of buffer. If \r
1407    *           there are no spaces to expand the LamAlef, an exception is thrown. \r
1408    */\r
1409 \r
1410   private boolean  expandCompositCharAtEnd(char[] dest,int start, int length,\r
1411                           int lacount){\r
1412       boolean spaceNotFound = false;\r
1413       \r
1414       if (lacount > countSpacesLeft(dest, start, length)) {\r
1415           spaceNotFound = true;\r
1416           return spaceNotFound;\r
1417       }\r
1418       for (int r = start + lacount, w = start, e = start + length; r < e; ++r) {\r
1419           char ch = dest[r];\r
1420           if (isNormalizedLamAlefChar(ch)) {\r
1421               dest[w++] = convertNormalizedLamAlef[ch - '\u065C'];\r
1422               dest[w++] = LAM_CHAR;\r
1423           } else {\r
1424               dest[w++] = ch;\r
1425           }\r
1426       }\r
1427       return spaceNotFound;\r
1428   }\r
1429 \r
1430   /*\r
1431    *Name     : expandCompositCharAtNear\r
1432    *Function : Expands the LamAlef character into Lam + Alef, YehHamza character\r
1433    *           into Yeh + Hamza, SeenFamily character into SeenFamily character \r
1434    *           + Tail, while consuming the space next to the character. \r
1435    */\r
1436 \r
1437   private boolean expandCompositCharAtNear(char[] dest,int start, int length,\r
1438                                        int yehHamzaOption, int seenTailOption, int lamAlefOption){\r
1439       \r
1440       boolean spaceNotFound = false;\r
1441       \r
1442       \r
1443       \r
1444       if (isNormalizedLamAlefChar(dest[start])) {\r
1445           spaceNotFound = true;\r
1446           return spaceNotFound;\r
1447       }\r
1448       for (int i = start + length; --i >=start;) { \r
1449           char ch = dest[i];\r
1450           if (lamAlefOption == 1 && isNormalizedLamAlefChar(ch)) {\r
1451               if (i>start &&dest[i-1] == SPACE_CHAR) {\r
1452                   dest[i] = LAM_CHAR;\r
1453                   dest[--i] = convertNormalizedLamAlef[ch - '\u065C'];\r
1454               } else {\r
1455                   spaceNotFound = true;\r
1456                   return spaceNotFound;\r
1457               }\r
1458           }else if(seenTailOption == 1 && isSeenTailFamilyChar(ch) == 1){\r
1459               if(i>start &&dest[i-1] == SPACE_CHAR){\r
1460                   dest[i-1] = tailChar;\r
1461               } else{\r
1462                   spaceNotFound = true;\r
1463                   return spaceNotFound;\r
1464               }\r
1465           }else if(yehHamzaOption == 1 && isYehHamzaChar(ch)){\r
1466               \r
1467                if(i>start &&dest[i-1] == SPACE_CHAR){\r
1468                   dest[i] = yehHamzaToYeh[ch - YEH_HAMZAFE_CHAR];\r
1469                   dest[i-1] = HAMZAFE_CHAR;\r
1470               }else{\r
1471                   spaceNotFound = true;\r
1472                   return spaceNotFound;\r
1473                 }\r
1474               \r
1475               \r
1476           }\r
1477       }\r
1478       return false;\r
1479 \r
1480   }\r
1481     \r
1482     /*\r
1483      * Name    : expandCompositChar\r
1484      * Function: LamAlef needs special handling as the LamAlef is\r
1485      *           one character while expanding it will give two\r
1486      *           characters Lam + Alef, so we need to expand the LamAlef\r
1487      *           in near or far spaces according to the options the user\r
1488      *           specifies or increase the buffer size.\r
1489      *           Dest has enough room for the expansion if we are growing.\r
1490      *           lamalef are normalized to the 'special characters'\r
1491      */\r
1492     private int expandCompositChar(char[] dest,\r
1493                               int start,\r
1494                               int length,\r
1495                               int lacount,\r
1496                               int shapingMode) throws ArabicShapingException {\r
1497 \r
1498         int lenOptionsLamAlef = options & LAMALEF_MASK;\r
1499         int lenOptionsSeen = options & SEEN_MASK;\r
1500         int lenOptionsYehHamza = options & YEHHAMZA_MASK; \r
1501         boolean spaceNotFound = false;\r
1502         \r
1503         if (!isLogical && !spacesRelativeToTextBeginEnd) {\r
1504             switch (lenOptionsLamAlef) {\r
1505             case LAMALEF_BEGIN: lenOptionsLamAlef = LAMALEF_END; break;\r
1506             case LAMALEF_END: lenOptionsLamAlef = LAMALEF_BEGIN; break;\r
1507             default: break;\r
1508             }\r
1509         }\r
1510         \r
1511         if(shapingMode == 1){\r
1512             if(lenOptionsLamAlef == LAMALEF_AUTO){\r
1513                 if(isLogical){\r
1514                     spaceNotFound = expandCompositCharAtEnd(dest, start, length, lacount);\r
1515                     if(spaceNotFound){\r
1516                         spaceNotFound = expandCompositCharAtBegin(dest, start, length, lacount);\r
1517                     }\r
1518                     if(spaceNotFound){\r
1519                         spaceNotFound = expandCompositCharAtNear(dest, start, length,0,0,1);\r
1520                     }\r
1521                     if(spaceNotFound){\r
1522                         throw new ArabicShapingException("No spacefor lamalef");\r
1523                     }\r
1524                 }else{\r
1525                     spaceNotFound = expandCompositCharAtBegin(dest, start, length, lacount);\r
1526                     if(spaceNotFound){\r
1527                         spaceNotFound = expandCompositCharAtEnd(dest, start, length, lacount);\r
1528                     }\r
1529                     if(spaceNotFound){\r
1530                         spaceNotFound = expandCompositCharAtNear(dest, start, length,0,0,1);\r
1531                     }\r
1532                     if(spaceNotFound){\r
1533                         throw new ArabicShapingException("No spacefor lamalef");\r
1534                     }\r
1535                 }\r
1536             }else if(lenOptionsLamAlef == LAMALEF_END){\r
1537                 spaceNotFound = expandCompositCharAtEnd(dest, start, length, lacount);\r
1538                 if(spaceNotFound){\r
1539                     throw new ArabicShapingException("No spacefor lamalef");\r
1540                 }\r
1541             }else if(lenOptionsLamAlef == LAMALEF_BEGIN){\r
1542                 spaceNotFound = expandCompositCharAtBegin(dest, start, length, lacount);\r
1543                 if(spaceNotFound){\r
1544                     throw new ArabicShapingException("No spacefor lamalef");\r
1545                 }                \r
1546             }else if(lenOptionsLamAlef == LAMALEF_NEAR){\r
1547                 spaceNotFound = expandCompositCharAtNear(dest, start, length,0,0,1);\r
1548                 if(spaceNotFound){\r
1549                     throw new ArabicShapingException("No spacefor lamalef");                \r
1550             }\r
1551             }else if(lenOptionsLamAlef == LAMALEF_RESIZE){\r
1552                 for (int r = start + length, w = r + lacount; --r >= start;) {\r
1553                     char ch = dest[r];\r
1554                     if (isNormalizedLamAlefChar(ch)) {\r
1555                         dest[--w] = '\u0644';\r
1556                         dest[--w] = convertNormalizedLamAlef[ch - '\u065C'];\r
1557                     } else {\r
1558                         dest[--w] = ch;\r
1559                     }\r
1560                 }\r
1561                 length += lacount;\r
1562             }\r
1563             }else{\r
1564                 if(lenOptionsSeen == SEEN_TWOCELL_NEAR){\r
1565                 spaceNotFound = expandCompositCharAtNear(dest, start, length,0,1,0);\r
1566                 if(spaceNotFound){\r
1567                     throw new ArabicShapingException("No space for Seen tail expansion"); \r
1568                 }\r
1569             }\r
1570             if(lenOptionsYehHamza == YEHHAMZA_TWOCELL_NEAR){\r
1571                 spaceNotFound = expandCompositCharAtNear(dest, start, length,1,0,0);\r
1572                 if(spaceNotFound){\r
1573                     throw new ArabicShapingException("No space for YehHamza expansion"); \r
1574                 }\r
1575             }   \r
1576             }\r
1577         return length;\r
1578     }\r
1579 \r
1580     \r
1581     /* Convert the input buffer from FExx Range into 06xx Range\r
1582      * to put all characters into the 06xx range\r
1583      * even the lamalef is converted to the special region in\r
1584      * the 06xx range.  Return the number of lamalef chars found.\r
1585      */\r
1586     private int normalize(char[] dest, int start, int length) {\r
1587         int lacount = 0;\r
1588         for (int i = start, e = i + length; i < e; ++i) {\r
1589             char ch = dest[i];\r
1590             if (ch >= '\uFE70' && ch <= '\uFEFC') {\r
1591                 if (isLamAlefChar(ch)) {\r
1592                     ++lacount;\r
1593                 }\r
1594                 dest[i] = (char)convertFEto06[ch - '\uFE70'];\r
1595             }\r
1596         }\r
1597         return lacount;\r
1598     }\r
1599 \r
1600     /*\r
1601      * Name    : deshapeNormalize\r
1602      * Function: Convert the input buffer from FExx Range into 06xx Range\r
1603      *           even the lamalef is converted to the special region in the 06xx range.  \r
1604      *           According to the options the user enters, all seen family characters \r
1605      *           followed by a tail character are merged to seen tail family character and \r
1606      *           any yeh followed by a hamza character are merged to yehhamza character.\r
1607      *           Method returns the number of lamalef chars found.\r
1608      */\r
1609     private int deshapeNormalize(char[] dest, int start, int length) {\r
1610         int lacount = 0;\r
1611         int yehHamzaComposeEnabled = 0;\r
1612         int seenComposeEnabled = 0;\r
1613 \r
1614         yehHamzaComposeEnabled = ((options&YEHHAMZA_MASK) == YEHHAMZA_TWOCELL_NEAR) ? 1 : 0;\r
1615         seenComposeEnabled = ((options&SEEN_MASK) == SEEN_TWOCELL_NEAR)? 1 : 0;\r
1616     \r
1617         for (int i = start, e = i + length; i < e; ++i) {\r
1618             char ch = dest[i];\r
1619         \r
1620         if( (yehHamzaComposeEnabled == 1) && ((ch == HAMZA06_CHAR) || (ch == HAMZAFE_CHAR)) \r
1621                && (i < (length - 1)) && isAlefMaksouraChar(dest[i+1] )) {\r
1622                 dest[i] = SPACE_CHAR;\r
1623                 dest[i+1] = YEH_HAMZA_CHAR;\r
1624        } else if ( (seenComposeEnabled == 1) && (isTailChar(ch)) && (i< (length - 1)) \r
1625                        && (isSeenTailFamilyChar(dest[i+1])==1) ) {\r
1626                dest[i] = SPACE_CHAR;\r
1627        }\r
1628        else if (ch >= '\uFE70' && ch <= '\uFEFC') {\r
1629                 if (isLamAlefChar(ch)) {\r
1630                     ++lacount;\r
1631                 }\r
1632                 dest[i] = (char)convertFEto06[ch - '\uFE70'];\r
1633             }\r
1634         }\r
1635         return lacount;\r
1636     }\r
1637 \r
1638     /*\r
1639      * Name    : shapeUnicode\r
1640      * Function: Converts an Arabic Unicode buffer in 06xx Range into a shaped\r
1641      *           arabic Unicode buffer in FExx Range\r
1642      */\r
1643     private int shapeUnicode(char[] dest, \r
1644                              int start,\r
1645                              int length,\r
1646                              int destSize,\r
1647                              int tashkeelFlag)throws ArabicShapingException {\r
1648         \r
1649         int lamalef_count = normalize(dest, start, length);\r
1650 \r
1651         // resolve the link between the characters.\r
1652         // Arabic characters have four forms: Isolated, Initial, Medial and Final.\r
1653         // Tashkeel characters have two, isolated or medial, and sometimes only isolated.\r
1654         // tashkeelFlag == 0: shape normally, 1: shape isolated, 2: don't shape\r
1655 \r
1656         boolean lamalef_found = false, seenfam_found = false;\r
1657         boolean yehhamza_found = false, tashkeel_found = false;\r
1658         int i = start + length - 1;\r
1659         int currLink = getLink(dest[i]);\r
1660         int nextLink = 0;\r
1661         int prevLink = 0;\r
1662         int lastLink = 0;\r
1663         //int prevPos = i;\r
1664         int lastPos = i;\r
1665         int nx = -2;\r
1666         int nw = 0;\r
1667 \r
1668         while (i >= 0) {\r
1669             // If high byte of currLink > 0 then there might be more than one shape\r
1670             if ((currLink & '\uFF00') > 0 || isTashkeelChar(dest[i])) {\r
1671                 nw = i - 1;\r
1672                 nx = -2;\r
1673                 while (nx < 0) { // we need to know about next char\r
1674                     if (nw == -1) {\r
1675                         nextLink = 0;\r
1676                         nx = Integer.MAX_VALUE;\r
1677                     } else {\r
1678                         nextLink = getLink(dest[nw]);\r
1679                         if ((nextLink & IRRELEVANT) == 0) {\r
1680                             nx = nw;\r
1681                         } else {\r
1682                             --nw;\r
1683                         }\r
1684                     }\r
1685                 }\r
1686 \r
1687                 if (((currLink & ALEFTYPE) > 0) && ((lastLink & LAMTYPE) > 0)) {\r
1688                     lamalef_found = true; \r
1689                     char wLamalef = changeLamAlef(dest[i]); // get from 0x065C-0x065f\r
1690                     if (wLamalef != '\u0000') {\r
1691                         // replace alef by marker, it will be removed later\r
1692                         dest[i] = '\uffff';\r
1693                         dest[lastPos] = wLamalef;\r
1694                         i = lastPos;\r
1695                     }\r
1696 \r
1697                     lastLink = prevLink;\r
1698                     currLink = getLink(wLamalef); // requires '\u0000', unfortunately\r
1699                 }\r
1700                 if ((i > 0) && (dest[i-1] == SPACE_CHAR))\r
1701                 { \r
1702                     if ( isSeenFamilyChar(dest[i]) == 1){\r
1703                         seenfam_found = true;\r
1704                     } else if (dest[i] == YEH_HAMZA_CHAR) {\r
1705                         yehhamza_found = true;\r
1706                     }\r
1707                 }\r
1708                 else if(i==0){\r
1709                     if ( isSeenFamilyChar(dest[i]) == 1){\r
1710                         seenfam_found = true;\r
1711                     } else if (dest[i] == YEH_HAMZA_CHAR) {\r
1712                         yehhamza_found = true;\r
1713                     }\r
1714                 }\r
1715 \r
1716 \r
1717                 // get the proper shape according to link ability of neighbors\r
1718                 // and of character; depends on the order of the shapes\r
1719                 // (isolated, initial, middle, final) in the compatibility area\r
1720 \r
1721                 int flag = specialChar(dest[i]);\r
1722 \r
1723                 int shape = shapeTable[nextLink & LINK_MASK]\r
1724                     [lastLink & LINK_MASK]\r
1725                     [currLink & LINK_MASK];\r
1726 \r
1727                 if (flag == 1) {\r
1728                     shape &= 0x1;\r
1729                 } else if (flag == 2) {\r
1730                     if (tashkeelFlag == 0 &&\r
1731                         ((lastLink & LINKL) != 0) && \r
1732                         ((nextLink & LINKR) != 0) && \r
1733                         dest[i] != '\u064C' && \r
1734                         dest[i] != '\u064D' &&\r
1735                         !((nextLink & ALEFTYPE) == ALEFTYPE && \r
1736                           (lastLink & LAMTYPE) == LAMTYPE)) {\r
1737         \r
1738                         shape = 1;\r
1739                     } else {\r
1740                         shape = 0;\r
1741                     }\r
1742                 }\r
1743                 if (flag == 2) {\r
1744                     if (tashkeelFlag == 2) {\r
1745                         dest[i] = TASHKEEL_SPACE_SUB;\r
1746                         tashkeel_found = true;\r
1747                     }\r
1748                     else{\r
1749                         dest[i] = (char)('\uFE70' + irrelevantPos[dest[i] - '\u064B'] + shape);\r
1750                     }\r
1751                     // else leave tashkeel alone                    \r
1752                 } else {\r
1753                     dest[i] = (char)('\uFE70' + (currLink >> 8) + shape);\r
1754                 }\r
1755             }\r
1756 \r
1757             // move one notch forward\r
1758             if ((currLink & IRRELEVANT) == 0) {\r
1759                 prevLink = lastLink;\r
1760                 lastLink = currLink;\r
1761                 //prevPos = lastPos;\r
1762                 lastPos = i;\r
1763             }\r
1764 \r
1765             --i;\r
1766             if (i == nx) {\r
1767                 currLink = nextLink;\r
1768                 nx = -2;\r
1769             } else if (i != -1) {\r
1770                 currLink = getLink(dest[i]);\r
1771             }\r
1772         }\r
1773 \r
1774         // If we found a lam/alef pair in the buffer \r
1775         // call handleGeneratedSpaces to remove the spaces that were added\r
1776 \r
1777         destSize = length;\r
1778         if (lamalef_found || tashkeel_found) {\r
1779             destSize = handleGeneratedSpaces(dest, start, length);\r
1780         }\r
1781         if (seenfam_found || yehhamza_found){\r
1782             destSize = expandCompositChar(dest, start, destSize, lamalef_count, SHAPE_MODE);\r
1783         }\r
1784         return destSize;\r
1785     }\r
1786 \r
1787     /*\r
1788      * Name    : deShapeUnicode\r
1789      * Function: Converts an Arabic Unicode buffer in FExx Range into unshaped\r
1790      *           arabic Unicode buffer in 06xx Range\r
1791      */\r
1792     private int deShapeUnicode(char[] dest, \r
1793                                int start,\r
1794                                int length,\r
1795                                int destSize) throws ArabicShapingException {\r
1796 \r
1797         int lamalef_count = deshapeNormalize(dest, start, length); \r
1798 \r
1799         // If there was a lamalef in the buffer call expandLamAlef\r
1800         if (lamalef_count != 0) {\r
1801             // need to adjust dest to fit expanded buffer... !!!\r
1802             destSize = expandCompositChar(dest, start, length, lamalef_count,DESHAPE_MODE); \r
1803         } else {\r
1804             destSize = length;\r
1805         }\r
1806 \r
1807         return destSize;\r
1808     }\r
1809 \r
1810     private int internalShape(char[] source, \r
1811                               int sourceStart,\r
1812                               int sourceLength,\r
1813                               char[] dest,\r
1814                               int destStart,\r
1815                               int destSize) throws ArabicShapingException {\r
1816 \r
1817         if (sourceLength == 0) {\r
1818             return 0;\r
1819         }\r
1820 \r
1821         if (destSize == 0) {\r
1822             if (((options & LETTERS_MASK) != LETTERS_NOOP) &&\r
1823                 ((options & LAMALEF_MASK) == LAMALEF_RESIZE)) {\r
1824     \r
1825                 return calculateSize(source, sourceStart, sourceLength);\r
1826             } else {\r
1827                 return sourceLength; // by definition\r
1828             }\r
1829         }\r
1830 \r
1831         // always use temp buffer\r
1832         char[] temp = new char[sourceLength * 2]; // all lamalefs requiring expansion\r
1833         System.arraycopy(source, sourceStart, temp, 0, sourceLength);\r
1834 \r
1835         if (isLogical) {\r
1836             invertBuffer(temp, 0, sourceLength);\r
1837         }\r
1838 \r
1839         int outputSize = sourceLength;\r
1840 \r
1841         switch (options & LETTERS_MASK) {\r
1842         case LETTERS_SHAPE_TASHKEEL_ISOLATED:\r
1843             outputSize = shapeUnicode(temp, 0, sourceLength, destSize, 1);\r
1844             break;\r
1845 \r
1846         case LETTERS_SHAPE:\r
1847             if( ((options&TASHKEEL_MASK)> 0) && \r
1848                 ((options&TASHKEEL_MASK) !=TASHKEEL_REPLACE_BY_TATWEEL)) {\r
1849                    /* Call the shaping function with tashkeel flag == 2 for removal of tashkeel */\r
1850                 outputSize = shapeUnicode(temp, 0, sourceLength, destSize, 2);\r
1851                 }else {\r
1852                    //default Call the shaping function with tashkeel flag == 1 */\r
1853                     outputSize = shapeUnicode(temp, 0, sourceLength, destSize, 0);\r
1854 \r
1855                    /*After shaping text check if user wants to remove tashkeel and replace it with tatweel*/\r
1856                    if( (options&TASHKEEL_MASK) == TASHKEEL_REPLACE_BY_TATWEEL){\r
1857                        outputSize = handleTashkeelWithTatweel(temp,sourceLength);\r
1858                    }\r
1859                }\r
1860             break;\r
1861 \r
1862         case LETTERS_UNSHAPE:\r
1863             outputSize = deShapeUnicode(temp, 0, sourceLength, destSize);\r
1864             break; \r
1865 \r
1866         default:\r
1867             break;\r
1868         }\r
1869                 \r
1870         if (outputSize > destSize) {\r
1871             throw new ArabicShapingException("not enough room for result data");\r
1872         }\r
1873 \r
1874         if ((options & DIGITS_MASK) != DIGITS_NOOP) {\r
1875             char digitBase = '\u0030'; // European digits\r
1876             switch (options & DIGIT_TYPE_MASK) {\r
1877             case DIGIT_TYPE_AN:\r
1878                 digitBase = '\u0660';  // Arabic-Indic digits\r
1879                 break;\r
1880 \r
1881             case DIGIT_TYPE_AN_EXTENDED:\r
1882                 digitBase = '\u06f0';  // Eastern Arabic-Indic digits (Persian and Urdu)\r
1883                 break;\r
1884 \r
1885             default:\r
1886                 break;\r
1887             }\r
1888 \r
1889             switch (options & DIGITS_MASK) {\r
1890             case DIGITS_EN2AN:\r
1891                 {\r
1892                     int digitDelta = digitBase - '\u0030';\r
1893                     for (int i = 0; i < outputSize; ++i) {\r
1894                         char ch = temp[i];\r
1895                         if (ch <= '\u0039' && ch >= '\u0030') {\r
1896                             temp[i] += digitDelta;\r
1897                         }\r
1898                     }\r
1899                 }\r
1900                 break;\r
1901 \r
1902             case DIGITS_AN2EN:\r
1903                 {\r
1904                     char digitTop = (char)(digitBase + 9);\r
1905                     int digitDelta = '\u0030' - digitBase;\r
1906                     for (int i = 0; i < outputSize; ++i) {\r
1907                         char ch = temp[i];\r
1908                         if (ch <= digitTop && ch >= digitBase) {\r
1909                             temp[i] += digitDelta;\r
1910                         }\r
1911                     }\r
1912                 }\r
1913                 break;\r
1914 \r
1915             case DIGITS_EN2AN_INIT_LR:\r
1916                 shapeToArabicDigitsWithContext(temp, 0, outputSize, digitBase, false);\r
1917                 break;\r
1918 \r
1919             case DIGITS_EN2AN_INIT_AL:\r
1920                 shapeToArabicDigitsWithContext(temp, 0, outputSize, digitBase, true);\r
1921                 break;\r
1922 \r
1923             default:\r
1924                 break;\r
1925             }\r
1926         }\r
1927 \r
1928         if (isLogical) {\r
1929             invertBuffer(temp, 0, outputSize);\r
1930         }\r
1931       \r
1932         System.arraycopy(temp, 0, dest, destStart, outputSize);\r
1933       \r
1934         return outputSize;\r
1935     }\r
1936 }\r