blob: 7d7e3a935f94f80a24a23e92599282b1be946b93 [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
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080022import java.nio.CharBuffer;
23
Doug Feltcb3791202011-07-07 11:57:48 -070024/**
25 * Some objects that implement TextDirectionHeuristic.
Gilles Debunnecefb4bc2012-05-02 18:14:56 -070026 *
Doug Feltcb3791202011-07-07 11:57:48 -070027 */
28public class TextDirectionHeuristics {
29
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080030 /**
31 * Always decides that the direction is left to right.
32 */
Doug Feltcb3791202011-07-07 11:57:48 -070033 public static final TextDirectionHeuristic LTR =
34 new TextDirectionHeuristicInternal(null /* no algorithm */, false);
35
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080036 /**
37 * Always decides that the direction is right to left.
38 */
Doug Feltcb3791202011-07-07 11:57:48 -070039 public static final TextDirectionHeuristic RTL =
40 new TextDirectionHeuristicInternal(null /* no algorithm */, true);
41
42 /**
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080043 * 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.
Doug Feltcb3791202011-07-07 11:57:48 -070046 */
47 public static final TextDirectionHeuristic FIRSTSTRONG_LTR =
48 new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false);
49
50 /**
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080051 * 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.
Doug Feltcb3791202011-07-07 11:57:48 -070054 */
55 public static final TextDirectionHeuristic FIRSTSTRONG_RTL =
56 new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true);
57
58 /**
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080059 * 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.
Fabrice Di Meglio4b60c302011-08-17 16:56:55 -070061 */
62 public static final TextDirectionHeuristic ANYRTL_LTR =
63 new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false);
64
65 /**
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -070066 * Force the paragraph direction to the Locale direction. Falls back to left to right.
67 */
68 public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE;
69
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080070 /**
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
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
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 }
Doug Feltcb3791202011-07-07 11:57:48 -0700103 }
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.
Doug Feltcb3791202011-07-07 11:57:48 -0700109 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800110 private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
Doug Feltcb3791202011-07-07 11:57:48 -0700111 private final TextDirectionAlgorithm mAlgorithm;
112
113 public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
114 mAlgorithm = algorithm;
115 }
116
117 /**
118 * Return true if the default text direction is rtl.
119 */
120 abstract protected boolean defaultIsRtl();
121
122 @Override
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800123 public boolean isRtl(char[] array, int start, int count) {
124 return isRtl(CharBuffer.wrap(array), start, count);
125 }
126
127 @Override
128 public boolean isRtl(CharSequence cs, int start, int count) {
129 if (cs == null || start < 0 || count < 0 || cs.length() - count < start) {
Doug Feltcb3791202011-07-07 11:57:48 -0700130 throw new IllegalArgumentException();
131 }
132 if (mAlgorithm == null) {
133 return defaultIsRtl();
134 }
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800135 return doCheck(cs, start, count);
Doug Feltcb3791202011-07-07 11:57:48 -0700136 }
137
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800138 private boolean doCheck(CharSequence cs, int start, int count) {
139 switch(mAlgorithm.checkRtl(cs, start, count)) {
140 case STATE_TRUE:
Doug Feltcb3791202011-07-07 11:57:48 -0700141 return true;
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800142 case STATE_FALSE:
Doug Feltcb3791202011-07-07 11:57:48 -0700143 return false;
144 default:
145 return defaultIsRtl();
146 }
147 }
148 }
149
150 private static class TextDirectionHeuristicInternal extends TextDirectionHeuristicImpl {
151 private final boolean mDefaultIsRtl;
152
153 private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm,
154 boolean defaultIsRtl) {
155 super(algorithm);
156 mDefaultIsRtl = defaultIsRtl;
157 }
158
159 @Override
160 protected boolean defaultIsRtl() {
161 return mDefaultIsRtl;
162 }
163 }
164
Doug Feltcb3791202011-07-07 11:57:48 -0700165 /**
166 * Interface for an algorithm to guess the direction of a paragraph of text.
Doug Feltcb3791202011-07-07 11:57:48 -0700167 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800168 private static interface TextDirectionAlgorithm {
Doug Feltcb3791202011-07-07 11:57:48 -0700169 /**
170 * Returns whether the range of text is RTL according to the algorithm.
Doug Feltcb3791202011-07-07 11:57:48 -0700171 */
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800172 int checkRtl(CharSequence cs, int start, int count);
Doug Feltcb3791202011-07-07 11:57:48 -0700173 }
174
175 /**
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800176 * Algorithm that uses the first strong directional character to determine the paragraph
177 * direction. This is the standard Unicode Bidirectional algorithm.
Doug Feltcb3791202011-07-07 11:57:48 -0700178 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800179 private static class FirstStrong implements TextDirectionAlgorithm {
Doug Feltcb3791202011-07-07 11:57:48 -0700180 @Override
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800181 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)));
Doug Feltcb3791202011-07-07 11:57:48 -0700185 }
186 return result;
187 }
188
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.
Doug Feltcb3791202011-07-07 11:57:48 -0700199 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800200 private static class AnyStrong implements TextDirectionAlgorithm {
Doug Feltcb3791202011-07-07 11:57:48 -0700201 private final boolean mLookForRtl;
202
203 @Override
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800204 public int checkRtl(CharSequence cs, int start, int count) {
Doug Feltcb3791202011-07-07 11:57:48 -0700205 boolean haveUnlookedFor = false;
206 for (int i = start, e = start + count; i < e; ++i) {
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800207 switch (isRtlText(Character.getDirectionality(cs.charAt(i)))) {
208 case STATE_TRUE:
Doug Feltcb3791202011-07-07 11:57:48 -0700209 if (mLookForRtl) {
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800210 return STATE_TRUE;
Doug Feltcb3791202011-07-07 11:57:48 -0700211 }
212 haveUnlookedFor = true;
213 break;
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800214 case STATE_FALSE:
Doug Feltcb3791202011-07-07 11:57:48 -0700215 if (!mLookForRtl) {
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800216 return STATE_FALSE;
Doug Feltcb3791202011-07-07 11:57:48 -0700217 }
218 haveUnlookedFor = true;
219 break;
220 default:
221 break;
222 }
223 }
224 if (haveUnlookedFor) {
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800225 return mLookForRtl ? STATE_FALSE : STATE_TRUE;
Doug Feltcb3791202011-07-07 11:57:48 -0700226 }
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800227 return STATE_UNKNOWN;
Doug Feltcb3791202011-07-07 11:57:48 -0700228 }
229
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 /**
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -0700239 * Algorithm that uses the Locale direction to force the direction of a paragraph.
240 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800241 private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -0700242
243 public TextDirectionHeuristicLocale() {
244 super(null);
245 }
246
247 @Override
248 protected boolean defaultIsRtl() {
Fabrice Di Megliod3d9f3f2012-09-18 12:55:32 -0700249 final int dir = TextUtils.getLayoutDirectionFromLocale(java.util.Locale.getDefault());
Fabrice Di Meglio3fb824b2012-02-28 17:58:31 -0800250 return (dir == View.LAYOUT_DIRECTION_RTL);
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -0700251 }
252
253 public static final TextDirectionHeuristicLocale INSTANCE =
254 new TextDirectionHeuristicLocale();
255 }
Doug Feltcb3791202011-07-07 11:57:48 -0700256}