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 package android.icu.text; 6 7 import android.icu.lang.UCharacter; 8 9 /** 10 * Bidi Layout Transformation Engine. 11 * 12 * @author Lina Kemmel 13 * 14 * @hide Only a subset of ICU is exposed in Android 15 * @hide draft / provisional / internal are hidden on Android 16 */ 17 public class BidiTransform 18 { 19 /** 20 * <code>{@link Order}</code> indicates the order of text. 21 * <p> 22 * This bidi transformation engine supports all possible combinations (4 in 23 * total) of input and output text order: 24 * <ul> 25 * <li>{logical input, visual output}: unless the output direction is RTL, 26 * this corresponds to a normal operation of the Bidi algorithm as 27 * described in the Unicode Technical Report and implemented by 28 * <code>{@link Bidi}</code> when the reordering mode is set to 29 * <code>Bidi#REORDER_DEFAULT</code>. Visual RTL mode is not supported by 30 * <code>{@link Bidi}</code> and is accomplished through reversing a visual 31 * LTR string,</li> 32 * <li>{visual input, logical output}: unless the input direction is RTL, 33 * this corresponds to an "inverse bidi algorithm" in 34 * <code>{@link Bidi}</code> with the reordering mode set to 35 * <code>{@link Bidi#REORDER_INVERSE_LIKE_DIRECT}</code>. Visual RTL mode 36 * is not not supported by <code>{@link Bidi}</code> and is accomplished 37 * through reversing a visual LTR string,</li> 38 * <li>{logical input, logical output}: if the input and output base 39 * directions mismatch, this corresponds to the <code>{@link Bidi}</code> 40 * implementation with the reordering mode set to 41 * <code>{@link Bidi#REORDER_RUNS_ONLY}</code>; and if the input and output 42 * base directions are identical, the transformation engine will only 43 * handle character mirroring and Arabic shaping operations without 44 * reordering,</li> 45 * <li>{visual input, visual output}: this reordering mode is not supported 46 * by the <code>{@link Bidi}</code> engine; it implies character mirroring, 47 * Arabic shaping, and - if the input/output base directions mismatch - 48 * string reverse operations.</li> 49 * </ul> 50 * 51 * @see Bidi#setInverse 52 * @see Bidi#setReorderingMode 53 * @see Bidi#REORDER_DEFAULT 54 * @see Bidi#REORDER_INVERSE_LIKE_DIRECT 55 * @see Bidi#REORDER_RUNS_ONLY 56 * @hide draft / provisional / internal are hidden on Android 57 */ 58 public enum Order { 59 /** 60 * Constant indicating a logical order. 61 * 62 * @hide draft / provisional / internal are hidden on Android 63 */ 64 LOGICAL, 65 /** 66 * Constant indicating a visual order. 67 * 68 * @hide draft / provisional / internal are hidden on Android 69 */ 70 VISUAL; 71 } 72 73 /** 74 * <code>{@link Mirroring}</code> indicates whether or not characters with 75 * the "mirrored" property in RTL runs should be replaced with their 76 * mirror-image counterparts. 77 * 78 * @see Bidi#DO_MIRRORING 79 * @see Bidi#setReorderingOptions 80 * @see Bidi#writeReordered 81 * @see Bidi#writeReverse 82 * @hide draft / provisional / internal are hidden on Android 83 */ 84 public enum Mirroring { 85 /** 86 * Constant indicating that character mirroring should not be 87 * performed. 88 * 89 * @hide draft / provisional / internal are hidden on Android 90 */ 91 OFF, 92 /** 93 * Constant indicating that character mirroring should be performed. 94 * <p> 95 * This corresponds to calling <code>{@link Bidi#writeReordered}</code> 96 * or <code>{@link Bidi#writeReverse}</code> with the 97 * <code>{@link Bidi#DO_MIRRORING}</code> option bit set. 98 * 99 * @hide draft / provisional / internal are hidden on Android 100 */ 101 ON; 102 } 103 104 private Bidi bidi; 105 private String text; 106 private int reorderingOptions; 107 private int shapingOptions; 108 109 /** 110 * <code>{@link BidiTransform}</code> default constructor. 111 * 112 * @hide draft / provisional / internal are hidden on Android 113 */ 114 public BidiTransform() 115 { 116 } 117 118 /** 119 * Performs transformation of text from the bidi layout defined by the 120 * input ordering scheme to the bidi layout defined by the output ordering 121 * scheme, and applies character mirroring and Arabic shaping operations. 122 * <p> 123 * In terms of <code>{@link Bidi}</code> class, such a transformation 124 * implies: 125 * <ul> 126 * <li>calling <code>{@link Bidi#setReorderingMode}</code> as needed (when 127 * the reordering mode is other than normal),</li> 128 * <li>calling <code>{@link Bidi#setInverse}</code> as needed (when text 129 * should be transformed from a visual to a logical form),</li> 130 * <li>resolving embedding levels of each character in the input text by 131 * calling <code>{@link Bidi#setPara}</code>,</li> 132 * <li>reordering the characters based on the computed embedding levels, 133 * also performing character mirroring as needed, and streaming the result 134 * to the output, by calling <code>{@link Bidi#writeReordered}</code>,</li> 135 * <li>performing Arabic digit and letter shaping on the output text by 136 * calling <code>{@link ArabicShaping#shape}</code>.</li> 137 * </ul><p> 138 * An "ordering scheme" encompasses the base direction and the order of 139 * text, and these characteristics must be defined by the caller for both 140 * input and output explicitly .<p> 141 * There are 36 possible combinations of {input, output} ordering schemes, 142 * which are partially supported by <code>{@link Bidi}</code> already. 143 * Examples of the currently supported combinations: 144 * <ul> 145 * <li>{Logical LTR, Visual LTR}: this is equivalent to calling 146 * <code>{@link Bidi#setPara}</code> with 147 * <code>paraLevel == {@link Bidi#LTR}</code>,</li> 148 * <li>{Logical RTL, Visual LTR}: this is equivalent to calling 149 * <code>{@link Bidi#setPara}</code> with 150 * <code>paraLevel == {@link Bidi#RTL}</code>,</li> 151 * <li>{Logical Default ("Auto") LTR, Visual LTR}: this is equivalent to 152 * calling <code>{@link Bidi#setPara}</code> with 153 * <code>paraLevel == {@link Bidi#LEVEL_DEFAULT_LTR}</code>,</li> 154 * <li>{Logical Default ("Auto") RTL, Visual LTR}: this is equivalent to 155 * calling <code>{@link Bidi#setPara}</code> with 156 * <code>paraLevel == {@link Bidi#LEVEL_DEFAULT_RTL}</code>,</li> 157 * <li>{Visual LTR, Logical LTR}: this is equivalent to 158 * calling <code>{@link Bidi#setInverse}(true)</code> and then 159 * <code>{@link Bidi#setPara}</code> with 160 * <code>paraLevel == {@link Bidi#LTR}</code>,</li> 161 * <li>{Visual LTR, Logical RTL}: this is equivalent to calling 162 * <code>{@link Bidi#setInverse}(true)</code> and then 163 * <code>{@link Bidi#setPara}</code> with 164 * <code>paraLevel == {@link Bidi#RTL}</code>.</li> 165 * </ul><p> 166 * All combinations that involve the Visual RTL scheme are unsupported by 167 * <code>{@link Bidi}</code>, for instance: 168 * <ul> 169 * <li>{Logical LTR, Visual RTL},</li> 170 * <li>{Visual RTL, Logical RTL}.</li> 171 * </ul> 172 * <p>Example of usage of the transformation engine:</p> 173 * <pre> 174 * BidiTransform bidiTransform = new BidiTransform(); 175 * String in = "abc \u06f0123"; // "abc \\u06f0123" 176 * // Run a transformation. 177 * String out = bidiTransform.transform(in, 178 * Bidi.LTR, Order.VISUAL, 179 * Bidi.RTL, Order.LOGICAL, 180 * Mirroring.OFF, 181 * ArabicShaping.DIGITS_AN2EN | ArabicShaping.DIGIT_TYPE_AN_EXTENDED); 182 * // Result: "0123 abc". 183 * // Do something with out. 184 * out = out.replace('0', '4'); 185 * // Result: "4123 abc". 186 * // Run a reverse transformation. 187 * String inNew = bidiTransform.transform(out, 188 * Bidi.RTL, Order.LOGICAL, 189 * Bidi.LTR, Order.VISUAL, 190 * Mirroring.OFF, 191 * ArabicShaping.DIGITS_EN2AN | ArabicShaping.DIGIT_TYPE_AN_EXTENDED); 192 * // Result: "abc \\u06f4\\u06f1\\u06f2\\u06f3" 193 * </pre> 194 * 195 * @param text An input character sequence that the Bidi layout 196 * transformations will be performed on. 197 * @param inParaLevel A base embedding level of the input as defined in 198 * <code>{@link Bidi#setPara(String, byte, byte[])}</code> 199 * documentation for the <code>paraLevel</code> parameter. 200 * @param inOrder An order of the input, which can be one of the 201 * <code>{@link Order}</code> values. 202 * @param outParaLevel A base embedding level of the output as defined in 203 * <code>{@link Bidi#setPara(String, byte, byte[])}</code> 204 * documentation for the <code>paraLevel</code> parameter. 205 * @param outOrder An order of the output, which can be one of the 206 * <code>{@link Order}</code> values. 207 * @param doMirroring Indicates whether or not to perform character 208 * mirroring, and can accept one of the 209 * <code>{@link Mirroring}</code> values. 210 * @param shapingOptions Arabic digit and letter shaping options defined in 211 * the <code>{@link ArabicShaping}</code> documentation. 212 * <p><strong>Note:</strong> Direction indicator options are 213 * computed by the transformation engine based on the effective 214 * ordering schemes, so user-defined direction indicators will be 215 * ignored. 216 * @return The output string, which is the result of the layout 217 * transformation. 218 * @throws IllegalArgumentException if <code>text</code>, 219 * <code>inOrder</code>, <code>outOrder</code>, or 220 * <code>doMirroring</code> parameter is <code>null</code>. 221 * @hide draft / provisional / internal are hidden on Android 222 */ 223 public String transform(CharSequence text, 224 byte inParaLevel, Order inOrder, 225 byte outParaLevel, Order outOrder, 226 Mirroring doMirroring, int shapingOptions) 227 { 228 if (text == null || inOrder == null || outOrder == null || doMirroring == null) { 229 throw new IllegalArgumentException(); 230 } 231 this.text = text.toString(); 232 233 byte[] levels = {inParaLevel, outParaLevel}; 234 resolveBaseDirection(levels); 235 236 ReorderingScheme currentScheme = findMatchingScheme(levels[0], inOrder, 237 levels[1], outOrder); 238 if (currentScheme != null) { 239 this.bidi = new Bidi(); 240 this.reorderingOptions = Mirroring.ON.equals(doMirroring) 241 ? Bidi.DO_MIRRORING : Bidi.REORDER_DEFAULT; 242 243 /* Ignore TEXT_DIRECTION_* flags, as we apply our own depending on the 244 text scheme at the time shaping is invoked. */ 245 this.shapingOptions = shapingOptions & ~ArabicShaping.TEXT_DIRECTION_MASK; 246 currentScheme.doTransform(this); 247 } 248 return this.text; 249 } 250 251 /** 252 * When the direction option is 253 * <code>{@link Bidi#LEVEL_DEFAULT_LTR}</code> or 254 * <code>{@link Bidi#LEVEL_DEFAULT_RTL}</code>, resolves the base 255 * direction according to that of the first strong directional character in 256 * the text. 257 * 258 * @param levels Byte array, where levels[0] is an input level levels[1] is 259 * an output level. Resolved levels override these. 260 */ 261 private void resolveBaseDirection(byte[] levels) { 262 if (Bidi.IsDefaultLevel(levels[0])) { 263 byte level = Bidi.getBaseDirection(text); 264 levels[0] = level != Bidi.NEUTRAL ? level 265 : levels[0] == Bidi.LEVEL_DEFAULT_RTL ? Bidi.RTL : Bidi.LTR; 266 } else { 267 levels[0] &= 1; 268 } 269 if (Bidi.IsDefaultLevel(levels[1])) { 270 levels[1] = levels[0]; 271 } else { 272 levels[1] &= 1; 273 } 274 } 275 276 /** 277 * Finds a valid <code>{@link ReorderingScheme}</code> matching the 278 * caller-defined scheme. 279 * 280 * @return A valid <code>ReorderingScheme</code> object or null 281 */ 282 private ReorderingScheme findMatchingScheme(byte inLevel, Order inOrder, 283 byte outLevel, Order outOrder) { 284 for (ReorderingScheme scheme : ReorderingScheme.values()) { 285 if (scheme.matches(inLevel, inOrder, outLevel, outOrder)) { 286 return scheme; 287 } 288 } 289 return null; 290 } 291 292 /** 293 * Performs bidi resolution of text. 294 * 295 * @param level Base embedding level 296 * @param options Reordering options 297 */ 298 private void resolve(byte level, int options) { 299 bidi.setInverse((options & Bidi.REORDER_INVERSE_LIKE_DIRECT) != 0); 300 bidi.setReorderingMode(options); 301 bidi.setPara(text, level, null); 302 } 303 304 /** 305 * Performs basic reordering of text (Logical LTR or RTL to Visual LTR). 306 * 307 */ 308 private void reorder() { 309 text = bidi.writeReordered(reorderingOptions); 310 reorderingOptions = Bidi.REORDER_DEFAULT; 311 } 312 313 /** 314 * Performs string reverse. 315 */ 316 private void reverse() { 317 text = Bidi.writeReverse(text, Bidi.OPTION_DEFAULT); 318 } 319 320 /** 321 * Performs character mirroring without reordering. When this method is 322 * called, <code>{@link #text}</code> should be in a Logical form. 323 */ 324 private void mirror() { 325 if ((reorderingOptions & Bidi.DO_MIRRORING) == 0) { 326 return; 327 } 328 StringBuffer sb = new StringBuffer(text); 329 byte[] levels = bidi.getLevels(); 330 for (int i = 0, n = levels.length; i < n;) { 331 int ch = UTF16.charAt(sb, i); 332 if ((levels[i] & 1) != 0) { 333 UTF16.setCharAt(sb, i, UCharacter.getMirror(ch)); 334 } 335 i += UTF16.getCharCount(ch); 336 } 337 text = sb.toString(); 338 reorderingOptions &= ~Bidi.DO_MIRRORING; 339 } 340 341 /** 342 * Performs digit and letter shaping 343 * 344 * @param digitsDir Digit shaping option that indicates whether the text 345 * should be treated as logical or visual. 346 * @param lettersDir Letter shaping option that indicates whether the text 347 * should be treated as logical or visual form (can mismatch the digit 348 * option). 349 */ 350 private void shapeArabic(int digitsDir, int lettersDir) { 351 if (digitsDir == lettersDir) { 352 shapeArabic(shapingOptions | digitsDir); 353 } else { 354 /* Honor all shape options other than letters (not necessarily digits 355 only) */ 356 shapeArabic((shapingOptions & ~ArabicShaping.LETTERS_MASK) | digitsDir); 357 358 /* Honor all shape options other than digits (not necessarily letters 359 only) */ 360 shapeArabic((shapingOptions & ~ArabicShaping.DIGITS_MASK) | lettersDir); 361 } 362 } 363 364 /** 365 * Performs digit and letter shaping 366 * 367 * @param options Shaping options covering both letters and digits 368 */ 369 private void shapeArabic(int options) { 370 if (options != 0) { 371 ArabicShaping shaper = new ArabicShaping(options); 372 try { 373 text = shaper.shape(text); 374 } catch(ArabicShapingException e) { 375 } 376 } 377 } 378 379 private enum ReorderingScheme { 380 LOG_LTR_TO_VIS_LTR { 381 @Override 382 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 383 return IsLTR(inLevel) && IsLogical(inOrder) 384 && IsLTR(outLevel) && IsVisual(outOrder); 385 } 386 @Override 387 void doTransform(BidiTransform transform) { 388 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); 389 transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); 390 transform.reorder(); 391 } 392 }, 393 LOG_RTL_TO_VIS_LTR { 394 @Override 395 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 396 return IsRTL(inLevel) && IsLogical(inOrder) 397 && IsLTR(outLevel) && IsVisual(outOrder); 398 } 399 @Override 400 void doTransform(BidiTransform transform) { 401 transform.resolve(Bidi.RTL, Bidi.REORDER_DEFAULT); 402 transform.reorder(); 403 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); 404 } 405 }, 406 LOG_LTR_TO_VIS_RTL { 407 @Override 408 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 409 return IsLTR(inLevel) && IsLogical(inOrder) 410 && IsRTL(outLevel) && IsVisual(outOrder); 411 } 412 @Override 413 void doTransform(BidiTransform transform) { 414 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); 415 transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); 416 transform.reorder(); 417 transform.reverse(); 418 } 419 }, 420 LOG_RTL_TO_VIS_RTL { 421 @Override 422 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 423 return IsRTL(inLevel) && IsLogical(inOrder) 424 && IsRTL(outLevel) && IsVisual(outOrder); 425 } 426 @Override 427 void doTransform(BidiTransform transform) { 428 transform.resolve(Bidi.RTL, Bidi.REORDER_DEFAULT); 429 transform.reorder(); 430 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); 431 transform.reverse(); 432 } 433 }, 434 VIS_LTR_TO_LOG_RTL { 435 @Override 436 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 437 return IsLTR(inLevel) && IsVisual(inOrder) 438 && IsRTL(outLevel) && IsLogical(outOrder); 439 } 440 @Override 441 void doTransform(BidiTransform transform) { 442 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); 443 transform.resolve(Bidi.RTL, Bidi.REORDER_INVERSE_LIKE_DIRECT); 444 transform.reorder(); 445 } 446 }, 447 VIS_RTL_TO_LOG_RTL { 448 @Override 449 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 450 return IsRTL(inLevel) && IsVisual(inOrder) 451 && IsRTL(outLevel) && IsLogical(outOrder); 452 } 453 @Override 454 void doTransform(BidiTransform transform) { 455 transform.reverse(); 456 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); 457 transform.resolve(Bidi.RTL, Bidi.REORDER_INVERSE_LIKE_DIRECT); 458 transform.reorder(); 459 } 460 }, 461 VIS_LTR_TO_LOG_LTR { 462 @Override 463 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 464 return IsLTR(inLevel) && IsVisual(inOrder) 465 && IsLTR(outLevel) && IsLogical(outOrder); 466 } 467 @Override 468 void doTransform(BidiTransform transform) { 469 transform.resolve(Bidi.LTR, Bidi.REORDER_INVERSE_LIKE_DIRECT); 470 transform.reorder(); 471 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); 472 } 473 }, 474 VIS_RTL_TO_LOG_LTR { 475 @Override 476 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 477 return IsRTL(inLevel) && IsVisual(inOrder) 478 && IsLTR(outLevel) && IsLogical(outOrder); 479 } 480 @Override 481 void doTransform(BidiTransform transform) { 482 transform.reverse(); 483 transform.resolve(Bidi.LTR, Bidi.REORDER_INVERSE_LIKE_DIRECT); 484 transform.reorder(); 485 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); 486 } 487 }, 488 LOG_LTR_TO_LOG_RTL { 489 @Override 490 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 491 return IsLTR(inLevel) && IsLogical(inOrder) 492 && IsRTL(outLevel) && IsLogical(outOrder); 493 } 494 @Override 495 void doTransform(BidiTransform transform) { 496 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); 497 transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); 498 transform.mirror(); 499 transform.resolve(Bidi.LTR, Bidi.REORDER_RUNS_ONLY); 500 transform.reorder(); 501 } 502 }, 503 LOG_RTL_TO_LOG_LTR { 504 @Override 505 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 506 return IsRTL(inLevel) && IsLogical(inOrder) 507 && IsLTR(outLevel) && IsLogical(outOrder); 508 } 509 @Override 510 void doTransform(BidiTransform transform) { 511 transform.resolve(Bidi.RTL, Bidi.REORDER_DEFAULT); 512 transform.mirror(); 513 transform.resolve(Bidi.RTL, Bidi.REORDER_RUNS_ONLY); 514 transform.reorder(); 515 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); 516 } 517 }, 518 VIS_LTR_TO_VIS_RTL { 519 @Override 520 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 521 return IsLTR(inLevel) && IsVisual(inOrder) 522 && IsRTL(outLevel) && IsVisual(outOrder); 523 } 524 @Override 525 void doTransform(BidiTransform transform) { 526 transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); 527 transform.mirror(); 528 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); 529 transform.reverse(); 530 } 531 }, 532 VIS_RTL_TO_VIS_LTR { 533 @Override 534 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 535 return IsRTL(inLevel) && IsVisual(inOrder) 536 && IsLTR(outLevel) && IsVisual(outOrder); 537 } 538 @Override 539 void doTransform(BidiTransform transform) { 540 transform.reverse(); 541 transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); 542 transform.mirror(); 543 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); 544 } 545 }, 546 LOG_LTR_TO_LOG_LTR { 547 @Override 548 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 549 return IsLTR(inLevel) && IsLogical(inOrder) 550 && IsLTR(outLevel) && IsLogical(outOrder); 551 } 552 @Override 553 void doTransform(BidiTransform transform) { 554 transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); 555 transform.mirror(); 556 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); 557 } 558 }, 559 LOG_RTL_TO_LOG_RTL { 560 @Override 561 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 562 return IsRTL(inLevel) && IsLogical(inOrder) 563 && IsRTL(outLevel) && IsLogical(outOrder); 564 } 565 @Override 566 void doTransform(BidiTransform transform) { 567 transform.resolve(Bidi.RTL, Bidi.REORDER_DEFAULT); 568 transform.mirror(); 569 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_VISUAL_LTR, ArabicShaping.TEXT_DIRECTION_LOGICAL); 570 } 571 }, 572 VIS_LTR_TO_VIS_LTR { 573 @Override 574 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 575 return IsLTR(inLevel) && IsVisual(inOrder) 576 && IsLTR(outLevel) && IsVisual(outOrder); 577 } 578 @Override 579 void doTransform(BidiTransform transform) { 580 transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); 581 transform.mirror(); 582 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); 583 } 584 }, 585 VIS_RTL_TO_VIS_RTL { 586 @Override 587 boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { 588 return IsRTL(inLevel) && IsVisual(inOrder) 589 && IsRTL(outLevel) && IsVisual(outOrder); 590 } 591 @Override 592 void doTransform(BidiTransform transform) { 593 transform.reverse(); 594 transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); 595 transform.mirror(); 596 transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); 597 transform.reverse(); 598 } 599 }; 600 601 /** 602 * Indicates whether this scheme matches another one in terms of 603 * equality of base direction and ordering scheme. 604 * 605 * @param inLevel Base level of the input text 606 * @param inOrder Order of the input text 607 * @param outLevel Base level of the output text 608 * @param outOrder Order of the output text 609 * 610 * @return <code>true</code> if it's a match, <code>false</code> 611 * otherwise 612 */ 613 abstract boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder); 614 615 /** 616 * Performs a series of bidi layout transformations unique for the current 617 * scheme. 618 619 * @param transform Bidi transformation engine 620 */ 621 abstract void doTransform(BidiTransform transform); 622 } 623 624 /** 625 * Is level LTR? convenience method 626 627 * @param level Embedding level 628 */ 629 private static boolean IsLTR(byte level) { 630 return (level & 1) == 0; 631 } 632 633 /** 634 * Is level RTL? convenience method 635 636 * @param level Embedding level 637 */ 638 private static boolean IsRTL(byte level) { 639 return (level & 1) == 1; 640 } 641 642 /** 643 * Is order logical? convenience method 644 645 * @param level Order value 646 */ 647 private static boolean IsLogical(Order order) { 648 return Order.LOGICAL.equals(order); 649 } 650 651 /** 652 * Is order visual? convenience method 653 654 * @param level Order value 655 */ 656 private static boolean IsVisual(Order order) { 657 return Order.VISUAL.equals(order); 658 } 659 660 } 661