Home | History | Annotate | Download | only in text
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.text;
     18 
     19 
     20 import java.util.Locale;
     21 
     22 import android.util.LocaleUtil;
     23 
     24 /**
     25  * Some objects that implement TextDirectionHeuristic.
     26  * @hide
     27  */
     28 public class TextDirectionHeuristics {
     29 
     30     /** Always decides that the direction is left to right. */
     31     public static final TextDirectionHeuristic LTR =
     32         new TextDirectionHeuristicInternal(null /* no algorithm */, false);
     33 
     34     /** Always decides that the direction is right to left. */
     35     public static final TextDirectionHeuristic RTL =
     36         new TextDirectionHeuristicInternal(null /* no algorithm */, true);
     37 
     38     /**
     39      * Determines the direction based on the first strong directional character,
     40      * including bidi format chars, falling back to left to right if it
     41      * finds none.  This is the default behavior of the Unicode Bidirectional
     42      * Algorithm.
     43      */
     44     public static final TextDirectionHeuristic FIRSTSTRONG_LTR =
     45         new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false);
     46 
     47     /**
     48      * Determines the direction based on the first strong directional character,
     49      * including bidi format chars, falling back to right to left if it
     50      * finds none.  This is similar to the default behavior of the Unicode
     51      * Bidirectional Algorithm, just with different fallback behavior.
     52      */
     53     public static final TextDirectionHeuristic FIRSTSTRONG_RTL =
     54         new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true);
     55 
     56     /**
     57      * If the text contains any strong right to left non-format character, determines
     58      * that the direction is right to left, falling back to left to right if it
     59      * finds none.
     60      */
     61     public static final TextDirectionHeuristic ANYRTL_LTR =
     62         new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false);
     63 
     64     /**
     65      * Force the paragraph direction to the Locale direction. Falls back to left to right.
     66      */
     67     public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE;
     68 
     69     private static enum TriState {
     70         TRUE, FALSE, UNKNOWN;
     71     }
     72 
     73     /**
     74      * Computes the text direction based on an algorithm.  Subclasses implement
     75      * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the
     76      * direction from the text alone.
     77      * @hide
     78      */
     79     public static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
     80         private final TextDirectionAlgorithm mAlgorithm;
     81 
     82         public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
     83             mAlgorithm = algorithm;
     84         }
     85 
     86         /**
     87          * Return true if the default text direction is rtl.
     88          */
     89         abstract protected boolean defaultIsRtl();
     90 
     91         @Override
     92         public boolean isRtl(char[] chars, int start, int count) {
     93             if (chars == null || start < 0 || count < 0 || chars.length - count < start) {
     94                 throw new IllegalArgumentException();
     95             }
     96             if (mAlgorithm == null) {
     97                 return defaultIsRtl();
     98             }
     99             return doCheck(chars, start, count);
    100         }
    101 
    102         private boolean doCheck(char[] chars, int start, int count) {
    103             switch(mAlgorithm.checkRtl(chars, start, count)) {
    104                 case TRUE:
    105                     return true;
    106                 case FALSE:
    107                     return false;
    108                 default:
    109                     return defaultIsRtl();
    110             }
    111         }
    112     }
    113 
    114     private static class TextDirectionHeuristicInternal extends TextDirectionHeuristicImpl {
    115         private final boolean mDefaultIsRtl;
    116 
    117         private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm,
    118                 boolean defaultIsRtl) {
    119             super(algorithm);
    120             mDefaultIsRtl = defaultIsRtl;
    121         }
    122 
    123         @Override
    124         protected boolean defaultIsRtl() {
    125             return mDefaultIsRtl;
    126         }
    127     }
    128 
    129     private static TriState isRtlText(int directionality) {
    130         switch (directionality) {
    131             case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
    132                 return TriState.FALSE;
    133             case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
    134             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
    135                 return TriState.TRUE;
    136             default:
    137                 return TriState.UNKNOWN;
    138         }
    139     }
    140 
    141     private static TriState isRtlTextOrFormat(int directionality) {
    142         switch (directionality) {
    143             case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
    144             case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
    145             case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
    146                 return TriState.FALSE;
    147             case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
    148             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
    149             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
    150             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
    151                 return TriState.TRUE;
    152             default:
    153                 return TriState.UNKNOWN;
    154         }
    155     }
    156 
    157     /**
    158      * Interface for an algorithm to guess the direction of a paragraph of text.
    159      *
    160      * @hide
    161      */
    162     public static interface TextDirectionAlgorithm {
    163         /**
    164          * Returns whether the range of text is RTL according to the algorithm.
    165          *
    166          * @hide
    167          */
    168         TriState checkRtl(char[] text, int start, int count);
    169     }
    170 
    171     /**
    172      * Algorithm that uses the first strong directional character to determine
    173      * the paragraph direction.  This is the standard Unicode Bidirectional
    174      * algorithm.
    175      *
    176      * @hide
    177      */
    178     public static class FirstStrong implements TextDirectionAlgorithm {
    179         @Override
    180         public TriState checkRtl(char[] text, int start, int count) {
    181             TriState result = TriState.UNKNOWN;
    182             for (int i = start, e = start + count; i < e && result == TriState.UNKNOWN; ++i) {
    183                 result = isRtlTextOrFormat(Character.getDirectionality(text[i]));
    184             }
    185             return result;
    186         }
    187 
    188         private FirstStrong() {
    189         }
    190 
    191         public static final FirstStrong INSTANCE = new FirstStrong();
    192     }
    193 
    194     /**
    195      * Algorithm that uses the presence of any strong directional non-format
    196      * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
    197      * direction of text.
    198      *
    199      * @hide
    200      */
    201     public static class AnyStrong implements TextDirectionAlgorithm {
    202         private final boolean mLookForRtl;
    203 
    204         @Override
    205         public TriState checkRtl(char[] text, int start, int count) {
    206             boolean haveUnlookedFor = false;
    207             for (int i = start, e = start + count; i < e; ++i) {
    208                 switch (isRtlText(Character.getDirectionality(text[i]))) {
    209                     case TRUE:
    210                         if (mLookForRtl) {
    211                             return TriState.TRUE;
    212                         }
    213                         haveUnlookedFor = true;
    214                         break;
    215                     case FALSE:
    216                         if (!mLookForRtl) {
    217                             return TriState.FALSE;
    218                         }
    219                         haveUnlookedFor = true;
    220                         break;
    221                     default:
    222                         break;
    223                 }
    224             }
    225             if (haveUnlookedFor) {
    226                 return mLookForRtl ? TriState.FALSE : TriState.TRUE;
    227             }
    228             return TriState.UNKNOWN;
    229         }
    230 
    231         private AnyStrong(boolean lookForRtl) {
    232             this.mLookForRtl = lookForRtl;
    233         }
    234 
    235         public static final AnyStrong INSTANCE_RTL = new AnyStrong(true);
    236         public static final AnyStrong INSTANCE_LTR = new AnyStrong(false);
    237     }
    238 
    239     /**
    240      * Algorithm that uses the Locale direction to force the direction of a paragraph.
    241      */
    242     public static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
    243 
    244         public TextDirectionHeuristicLocale() {
    245             super(null);
    246         }
    247 
    248         @Override
    249         protected boolean defaultIsRtl() {
    250             final int dir = LocaleUtil.getLayoutDirectionFromLocale(java.util.Locale.getDefault());
    251             return (dir == LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE);
    252         }
    253 
    254         public static final TextDirectionHeuristicLocale INSTANCE =
    255                 new TextDirectionHeuristicLocale();
    256     }
    257 }
    258