blob: df8c4c665f1e6a8f1037f1c16e2c1956382ecc8b [file] [log] [blame]
Fabrice Di Meglio4b60c302011-08-17 16:56:55 -07001/*
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 */
Doug Feltcb3791202011-07-07 11:57:48 -070016
17package android.text;
18
19
Fabrice Di Meglio3fb824b2012-02-28 17:58:31 -080020import android.view.View;
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -070021
Doug Feltcb3791202011-07-07 11:57:48 -070022/**
23 * Some objects that implement TextDirectionHeuristic.
Gilles Debunnecefb4bc2012-05-02 18:14:56 -070024 *
25 * @hide
Doug Feltcb3791202011-07-07 11:57:48 -070026 */
27public 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 /**
Fabrice Di Meglio4b60c302011-08-17 16:56:55 -070056 * 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 /**
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -070064 * Force the paragraph direction to the Locale direction. Falls back to left to right.
65 */
66 public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE;
67
Doug Feltcb3791202011-07-07 11:57:48 -070068 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.
Doug Feltcb3791202011-07-07 11:57:48 -070076 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -080077 private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
Doug Feltcb3791202011-07-07 11:57:48 -070078 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
Doug Feltcb3791202011-07-07 11:57:48 -070090 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:
Doug Feltcb3791202011-07-07 11:57:48 -0700149 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 *
Doug Feltcb3791202011-07-07 11:57:48 -0700158 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800159 private static interface TextDirectionAlgorithm {
Doug Feltcb3791202011-07-07 11:57:48 -0700160 /**
161 * Returns whether the range of text is RTL according to the algorithm.
162 *
Doug Feltcb3791202011-07-07 11:57:48 -0700163 */
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 *
Doug Feltcb3791202011-07-07 11:57:48 -0700172 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800173 private static class FirstStrong implements TextDirectionAlgorithm {
Doug Feltcb3791202011-07-07 11:57:48 -0700174 @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 *
Doug Feltcb3791202011-07-07 11:57:48 -0700194 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800195 private static class AnyStrong implements TextDirectionAlgorithm {
Doug Feltcb3791202011-07-07 11:57:48 -0700196 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 /**
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -0700234 * Algorithm that uses the Locale direction to force the direction of a paragraph.
235 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800236 private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -0700237
238 public TextDirectionHeuristicLocale() {
239 super(null);
240 }
241
242 @Override
243 protected boolean defaultIsRtl() {
Fabrice Di Megliod3d9f3f2012-09-18 12:55:32 -0700244 final int dir = TextUtils.getLayoutDirectionFromLocale(java.util.Locale.getDefault());
Fabrice Di Meglio3fb824b2012-02-28 17:58:31 -0800245 return (dir == View.LAYOUT_DIRECTION_RTL);
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -0700246 }
247
248 public static final TextDirectionHeuristicLocale INSTANCE =
249 new TextDirectionHeuristicLocale();
250 }
Doug Feltcb3791202011-07-07 11:57:48 -0700251}