001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.text;
019:
020: import org.apache.harmony.text.internal.nls.Messages;
021:
022: /**
023: * Bidi is the class providing the bidirectional algorithm. The algorithm is
024: * defined in the Unicode Standard Annex #9, version 13, also described in The
025: * Unicode Standard, Version 4.0 .
026: *
027: * Use a Bidi object to get the information on the position reordering of a
028: * bidirectional text, such as Arabic or Hebrew. The natural display ordering of
029: * horizontal text in these languages is from right to left, while they order
030: * numbers from left to right.
031: *
032: * If the text contains multiple runs, the information of each run can be
033: * obtained from the run index. The level of any particular run indicates the
034: * direction of the text as well as the nesting level. Left-to-right runs have
035: * even levels while right-to-left runs have odd levels.
036: */
037: public final class Bidi {
038: /**
039: * Constant that indicates the default base level. If there is no strong
040: * character, then set the paragraph level to left-to-right.
041: */
042: public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = com.ibm.icu.text.Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
043:
044: /**
045: * Constant that indicates the default base level. If there is no strong
046: * character, then set the paragraph level to right-to-left.
047: */
048: public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = com.ibm.icu.text.Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
049:
050: /**
051: * Constant that specifies the default base level as
052: * left-to-right.
053: */
054: public static final int DIRECTION_LEFT_TO_RIGHT = com.ibm.icu.text.Bidi.DIRECTION_LEFT_TO_RIGHT;
055:
056: /**
057: * Constant that specifies the default base level right-to-left.
058: */
059: public static final int DIRECTION_RIGHT_TO_LEFT = com.ibm.icu.text.Bidi.DIRECTION_RIGHT_TO_LEFT;
060:
061: /*
062: * Use an embedded ICU4J Bidi object to do all the work
063: */
064: private com.ibm.icu.text.Bidi icuBidi;
065:
066: /**
067: * Create a Bidi object from the AttributedCharacterIterator of a paragraph
068: * text.
069: *
070: * The RUN_DIRECTION attribute determines the base direction of the
071: * bidirectional text. If it's not specified explicitly, the algorithm uses
072: * DIRECTION_DEFAULT_LEFT_TO_RIGHT by default.
073: *
074: * The BIDI_EMBEDDING attribute specifies the level of embedding for each
075: * character. Values between -1 and -62 denote overrides at the level's
076: * absolute value, values from 1 to 62 indicate embeddings, and the 0 value
077: * indicates the level is calculated by the algorithm automatically. For the
078: * character with no BIDI_EMBEDDING attribute or with a improper attribute
079: * value, such as a null value, the algorithm treats its embedding level as
080: * 0.
081: *
082: * The NUMERIC_SHAPING attribute specifies the instance of NumericShaper
083: * used to convert European digits to other decimal digits before performing
084: * the bidi algorithm.
085: *
086: * @param paragraph
087: *
088: * @see java.awt.font.TextAttribute#BIDI_EMBEDDING
089: * @see java.awt.font.TextAttribute#NUMERIC_SHAPING
090: * @see java.awt.font.TextAttribute#RUN_DIRECTION
091: */
092: public Bidi(AttributedCharacterIterator paragraph) {
093: if (paragraph == null) {
094: // text.14=paragraph is null
095: throw new IllegalArgumentException(Messages
096: .getString("text.14")); //$NON-NLS-1$
097: }
098:
099: icuBidi = new com.ibm.icu.text.Bidi(paragraph);
100: }
101:
102: /**
103: * Create a Bidi object.
104: *
105: * @param text
106: * the char array of the paragraph text.
107: * @param textStart
108: * the start offset of the text array to perform the algorithm.
109: * @param embeddings
110: * the embedding level array of the paragraph text, specifying
111: * the embedding level information for each character. Values
112: * between -1 and -62 denote overrides at the level's absolute
113: * value, values from 1 to 62 indicate embeddings, and the 0
114: * value indicates the level is calculated by the algorithm
115: * automatically.
116: * @param embStart
117: * the start offset of the embeddings array to perform the
118: * algorithm.
119: * @param paragraphLength
120: * the length of the text to perform the algorithm. It must be
121: * text.length >= textStart + paragraphLength, and
122: * embeddings.length >= embStart + paragraphLength.
123: * @param flags
124: * indicates the base direction of the bidirectional text. It is
125: * expected that this will be one of the direction constant
126: * values defined in this class. An unknown value is treated as
127: * DIRECTION_DEFAULT_LEFT_TO_RIGHT.
128: *
129: * @see #DIRECTION_LEFT_TO_RIGHT
130: * @see #DIRECTION_RIGHT_TO_LEFT
131: * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT
132: * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT
133: */
134: public Bidi(char[] text, int textStart, byte[] embeddings,
135: int embStart, int paragraphLength, int flags) {
136:
137: if (text == null || text.length - textStart < paragraphLength) {
138: throw new IllegalArgumentException();
139: }
140:
141: if (embeddings != null) {
142: if (embeddings.length - embStart < paragraphLength) {
143: throw new IllegalArgumentException();
144: }
145: }
146:
147: if (textStart < 0) {
148: // text.0D=Negative textStart value {0}
149: throw new IllegalArgumentException(Messages.getString(
150: "text.0D", textStart)); //$NON-NLS-1$
151: }
152: if (embStart < 0) {
153: // text.10=Negative embStart value {0}
154: throw new IllegalArgumentException(Messages.getString(
155: "text.10", embStart)); //$NON-NLS-1$
156: }
157: if (paragraphLength < 0) {
158: // text.11=Negative paragraph length {0}
159: throw new IllegalArgumentException(Messages.getString(
160: "text.11", paragraphLength)); //$NON-NLS-1$
161: }
162:
163: icuBidi = new com.ibm.icu.text.Bidi(text, textStart,
164: embeddings, embStart, paragraphLength, flags);
165: }
166:
167: /**
168: * Create a Bidi object.
169: *
170: * @param paragraph
171: * the String containing the paragraph text to perform the
172: * algorithm.
173: * @param flags
174: * indicates the base direction of the bidirectional text. It is
175: * expected that this will be one of the direction constant
176: * values defined in this class. An unknown value is treated as
177: * DIRECTION_DEFAULT_LEFT_TO_RIGHT.
178: *
179: * @see #DIRECTION_LEFT_TO_RIGHT
180: * @see #DIRECTION_RIGHT_TO_LEFT
181: * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT
182: * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT
183: */
184: public Bidi(String paragraph, int flags) {
185: this ((paragraph == null ? null : paragraph.toCharArray()), 0,
186: null, 0, (paragraph == null ? 0 : paragraph.length()),
187: flags);
188: }
189:
190: /* private constructor used by createLineBidi() */
191: private Bidi(com.ibm.icu.text.Bidi bidi) {
192: this .icuBidi = bidi;
193: }
194:
195: /**
196: * Return whether the base level is from left to right.
197: *
198: * @return true if the base level is from left to right.
199: */
200: public boolean baseIsLeftToRight() {
201: return icuBidi.baseIsLeftToRight();
202: }
203:
204: /**
205: * Create a new Bidi object containing the information of one line from this
206: * object.
207: *
208: * @param lineStart
209: * the start offset of the line.
210: * @param lineLimit
211: * the limit of the line.
212: * @return the new line Bidi object. In this new object, the indices will
213: * range from 0 to (limit - start - 1).
214: */
215: public Bidi createLineBidi(int lineStart, int lineLimit) {
216: int length = icuBidi.getLength();
217: if (lineStart < 0 || lineLimit < 0 || lineLimit > length
218: || lineStart > lineLimit) {
219: // text.12=Invalid ranges (start={0}, limit={1}, length={2})
220: throw new IllegalArgumentException(
221: Messages
222: .getString(
223: "text.12", new Object[] { lineStart, lineLimit, length })); //$NON-NLS-1$
224: }
225:
226: com.ibm.icu.text.Bidi lineBidi = icuBidi.createLineBidi(
227: lineStart, lineLimit);
228: Bidi bidi = new Bidi(lineBidi);
229: return bidi;
230: }
231:
232: /**
233: * Return the base level.
234: *
235: * @return the int value of the base level.
236: */
237: public int getBaseLevel() {
238: return icuBidi.getBaseLevel();
239: }
240:
241: /**
242: * Return the length of the text in the Bidi object.
243: *
244: * @return the int value of the length.
245: */
246: public int getLength() {
247: return icuBidi.getLength();
248: }
249:
250: /**
251: * Return the level of a specified character.
252: *
253: * @param offset
254: * the offset of the character.
255: * @return the int value of the level.
256: */
257: public int getLevelAt(int offset) {
258: try {
259: return icuBidi.getLevelAt(offset);
260: } catch (RuntimeException e) {
261: return icuBidi.getBaseLevel();
262: }
263: }
264:
265: /**
266: * Return the number of runs in the bidirectional text.
267: *
268: * @return the int value of runs, at least 1.
269: */
270: public int getRunCount() {
271: int runCount = icuBidi.getRunCount();
272: return (runCount < 1) ? 1 : runCount;
273: }
274:
275: /**
276: * Return the level of a specified run.
277: *
278: * @param run
279: * the index of the run.
280: * @return the level of the run.
281: */
282: public int getRunLevel(int run) {
283: if (icuBidi.getRunCount() == 0) {
284: return icuBidi.getBaseLevel();
285: } else {
286: return icuBidi.getRunLevel(run);
287: }
288: }
289:
290: /**
291: * Return the limit offset of a specified run.
292: *
293: * @param run
294: * the index of the run.
295: * @return the limit offset of the run.
296: */
297: public int getRunLimit(int run) {
298: if (icuBidi.getRunCount() == 0) {
299: return icuBidi.getLength();
300: } else {
301: return icuBidi.getRunLimit(run);
302: }
303: }
304:
305: /**
306: * Return the start offset of a specified run.
307: *
308: * @param run
309: * the index of the run.
310: * @return the start offset of the run.
311: */
312: public int getRunStart(int run) {
313: if (icuBidi.getRunCount() == 0) {
314: return 0;
315: } else {
316: return icuBidi.getRunStart(run);
317: }
318: }
319:
320: /**
321: * Return whether the text is from left to right, that is, both the base
322: * direction and the text direction is from left to right.
323: *
324: * @return true if the text is from left to right.
325: */
326: public boolean isLeftToRight() {
327: return icuBidi.isLeftToRight();
328: }
329:
330: /**
331: * Return whether the text direction is mixed.
332: *
333: * @return true if the text direction is mixed.
334: */
335: public boolean isMixed() {
336: return icuBidi.isMixed();
337: }
338:
339: /**
340: * Return whether the text is from right to left, that is, both the base
341: * direction and the text direction is from right to left.
342: *
343: * @return true if the text is from right to left.
344: */
345: public boolean isRightToLeft() {
346: return icuBidi.isRightToLeft();
347: }
348:
349: /**
350: * Reorder a range of objects according to their specified levels. This is a
351: * convenience function that does not use a Bidi object. The range of
352: * objects at index from objectStart to objectStart + count will be
353: * reordered according to the range of levels at index from levelStart to
354: * levelStart + count.
355: *
356: * @param levels
357: * the level array, which is already determined.
358: * @param levelStart
359: * the start offset of the range of the levels.
360: * @param objects
361: * the object array to reorder.
362: * @param objectStart
363: * the start offset of the range of objects.
364: * @param count
365: * the count of the range of objects to reorder.
366: */
367: public static void reorderVisually(byte[] levels, int levelStart,
368: Object[] objects, int objectStart, int count) {
369: if (count < 0 || levelStart < 0 || objectStart < 0
370: || count > levels.length - levelStart
371: || count > objects.length - objectStart) {
372: // text.13=Invalid ranges (levels={0}, levelStart={1}, objects={2},
373: // objectStart={3}, count={4})
374: throw new IllegalArgumentException(Messages.getString(
375: "text.13", //$NON-NLS-1$
376: new Object[] { levels.length, levelStart,
377: objects.length, objectStart, count }));
378: }
379:
380: com.ibm.icu.text.Bidi.reorderVisually(levels, levelStart,
381: objects, objectStart, count);
382: }
383:
384: /**
385: * Return whether a range of characters of a text requires a Bidi object to
386: * display properly.
387: *
388: * @param text
389: * the char array of the text.
390: * @param start
391: * the start offset of the range of characters.
392: * @param limit
393: * the limit offset of the range of characters.
394: * @return true if the range of characters requires a Bidi object.
395: */
396: public static boolean requiresBidi(char[] text, int start, int limit) {
397: //int length = text.length;
398: if (limit < 0 || start < 0 || start > limit
399: || limit > text.length) {
400: throw new IllegalArgumentException();
401: }
402:
403: return com.ibm.icu.text.Bidi.requiresBidi(text, start, limit);
404: }
405:
406: /**
407: * Return the internal message of the Bidi object, used in debugging.
408: *
409: * @return a string containing the internal message.
410: */
411: @Override
412: public String toString() {
413: return icuBidi.toString();
414: }
415: }
|