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