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