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