TextDirectionHeuristics.java revision e7beae3f4c9c170c7c6c42cf9b572f0ee1ec9c81
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 17package android.text; 18 19 20import android.util.LocaleUtil; 21 22/** 23 * Some objects that implement TextDirectionHeuristic. 24 */ 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 /** 54 * 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 /** 62 * Force the paragraph direction to the Locale direction. Falls back to left to right. 63 */ 64 public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE; 65 66 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. 74 */ 75 private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic { 76 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 88 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: 147 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 * 156 */ 157 private static interface TextDirectionAlgorithm { 158 /** 159 * Returns whether the range of text is RTL according to the algorithm. 160 * 161 */ 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 * 170 */ 171 private static class FirstStrong implements TextDirectionAlgorithm { 172 @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 * 192 */ 193 private static class AnyStrong implements TextDirectionAlgorithm { 194 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 /** 232 * Algorithm that uses the Locale direction to force the direction of a paragraph. 233 */ 234 private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl { 235 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 } 249} 250