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