Bidipublic final class Bidi extends Object Provides the Unicode Bidirectional Algorithm. The algorithm is
defined in the Unicode Standard Annex #9, version 13, also described in The
Unicode Standard, Version 4.0 .
Use a {@code Bidi} object to get the information on the position reordering of a
bidirectional text, such as Arabic or Hebrew. The natural display ordering of
horizontal text in these languages is from right to left, while they order
numbers from left to right.
If the text contains multiple runs, the information of each run can be
obtained from the run index. The level of any particular run indicates the
direction of the text as well as the nesting level. Left-to-right runs have
even levels while right-to-left runs have odd levels. |
Fields Summary |
---|
public static final int | DIRECTION_DEFAULT_LEFT_TO_RIGHTConstant that indicates the default base level. If there is no strong
character, then set the paragraph level to 0 (left-to-right). | public static final int | DIRECTION_DEFAULT_RIGHT_TO_LEFTConstant that indicates the default base level. If there is no strong
character, then set the paragraph level to 1 (right-to-left). | public static final int | DIRECTION_LEFT_TO_RIGHTConstant that specifies the default base level as 0 (left-to-right). | public static final int | DIRECTION_RIGHT_TO_LEFTConstant that specifies the default base level as 1 (right-to-left). | private int | baseLevel | private int | length | private byte[] | offsetLevel | private org.apache.harmony.text.BidiRun[] | runs | private int | direction | private boolean | unidirectional |
Constructors Summary |
---|
public Bidi(AttributedCharacterIterator paragraph)Creates a {@code Bidi} object from the {@code
AttributedCharacterIterator} of a paragraph text. The RUN_DIRECTION
attribute determines the base direction of the bidirectional text. If it
is not specified explicitly, the algorithm uses
DIRECTION_DEFAULT_LEFT_TO_RIGHT by default. The BIDI_EMBEDDING attribute
specifies the level of embedding for each character. Values between -1
and -62 denote overrides at the level's absolute value, values from 1 to
62 indicate embeddings, and the 0 value indicates the level is calculated
by the algorithm automatically. For the character with no BIDI_EMBEDDING
attribute or with a improper attribute value, such as a {@code null}
value, the algorithm treats its embedding level as 0. The NUMERIC_SHAPING
attribute specifies the instance of NumericShaper used to convert
European digits to other decimal digits before performing the bidi
algorithm.
if (paragraph == null) {
// text.14=paragraph is null
throw new IllegalArgumentException(Messages.getString("text.14")); //$NON-NLS-1$
}
int begin = paragraph.getBeginIndex();
int end = paragraph.getEndIndex();
int length = end - begin;
char text[] = new char[length + 1]; // One more char for
// AttributedCharacterIterator.DONE
if (length != 0) {
text[0] = paragraph.first();
} else {
paragraph.first();
}
// First check the RUN_DIRECTION attribute.
int flags = DIRECTION_DEFAULT_LEFT_TO_RIGHT;
Object direction = paragraph.getAttribute(TextAttribute.RUN_DIRECTION);
if (direction != null && direction instanceof Boolean) {
if (direction.equals(TextAttribute.RUN_DIRECTION_LTR)) {
flags = DIRECTION_LEFT_TO_RIGHT;
} else {
flags = DIRECTION_RIGHT_TO_LEFT;
}
}
// Retrieve the text and gather BIDI_EMBEDDINGS
byte embeddings[] = null;
for (int textLimit = 1, i = 1; i < length; textLimit = paragraph
.getRunLimit(TextAttribute.BIDI_EMBEDDING)
- begin + 1) {
Object embedding = paragraph
.getAttribute(TextAttribute.BIDI_EMBEDDING);
if (embedding != null && embedding instanceof Integer) {
int embLevel = ((Integer) embedding).intValue();
if (embeddings == null) {
embeddings = new byte[length];
}
for (; i < textLimit; i++) {
text[i] = paragraph.next();
embeddings[i - 1] = (byte) embLevel;
}
} else {
for (; i < textLimit; i++) {
text[i] = paragraph.next();
}
}
}
// Apply NumericShaper to the text
Object numericShaper = paragraph
.getAttribute(TextAttribute.NUMERIC_SHAPING);
if (numericShaper != null && numericShaper instanceof NumericShaper) {
((NumericShaper) numericShaper).shape(text, 0, length);
}
long pBidi = createUBiDi(text, 0, embeddings, 0, length, flags);
readBidiInfo(pBidi);
BidiWrapper.ubidi_close(pBidi);
| public Bidi(char[] text, int textStart, byte[] embeddings, int embStart, int paragraphLength, int flags)Creates a {@code Bidi} object.
if (textStart < 0) {
// text.0D=Negative textStart value {0}
throw new IllegalArgumentException(Messages.getString(
"text.0D", textStart)); //$NON-NLS-1$
}
if (embStart < 0) {
// text.10=Negative embStart value {0}
throw new IllegalArgumentException(Messages.getString(
"text.10", embStart)); //$NON-NLS-1$
}
if (paragraphLength < 0) {
// text.11=Negative paragraph length {0}
throw new IllegalArgumentException(Messages.getString(
"text.11", paragraphLength)); //$NON-NLS-1$
}
long pBidi = createUBiDi(text, textStart, embeddings, embStart,
paragraphLength, flags);
readBidiInfo(pBidi);
BidiWrapper.ubidi_close(pBidi);
| public Bidi(String paragraph, int flags)Creates a {@code Bidi} object.
this((paragraph == null ? null : paragraph.toCharArray()), 0, null, 0,
(paragraph == null ? 0 : paragraph.length()), flags);
| private Bidi(long pBidi)
readBidiInfo(pBidi);
|
Methods Summary |
---|
public boolean | baseIsLeftToRight()Returns whether the base level is from left to right.
return baseLevel % 2 == 0 ? true : false;
| public java.text.Bidi | createLineBidi(int lineStart, int lineLimit)Creates a new {@code Bidi} object containing the information of one line
from this object.
if (lineStart < 0 || lineLimit < 0 || lineLimit > length
|| lineStart > lineLimit) {
// text.12=Invalid ranges (start={0}, limit={1}, length={2})
throw new IllegalArgumentException(Messages.getString(
"text.12", new Object[] { lineStart, lineLimit, length })); //$NON-NLS-1$
}
char[] text = new char[this.length];
Arrays.fill(text, 'a");
byte[] embeddings = new byte[this.length];
for (int i = 0; i < embeddings.length; i++) {
embeddings[i] = (byte) -this.offsetLevel[i];
}
int dir = this.baseIsLeftToRight() ? Bidi.DIRECTION_LEFT_TO_RIGHT
: Bidi.DIRECTION_RIGHT_TO_LEFT;
long parent = createUBiDi(text, 0, embeddings, 0, this.length, dir);
long line = BidiWrapper.ubidi_setLine(parent, lineStart, lineLimit);
Bidi result = new Bidi(line);
BidiWrapper.ubidi_close(line);
BidiWrapper.ubidi_close(parent);
return result;
| private static long | createUBiDi(char[] text, int textStart, byte[] embeddings, int embStart, int paragraphLength, int flags)
char[] realText = null;
byte[] realEmbeddings = null;
if (text == null || text.length - textStart < paragraphLength) {
throw new IllegalArgumentException();
}
realText = new char[paragraphLength];
System.arraycopy(text, textStart, realText, 0, paragraphLength);
if (embeddings != null) {
if (embeddings.length - embStart < paragraphLength) {
throw new IllegalArgumentException();
}
if (paragraphLength > 0) {
Bidi temp = new Bidi(text, textStart, null, 0, paragraphLength,
flags);
realEmbeddings = new byte[paragraphLength];
System.arraycopy(temp.offsetLevel, 0, realEmbeddings, 0,
paragraphLength);
for (int i = 0; i < paragraphLength; i++) {
byte e = embeddings[i];
if (e < 0) {
realEmbeddings[i] = (byte) (BidiWrapper.UBIDI_LEVEL_OVERRIDE - e);
} else if (e > 0) {
realEmbeddings[i] = e;
} else {
realEmbeddings[i] |= (byte) BidiWrapper.UBIDI_LEVEL_OVERRIDE;
}
}
}
}
if (flags > 1 || flags < -2) {
flags = 0;
}
long bidi = BidiWrapper.ubidi_open();
BidiWrapper.ubidi_setPara(bidi, realText, paragraphLength,
(byte) flags, realEmbeddings);
return bidi;
| public int | getBaseLevel()Returns the base level.
return baseLevel;
| public int | getLength()Returns the length of the text in the {@code Bidi} object.
return length;
| public int | getLevelAt(int offset)Returns the level of a specified character.
try {
return offsetLevel[offset] & ~BidiWrapper.UBIDI_LEVEL_OVERRIDE;
} catch (RuntimeException e) {
return baseLevel;
}
| public int | getRunCount()Returns the number of runs in the bidirectional text.
return unidirectional ? 1 : runs.length;
| public int | getRunLevel(int run)Returns the level of the specified run.
return unidirectional ? baseLevel : runs[run].getLevel();
| public int | getRunLimit(int run)Returns the limit offset of the specified run.
return unidirectional ? length : runs[run].getLimit();
| public int | getRunStart(int run)Returns the start offset of the specified run.
return unidirectional ? 0 : runs[run].getStart();
| public boolean | isLeftToRight()Indicates whether the text is from left to right, that is, both the base
direction and the text direction is from left to right.
return direction == BidiWrapper.UBiDiDirection_UBIDI_LTR;
| public boolean | isMixed()Indicates whether the text direction is mixed.
return direction == BidiWrapper.UBiDiDirection_UBIDI_MIXED;
| public boolean | isRightToLeft()Indicates whether the text is from right to left, that is, both the base
direction and the text direction is from right to left.
return direction == BidiWrapper.UBiDiDirection_UBIDI_RTL;
| private void | readBidiInfo(long pBidi)
length = BidiWrapper.ubidi_getLength(pBidi);
offsetLevel = (length == 0) ? null : BidiWrapper.ubidi_getLevels(pBidi);
baseLevel = BidiWrapper.ubidi_getParaLevel(pBidi);
int runCount = BidiWrapper.ubidi_countRuns(pBidi);
if (runCount == 0) {
unidirectional = true;
runs = null;
} else if (runCount < 0) {
runs = null;
} else {
runs = BidiWrapper.ubidi_getRuns(pBidi);
// Simplified case for one run which has the base level
if (runCount == 1 && runs[0].getLevel() == baseLevel) {
unidirectional = true;
runs = null;
}
}
direction = BidiWrapper.ubidi_getDirection(pBidi);
| public static void | reorderVisually(byte[] levels, int levelStart, java.lang.Object[] objects, int objectStart, int count)Reorders a range of objects according to their specified levels. This is
a convenience function that does not use a {@code Bidi} object. The range
of objects at {@code index} from {@code objectStart} to {@code
objectStart + count} will be reordered according to the range of levels
at {@code index} from {@code levelStart} to {@code levelStart + count}.
if (count < 0 || levelStart < 0 || objectStart < 0
|| count > levels.length - levelStart
|| count > objects.length - objectStart) {
// text.13=Invalid ranges (levels={0}, levelStart={1}, objects={2},
// objectStart={3}, count={4})
throw new IllegalArgumentException(Messages.getString("text.13", //$NON-NLS-1$
new Object[] { levels.length, levelStart, objects.length,
objectStart, count }));
}
byte[] realLevels = new byte[count];
System.arraycopy(levels, levelStart, realLevels, 0, count);
int[] indices = BidiWrapper.ubidi_reorderVisual(realLevels, count);
LinkedList<Object> result = new LinkedList<Object>();
for (int i = 0; i < count; i++) {
result.addLast(objects[objectStart + indices[i]]);
}
System.arraycopy(result.toArray(), 0, objects, objectStart, count);
| public static boolean | requiresBidi(char[] text, int start, int limit)Indicates whether a range of characters of a text requires a {@code Bidi}
object to display properly.
int length = text.length;
if (limit < 0 || start < 0 || start > limit || limit > length) {
throw new IllegalArgumentException();
}
Bidi bidi = new Bidi(text, start, null, 0, limit - start, 0);
return !bidi.isLeftToRight();
| public java.lang.String | toString()Returns the internal message of the {@code Bidi} object, used in
debugging.
return super.toString()
+ "[direction: " + direction + " baselevel: " + baseLevel //$NON-NLS-1$ //$NON-NLS-2$
+ " length: " + length + " runs: " + (unidirectional ? "null" : runs.toString()) + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|
|