• 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 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 
TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm)82         public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
83             mAlgorithm = algorithm;
84         }
85 
86         /**
87          * Return true if the default text direction is rtl.
88          */
defaultIsRtl()89         abstract protected boolean defaultIsRtl();
90 
91         @Override
isRtl(char[] chars, int start, int count)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 
doCheck(char[] chars, int start, int count)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 
TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm, boolean defaultIsRtl)117         private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm,
118                 boolean defaultIsRtl) {
119             super(algorithm);
120             mDefaultIsRtl = defaultIsRtl;
121         }
122 
123         @Override
defaultIsRtl()124         protected boolean defaultIsRtl() {
125             return mDefaultIsRtl;
126         }
127     }
128 
isRtlText(int directionality)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 
isRtlTextOrFormat(int directionality)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          */
checkRtl(char[] text, int start, int count)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
checkRtl(char[] text, int start, int count)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 
FirstStrong()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
checkRtl(char[] text, int start, int count)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 
AnyStrong(boolean lookForRtl)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 
TextDirectionHeuristicLocale()244         public TextDirectionHeuristicLocale() {
245             super(null);
246         }
247 
248         @Override
defaultIsRtl()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