1f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the License.  You may obtain a copy of the License at
8f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.text;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.awt.font.NumericShaper;
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.awt.font.TextAttribute;
22f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughesimport java.util.ArrayList;
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Arrays;
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
26bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes * Implements the <a href="http://unicode.org/reports/tr9/">Unicode Bidirectional Algorithm</a>.
27f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
28bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes * <p>Use a {@code Bidi} object to get the information on the position reordering of a
29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * bidirectional text, such as Arabic or Hebrew. The natural display ordering of
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * horizontal text in these languages is from right to left, while they order
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * numbers from left to right.
32f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
33bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes * <p>If the text contains multiple runs, the information of each run can be
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * obtained from the run index. The level of any particular run indicates the
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * direction of the text as well as the nesting level. Left-to-right runs have
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * even levels while right-to-left runs have odd levels.
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic final class Bidi {
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constant that indicates the default base level. If there is no strong
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * character, then set the paragraph level to 0 (left-to-right).
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2;
44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constant that indicates the default base level. If there is no strong
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * character, then set the paragraph level to 1 (right-to-left).
48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1;
50adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
51adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constant that specifies the default base level as 0 (left-to-right).
53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
54adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public static final int DIRECTION_LEFT_TO_RIGHT = 0;
55adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
56adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
57adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constant that specifies the default base level as 1 (right-to-left).
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public static final int DIRECTION_RIGHT_TO_LEFT = 1;
60adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
61adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
62bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes     * TODO: if we care about performance, we might just want to use an int[] instead of a Run[].
63bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes     */
64bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    static class Run {
65bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        private final int start;
66bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        private final int limit;
67bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        private final int level;
68bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes
69bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        public Run(int start, int limit, int level) {
70bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            this.start = start;
71bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            this.limit = limit;
72bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            this.level = level;
73bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        }
74bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes
75bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        public int getLevel() {
76bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            return level;
77bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        }
78bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes
79bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        public int getLimit() {
80bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            return limit;
81bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        }
82bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes
83bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        public int getStart() {
84bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            return start;
85bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        }
86bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    }
87bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes
88bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    /**
89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a {@code Bidi} object from the {@code
90adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * AttributedCharacterIterator} of a paragraph text. The RUN_DIRECTION
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * attribute determines the base direction of the bidirectional text. If it
92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * is not specified explicitly, the algorithm uses
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * DIRECTION_DEFAULT_LEFT_TO_RIGHT by default. The BIDI_EMBEDDING attribute
94adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * specifies the level of embedding for each character. Values between -1
95adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * and -62 denote overrides at the level's absolute value, values from 1 to
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * 62 indicate embeddings, and the 0 value indicates the level is calculated
97adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * by the algorithm automatically. For the character with no BIDI_EMBEDDING
98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * attribute or with a improper attribute value, such as a {@code null}
99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * value, the algorithm treats its embedding level as 0. The NUMERIC_SHAPING
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * attribute specifies the instance of NumericShaper used to convert
101adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * European digits to other decimal digits before performing the bidi
102adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * algorithm.
1039b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson     *
104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param paragraph
105adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the String containing the paragraph text to perform the
106adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            algorithm.
10703c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughes     * @throws IllegalArgumentException if {@code paragraph == null}
1089b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson     * @see java.awt.font.TextAttribute#BIDI_EMBEDDING
1099b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson     * @see java.awt.font.TextAttribute#NUMERIC_SHAPING
1109b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson     * @see java.awt.font.TextAttribute#RUN_DIRECTION
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
112adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Bidi(AttributedCharacterIterator paragraph) {
113adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (paragraph == null) {
11403c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughes            throw new IllegalArgumentException("paragraph is null");
115adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
116adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int begin = paragraph.getBeginIndex();
118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int end = paragraph.getEndIndex();
119adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int length = end - begin;
120171dc20afe5071d5cbfad7103903bfa2c1f8d00fElliott Hughes        char[] text = new char[length + 1]; // One more char for AttributedCharacterIterator.DONE
121adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (length != 0) {
123adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            text[0] = paragraph.first();
124adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            paragraph.first();
126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
127adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
128adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // First check the RUN_DIRECTION attribute.
129adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int flags = DIRECTION_DEFAULT_LEFT_TO_RIGHT;
130adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Object direction = paragraph.getAttribute(TextAttribute.RUN_DIRECTION);
131adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (direction != null && direction instanceof Boolean) {
132adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (direction.equals(TextAttribute.RUN_DIRECTION_LTR)) {
133adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                flags = DIRECTION_LEFT_TO_RIGHT;
134adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
135adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                flags = DIRECTION_RIGHT_TO_LEFT;
136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
137adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
138adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
139adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // Retrieve the text and gather BIDI_EMBEDDINGS
140171dc20afe5071d5cbfad7103903bfa2c1f8d00fElliott Hughes        byte[] embeddings = null;
141adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (int textLimit = 1, i = 1; i < length; textLimit = paragraph
142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                .getRunLimit(TextAttribute.BIDI_EMBEDDING)
143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                - begin + 1) {
144f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes            Object embedding = paragraph.getAttribute(TextAttribute.BIDI_EMBEDDING);
145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (embedding != null && embedding instanceof Integer) {
146adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                int embLevel = ((Integer) embedding).intValue();
147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
148adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (embeddings == null) {
149adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    embeddings = new byte[length];
150adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
151adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
152adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                for (; i < textLimit; i++) {
153adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    text[i] = paragraph.next();
154adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    embeddings[i - 1] = (byte) embLevel;
155adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                for (; i < textLimit; i++) {
158adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    text[i] = paragraph.next();
159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // Apply NumericShaper to the text
164f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        Object numericShaper = paragraph.getAttribute(TextAttribute.NUMERIC_SHAPING);
165adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (numericShaper != null && numericShaper instanceof NumericShaper) {
166adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            ((NumericShaper) numericShaper).shape(text, 0, length);
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
169f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        long bidi = 0;
170f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        try {
171f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes            bidi = createUBiDi(text, 0, embeddings, 0, length, flags);
172f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes            readBidiInfo(bidi);
173f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        } finally {
174bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            ubidi_close(bidi);
175f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        }
176adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a {@code Bidi} object.
180f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param text
182adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the char array of the paragraph text that is processed.
183adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param textStart
184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the index in {@code text} of the start of the paragraph.
185adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param embeddings
186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the embedding level array of the paragraph text, specifying
187adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the embedding level information for each character. Values
188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            between -1 and -61 denote overrides at the level's absolute
189adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            value, values from 1 to 61 indicate embeddings, and the 0
190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            value indicates the level is calculated by the algorithm
191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            automatically.
192adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param embStart
193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the index in {@code embeddings} of the start of the paragraph.
194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param paragraphLength
195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the length of the text to perform the algorithm.
196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param flags
197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            indicates the base direction of the bidirectional text. It is
198adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            expected that this will be one of the direction constant
199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            values defined in this class. An unknown value is treated as
200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            DIRECTION_DEFAULT_LEFT_TO_RIGHT.
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IllegalArgumentException
202adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if {@code textStart}, {@code embStart}, or {@code
2039b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson     *             paragraphLength} is negative; if
2049b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson     *             {@code text.length < textStart + paragraphLength} or
205adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             {@code embeddings.length < embStart + paragraphLength}.
206adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #DIRECTION_LEFT_TO_RIGHT
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #DIRECTION_RIGHT_TO_LEFT
208adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT
209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT
210adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Bidi(char[] text, int textStart, byte[] embeddings, int embStart,
212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            int paragraphLength, int flags) {
2139b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson
2149b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson        if (text == null || text.length - textStart < paragraphLength) {
2159b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson            throw new IllegalArgumentException();
2169b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson        }
2179b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson
2189b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson        if (embeddings != null) {
2199b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson            if (embeddings.length - embStart < paragraphLength) {
2209b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson                throw new IllegalArgumentException();
2219b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson            }
2229b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson        }
2239b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson
224adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (textStart < 0) {
22503c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughes            throw new IllegalArgumentException("Negative textStart value " + textStart);
226adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
227adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (embStart < 0) {
22803c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughes            throw new IllegalArgumentException("Negative embStart value " + embStart);
229adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
230adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (paragraphLength < 0) {
23103c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughes            throw new IllegalArgumentException("Negative paragraph length " + paragraphLength);
232adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
233f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
234f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        long bidi = 0;
235f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        try {
236f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes            bidi = createUBiDi(text, textStart, embeddings, embStart, paragraphLength, flags);
237f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes            readBidiInfo(bidi);
238f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        } finally {
239bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            ubidi_close(bidi);
240f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        }
241adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
242adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
243adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
244adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a {@code Bidi} object.
245f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
246adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param paragraph
247adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the string containing the paragraph text to perform the
248adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            algorithm on.
249adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param flags
250adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            indicates the base direction of the bidirectional text. It is
251adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            expected that this will be one of the direction constant
252adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            values defined in this class. An unknown value is treated as
253adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            DIRECTION_DEFAULT_LEFT_TO_RIGHT.
254adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #DIRECTION_LEFT_TO_RIGHT
255adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #DIRECTION_RIGHT_TO_LEFT
256adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT
257adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT
258adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
259adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Bidi(String paragraph, int flags) {
260adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this((paragraph == null ? null : paragraph.toCharArray()), 0, null, 0,
261adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                (paragraph == null ? 0 : paragraph.length()), flags);
262adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
263adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
264adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    // create the native UBiDi struct, need to be closed with ubidi_close().
265adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static long createUBiDi(char[] text, int textStart,
266adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            byte[] embeddings, int embStart, int paragraphLength, int flags) {
267adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        char[] realText = null;
268adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
269adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        byte[] realEmbeddings = null;
270adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
271adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (text == null || text.length - textStart < paragraphLength) {
272adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new IllegalArgumentException();
273adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
274adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        realText = new char[paragraphLength];
275adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        System.arraycopy(text, textStart, realText, 0, paragraphLength);
276adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
277adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (embeddings != null) {
278adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (embeddings.length - embStart < paragraphLength) {
279adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                throw new IllegalArgumentException();
280adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
281adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (paragraphLength > 0) {
282f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes                Bidi temp = new Bidi(text, textStart, null, 0, paragraphLength, flags);
283adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                realEmbeddings = new byte[paragraphLength];
284f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes                System.arraycopy(temp.offsetLevel, 0, realEmbeddings, 0, paragraphLength);
285adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                for (int i = 0; i < paragraphLength; i++) {
286adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    byte e = embeddings[i];
287adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    if (e < 0) {
288bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes                        realEmbeddings[i] = (byte) (UBIDI_LEVEL_OVERRIDE - e);
289adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    } else if (e > 0) {
290adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        realEmbeddings[i] = e;
291adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    } else {
292bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes                        realEmbeddings[i] |= (byte) UBIDI_LEVEL_OVERRIDE;
293adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
294adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
295adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
296adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
297adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
298adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (flags > 1 || flags < -2) {
299adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            flags = 0;
300adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
301adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
302f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        long bidi = 0;
303f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        boolean needsDeletion = true;
304f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        try {
305bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            bidi = ubidi_open();
306bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            ubidi_setPara(bidi, realText, paragraphLength, flags, realEmbeddings);
307f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes            needsDeletion = false;
308f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        } finally {
309f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes            if (needsDeletion) {
310bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes                ubidi_close(bidi);
311f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes            }
312f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        }
313adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return bidi;
314adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
315adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
3169b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson    /* private constructor used by createLineBidi() */
317adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private Bidi(long pBidi) {
318adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        readBidiInfo(pBidi);
319adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
320adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
321adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    // read info from the native UBiDi struct
322adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void readBidiInfo(long pBidi) {
323bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        length = ubidi_getLength(pBidi);
324adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
325bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        offsetLevel = (length == 0) ? null : ubidi_getLevels(pBidi);
326adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
327bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        baseLevel = ubidi_getParaLevel(pBidi);
328adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
329bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        int runCount = ubidi_countRuns(pBidi);
330adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (runCount == 0) {
331adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            unidirectional = true;
332adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            runs = null;
333adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else if (runCount < 0) {
334adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            runs = null;
335adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
336bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            runs = ubidi_getRuns(pBidi);
337adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
338adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            // Simplified case for one run which has the base level
339adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (runCount == 1 && runs[0].getLevel() == baseLevel) {
340adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                unidirectional = true;
341adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                runs = null;
342adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
343adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
344adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
345bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        direction = ubidi_getDirection(pBidi);
346adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
347adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
348adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int baseLevel;
349adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
350adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int length;
351adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
352adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private byte[] offsetLevel;
353adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
354bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private Run[] runs;
355adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
356adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int direction;
357adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
358adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private boolean unidirectional;
359adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
360adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
361adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns whether the base level is from left to right.
362f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
363adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return true if the base level is from left to right.
364adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
365adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public boolean baseIsLeftToRight() {
366adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return baseLevel % 2 == 0 ? true : false;
367adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
368adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
369adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
370adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a new {@code Bidi} object containing the information of one line
371adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * from this object.
372f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
373adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param lineStart
374adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the start offset of the line.
375adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param lineLimit
376adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the limit of the line.
377adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the new line Bidi object. In this new object, the indices will
378adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *         range from 0 to (limit - start - 1).
379adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IllegalArgumentException
380adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if {@code lineStart < 0}, {@code lineLimit < 0}, {@code
381adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             lineStart > lineLimit} or if {@code lineStart} is greater
382adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             than the length of this object's paragraph text.
383adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
384adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Bidi createLineBidi(int lineStart, int lineLimit) {
385ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        if (lineStart < 0 || lineLimit < 0 || lineLimit > length || lineStart > lineLimit) {
38603c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughes            throw new IllegalArgumentException("Invalid ranges (start=" + lineStart + ", " +
38703c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughes                    "limit=" + lineLimit + ", length=" + length + ")");
388adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
389ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes
390adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        char[] text = new char[this.length];
391adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Arrays.fill(text, 'a');
392adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        byte[] embeddings = new byte[this.length];
393adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (int i = 0; i < embeddings.length; i++) {
394adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            embeddings[i] = (byte) -this.offsetLevel[i];
395adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
396adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
397ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        int dir = this.baseIsLeftToRight()
398ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes                ? Bidi.DIRECTION_LEFT_TO_RIGHT
399adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                : Bidi.DIRECTION_RIGHT_TO_LEFT;
400ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        long parent = 0;
401ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        try {
402ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes            parent = createUBiDi(text, 0, embeddings, 0, this.length, dir);
403ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes            if (lineStart == lineLimit) {
404ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes                return createEmptyLineBidi(parent);
405ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes            }
406bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            return new Bidi(ubidi_setLine(parent, lineStart, lineLimit));
407ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        } finally {
408bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            ubidi_close(parent);
409ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        }
410ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes    }
411adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
412ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes    private Bidi createEmptyLineBidi(long parent) {
413ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        // ICU4C doesn't allow this case, but the RI does.
414ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        Bidi result = new Bidi(parent);
415ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        result.length = 0;
416ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        result.offsetLevel = null;
417ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        result.runs = null;
418ccfdf99802ae83d6da20c5052b84bbefa63e2bf8Elliott Hughes        result.unidirectional = true;
419adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return result;
420adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
421adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
422adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
423adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the base level.
424adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
425adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getBaseLevel() {
426adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return baseLevel;
427adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
428adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
429adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
43074473971cc9d960376295fbcc430320c9ed62991Elliott Hughes     * Returns the length of the text.
431adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
432adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getLength() {
433adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return length;
434adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
435adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
436adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
43774473971cc9d960376295fbcc430320c9ed62991Elliott Hughes     * Returns the level of the given character.
438adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
439adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getLevelAt(int offset) {
440adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
441bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes            return offsetLevel[offset] & ~UBIDI_LEVEL_OVERRIDE;
442adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (RuntimeException e) {
443adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return baseLevel;
444adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
445adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
446adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
447adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
44874473971cc9d960376295fbcc430320c9ed62991Elliott Hughes     * Returns the number of runs in the text, at least 1.
449adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
450adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getRunCount() {
451adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return unidirectional ? 1 : runs.length;
452adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
453adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
454adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
45574473971cc9d960376295fbcc430320c9ed62991Elliott Hughes     * Returns the level of the given run.
456adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
457adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getRunLevel(int run) {
458adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return unidirectional ? baseLevel : runs[run].getLevel();
459adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
460adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
461adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
46274473971cc9d960376295fbcc430320c9ed62991Elliott Hughes     * Returns the limit offset of the given run.
463adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
464adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getRunLimit(int run) {
465adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return unidirectional ? length : runs[run].getLimit();
466adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
467adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
468adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
46974473971cc9d960376295fbcc430320c9ed62991Elliott Hughes     * Returns the start offset of the given run.
470adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
471adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getRunStart(int run) {
472adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return unidirectional ? 0 : runs[run].getStart();
473adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
474adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
475adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
47674473971cc9d960376295fbcc430320c9ed62991Elliott Hughes     * Returns true if the text is from left to right, that is, both the base
477adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * direction and the text direction is from left to right.
478adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
479adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public boolean isLeftToRight() {
480bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        return direction == UBiDiDirection_UBIDI_LTR;
481adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
482adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
483adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
48474473971cc9d960376295fbcc430320c9ed62991Elliott Hughes     * Returns true if the text direction is mixed.
485adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
486adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public boolean isMixed() {
487bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        return direction == UBiDiDirection_UBIDI_MIXED;
488adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
489adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
490adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
49174473971cc9d960376295fbcc430320c9ed62991Elliott Hughes     * Returns true if the text is from right to left, that is, both the base
492adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * direction and the text direction is from right to left.
493adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
494adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public boolean isRightToLeft() {
495bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        return direction == UBiDiDirection_UBIDI_RTL;
496adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
497adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
498adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
499adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Reorders a range of objects according to their specified levels. This is
500adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * a convenience function that does not use a {@code Bidi} object. The range
501adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * of objects at {@code index} from {@code objectStart} to {@code
502adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * objectStart + count} will be reordered according to the range of levels
503adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * at {@code index} from {@code levelStart} to {@code levelStart + count}.
504f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
505adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param levels
506adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the level array, which is already determined.
507adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param levelStart
508adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the start offset of the range of the levels.
509adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param objects
510adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the object array to reorder.
511adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param objectStart
512adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the start offset of the range of objects.
513adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param count
514adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the count of the range of objects to reorder.
515adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IllegalArgumentException
516adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if {@code count}, {@code levelStart} or {@code objectStart}
517adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             is negative; if {@code count > levels.length - levelStart} or
518adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if {@code count > objects.length - objectStart}.
519adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
520adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public static void reorderVisually(byte[] levels, int levelStart,
521adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            Object[] objects, int objectStart, int count) {
522adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (count < 0 || levelStart < 0 || objectStart < 0
523adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                || count > levels.length - levelStart
524adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                || count > objects.length - objectStart) {
52503c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughes            throw new IllegalArgumentException("Invalid ranges (levels=" + levels.length +
52603c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughes                    ", levelStart=" + levelStart + ", objects=" + objects.length +
52703c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughes                    ", objectStart=" + objectStart + ", count=" + count + ")");
528adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
529f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
530adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        byte[] realLevels = new byte[count];
531adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        System.arraycopy(levels, levelStart, realLevels, 0, count);
532adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
533bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes        int[] indices = ubidi_reorderVisual(realLevels, count);
534adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
535f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        ArrayList<Object> result = new ArrayList<Object>(count);
536adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (int i = 0; i < count; i++) {
537f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes            result.add(objects[objectStart + indices[i]]);
538adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
539adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
540adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        System.arraycopy(result.toArray(), 0, objects, objectStart, count);
541adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
542adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
543adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
544adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Indicates whether a range of characters of a text requires a {@code Bidi}
545adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * object to display properly.
546f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
547adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param text
548adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the char array of the text.
549adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param start
550adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the start offset of the range of characters.
551adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param limit
552adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the limit offset of the range of characters.
553adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return {@code true} if the range of characters requires a {@code Bidi}
554adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *         object; {@code false} otherwise.
555adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IllegalArgumentException
556adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if {@code start} or {@code limit} is negative; {@code start >
557adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             limit} or {@code limit} is greater than the length of this
558adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             object's paragraph text.
559adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
560adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public static boolean requiresBidi(char[] text, int start, int limit) {
5619b354e75f2418e54638e02d153216660b67d01b8Jesse Wilson        if (limit < 0 || start < 0 || start > limit || limit > text.length) {
562adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new IllegalArgumentException();
563adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
564f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
565adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Bidi bidi = new Bidi(text, start, null, 0, limit - start, 0);
566adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return !bidi.isLeftToRight();
567adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
568adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
569adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
570adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String toString() {
571f10b2437ae5ec073f8c4118f7235022ba83667c4Elliott Hughes        return getClass().getName()
572deacd761e85ee4d75a6adbdd63225fc4a6d3088dElliott Hughes                + "[direction: " + direction + " baseLevel: " + baseLevel
573deacd761e85ee4d75a6adbdd63225fc4a6d3088dElliott Hughes                + " length: " + length + " runs: " + Arrays.toString(runs) + "]";
574adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
575bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes
576bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    // ICU4C constants.
577bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static final int UBIDI_LEVEL_OVERRIDE = 0x80;
578bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static final int UBiDiDirection_UBIDI_LTR = 0;
579bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static final int UBiDiDirection_UBIDI_RTL = 1;
580bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static final int UBiDiDirection_UBIDI_MIXED = 2;
581bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes
582bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    // ICU4C functions.
583bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native long ubidi_open();
584bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native void ubidi_close(long pBiDi);
585bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native void ubidi_setPara(long pBiDi, char[] text, int length, int paraLevel, byte[] embeddingLevels);
586bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native long ubidi_setLine(final long pParaBiDi, int start, int limit);
587bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native int ubidi_getDirection(final long pBiDi);
588bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native int ubidi_getLength(final long pBiDi);
589bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native byte ubidi_getParaLevel(final long pBiDi);
590bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native byte[] ubidi_getLevels(long pBiDi);
591bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native int ubidi_countRuns(long pBiDi);
592bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native Bidi.Run[] ubidi_getRuns(long pBidi);
593bb1c04167bdff1939e9e71ed04c57337d4951008Elliott Hughes    private static native int[] ubidi_reorderVisual(byte[] levels, int length);
594adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
595