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