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
17package android.text;
18
19import android.text.Layout.Directions;
20
21import com.android.internal.annotations.VisibleForTesting;
22
23/**
24 * Access the ICU bidi implementation.
25 * @hide
26 */
27@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
28public class AndroidBidi {
29
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     */
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
182    private native static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo);
183}