• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 import android.text.Layout.Directions;
20 
21 import com.android.internal.annotations.VisibleForTesting;
22 
23 /**
24  * Access the ICU bidi implementation.
25  * @hide
26  */
27 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
28 public class AndroidBidi {
29 
bidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo)30     public static int bidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo) {
31         if (chs == null || chInfo == null) {
32             throw new NullPointerException();
33         }
34 
35         if (n < 0 || chs.length < n || chInfo.length < n) {
36             throw new IndexOutOfBoundsException();
37         }
38 
39         switch(dir) {
40             case Layout.DIR_REQUEST_LTR: dir = 0; break;
41             case Layout.DIR_REQUEST_RTL: dir = 1; break;
42             case Layout.DIR_REQUEST_DEFAULT_LTR: dir = -2; break;
43             case Layout.DIR_REQUEST_DEFAULT_RTL: dir = -1; break;
44             default: dir = 0; break;
45         }
46 
47         int result = runBidi(dir, chs, chInfo, n, haveInfo);
48         result = (result & 0x1) == 0 ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT;
49         return result;
50     }
51 
52     /**
53      * Returns run direction information for a line within a paragraph.
54      *
55      * @param dir base line direction, either Layout.DIR_LEFT_TO_RIGHT or
56      *     Layout.DIR_RIGHT_TO_LEFT
57      * @param levels levels as returned from {@link #bidi}
58      * @param lstart start of the line in the levels array
59      * @param chars the character array (used to determine whitespace)
60      * @param cstart the start of the line in the chars array
61      * @param len the length of the line
62      * @return the directions
63      */
directions(int dir, byte[] levels, int lstart, char[] chars, int cstart, int len)64     public static Directions directions(int dir, byte[] levels, int lstart,
65             char[] chars, int cstart, int len) {
66         if (len == 0) {
67             return Layout.DIRS_ALL_LEFT_TO_RIGHT;
68         }
69 
70         int baseLevel = dir == Layout.DIR_LEFT_TO_RIGHT ? 0 : 1;
71         int curLevel = levels[lstart];
72         int minLevel = curLevel;
73         int runCount = 1;
74         for (int i = lstart + 1, e = lstart + len; i < e; ++i) {
75             int level = levels[i];
76             if (level != curLevel) {
77                 curLevel = level;
78                 ++runCount;
79             }
80         }
81 
82         // add final run for trailing counter-directional whitespace
83         int visLen = len;
84         if ((curLevel & 1) != (baseLevel & 1)) {
85             // look for visible end
86             while (--visLen >= 0) {
87                 char ch = chars[cstart + visLen];
88 
89                 if (ch == '\n') {
90                     --visLen;
91                     break;
92                 }
93 
94                 if (ch != ' ' && ch != '\t') {
95                     break;
96                 }
97             }
98             ++visLen;
99             if (visLen != len) {
100                 ++runCount;
101             }
102         }
103 
104         if (runCount == 1 && minLevel == baseLevel) {
105             // we're done, only one run on this line
106             if ((minLevel & 1) != 0) {
107                 return Layout.DIRS_ALL_RIGHT_TO_LEFT;
108             }
109             return Layout.DIRS_ALL_LEFT_TO_RIGHT;
110         }
111 
112         int[] ld = new int[runCount * 2];
113         int maxLevel = minLevel;
114         int levelBits = minLevel << Layout.RUN_LEVEL_SHIFT;
115         {
116             // Start of first pair is always 0, we write
117             // length then start at each new run, and the
118             // last run length after we're done.
119             int n = 1;
120             int prev = lstart;
121             curLevel = minLevel;
122             for (int i = lstart, e = lstart + visLen; i < e; ++i) {
123                 int level = levels[i];
124                 if (level != curLevel) {
125                     curLevel = level;
126                     if (level > maxLevel) {
127                         maxLevel = level;
128                     } else if (level < minLevel) {
129                         minLevel = level;
130                     }
131                     // XXX ignore run length limit of 2^RUN_LEVEL_SHIFT
132                     ld[n++] = (i - prev) | levelBits;
133                     ld[n++] = i - lstart;
134                     levelBits = curLevel << Layout.RUN_LEVEL_SHIFT;
135                     prev = i;
136                 }
137             }
138             ld[n] = (lstart + visLen - prev) | levelBits;
139             if (visLen < len) {
140                 ld[++n] = visLen;
141                 ld[++n] = (len - visLen) | (baseLevel << Layout.RUN_LEVEL_SHIFT);
142             }
143         }
144 
145         // See if we need to swap any runs.
146         // If the min level run direction doesn't match the base
147         // direction, we always need to swap (at this point
148         // we have more than one run).
149         // Otherwise, we don't need to swap the lowest level.
150         // Since there are no logically adjacent runs at the same
151         // level, if the max level is the same as the (new) min
152         // level, we have a series of alternating levels that
153         // is already in order, so there's no more to do.
154         //
155         boolean swap;
156         if ((minLevel & 1) == baseLevel) {
157             minLevel += 1;
158             swap = maxLevel > minLevel;
159         } else {
160             swap = runCount > 1;
161         }
162         if (swap) {
163             for (int level = maxLevel - 1; level >= minLevel; --level) {
164                 for (int i = 0; i < ld.length; i += 2) {
165                     if (levels[ld[i]] >= level) {
166                         int e = i + 2;
167                         while (e < ld.length && levels[ld[e]] >= level) {
168                             e += 2;
169                         }
170                         for (int low = i, hi = e - 2; low < hi; low += 2, hi -= 2) {
171                             int x = ld[low]; ld[low] = ld[hi]; ld[hi] = x;
172                             x = ld[low+1]; ld[low+1] = ld[hi+1]; ld[hi+1] = x;
173                         }
174                         i = e + 2;
175                     }
176                 }
177             }
178         }
179         return new Directions(ld);
180     }
181 
runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo)182     private native static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo);
183 }