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