1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 /*
3 *******************************************************************************
4 *   Copyright (C) 2001-2010, International Business Machines
5 *   Corporation and others.  All Rights Reserved.
6 *******************************************************************************
7 */
8 /* Written by Simon Montagu, Matitiahu Allouche
9  * (ported from C code written by Markus W. Scherer)
10  */
11 
12 package android.icu.text;
13 
14 import android.icu.lang.UCharacter;
15 
16 final class BidiWriter {
17 
18     /** Bidi control code points */
19     static final char LRM_CHAR = 0x200e;
20     static final char RLM_CHAR = 0x200f;
21     static final int MASK_R_AL = (1 << UCharacter.RIGHT_TO_LEFT |
22                                   1 << UCharacter.RIGHT_TO_LEFT_ARABIC);
23 
IsCombining(int type)24     private static boolean IsCombining(int type)
25     {
26         return ((1<<type &
27                 (1<<UCharacter.NON_SPACING_MARK |
28                  1<<UCharacter.COMBINING_SPACING_MARK |
29                  1<<UCharacter.ENCLOSING_MARK)) != 0);
30     }
31 
32     /*
33      * When we have OUTPUT_REVERSE set on writeReordered(), then we
34      * semantically write RTL runs in reverse and later reverse them again.
35      * Instead, we actually write them in forward order to begin with.
36      * However, if the RTL run was to be mirrored, we need to mirror here now
37      * since the implicit second reversal must not do it.
38      * It looks strange to do mirroring in LTR output, but it is only because
39      * we are writing RTL output in reverse.
40      */
doWriteForward(String src, int options)41     private static String doWriteForward(String src, int options) {
42         /* optimize for several combinations of options */
43         switch(options&(Bidi.REMOVE_BIDI_CONTROLS|Bidi.DO_MIRRORING)) {
44         case 0: {
45             /* simply return the LTR run */
46             return src;
47         }
48         case Bidi.DO_MIRRORING: {
49             StringBuffer dest = new StringBuffer(src.length());
50 
51             /* do mirroring */
52             int i=0;
53             int c;
54 
55             do {
56                 c = UTF16.charAt(src, i);
57                 i += UTF16.getCharCount(c);
58                 UTF16.append(dest, UCharacter.getMirror(c));
59             } while(i < src.length());
60             return dest.toString();
61         }
62         case Bidi.REMOVE_BIDI_CONTROLS: {
63             StringBuilder dest = new StringBuilder(src.length());
64 
65             /* copy the LTR run and remove any Bidi control characters */
66             int i = 0;
67             char c;
68             do {
69                 c = src.charAt(i++);
70                 if(!Bidi.IsBidiControlChar(c)) {
71                     dest.append(c);
72                 }
73             } while(i < src.length());
74             return dest.toString();
75         }
76         default: {
77             StringBuffer dest = new StringBuffer(src.length());
78 
79             /* remove Bidi control characters and do mirroring */
80             int i = 0;
81             int c;
82             do {
83                 c = UTF16.charAt(src, i);
84                 i += UTF16.getCharCount(c);
85                 if(!Bidi.IsBidiControlChar(c)) {
86                     UTF16.append(dest, UCharacter.getMirror(c));
87                 }
88             } while(i < src.length());
89             return dest.toString();
90         }
91         } /* end of switch */
92     }
93 
doWriteForward(char[] text, int start, int limit, int options)94     private static String doWriteForward(char[] text, int start, int limit,
95                                          int options)
96     {
97         return doWriteForward(new String(text, start, limit - start), options);
98     }
99 
writeReverse(String src, int options)100     static String writeReverse(String src, int options) {
101         /*
102          * RTL run -
103          *
104          * RTL runs need to be copied to the destination in reverse order
105          * of code points, not code units, to keep Unicode characters intact.
106          *
107          * The general strategy for this is to read the source text
108          * in backward order, collect all code units for a code point
109          * (and optionally following combining characters, see below),
110          * and copy all these code units in ascending order
111          * to the destination for this run.
112          *
113          * Several options request whether combining characters
114          * should be kept after their base characters,
115          * whether Bidi control characters should be removed, and
116          * whether characters should be replaced by their mirror-image
117          * equivalent Unicode characters.
118          */
119         StringBuffer dest = new StringBuffer(src.length());
120 
121         /* optimize for several combinations of options */
122         switch (options &
123                 (Bidi.REMOVE_BIDI_CONTROLS |
124                  Bidi.DO_MIRRORING |
125                  Bidi.KEEP_BASE_COMBINING)) {
126 
127         case 0:
128             /*
129              * With none of the "complicated" options set, the destination
130              * run will have the same length as the source run,
131              * and there is no mirroring and no keeping combining characters
132              * with their base characters.
133              *
134              * XXX: or dest = UTF16.reverse(new StringBuffer(src));
135              */
136 
137             int srcLength = src.length();
138 
139             /* preserve character integrity */
140             do {
141                 /* i is always after the last code unit known to need to be kept
142                  *  in this segment */
143                 int i = srcLength;
144 
145                 /* collect code units for one base character */
146                 srcLength -= UTF16.getCharCount(UTF16.charAt(src,
147                                                              srcLength - 1));
148 
149                 /* copy this base character */
150                 dest.append(src.substring(srcLength, i));
151             } while(srcLength > 0);
152             break;
153 
154         case Bidi.KEEP_BASE_COMBINING:
155             /*
156              * Here, too, the destination
157              * run will have the same length as the source run,
158              * and there is no mirroring.
159              * We do need to keep combining characters with their base
160              * characters.
161              */
162             srcLength = src.length();
163 
164             /* preserve character integrity */
165             do {
166                 /* i is always after the last code unit known to need to be kept
167                  *  in this segment */
168                 int c;
169                 int i = srcLength;
170 
171                 /* collect code units and modifier letters for one base
172                  * character */
173                 do {
174                     c = UTF16.charAt(src, srcLength - 1);
175                     srcLength -= UTF16.getCharCount(c);
176                 } while(srcLength > 0 && IsCombining(UCharacter.getType(c)));
177 
178                 /* copy this "user character" */
179                 dest.append(src.substring(srcLength, i));
180             } while(srcLength > 0);
181             break;
182 
183         default:
184             /*
185              * With several "complicated" options set, this is the most
186              * general and the slowest copying of an RTL run.
187              * We will do mirroring, remove Bidi controls, and
188              * keep combining characters with their base characters
189              * as requested.
190              */
191             srcLength = src.length();
192 
193             /* preserve character integrity */
194             do {
195                 /* i is always after the last code unit known to need to be kept
196                  *  in this segment */
197                 int i = srcLength;
198 
199                 /* collect code units for one base character */
200                 int c = UTF16.charAt(src, srcLength - 1);
201                 srcLength -= UTF16.getCharCount(c);
202                 if ((options & Bidi.KEEP_BASE_COMBINING) != 0) {
203                     /* collect modifier letters for this base character */
204                     while(srcLength > 0 && IsCombining(UCharacter.getType(c))) {
205                         c = UTF16.charAt(src, srcLength - 1);
206                         srcLength -= UTF16.getCharCount(c);
207                     }
208                 }
209 
210                 if ((options & Bidi.REMOVE_BIDI_CONTROLS) != 0 &&
211                     Bidi.IsBidiControlChar(c)) {
212                     /* do not copy this Bidi control character */
213                     continue;
214                 }
215 
216                 /* copy this "user character" */
217                 int j = srcLength;
218                 if((options & Bidi.DO_MIRRORING) != 0) {
219                     /* mirror only the base character */
220                     c = UCharacter.getMirror(c);
221                     UTF16.append(dest, c);
222                     j += UTF16.getCharCount(c);
223                 }
224                 dest.append(src.substring(j, i));
225             } while(srcLength > 0);
226             break;
227         } /* end of switch */
228 
229         return dest.toString();
230     }
231 
doWriteReverse(char[] text, int start, int limit, int options)232     static String doWriteReverse(char[] text, int start, int limit, int options)
233     {
234         return writeReverse(new String(text, start, limit - start), options);
235     }
236 
writeReordered(Bidi bidi, int options)237     static String writeReordered(Bidi bidi, int options)
238     {
239         int run, runCount;
240         StringBuilder dest;
241         char[] text = bidi.text;
242         runCount = bidi.countRuns();
243 
244         /*
245          * Option "insert marks" implies Bidi.INSERT_LRM_FOR_NUMERIC if the
246          * reordering mode (checked below) is appropriate.
247          */
248         if ((bidi.reorderingOptions & Bidi.OPTION_INSERT_MARKS) != 0) {
249             options |= Bidi.INSERT_LRM_FOR_NUMERIC;
250             options &= ~Bidi.REMOVE_BIDI_CONTROLS;
251         }
252         /*
253          * Option "remove controls" implies Bidi.REMOVE_BIDI_CONTROLS
254          * and cancels Bidi.INSERT_LRM_FOR_NUMERIC.
255          */
256         if ((bidi.reorderingOptions & Bidi.OPTION_REMOVE_CONTROLS) != 0) {
257             options |= Bidi.REMOVE_BIDI_CONTROLS;
258             options &= ~Bidi.INSERT_LRM_FOR_NUMERIC;
259         }
260         /*
261          * If we do not perform the "inverse Bidi" algorithm, then we
262          * don't need to insert any LRMs, and don't need to test for it.
263          */
264         if ((bidi.reorderingMode != Bidi.REORDER_INVERSE_NUMBERS_AS_L) &&
265             (bidi.reorderingMode != Bidi.REORDER_INVERSE_LIKE_DIRECT)  &&
266             (bidi.reorderingMode != Bidi.REORDER_INVERSE_FOR_NUMBERS_SPECIAL) &&
267             (bidi.reorderingMode != Bidi.REORDER_RUNS_ONLY)) {
268             options &= ~Bidi.INSERT_LRM_FOR_NUMERIC;
269         }
270         dest = new StringBuilder((options & Bidi.INSERT_LRM_FOR_NUMERIC) != 0 ?
271                                  bidi.length * 2 : bidi.length);
272         /*
273          * Iterate through all visual runs and copy the run text segments to
274          * the destination, according to the options.
275          *
276          * The tests for where to insert LRMs ignore the fact that there may be
277          * BN codes or non-BMP code points at the beginning and end of a run;
278          * they may insert LRMs unnecessarily but the tests are faster this way
279          * (this would have to be improved for UTF-8).
280          */
281         if ((options & Bidi.OUTPUT_REVERSE) == 0) {
282             /* forward output */
283             if ((options & Bidi.INSERT_LRM_FOR_NUMERIC) == 0) {
284                 /* do not insert Bidi controls */
285                 for (run = 0; run < runCount; ++run) {
286                     BidiRun bidiRun = bidi.getVisualRun(run);
287                     if (bidiRun.isEvenRun()) {
288                         dest.append(doWriteForward(text, bidiRun.start,
289                                                    bidiRun.limit,
290                                                    options & ~Bidi.DO_MIRRORING));
291                      } else {
292                         dest.append(doWriteReverse(text, bidiRun.start,
293                                                    bidiRun.limit, options));
294                      }
295                 }
296             } else {
297                 /* insert Bidi controls for "inverse Bidi" */
298                 byte[] dirProps = bidi.dirProps;
299                 char uc;
300                 int markFlag;
301 
302                 for (run = 0; run < runCount; ++run) {
303                     BidiRun bidiRun = bidi.getVisualRun(run);
304                     markFlag=0;
305                     /* check if something relevant in insertPoints */
306                     markFlag = bidi.runs[run].insertRemove;
307                     if (markFlag < 0) { /* bidi controls count */
308                         markFlag = 0;
309                     }
310                     if (bidiRun.isEvenRun()) {
311                         if (bidi.isInverse() &&
312                                 dirProps[bidiRun.start] != Bidi.L) {
313                             markFlag |= Bidi.LRM_BEFORE;
314                         }
315                         if ((markFlag & Bidi.LRM_BEFORE) != 0) {
316                             uc = LRM_CHAR;
317                         } else if ((markFlag & Bidi.RLM_BEFORE) != 0) {
318                             uc = RLM_CHAR;
319                         } else {
320                             uc = 0;
321                         }
322                         if (uc != 0) {
323                             dest.append(uc);
324                         }
325                         dest.append(doWriteForward(text,
326                                                    bidiRun.start, bidiRun.limit,
327                                                    options & ~Bidi.DO_MIRRORING));
328 
329                         if (bidi.isInverse() &&
330                              dirProps[bidiRun.limit - 1] != Bidi.L) {
331                             markFlag |= Bidi.LRM_AFTER;
332                         }
333                         if ((markFlag & Bidi.LRM_AFTER) != 0) {
334                             uc = LRM_CHAR;
335                         } else if ((markFlag & Bidi.RLM_AFTER) != 0) {
336                             uc = RLM_CHAR;
337                         } else {
338                             uc = 0;
339                         }
340                         if (uc != 0) {
341                             dest.append(uc);
342                         }
343                     } else { /* RTL run */
344                         if (bidi.isInverse() &&
345                             !bidi.testDirPropFlagAt(MASK_R_AL,
346                                                     bidiRun.limit - 1)) {
347                             markFlag |= Bidi.RLM_BEFORE;
348                         }
349                         if ((markFlag & Bidi.LRM_BEFORE) != 0) {
350                             uc = LRM_CHAR;
351                         } else if ((markFlag & Bidi.RLM_BEFORE) != 0) {
352                             uc = RLM_CHAR;
353                         } else {
354                             uc = 0;
355                         }
356                         if (uc != 0) {
357                             dest.append(uc);
358                         }
359                         dest.append(doWriteReverse(text, bidiRun.start,
360                                                    bidiRun.limit, options));
361 
362                         if(bidi.isInverse() &&
363                                 (MASK_R_AL & Bidi.DirPropFlag(dirProps[bidiRun.start])) == 0) {
364                             markFlag |= Bidi.RLM_AFTER;
365                         }
366                         if ((markFlag & Bidi.LRM_AFTER) != 0) {
367                             uc = LRM_CHAR;
368                         } else if ((markFlag & Bidi.RLM_AFTER) != 0) {
369                             uc = RLM_CHAR;
370                         } else {
371                             uc = 0;
372                         }
373                         if (uc != 0) {
374                             dest.append(uc);
375                         }
376                     }
377                 }
378             }
379         } else {
380             /* reverse output */
381             if((options & Bidi.INSERT_LRM_FOR_NUMERIC) == 0) {
382                 /* do not insert Bidi controls */
383                 for(run = runCount; --run >= 0; ) {
384                     BidiRun bidiRun = bidi.getVisualRun(run);
385                     if (bidiRun.isEvenRun()) {
386                         dest.append(doWriteReverse(text,
387                                                    bidiRun.start, bidiRun.limit,
388                                                    options & ~Bidi.DO_MIRRORING));
389                     } else {
390                         dest.append(doWriteForward(text, bidiRun.start,
391                                                    bidiRun.limit, options));
392                     }
393                 }
394             } else {
395                 /* insert Bidi controls for "inverse Bidi" */
396 
397                 byte[] dirProps = bidi.dirProps;
398 
399                 for (run = runCount; --run >= 0; ) {
400                     /* reverse output */
401                     BidiRun bidiRun = bidi.getVisualRun(run);
402                     if (bidiRun.isEvenRun()) {
403                         if (dirProps[bidiRun.limit - 1] != Bidi.L) {
404                             dest.append(LRM_CHAR);
405                         }
406 
407                         dest.append(doWriteReverse(text, bidiRun.start,
408                                 bidiRun.limit, options & ~Bidi.DO_MIRRORING));
409 
410                         if (dirProps[bidiRun.start] != Bidi.L) {
411                             dest.append(LRM_CHAR);
412                         }
413                     } else {
414                         if ((MASK_R_AL & Bidi.DirPropFlag(dirProps[bidiRun.start])) == 0) {
415                             dest.append(RLM_CHAR);
416                         }
417 
418                         dest.append(doWriteForward(text, bidiRun.start,
419                                                    bidiRun.limit, options));
420 
421                         if ((MASK_R_AL & Bidi.DirPropFlag(dirProps[bidiRun.limit - 1])) == 0) {
422                             dest.append(RLM_CHAR);
423                         }
424                     }
425                 }
426             }
427         }
428 
429         return dest.toString();
430     }
431 }
432