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