| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html#License |
| |
| package com.ibm.icu.text; |
| |
| import com.ibm.icu.lang.UCharacter; |
| |
| /** |
| * Bidi Layout Transformation Engine. |
| * |
| * @author Lina Kemmel |
| * |
| * @stable ICU 58 |
| */ |
| public class BidiTransform |
| { |
| /** |
| * <code>{@link Order}</code> indicates the order of text. |
| * <p> |
| * This bidi transformation engine supports all possible combinations (4 in |
| * total) of input and output text order: |
| * <ul> |
| * <li>{logical input, visual output}: unless the output direction is RTL, |
| * this corresponds to a normal operation of the Bidi algorithm as |
| * described in the Unicode Technical Report and implemented by |
| * <code>{@link Bidi}</code> when the reordering mode is set to |
| * <code>Bidi#REORDER_DEFAULT</code>. Visual RTL mode is not supported by |
| * <code>{@link Bidi}</code> and is accomplished through reversing a visual |
| * LTR string,</li> |
| * <li>{visual input, logical output}: unless the input direction is RTL, |
| * this corresponds to an "inverse bidi algorithm" in |
| * <code>{@link Bidi}</code> with the reordering mode set to |
| * <code>{@link Bidi#REORDER_INVERSE_LIKE_DIRECT}</code>. Visual RTL mode |
| * is not not supported by <code>{@link Bidi}</code> and is accomplished |
| * through reversing a visual LTR string,</li> |
| * <li>{logical input, logical output}: if the input and output base |
| * directions mismatch, this corresponds to the <code>{@link Bidi}</code> |
| * implementation with the reordering mode set to |
| * <code>{@link Bidi#REORDER_RUNS_ONLY}</code>; and if the input and output |
| * base directions are identical, the transformation engine will only |
| * handle character mirroring and Arabic shaping operations without |
| * reordering,</li> |
| * <li>{visual input, visual output}: this reordering mode is not supported |
| * by the <code>{@link Bidi}</code> engine; it implies character mirroring, |
| * Arabic shaping, and - if the input/output base directions mismatch - |
| * string reverse operations.</li> |
| * </ul> |
| * |
| * @see Bidi#setInverse |
| * @see Bidi#setReorderingMode |
| * @see Bidi#REORDER_DEFAULT |
| * @see Bidi#REORDER_INVERSE_LIKE_DIRECT |
| * @see Bidi#REORDER_RUNS_ONLY |
| * @stable ICU 58 |
| */ |
| public enum Order { |
| /** |
| * Constant indicating a logical order. |
| * |
| * @stable ICU 58 |
| */ |
| LOGICAL, |
| /** |
| * Constant indicating a visual order. |
| * |
| * @stable ICU 58 |
| */ |
| VISUAL; |
| } |
| |
| /** |
| * <code>{@link Mirroring}</code> indicates whether or not characters with |
| * the "mirrored" property in RTL runs should be replaced with their |
| * mirror-image counterparts. |
| * |
| * @see Bidi#DO_MIRRORING |
| * @see Bidi#setReorderingOptions |
| * @see Bidi#writeReordered |
| * @see Bidi#writeReverse |
| * @stable ICU 58 |
| */ |
| public enum Mirroring { |
| /** |
| * Constant indicating that character mirroring should not be |
| * performed. |
| * |
| * @stable ICU 58 |
| */ |
| OFF, |
| /** |
| * Constant indicating that character mirroring should be performed. |
| * <p> |
| * This corresponds to calling <code>{@link Bidi#writeReordered}</code> |
| * or <code>{@link Bidi#writeReverse}</code> with the |
| * <code>{@link Bidi#DO_MIRRORING}</code> option bit set. |
| * |
| * @stable ICU 58 |
| */ |
| ON; |
| } |
| |
| private Bidi bidi; |
| private String text; |
| private int reorderingOptions; |
| private int shapingOptions; |
| |
| /** |
| * <code>{@link BidiTransform}</code> default constructor. |
| * |
| * @stable ICU 58 |
| */ |
| public BidiTransform() |
| { |
| } |
| |
| /** |
| * Performs transformation of text from the bidi layout defined by the |
| * input ordering scheme to the bidi layout defined by the output ordering |
| * scheme, and applies character mirroring and Arabic shaping operations. |
| * <p> |
| * In terms of <code>{@link Bidi}</code> class, such a transformation |
| * implies: |
| * <ul> |
| * <li>calling <code>{@link Bidi#setReorderingMode}</code> as needed (when |
| * the reordering mode is other than normal),</li> |
| * <li>calling <code>{@link Bidi#setInverse}</code> as needed (when text |
| * should be transformed from a visual to a logical form),</li> |
| * <li>resolving embedding levels of each character in the input text by |
| * calling <code>{@link Bidi#setPara}</code>,</li> |
| * <li>reordering the characters based on the computed embedding levels, |
| * also performing character mirroring as needed, and streaming the result |
| * to the output, by calling <code>{@link Bidi#writeReordered}</code>,</li> |
| * <li>performing Arabic digit and letter shaping on the output text by |
| * calling <code>{@link ArabicShaping#shape}</code>.</li> |
| * </ul><p> |
| * An "ordering scheme" encompasses the base direction and the order of |
| * text, and these characteristics must be defined by the caller for both |
| * input and output explicitly .<p> |
| * There are 36 possible combinations of {input, output} ordering schemes, |
| * which are partially supported by <code>{@link Bidi}</code> already. |
| * Examples of the currently supported combinations: |
| * <ul> |
| * <li>{Logical LTR, Visual LTR}: this is equivalent to calling |
| * <code>{@link Bidi#setPara}</code> with |
| * <code>paraLevel == {@link Bidi#LTR}</code>,</li> |
| * <li>{Logical RTL, Visual LTR}: this is equivalent to calling |
| * <code>{@link Bidi#setPara}</code> with |
| * <code>paraLevel == {@link Bidi#RTL}</code>,</li> |
| * <li>{Logical Default ("Auto") LTR, Visual LTR}: this is equivalent to |
| * calling <code>{@link Bidi#setPara}</code> with |
| * <code>paraLevel == {@link Bidi#LEVEL_DEFAULT_LTR}</code>,</li> |
| * <li>{Logical Default ("Auto") RTL, Visual LTR}: this is equivalent to |
| * calling <code>{@link Bidi#setPara}</code> with |
| * <code>paraLevel == {@link Bidi#LEVEL_DEFAULT_RTL}</code>,</li> |
| * <li>{Visual LTR, Logical LTR}: this is equivalent to |
| * calling <code>{@link Bidi#setInverse}(true)</code> and then |
| * <code>{@link Bidi#setPara}</code> with |
| * <code>paraLevel == {@link Bidi#LTR}</code>,</li> |
| * <li>{Visual LTR, Logical RTL}: this is equivalent to calling |
| * <code>{@link Bidi#setInverse}(true)</code> and then |
| * <code>{@link Bidi#setPara}</code> with |
| * <code>paraLevel == {@link Bidi#RTL}</code>.</li> |
| * </ul><p> |
| * All combinations that involve the Visual RTL scheme are unsupported by |
| * <code>{@link Bidi}</code>, for instance: |
| * <ul> |
| * <li>{Logical LTR, Visual RTL},</li> |
| * <li>{Visual RTL, Logical RTL}.</li> |
| * </ul> |
| * <p>Example of usage of the transformation engine:</p> |
| * <pre> |
| * BidiTransform bidiTransform = new BidiTransform(); |
| * String in = "abc \u06f0123"; // "abc \\u06f0123" |
| * // Run a transformation. |
| * String out = bidiTransform.transform(in, |
| * Bidi.LTR, Order.VISUAL, |
| * Bidi.RTL, Order.LOGICAL, |
| * Mirroring.OFF, |
| * ArabicShaping.DIGITS_AN2EN | ArabicShaping.DIGIT_TYPE_AN_EXTENDED); |
| * // Result: "0123 abc". |
| * // Do something with out. |
| * out = out.replace('0', '4'); |
| * // Result: "4123 abc". |
| * // Run a reverse transformation. |
| * String inNew = bidiTransform.transform(out, |
| * Bidi.RTL, Order.LOGICAL, |
| * Bidi.LTR, Order.VISUAL, |
| * Mirroring.OFF, |
| * ArabicShaping.DIGITS_EN2AN | ArabicShaping.DIGIT_TYPE_AN_EXTENDED); |
| * // Result: "abc \\u06f4\\u06f1\\u06f2\\u06f3" |
| * </pre> |
| * |
| * @param text An input character sequence that the Bidi layout |
| * transformations will be performed on. |
| * @param inParaLevel A base embedding level of the input as defined in |
| * <code>{@link Bidi#setPara(String, byte, byte[])}</code> |
| * documentation for the <code>paraLevel</code> parameter. |
| * @param inOrder An order of the input, which can be one of the |
| * <code>{@link Order}</code> values. |
| * @param outParaLevel A base embedding level of the output as defined in |
| * <code>{@link Bidi#setPara(String, byte, byte[])}</code> |
| * documentation for the <code>paraLevel</code> parameter. |
| * @param outOrder An order of the output, which can be one of the |
| * <code>{@link Order}</code> values. |
| * @param doMirroring Indicates whether or not to perform character |
| * mirroring, and can accept one of the |
| * <code>{@link Mirroring}</code> values. |
| * @param shapingOptions Arabic digit and letter shaping options defined in |
| * the <code>{@link ArabicShaping}</code> documentation. |
| * <p><strong>Note:</strong> Direction indicator options are |
| * computed by the transformation engine based on the effective |
| * ordering schemes, so user-defined direction indicators will be |
| * ignored. |
| * @return The output string, which is the result of the layout |
| * transformation. |
| * @throws IllegalArgumentException if <code>text</code>, |
| * <code>inOrder</code>, <code>outOrder</code>, or |
| * <code>doMirroring</code> parameter is <code>null</code>. |
| * @stable ICU 58 |
| */ |
| public String transform(CharSequence text, |
| byte inParaLevel, Order inOrder, |
| byte outParaLevel, Order outOrder, |
| Mirroring doMirroring, int shapingOptions) |
| { |
| if (text == null || inOrder == null || outOrder == null || doMirroring == null) { |
| throw new IllegalArgumentException(); |
| } |
| this.text = text.toString(); |
| |
| byte[] levels = {inParaLevel, outParaLevel}; |
| resolveBaseDirection(levels); |
| |
| ReorderingScheme currentScheme = findMatchingScheme(levels[0], inOrder, |
| levels[1], outOrder); |
| if (currentScheme != null) { |
| this.bidi = new Bidi(); |
| this.reorderingOptions = Mirroring.ON.equals(doMirroring) |
| ? Bidi.DO_MIRRORING : Bidi.REORDER_DEFAULT; |
| |
| /* Ignore TEXT_DIRECTION_* flags, as we apply our own depending on the |
| text scheme at the time shaping is invoked. */ |
| this.shapingOptions = shapingOptions & ~ArabicShaping.TEXT_DIRECTION_MASK; |
| currentScheme.doTransform(this); |
| } |
| return this.text; |
| } |
| |
| /** |
| * When the direction option is |
| * <code>{@link Bidi#LEVEL_DEFAULT_LTR}</code> or |
| * <code>{@link Bidi#LEVEL_DEFAULT_RTL}</code>, resolves the base |
| * direction according to that of the first strong directional character in |
| * the text. |
| * |
| * @param levels Byte array, where levels[0] is an input level levels[1] is |
| * an output level. Resolved levels override these. |
| */ |
| private void resolveBaseDirection(byte[] levels) { |
| if (Bidi.IsDefaultLevel(levels[0])) { |
| byte level = Bidi.getBaseDirection(text); |
| levels[0] = level != Bidi.NEUTRAL ? level |
| : levels[0] == Bidi.LEVEL_DEFAULT_RTL ? Bidi.RTL : Bidi.LTR; |
| } else { |
| levels[0] &= 1; |
| } |
| if (Bidi.IsDefaultLevel(levels[1])) { |
| levels[1] = levels[0]; |
| } else { |
| levels[1] &= 1; |
| } |
| } |
| |
| /** |
| * Finds a valid <code>{@link ReorderingScheme}</code> matching the |
| * caller-defined scheme. |
| * |
| * @return A valid <code>ReorderingScheme</code> object or null |
| */ |
| private ReorderingScheme findMatchingScheme(byte inLevel, Order inOrder, |
| byte outLevel, Order outOrder) { |
| for (ReorderingScheme scheme : ReorderingScheme.values()) { |
| if (scheme.matches(inLevel, inOrder, outLevel, outOrder)) { |
| return scheme; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Performs bidi resolution of text. |
| * |
| * @param level Base embedding level |
| * @param options Reordering options |
| */ |
| private void resolve(byte level, int options) { |
| bidi.setInverse((options & Bidi.REORDER_INVERSE_LIKE_DIRECT) != 0); |
| bidi.setReorderingMode(options); |
| bidi.setPara(text, level, null); |
| } |
| |
| /** |
| * Performs basic reordering of text (Logical LTR or RTL to Visual LTR). |
| * |
| */ |
| private void reorder() { |
| text = bidi.writeReordered(reorderingOptions); |
| reorderingOptions = Bidi.REORDER_DEFAULT; |
| } |
| |
| /** |
| * Performs string reverse. |
| */ |
| private void reverse() { |
| text = Bidi.writeReverse(text, Bidi.OPTION_DEFAULT); |
| } |
| |
| /** |
| * Performs character mirroring without reordering. When this method is |
| * called, <code>{@link #text}</code> should be in a Logical form. |
| */ |
| private void mirror() { |
| if ((reorderingOptions & Bidi.DO_MIRRORING) == 0) { |
| return; |
| } |
| StringBuffer sb = new StringBuffer(text); |
| byte[] levels = bidi.getLevels(); |
| for (int i = 0, n = levels.length; i < n;) { |
| int ch = UTF16.charAt(sb, i); |
| if ((levels[i] & 1) != 0) { |
| UTF16.setCharAt(sb, i, UCharacter.getMirror(ch)); |
| } |
| i += UTF16.getCharCount(ch); |
| } |
| text = sb.toString(); |
| reorderingOptions &= ~Bidi.DO_MIRRORING; |
| } |
| |
| /** |
| * Performs digit and letter shaping |
| * |
| * @param digitsDir Digit shaping option that indicates whether the text |
| * should be treated as logical or visual. |
| * @param lettersDir Letter shaping option that indicates whether the text |
| * should be treated as logical or visual form (can mismatch the digit |
| * option). |
| */ |
| private void shapeArabic(int digitsDir, int lettersDir) { |
| if (digitsDir == lettersDir) { |
| shapeArabic(shapingOptions | digitsDir); |
| } else { |
| /* Honor all shape options other than letters (not necessarily digits |
| only) */ |
| shapeArabic((shapingOptions & ~ArabicShaping.LETTERS_MASK) | digitsDir); |
| |
| /* Honor all shape options other than digits (not necessarily letters |
| only) */ |
| shapeArabic((shapingOptions & ~ArabicShaping.DIGITS_MASK) | lettersDir); |
| } |
| } |
| |
| /** |
| * Performs digit and letter shaping |
| * |
| * @param options Shaping options covering both letters and digits |
| */ |
| private void shapeArabic(int options) { |
| if (options != 0) { |
| ArabicShaping shaper = new ArabicShaping(options); |
| try { |
| text = shaper.shape(text); |
| } catch(ArabicShapingException e) { |
| } |
| } |
| } |
| |
| private enum ReorderingScheme { |
| LOG_LTR_TO_VIS_LTR { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsLTR(inLevel) && IsLogical(inOrder) |
| && IsLTR(outLevel) && IsVisual(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); |
| transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); |
| transform.reorder(); |
| } |
| }, |
| LOG_RTL_TO_VIS_LTR { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsRTL(inLevel) && IsLogical(inOrder) |
| && IsLTR(outLevel) && IsVisual(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.resolve(Bidi.RTL, Bidi.REORDER_DEFAULT); |
| transform.reorder(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); |
| } |
| }, |
| LOG_LTR_TO_VIS_RTL { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsLTR(inLevel) && IsLogical(inOrder) |
| && IsRTL(outLevel) && IsVisual(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); |
| transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); |
| transform.reorder(); |
| transform.reverse(); |
| } |
| }, |
| LOG_RTL_TO_VIS_RTL { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsRTL(inLevel) && IsLogical(inOrder) |
| && IsRTL(outLevel) && IsVisual(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.resolve(Bidi.RTL, Bidi.REORDER_DEFAULT); |
| transform.reorder(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); |
| transform.reverse(); |
| } |
| }, |
| VIS_LTR_TO_LOG_RTL { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsLTR(inLevel) && IsVisual(inOrder) |
| && IsRTL(outLevel) && IsLogical(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); |
| transform.resolve(Bidi.RTL, Bidi.REORDER_INVERSE_LIKE_DIRECT); |
| transform.reorder(); |
| } |
| }, |
| VIS_RTL_TO_LOG_RTL { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsRTL(inLevel) && IsVisual(inOrder) |
| && IsRTL(outLevel) && IsLogical(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.reverse(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); |
| transform.resolve(Bidi.RTL, Bidi.REORDER_INVERSE_LIKE_DIRECT); |
| transform.reorder(); |
| } |
| }, |
| VIS_LTR_TO_LOG_LTR { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsLTR(inLevel) && IsVisual(inOrder) |
| && IsLTR(outLevel) && IsLogical(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.resolve(Bidi.LTR, Bidi.REORDER_INVERSE_LIKE_DIRECT); |
| transform.reorder(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); |
| } |
| }, |
| VIS_RTL_TO_LOG_LTR { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsRTL(inLevel) && IsVisual(inOrder) |
| && IsLTR(outLevel) && IsLogical(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.reverse(); |
| transform.resolve(Bidi.LTR, Bidi.REORDER_INVERSE_LIKE_DIRECT); |
| transform.reorder(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); |
| } |
| }, |
| LOG_LTR_TO_LOG_RTL { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsLTR(inLevel) && IsLogical(inOrder) |
| && IsRTL(outLevel) && IsLogical(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); |
| transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); |
| transform.mirror(); |
| transform.resolve(Bidi.LTR, Bidi.REORDER_RUNS_ONLY); |
| transform.reorder(); |
| } |
| }, |
| LOG_RTL_TO_LOG_LTR { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsRTL(inLevel) && IsLogical(inOrder) |
| && IsLTR(outLevel) && IsLogical(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.resolve(Bidi.RTL, Bidi.REORDER_DEFAULT); |
| transform.mirror(); |
| transform.resolve(Bidi.RTL, Bidi.REORDER_RUNS_ONLY); |
| transform.reorder(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); |
| } |
| }, |
| VIS_LTR_TO_VIS_RTL { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsLTR(inLevel) && IsVisual(inOrder) |
| && IsRTL(outLevel) && IsVisual(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); |
| transform.mirror(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); |
| transform.reverse(); |
| } |
| }, |
| VIS_RTL_TO_VIS_LTR { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsRTL(inLevel) && IsVisual(inOrder) |
| && IsLTR(outLevel) && IsVisual(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.reverse(); |
| transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); |
| transform.mirror(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); |
| } |
| }, |
| LOG_LTR_TO_LOG_LTR { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsLTR(inLevel) && IsLogical(inOrder) |
| && IsLTR(outLevel) && IsLogical(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); |
| transform.mirror(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_LOGICAL); |
| } |
| }, |
| LOG_RTL_TO_LOG_RTL { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsRTL(inLevel) && IsLogical(inOrder) |
| && IsRTL(outLevel) && IsLogical(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.resolve(Bidi.RTL, Bidi.REORDER_DEFAULT); |
| transform.mirror(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_VISUAL_LTR, ArabicShaping.TEXT_DIRECTION_LOGICAL); |
| } |
| }, |
| VIS_LTR_TO_VIS_LTR { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsLTR(inLevel) && IsVisual(inOrder) |
| && IsLTR(outLevel) && IsVisual(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); |
| transform.mirror(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); |
| } |
| }, |
| VIS_RTL_TO_VIS_RTL { |
| @Override |
| boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder) { |
| return IsRTL(inLevel) && IsVisual(inOrder) |
| && IsRTL(outLevel) && IsVisual(outOrder); |
| } |
| @Override |
| void doTransform(BidiTransform transform) { |
| transform.reverse(); |
| transform.resolve(Bidi.LTR, Bidi.REORDER_DEFAULT); |
| transform.mirror(); |
| transform.shapeArabic(ArabicShaping.TEXT_DIRECTION_LOGICAL, ArabicShaping.TEXT_DIRECTION_VISUAL_LTR); |
| transform.reverse(); |
| } |
| }; |
| |
| /** |
| * Indicates whether this scheme matches another one in terms of |
| * equality of base direction and ordering scheme. |
| * |
| * @param inLevel Base level of the input text |
| * @param inOrder Order of the input text |
| * @param outLevel Base level of the output text |
| * @param outOrder Order of the output text |
| * |
| * @return <code>true</code> if it's a match, <code>false</code> |
| * otherwise |
| */ |
| abstract boolean matches(byte inLevel, Order inOrder, byte outLevel, Order outOrder); |
| |
| /** |
| * Performs a series of bidi layout transformations unique for the current |
| * scheme. |
| |
| * @param transform Bidi transformation engine |
| */ |
| abstract void doTransform(BidiTransform transform); |
| } |
| |
| /** |
| * Is level LTR? convenience method |
| |
| * @param level Embedding level |
| */ |
| private static boolean IsLTR(byte level) { |
| return (level & 1) == 0; |
| } |
| |
| /** |
| * Is level RTL? convenience method |
| |
| * @param level Embedding level |
| */ |
| private static boolean IsRTL(byte level) { |
| return (level & 1) == 1; |
| } |
| |
| /** |
| * Is order logical? convenience method |
| |
| * @param level Order value |
| */ |
| private static boolean IsLogical(Order order) { |
| return Order.LOGICAL.equals(order); |
| } |
| |
| /** |
| * Is order visual? convenience method |
| |
| * @param level Order value |
| */ |
| private static boolean IsVisual(Order order) { |
| return Order.VISUAL.equals(order); |
| } |
| |
| } |