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 android.view.View;
     21 
     22 import java.nio.CharBuffer;
     23 
     24 /**
     25  * Some objects that implement {@link TextDirectionHeuristic}. Use these with
     26  * the {@link BidiFormatter#unicodeWrap unicodeWrap()} methods in {@link BidiFormatter}.
     27  * Also notice that these direction heuristics correspond to the same types of constants
     28  * provided in the {@link android.view.View} class for {@link android.view.View#setTextDirection
     29  * setTextDirection()}, such as {@link android.view.View#TEXT_DIRECTION_RTL}.
     30  * <p>To support versions lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
     31  * you can use the support library's {@link android.support.v4.text.TextDirectionHeuristicsCompat}
     32  * class.
     33  *
     34  */
     35 public class TextDirectionHeuristics {
     36 
     37     /**
     38      * Always decides that the direction is left to right.
     39      */
     40     public static final TextDirectionHeuristic LTR =
     41         new TextDirectionHeuristicInternal(null /* no algorithm */, false);
     42 
     43     /**
     44      * Always decides that the direction is right to left.
     45      */
     46     public static final TextDirectionHeuristic RTL =
     47         new TextDirectionHeuristicInternal(null /* no algorithm */, true);
     48 
     49     /**
     50      * Determines the direction based on the first strong directional character, including bidi
     51      * format chars, falling back to left to right if it finds none. This is the default behavior
     52      * of the Unicode Bidirectional Algorithm.
     53      */
     54     public static final TextDirectionHeuristic FIRSTSTRONG_LTR =
     55         new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false);
     56 
     57     /**
     58      * Determines the direction based on the first strong directional character, including bidi
     59      * format chars, falling back to right to left if it finds none. This is similar to the default
     60      * behavior of the Unicode Bidirectional Algorithm, just with different fallback behavior.
     61      */
     62     public static final TextDirectionHeuristic FIRSTSTRONG_RTL =
     63         new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true);
     64 
     65     /**
     66      * If the text contains any strong right to left non-format character, determines that the
     67      * direction is right to left, falling back to left to right if it finds none.
     68      */
     69     public static final TextDirectionHeuristic ANYRTL_LTR =
     70         new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false);
     71 
     72     /**
     73      * Force the paragraph direction to the Locale direction. Falls back to left to right.
     74      */
     75     public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE;
     76 
     77     /**
     78      * State constants for taking care about true / false / unknown
     79      */
     80     private static final int STATE_TRUE = 0;
     81     private static final int STATE_FALSE = 1;
     82     private static final int STATE_UNKNOWN = 2;
     83 
     84     private static int isRtlText(int directionality) {
     85         switch (directionality) {
     86             case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
     87                 return STATE_FALSE;
     88             case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
     89             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
     90                 return STATE_TRUE;
     91             default:
     92                 return STATE_UNKNOWN;
     93         }
     94     }
     95 
     96     private static int isRtlTextOrFormat(int directionality) {
     97         switch (directionality) {
     98             case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
     99             case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
    100             case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
    101                 return STATE_FALSE;
    102             case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
    103             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
    104             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
    105             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
    106                 return STATE_TRUE;
    107             default:
    108                 return STATE_UNKNOWN;
    109         }
    110     }
    111 
    112     /**
    113      * Computes the text direction based on an algorithm.  Subclasses implement
    114      * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the
    115      * direction from the text alone.
    116      */
    117     private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
    118         private final TextDirectionAlgorithm mAlgorithm;
    119 
    120         public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
    121             mAlgorithm = algorithm;
    122         }
    123 
    124         /**
    125          * Return true if the default text direction is rtl.
    126          */
    127         abstract protected boolean defaultIsRtl();
    128 
    129         @Override
    130         public boolean isRtl(char[] array, int start, int count) {
    131             return isRtl(CharBuffer.wrap(array), start, count);
    132         }
    133 
    134         @Override
    135         public boolean isRtl(CharSequence cs, int start, int count) {
    136             if (cs == null || start < 0 || count < 0 || cs.length() - count < start) {
    137                 throw new IllegalArgumentException();
    138             }
    139             if (mAlgorithm == null) {
    140                 return defaultIsRtl();
    141             }
    142             return doCheck(cs, start, count);
    143         }
    144 
    145         private boolean doCheck(CharSequence cs, int start, int count) {
    146             switch(mAlgorithm.checkRtl(cs, start, count)) {
    147                 case STATE_TRUE:
    148                     return true;
    149                 case STATE_FALSE:
    150                     return false;
    151                 default:
    152                     return defaultIsRtl();
    153             }
    154         }
    155     }
    156 
    157     private static class TextDirectionHeuristicInternal extends TextDirectionHeuristicImpl {
    158         private final boolean mDefaultIsRtl;
    159 
    160         private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm,
    161                 boolean defaultIsRtl) {
    162             super(algorithm);
    163             mDefaultIsRtl = defaultIsRtl;
    164         }
    165 
    166         @Override
    167         protected boolean defaultIsRtl() {
    168             return mDefaultIsRtl;
    169         }
    170     }
    171 
    172     /**
    173      * Interface for an algorithm to guess the direction of a paragraph of text.
    174      */
    175     private static interface TextDirectionAlgorithm {
    176         /**
    177          * Returns whether the range of text is RTL according to the algorithm.
    178          */
    179         int checkRtl(CharSequence cs, int start, int count);
    180     }
    181 
    182     /**
    183      * Algorithm that uses the first strong directional character to determine the paragraph
    184      * direction. This is the standard Unicode Bidirectional algorithm.
    185      */
    186     private static class FirstStrong implements TextDirectionAlgorithm {
    187         @Override
    188         public int checkRtl(CharSequence cs, int start, int count) {
    189             int result = STATE_UNKNOWN;
    190             for (int i = start, e = start + count; i < e && result == STATE_UNKNOWN; ++i) {
    191                 result = isRtlTextOrFormat(Character.getDirectionality(cs.charAt(i)));
    192             }
    193             return result;
    194         }
    195 
    196         private FirstStrong() {
    197         }
    198 
    199         public static final FirstStrong INSTANCE = new FirstStrong();
    200     }
    201 
    202     /**
    203      * Algorithm that uses the presence of any strong directional non-format
    204      * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
    205      * direction of text.
    206      */
    207     private static class AnyStrong implements TextDirectionAlgorithm {
    208         private final boolean mLookForRtl;
    209 
    210         @Override
    211         public int checkRtl(CharSequence cs, int start, int count) {
    212             boolean haveUnlookedFor = false;
    213             for (int i = start, e = start + count; i < e; ++i) {
    214                 switch (isRtlText(Character.getDirectionality(cs.charAt(i)))) {
    215                     case STATE_TRUE:
    216                         if (mLookForRtl) {
    217                             return STATE_TRUE;
    218                         }
    219                         haveUnlookedFor = true;
    220                         break;
    221                     case STATE_FALSE:
    222                         if (!mLookForRtl) {
    223                             return STATE_FALSE;
    224                         }
    225                         haveUnlookedFor = true;
    226                         break;
    227                     default:
    228                         break;
    229                 }
    230             }
    231             if (haveUnlookedFor) {
    232                 return mLookForRtl ? STATE_FALSE : STATE_TRUE;
    233             }
    234             return STATE_UNKNOWN;
    235         }
    236 
    237         private AnyStrong(boolean lookForRtl) {
    238             this.mLookForRtl = lookForRtl;
    239         }
    240 
    241         public static final AnyStrong INSTANCE_RTL = new AnyStrong(true);
    242         public static final AnyStrong INSTANCE_LTR = new AnyStrong(false);
    243     }
    244 
    245     /**
    246      * Algorithm that uses the Locale direction to force the direction of a paragraph.
    247      */
    248     private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
    249 
    250         public TextDirectionHeuristicLocale() {
    251             super(null);
    252         }
    253 
    254         @Override
    255         protected boolean defaultIsRtl() {
    256             final int dir = TextUtils.getLayoutDirectionFromLocale(java.util.Locale.getDefault());
    257             return (dir == View.LAYOUT_DIRECTION_RTL);
    258         }
    259 
    260         public static final TextDirectionHeuristicLocale INSTANCE =
    261                 new TextDirectionHeuristicLocale();
    262     }
    263 }
    264