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