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