blob: 866137cc3a63f4b5bca774bc39f7d5463a3964fd [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/**
Scott Maine4426622013-05-22 18:17:44 -070025 * Some objects that implement {@link TextDirectionHeuristic}. Use these with
26 * the {@link BidiFormatter#unicodeWrap unicodeWrap()} methods in {@link BidiFormatter}.
27 * Also notice that these direction heuristics correspond to the same types of constants
28 * provided in the {@link android.view.View} class for {@link android.view.View#setTextDirection
29 * setTextDirection()}, such as {@link android.view.View#TEXT_DIRECTION_RTL}.
30 * <p>To support versions lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
31 * you can use the support library's {@link android.support.v4.text.TextDirectionHeuristicsCompat}
32 * class.
Gilles Debunnecefb4bc2012-05-02 18:14:56 -070033 *
Doug Feltcb3791202011-07-07 11:57:48 -070034 */
35public class TextDirectionHeuristics {
36
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080037 /**
38 * Always decides that the direction is left to right.
39 */
Doug Feltcb3791202011-07-07 11:57:48 -070040 public static final TextDirectionHeuristic LTR =
41 new TextDirectionHeuristicInternal(null /* no algorithm */, false);
42
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080043 /**
44 * Always decides that the direction is right to left.
45 */
Doug Feltcb3791202011-07-07 11:57:48 -070046 public static final TextDirectionHeuristic RTL =
47 new TextDirectionHeuristicInternal(null /* no algorithm */, true);
48
49 /**
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080050 * Determines the direction based on the first strong directional character, including bidi
51 * format chars, falling back to left to right if it finds none. This is the default behavior
52 * of the Unicode Bidirectional Algorithm.
Doug Feltcb3791202011-07-07 11:57:48 -070053 */
54 public static final TextDirectionHeuristic FIRSTSTRONG_LTR =
55 new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false);
56
57 /**
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080058 * Determines the direction based on the first strong directional character, including bidi
59 * format chars, falling back to right to left if it finds none. This is similar to the default
60 * behavior of the Unicode Bidirectional Algorithm, just with different fallback behavior.
Doug Feltcb3791202011-07-07 11:57:48 -070061 */
62 public static final TextDirectionHeuristic FIRSTSTRONG_RTL =
63 new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true);
64
65 /**
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080066 * If the text contains any strong right to left non-format character, determines that the
67 * direction is right to left, falling back to left to right if it finds none.
Fabrice Di Meglio4b60c302011-08-17 16:56:55 -070068 */
69 public static final TextDirectionHeuristic ANYRTL_LTR =
70 new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false);
71
72 /**
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -070073 * Force the paragraph direction to the Locale direction. Falls back to left to right.
74 */
75 public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE;
76
Fabrice Di Meglio57a85742013-01-31 13:29:36 -080077 /**
78 * State constants for taking care about true / false / unknown
79 */
80 private static final int STATE_TRUE = 0;
81 private static final int STATE_FALSE = 1;
82 private static final int STATE_UNKNOWN = 2;
83
84 private static int isRtlText(int directionality) {
85 switch (directionality) {
86 case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
87 return STATE_FALSE;
88 case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
89 case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
90 return STATE_TRUE;
91 default:
92 return STATE_UNKNOWN;
93 }
94 }
95
96 private static int isRtlTextOrFormat(int directionality) {
97 switch (directionality) {
98 case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
99 case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
100 case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
101 return STATE_FALSE;
102 case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
103 case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
104 case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
105 case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
106 return STATE_TRUE;
107 default:
108 return STATE_UNKNOWN;
109 }
Doug Feltcb3791202011-07-07 11:57:48 -0700110 }
111
112 /**
113 * Computes the text direction based on an algorithm. Subclasses implement
114 * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the
115 * direction from the text alone.
Doug Feltcb3791202011-07-07 11:57:48 -0700116 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800117 private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
Doug Feltcb3791202011-07-07 11:57:48 -0700118 private final TextDirectionAlgorithm mAlgorithm;
119
120 public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
121 mAlgorithm = algorithm;
122 }
123
124 /**
125 * Return true if the default text direction is rtl.
126 */
127 abstract protected boolean defaultIsRtl();
128
129 @Override
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800130 public boolean isRtl(char[] array, int start, int count) {
131 return isRtl(CharBuffer.wrap(array), start, count);
132 }
133
134 @Override
135 public boolean isRtl(CharSequence cs, int start, int count) {
136 if (cs == null || start < 0 || count < 0 || cs.length() - count < start) {
Doug Feltcb3791202011-07-07 11:57:48 -0700137 throw new IllegalArgumentException();
138 }
139 if (mAlgorithm == null) {
140 return defaultIsRtl();
141 }
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800142 return doCheck(cs, start, count);
Doug Feltcb3791202011-07-07 11:57:48 -0700143 }
144
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800145 private boolean doCheck(CharSequence cs, int start, int count) {
146 switch(mAlgorithm.checkRtl(cs, start, count)) {
147 case STATE_TRUE:
Doug Feltcb3791202011-07-07 11:57:48 -0700148 return true;
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800149 case STATE_FALSE:
Doug Feltcb3791202011-07-07 11:57:48 -0700150 return false;
151 default:
152 return defaultIsRtl();
153 }
154 }
155 }
156
157 private static class TextDirectionHeuristicInternal extends TextDirectionHeuristicImpl {
158 private final boolean mDefaultIsRtl;
159
160 private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm,
161 boolean defaultIsRtl) {
162 super(algorithm);
163 mDefaultIsRtl = defaultIsRtl;
164 }
165
166 @Override
167 protected boolean defaultIsRtl() {
168 return mDefaultIsRtl;
169 }
170 }
171
Doug Feltcb3791202011-07-07 11:57:48 -0700172 /**
173 * Interface for an algorithm to guess the direction of a paragraph of text.
Doug Feltcb3791202011-07-07 11:57:48 -0700174 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800175 private static interface TextDirectionAlgorithm {
Doug Feltcb3791202011-07-07 11:57:48 -0700176 /**
177 * Returns whether the range of text is RTL according to the algorithm.
Doug Feltcb3791202011-07-07 11:57:48 -0700178 */
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800179 int checkRtl(CharSequence cs, int start, int count);
Doug Feltcb3791202011-07-07 11:57:48 -0700180 }
181
182 /**
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800183 * Algorithm that uses the first strong directional character to determine the paragraph
184 * direction. This is the standard Unicode Bidirectional algorithm.
Doug Feltcb3791202011-07-07 11:57:48 -0700185 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800186 private static class FirstStrong implements TextDirectionAlgorithm {
Doug Feltcb3791202011-07-07 11:57:48 -0700187 @Override
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800188 public int checkRtl(CharSequence cs, int start, int count) {
189 int result = STATE_UNKNOWN;
190 for (int i = start, e = start + count; i < e && result == STATE_UNKNOWN; ++i) {
191 result = isRtlTextOrFormat(Character.getDirectionality(cs.charAt(i)));
Doug Feltcb3791202011-07-07 11:57:48 -0700192 }
193 return result;
194 }
195
196 private FirstStrong() {
197 }
198
199 public static final FirstStrong INSTANCE = new FirstStrong();
200 }
201
202 /**
203 * Algorithm that uses the presence of any strong directional non-format
204 * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
205 * direction of text.
Doug Feltcb3791202011-07-07 11:57:48 -0700206 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800207 private static class AnyStrong implements TextDirectionAlgorithm {
Doug Feltcb3791202011-07-07 11:57:48 -0700208 private final boolean mLookForRtl;
209
210 @Override
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800211 public int checkRtl(CharSequence cs, int start, int count) {
Doug Feltcb3791202011-07-07 11:57:48 -0700212 boolean haveUnlookedFor = false;
213 for (int i = start, e = start + count; i < e; ++i) {
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800214 switch (isRtlText(Character.getDirectionality(cs.charAt(i)))) {
215 case STATE_TRUE:
Doug Feltcb3791202011-07-07 11:57:48 -0700216 if (mLookForRtl) {
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800217 return STATE_TRUE;
Doug Feltcb3791202011-07-07 11:57:48 -0700218 }
219 haveUnlookedFor = true;
220 break;
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800221 case STATE_FALSE:
Doug Feltcb3791202011-07-07 11:57:48 -0700222 if (!mLookForRtl) {
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800223 return STATE_FALSE;
Doug Feltcb3791202011-07-07 11:57:48 -0700224 }
225 haveUnlookedFor = true;
226 break;
227 default:
228 break;
229 }
230 }
231 if (haveUnlookedFor) {
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800232 return mLookForRtl ? STATE_FALSE : STATE_TRUE;
Doug Feltcb3791202011-07-07 11:57:48 -0700233 }
Fabrice Di Meglio57a85742013-01-31 13:29:36 -0800234 return STATE_UNKNOWN;
Doug Feltcb3791202011-07-07 11:57:48 -0700235 }
236
237 private AnyStrong(boolean lookForRtl) {
238 this.mLookForRtl = lookForRtl;
239 }
240
241 public static final AnyStrong INSTANCE_RTL = new AnyStrong(true);
242 public static final AnyStrong INSTANCE_LTR = new AnyStrong(false);
243 }
244
245 /**
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -0700246 * Algorithm that uses the Locale direction to force the direction of a paragraph.
247 */
Fabrice Di Meglioe7beae32012-02-13 15:23:57 -0800248 private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -0700249
250 public TextDirectionHeuristicLocale() {
251 super(null);
252 }
253
254 @Override
255 protected boolean defaultIsRtl() {
Fabrice Di Megliod3d9f3f2012-09-18 12:55:32 -0700256 final int dir = TextUtils.getLayoutDirectionFromLocale(java.util.Locale.getDefault());
Fabrice Di Meglio3fb824b2012-02-28 17:58:31 -0800257 return (dir == View.LAYOUT_DIRECTION_RTL);
Fabrice Di Meglio7810b5f2011-08-24 18:26:14 -0700258 }
259
260 public static final TextDirectionHeuristicLocale INSTANCE =
261 new TextDirectionHeuristicLocale();
262 }
Doug Feltcb3791202011-07-07 11:57:48 -0700263}