Home | History | Annotate | Download | only in text
      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 
     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      */
     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 
     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 
    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 
    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 
    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