/* GENERATED SOURCE. DO NOT MODIFY. */ // © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html package android.icu.text; import android.icu.lang.UCharacter; /** * Bidi Layout Transformation Engine. * * @author Lina Kemmel * * @hide Only a subset of ICU is exposed in Android */ public class BidiTransform { /** * {@link Order} indicates the order of text. *

* This bidi transformation engine supports all possible combinations (4 in * total) of input and output text order: *

* * @see Bidi#setInverse * @see Bidi#setReorderingMode * @see Bidi#REORDER_DEFAULT * @see Bidi#REORDER_INVERSE_LIKE_DIRECT * @see Bidi#REORDER_RUNS_ONLY * @hide Only a subset of ICU is exposed in Android */ public enum Order { /** * Constant indicating a logical order. */ LOGICAL, /** * Constant indicating a visual order. */ VISUAL; } /** * {@link Mirroring} 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 * @hide Only a subset of ICU is exposed in Android */ public enum Mirroring { /** * Constant indicating that character mirroring should not be * performed. */ OFF, /** * Constant indicating that character mirroring should be performed. *

* This corresponds to calling {@link Bidi#writeReordered} * or {@link Bidi#writeReverse} with the * {@link Bidi#DO_MIRRORING} option bit set. */ ON; } private Bidi bidi; private String text; private int reorderingOptions; private int shapingOptions; /** * {@link BidiTransform} default constructor. */ 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. *

* In terms of {@link Bidi} class, such a transformation * implies: *

* 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 .

* There are 36 possible combinations of {input, output} ordering schemes, * which are partially supported by {@link Bidi} already. * Examples of the currently supported combinations: *

* All combinations that involve the Visual RTL scheme are unsupported by * {@link Bidi}, for instance: *

*

Example of usage of the transformation engine:

*
     * 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"
     * 
* * @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 * {@link Bidi#setPara(String, byte, byte[])} * documentation for the paraLevel parameter. * @param inOrder An order of the input, which can be one of the * {@link Order} values. * @param outParaLevel A base embedding level of the output as defined in * {@link Bidi#setPara(String, byte, byte[])} * documentation for the paraLevel parameter. * @param outOrder An order of the output, which can be one of the * {@link Order} values. * @param doMirroring Indicates whether or not to perform character * mirroring, and can accept one of the * {@link Mirroring} values. * @param shapingOptions Arabic digit and letter shaping options defined in * the {@link ArabicShaping} documentation. *

Note: 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 text, * inOrder, outOrder, or * doMirroring parameter is null. */ 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 * {@link Bidi#LEVEL_DEFAULT_LTR} or * {@link Bidi#LEVEL_DEFAULT_RTL}, 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 {@link ReorderingScheme} matching the * caller-defined scheme. * * @return A valid ReorderingScheme 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, {@link #text} 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 true if it's a match, false * 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); } }