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