• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm)80         public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
81             mAlgorithm = algorithm;
82         }
83 
84         /**
85          * Return true if the default text direction is rtl.
86          */
defaultIsRtl()87         abstract protected boolean defaultIsRtl();
88 
89         @Override
isRtl(char[] chars, int start, int count)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 
doCheck(char[] chars, int start, int count)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 
TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm, boolean defaultIsRtl)115         private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm,
116                 boolean defaultIsRtl) {
117             super(algorithm);
118             mDefaultIsRtl = defaultIsRtl;
119         }
120 
121         @Override
defaultIsRtl()122         protected boolean defaultIsRtl() {
123             return mDefaultIsRtl;
124         }
125     }
126 
isRtlText(int directionality)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 
isRtlTextOrFormat(int directionality)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          */
checkRtl(char[] text, int start, int count)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
checkRtl(char[] text, int start, int count)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 
FirstStrong()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
checkRtl(char[] text, int start, int count)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 
AnyStrong(boolean lookForRtl)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 
TextDirectionHeuristicLocale()238         public TextDirectionHeuristicLocale() {
239             super(null);
240         }
241 
242         @Override
defaultIsRtl()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