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