|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
/* |
|
******************************************************************************* |
|
* (C) Copyright IBM Corp. and others, 1996-2009 - All Rights Reserved * |
|
* * |
|
* The original version of this source code and documentation is copyrighted * |
|
* and owned by IBM, These materials are provided under terms of a License * |
|
* Agreement between IBM and Sun. This technology is protected by multiple * |
|
* US and International patents. This notice and attribution to IBM may not * |
|
* to removed. * |
|
******************************************************************************* |
|
*/ |
|
/* Written by Simon Montagu, Matitiahu Allouche |
|
* (ported from C code written by Markus W. Scherer) |
|
*/ |
|
|
|
package sun.text.bidi; |
|
|
|
import java.text.Bidi; |
|
import java.util.Arrays; |
|
|
|
public final class BidiLine { |
|
|
|
/* |
|
* General remarks about the functions in this file: |
|
* |
|
* These functions deal with the aspects of potentially mixed-directional |
|
* text in a single paragraph or in a line of a single paragraph |
|
* which has already been processed according to |
|
* the Unicode 3.0 Bidi algorithm as defined in |
|
* http://www.unicode.org/unicode/reports/tr9/ , version 13, |
|
* also described in The Unicode Standard, Version 4.0.1 . |
|
* |
|
* This means that there is a Bidi object with a levels |
|
* and a dirProps array. |
|
* paraLevel and direction are also set. |
|
* Only if the length of the text is zero, then levels==dirProps==NULL. |
|
* |
|
* The overall directionality of the paragraph |
|
* or line is used to bypass the reordering steps if possible. |
|
* Even purely RTL text does not need reordering there because |
|
* the getLogical/VisualIndex() methods can compute the |
|
* index on the fly in such a case. |
|
* |
|
* The implementation of the access to same-level-runs and of the reordering |
|
* do attempt to provide better performance and less memory usage compared to |
|
* a direct implementation of especially rule (L2) with an array of |
|
* one (32-bit) integer per text character. |
|
* |
|
* Here, the levels array is scanned as soon as necessary, and a vector of |
|
* same-level-runs is created. Reordering then is done on this vector. |
|
* For each run of text positions that were resolved to the same level, |
|
* only 8 bytes are stored: the first text position of the run and the visual |
|
* position behind the run after reordering. |
|
* One sign bit is used to hold the directionality of the run. |
|
* This is inefficient if there are many very short runs. If the average run |
|
* length is <2, then this uses more memory. |
|
* |
|
* In a further attempt to save memory, the levels array is never changed |
|
* after all the resolution rules (Xn, Wn, Nn, In). |
|
* Many methods have to consider the field trailingWSStart: |
|
* if it is less than length, then there is an implicit trailing run |
|
* at the paraLevel, |
|
* which is not reflected in the levels array. |
|
* This allows a line Bidi object to use the same levels array as |
|
* its paragraph parent object. |
|
* |
|
* When a Bidi object is created for a line of a paragraph, then the |
|
* paragraph's levels and dirProps arrays are reused by way of setting |
|
* a pointer into them, not by copying. This again saves memory and forbids to |
|
* change the now shared levels for (L1). |
|
*/ |
|
|
|
/* handle trailing WS (L1) -------------------------------------------------- */ |
|
|
|
/* |
|
* setTrailingWSStart() sets the start index for a trailing |
|
* run of WS in the line. This is necessary because we do not modify |
|
* the paragraph's levels array that we just point into. |
|
* Using trailingWSStart is another form of performing (L1). |
|
* |
|
* To make subsequent operations easier, we also include the run |
|
* before the WS if it is at the paraLevel - we merge the two here. |
|
* |
|
* This method is called only from setLine(), so paraLevel is |
|
* set correctly for the line even when contextual multiple paragraphs. |
|
*/ |
|
|
|
static void setTrailingWSStart(BidiBase bidiBase) |
|
{ |
|
byte[] dirProps = bidiBase.dirProps; |
|
byte[] levels = bidiBase.levels; |
|
int start = bidiBase.length; |
|
byte paraLevel = bidiBase.paraLevel; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (BidiBase.NoContextRTL(dirProps[start - 1]) == BidiBase.B) { |
|
bidiBase.trailingWSStart = start; |
|
return; |
|
} |
|
|
|
while (start > 0 && |
|
(BidiBase.DirPropFlagNC(dirProps[start - 1]) & BidiBase.MASK_WS) != 0) { |
|
--start; |
|
} |
|
|
|
|
|
while (start > 0 && levels[start - 1] == paraLevel) { |
|
--start; |
|
} |
|
|
|
bidiBase.trailingWSStart=start; |
|
} |
|
|
|
public static Bidi setLine(Bidi bidi, BidiBase paraBidi, |
|
Bidi newBidi, BidiBase newBidiBase, |
|
int start, int limit) { |
|
int length; |
|
|
|
BidiBase lineBidi = newBidiBase; |
|
|
|
/* set the values in lineBidi from its paraBidi parent */ |
|
/* class members are already initialized to 0 */ |
|
// lineBidi.paraBidi = null; /* mark unfinished setLine */ |
|
// lineBidi.flags = 0; |
|
// lineBidi.controlCount = 0; |
|
|
|
length = lineBidi.length = lineBidi.originalLength = |
|
lineBidi.resultLength = limit - start; |
|
|
|
lineBidi.text = new char[length]; |
|
System.arraycopy(paraBidi.text, start, lineBidi.text, 0, length); |
|
lineBidi.paraLevel = paraBidi.GetParaLevelAt(start); |
|
lineBidi.paraCount = paraBidi.paraCount; |
|
lineBidi.runs = new BidiRun[0]; |
|
if (paraBidi.controlCount > 0) { |
|
int j; |
|
for (j = start; j < limit; j++) { |
|
if (BidiBase.IsBidiControlChar(paraBidi.text[j])) { |
|
lineBidi.controlCount++; |
|
} |
|
} |
|
lineBidi.resultLength -= lineBidi.controlCount; |
|
} |
|
|
|
lineBidi.getDirPropsMemory(length); |
|
lineBidi.dirProps = lineBidi.dirPropsMemory; |
|
System.arraycopy(paraBidi.dirProps, start, lineBidi.dirProps, 0, |
|
length); |
|
|
|
lineBidi.getLevelsMemory(length); |
|
lineBidi.levels = lineBidi.levelsMemory; |
|
System.arraycopy(paraBidi.levels, start, lineBidi.levels, 0, |
|
length); |
|
lineBidi.runCount = -1; |
|
|
|
if (paraBidi.direction != BidiBase.MIXED) { |
|
|
|
lineBidi.direction = paraBidi.direction; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (paraBidi.trailingWSStart <= start) { |
|
lineBidi.trailingWSStart = 0; |
|
} else if (paraBidi.trailingWSStart < limit) { |
|
lineBidi.trailingWSStart = paraBidi.trailingWSStart - start; |
|
} else { |
|
lineBidi.trailingWSStart = length; |
|
} |
|
} else { |
|
byte[] levels = lineBidi.levels; |
|
int i, trailingWSStart; |
|
byte level; |
|
|
|
setTrailingWSStart(lineBidi); |
|
trailingWSStart = lineBidi.trailingWSStart; |
|
|
|
|
|
if (trailingWSStart == 0) { |
|
|
|
lineBidi.direction = (byte)(lineBidi.paraLevel & 1); |
|
} else { |
|
|
|
level = (byte)(levels[0] & 1); |
|
|
|
|
|
is mixed */ |
|
if (trailingWSStart < length && |
|
(lineBidi.paraLevel & 1) != level) { |
|
|
|
levels[0] */ |
|
lineBidi.direction = BidiBase.MIXED; |
|
} else { |
|
|
|
direction as levels[0] and paraLevel */ |
|
for (i = 1; ; i++) { |
|
if (i == trailingWSStart) { |
|
|
|
lineBidi.direction = level; |
|
break; |
|
} else if ((levels[i] & 1) != level) { |
|
lineBidi.direction = BidiBase.MIXED; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
switch(lineBidi.direction) { |
|
case Bidi.DIRECTION_LEFT_TO_RIGHT: |
|
|
|
lineBidi.paraLevel = (byte) |
|
((lineBidi.paraLevel + 1) & ~1); |
|
|
|
|
|
getLevels()) */ |
|
lineBidi.trailingWSStart = 0; |
|
break; |
|
case Bidi.DIRECTION_RIGHT_TO_LEFT: |
|
|
|
lineBidi.paraLevel |= 1; |
|
|
|
|
|
getLevels()) */ |
|
lineBidi.trailingWSStart = 0; |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
newBidiBase.paraBidi = paraBidi; |
|
return newBidi; |
|
} |
|
|
|
static byte getLevelAt(BidiBase bidiBase, int charIndex) |
|
{ |
|
|
|
if (bidiBase.direction != BidiBase.MIXED || charIndex >= bidiBase.trailingWSStart) { |
|
return bidiBase.GetParaLevelAt(charIndex); |
|
} else { |
|
return bidiBase.levels[charIndex]; |
|
} |
|
} |
|
|
|
static byte[] getLevels(BidiBase bidiBase) |
|
{ |
|
int start = bidiBase.trailingWSStart; |
|
int length = bidiBase.length; |
|
|
|
if (start != length) { |
|
/* the current levels array does not reflect the WS run */ |
|
/* |
|
* After the previous if(), we know that the levels array |
|
* has an implicit trailing WS run and therefore does not fully |
|
* reflect itself all the levels. |
|
* This must be a Bidi object for a line, and |
|
* we need to create a new levels array. |
|
*/ |
|
|
|
since bidiBase is a line object */ |
|
Arrays.fill(bidiBase.levels, start, length, bidiBase.paraLevel); |
|
|
|
|
|
bidiBase.trailingWSStart = length; |
|
} |
|
if (length < bidiBase.levels.length) { |
|
byte[] levels = new byte[length]; |
|
System.arraycopy(bidiBase.levels, 0, levels, 0, length); |
|
return levels; |
|
} |
|
return bidiBase.levels; |
|
} |
|
|
|
static BidiRun getLogicalRun(BidiBase bidiBase, int logicalPosition) |
|
{ |
|
|
|
|
|
*/ |
|
BidiRun newRun = new BidiRun(), iRun; |
|
getRuns(bidiBase); |
|
int runCount = bidiBase.runCount; |
|
int visualStart = 0, logicalLimit = 0; |
|
iRun = bidiBase.runs[0]; |
|
|
|
for (int i = 0; i < runCount; i++) { |
|
iRun = bidiBase.runs[i]; |
|
logicalLimit = iRun.start + iRun.limit - visualStart; |
|
if ((logicalPosition >= iRun.start) && |
|
(logicalPosition < logicalLimit)) { |
|
break; |
|
} |
|
visualStart = iRun.limit; |
|
} |
|
newRun.start = iRun.start; |
|
newRun.limit = logicalLimit; |
|
newRun.level = iRun.level; |
|
return newRun; |
|
} |
|
|
|
|
|
private static void getSingleRun(BidiBase bidiBase, byte level) { |
|
|
|
bidiBase.runs = bidiBase.simpleRuns; |
|
bidiBase.runCount = 1; |
|
|
|
|
|
bidiBase.runs[0] = new BidiRun(0, bidiBase.length, level); |
|
} |
|
|
|
/* reorder the runs array (L2) ---------------------------------------------- */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static void reorderLine(BidiBase bidiBase, byte minLevel, byte maxLevel) { |
|
|
|
|
|
if (maxLevel<=(minLevel|1)) { |
|
return; |
|
} |
|
|
|
BidiRun[] runs; |
|
BidiRun tempRun; |
|
byte[] levels; |
|
int firstRun, endRun, limitRun, runCount; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
++minLevel; |
|
|
|
runs = bidiBase.runs; |
|
levels = bidiBase.levels; |
|
runCount = bidiBase.runCount; |
|
|
|
|
|
if (bidiBase.trailingWSStart < bidiBase.length) { |
|
--runCount; |
|
} |
|
|
|
while (--maxLevel >= minLevel) { |
|
firstRun = 0; |
|
|
|
|
|
for ( ; ; ) { |
|
/* look for a sequence of runs that are all at >=maxLevel */ |
|
|
|
while (firstRun < runCount && levels[runs[firstRun].start] < maxLevel) { |
|
++firstRun; |
|
} |
|
if (firstRun >= runCount) { |
|
break; /* no more such runs */ |
|
} |
|
|
|
|
|
for (limitRun = firstRun; ++limitRun < runCount && |
|
levels[runs[limitRun].start]>=maxLevel; ) {} |
|
|
|
|
|
endRun = limitRun - 1; |
|
while (firstRun < endRun) { |
|
tempRun = runs[firstRun]; |
|
runs[firstRun] = runs[endRun]; |
|
runs[endRun] = tempRun; |
|
++firstRun; |
|
--endRun; |
|
} |
|
|
|
if (limitRun == runCount) { |
|
break; /* no more such runs */ |
|
} else { |
|
firstRun = limitRun + 1; |
|
} |
|
} |
|
} |
|
|
|
|
|
if ((minLevel & 1) == 0) { |
|
firstRun = 0; |
|
|
|
|
|
if (bidiBase.trailingWSStart == bidiBase.length) { |
|
--runCount; |
|
} |
|
|
|
|
|
while (firstRun < runCount) { |
|
tempRun = runs[firstRun]; |
|
runs[firstRun] = runs[runCount]; |
|
runs[runCount] = tempRun; |
|
++firstRun; |
|
--runCount; |
|
} |
|
} |
|
} |
|
|
|
/* compute the runs array --------------------------------------------------- */ |
|
|
|
static int getRunFromLogicalIndex(BidiBase bidiBase, int logicalIndex) { |
|
BidiRun[] runs = bidiBase.runs; |
|
int runCount = bidiBase.runCount, visualStart = 0, i, length, logicalStart; |
|
|
|
for (i = 0; i < runCount; i++) { |
|
length = runs[i].limit - visualStart; |
|
logicalStart = runs[i].start; |
|
if ((logicalIndex >= logicalStart) && (logicalIndex < (logicalStart+length))) { |
|
return i; |
|
} |
|
visualStart += length; |
|
} |
|
|
|
throw new IllegalStateException("Internal ICU error in getRunFromLogicalIndex"); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void getRuns(BidiBase bidiBase) { |
|
|
|
|
|
|
|
*/ |
|
if (bidiBase.runCount >= 0) { |
|
return; |
|
} |
|
if (bidiBase.direction != BidiBase.MIXED) { |
|
/* simple, single-run case - this covers length==0 */ |
|
|
|
getSingleRun(bidiBase, bidiBase.paraLevel); |
|
} else { |
|
|
|
int length = bidiBase.length, limit; |
|
byte[] levels = bidiBase.levels; |
|
int i, runCount; |
|
byte level = BidiBase.INTERNAL_LEVEL_DEFAULT_LTR; /* initialize with no valid level */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
limit = bidiBase.trailingWSStart; |
|
|
|
runCount = 0; |
|
for (i = 0; i < limit; ++i) { |
|
|
|
if (levels[i] != level) { |
|
++runCount; |
|
level = levels[i]; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
if (runCount == 1 && limit == length) { |
|
|
|
getSingleRun(bidiBase, levels[0]); |
|
} else { |
|
|
|
BidiRun[] runs; |
|
int runIndex, start; |
|
byte minLevel = BidiBase.MAX_EXPLICIT_LEVEL + 1; |
|
byte maxLevel=0; |
|
|
|
|
|
if (limit < length) { |
|
++runCount; |
|
} |
|
|
|
|
|
bidiBase.getRunsMemory(runCount); |
|
runs = bidiBase.runsMemory; |
|
|
|
/* set the runs */ |
|
|
|
|
|
|
|
|
|
*/ |
|
runIndex = 0; |
|
|
|
|
|
i = 0; |
|
do { |
|
|
|
start = i; |
|
level = levels[i]; |
|
if (level < minLevel) { |
|
minLevel = level; |
|
} |
|
if (level > maxLevel) { |
|
maxLevel = level; |
|
} |
|
|
|
|
|
while (++i < limit && levels[i] == level) {} |
|
|
|
|
|
runs[runIndex] = new BidiRun(start, i - start, level); |
|
++runIndex; |
|
} while (i < limit); |
|
|
|
if (limit < length) { |
|
|
|
runs[runIndex] = new BidiRun(limit, length - limit, bidiBase.paraLevel); |
|
|
|
if contextual multiple paragraphs. */ |
|
if (bidiBase.paraLevel < minLevel) { |
|
minLevel = bidiBase.paraLevel; |
|
} |
|
} |
|
|
|
|
|
bidiBase.runs = runs; |
|
bidiBase.runCount = runCount; |
|
|
|
reorderLine(bidiBase, minLevel, maxLevel); |
|
|
|
/* now add the direction flags and adjust the visualLimit's to be just that */ |
|
|
|
limit = 0; |
|
for (i = 0; i < runCount; ++i) { |
|
runs[i].level = levels[runs[i].start]; |
|
limit = (runs[i].limit += limit); |
|
} |
|
|
|
/* Set the embedding level for the trailing WS run. */ |
|
/* For a RTL paragraph, it will be the *first* run in visual order. */ |
|
|
|
contextual multiple paragraphs. */ |
|
if (runIndex < runCount) { |
|
int trailingRun = ((bidiBase.paraLevel & 1) != 0)? 0 : runIndex; |
|
runs[trailingRun].level = bidiBase.paraLevel; |
|
} |
|
} |
|
} |
|
|
|
|
|
if (bidiBase.insertPoints.size > 0) { |
|
BidiBase.Point point; |
|
int runIndex, ip; |
|
for (ip = 0; ip < bidiBase.insertPoints.size; ip++) { |
|
point = bidiBase.insertPoints.points[ip]; |
|
runIndex = getRunFromLogicalIndex(bidiBase, point.pos); |
|
bidiBase.runs[runIndex].insertRemove |= point.flag; |
|
} |
|
} |
|
|
|
|
|
if (bidiBase.controlCount > 0) { |
|
int runIndex, ic; |
|
char c; |
|
for (ic = 0; ic < bidiBase.length; ic++) { |
|
c = bidiBase.text[ic]; |
|
if (BidiBase.IsBidiControlChar(c)) { |
|
runIndex = getRunFromLogicalIndex(bidiBase, ic); |
|
bidiBase.runs[runIndex].insertRemove--; |
|
} |
|
} |
|
} |
|
} |
|
|
|
static int[] prepareReorder(byte[] levels, byte[] pMinLevel, byte[] pMaxLevel) |
|
{ |
|
int start; |
|
byte level, minLevel, maxLevel; |
|
|
|
if (levels == null || levels.length <= 0) { |
|
return null; |
|
} |
|
|
|
|
|
minLevel = BidiBase.MAX_EXPLICIT_LEVEL + 1; |
|
maxLevel = 0; |
|
for (start = levels.length; start>0; ) { |
|
level = levels[--start]; |
|
if (level > BidiBase.MAX_EXPLICIT_LEVEL + 1) { |
|
return null; |
|
} |
|
if (level < minLevel) { |
|
minLevel = level; |
|
} |
|
if (level > maxLevel) { |
|
maxLevel = level; |
|
} |
|
} |
|
pMinLevel[0] = minLevel; |
|
pMaxLevel[0] = maxLevel; |
|
|
|
|
|
int[] indexMap = new int[levels.length]; |
|
for (start = levels.length; start > 0; ) { |
|
--start; |
|
indexMap[start] = start; |
|
} |
|
|
|
return indexMap; |
|
} |
|
|
|
static int[] reorderVisual(byte[] levels) |
|
{ |
|
byte[] aMinLevel = new byte[1]; |
|
byte[] aMaxLevel = new byte[1]; |
|
int start, end, limit, temp; |
|
byte minLevel, maxLevel; |
|
|
|
int[] indexMap = prepareReorder(levels, aMinLevel, aMaxLevel); |
|
if (indexMap == null) { |
|
return null; |
|
} |
|
|
|
minLevel = aMinLevel[0]; |
|
maxLevel = aMaxLevel[0]; |
|
|
|
|
|
if (minLevel == maxLevel && (minLevel & 1) == 0) { |
|
return indexMap; |
|
} |
|
|
|
|
|
minLevel |= 1; |
|
|
|
|
|
do { |
|
start = 0; |
|
|
|
|
|
for ( ; ; ) { |
|
/* look for a sequence of levels that are all at >=maxLevel */ |
|
|
|
while (start < levels.length && levels[start] < maxLevel) { |
|
++start; |
|
} |
|
if (start >= levels.length) { |
|
break; /* no more such runs */ |
|
} |
|
|
|
|
|
for (limit = start; ++limit < levels.length && levels[limit] >= maxLevel; ) {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
end = limit - 1; |
|
while (start < end) { |
|
temp = indexMap[start]; |
|
indexMap[start] = indexMap[end]; |
|
indexMap[end] = temp; |
|
|
|
++start; |
|
--end; |
|
} |
|
|
|
if (limit == levels.length) { |
|
break; /* no more such sequences */ |
|
} else { |
|
start = limit + 1; |
|
} |
|
} |
|
} while (--maxLevel >= minLevel); |
|
|
|
return indexMap; |
|
} |
|
|
|
static int[] getVisualMap(BidiBase bidiBase) |
|
{ |
|
|
|
BidiRun[] runs = bidiBase.runs; |
|
int logicalStart, visualStart, visualLimit; |
|
int allocLength = bidiBase.length > bidiBase.resultLength ? bidiBase.length |
|
: bidiBase.resultLength; |
|
int[] indexMap = new int[allocLength]; |
|
|
|
visualStart = 0; |
|
int idx = 0; |
|
for (int j = 0; j < bidiBase.runCount; ++j) { |
|
logicalStart = runs[j].start; |
|
visualLimit = runs[j].limit; |
|
if (runs[j].isEvenRun()) { |
|
do { |
|
indexMap[idx++] = logicalStart++; |
|
} while (++visualStart < visualLimit); |
|
} else { |
|
logicalStart += visualLimit - visualStart; |
|
do { |
|
indexMap[idx++] = --logicalStart; |
|
} while (++visualStart < visualLimit); |
|
} |
|
/* visualStart==visualLimit; */ |
|
} |
|
|
|
if (bidiBase.insertPoints.size > 0) { |
|
int markFound = 0, runCount = bidiBase.runCount; |
|
int insertRemove, i, j, k; |
|
runs = bidiBase.runs; |
|
|
|
for (i = 0; i < runCount; i++) { |
|
insertRemove = runs[i].insertRemove; |
|
if ((insertRemove & (BidiBase.LRM_BEFORE|BidiBase.RLM_BEFORE)) > 0) { |
|
markFound++; |
|
} |
|
if ((insertRemove & (BidiBase.LRM_AFTER|BidiBase.RLM_AFTER)) > 0) { |
|
markFound++; |
|
} |
|
} |
|
|
|
k = bidiBase.resultLength; |
|
for (i = runCount - 1; i >= 0 && markFound > 0; i--) { |
|
insertRemove = runs[i].insertRemove; |
|
if ((insertRemove & (BidiBase.LRM_AFTER|BidiBase.RLM_AFTER)) > 0) { |
|
indexMap[--k] = BidiBase.MAP_NOWHERE; |
|
markFound--; |
|
} |
|
visualStart = i > 0 ? runs[i-1].limit : 0; |
|
for (j = runs[i].limit - 1; j >= visualStart && markFound > 0; j--) { |
|
indexMap[--k] = indexMap[j]; |
|
} |
|
if ((insertRemove & (BidiBase.LRM_BEFORE|BidiBase.RLM_BEFORE)) > 0) { |
|
indexMap[--k] = BidiBase.MAP_NOWHERE; |
|
markFound--; |
|
} |
|
} |
|
} |
|
else if (bidiBase.controlCount > 0) { |
|
int runCount = bidiBase.runCount, logicalEnd; |
|
int insertRemove, length, i, j, k, m; |
|
char uchar; |
|
boolean evenRun; |
|
runs = bidiBase.runs; |
|
visualStart = 0; |
|
|
|
k = 0; |
|
for (i = 0; i < runCount; i++, visualStart += length) { |
|
length = runs[i].limit - visualStart; |
|
insertRemove = runs[i].insertRemove; |
|
|
|
if ((insertRemove == 0) && (k == visualStart)) { |
|
k += length; |
|
continue; |
|
} |
|
|
|
if (insertRemove == 0) { |
|
visualLimit = runs[i].limit; |
|
for (j = visualStart; j < visualLimit; j++) { |
|
indexMap[k++] = indexMap[j]; |
|
} |
|
continue; |
|
} |
|
logicalStart = runs[i].start; |
|
evenRun = runs[i].isEvenRun(); |
|
logicalEnd = logicalStart + length - 1; |
|
for (j = 0; j < length; j++) { |
|
m = evenRun ? logicalStart + j : logicalEnd - j; |
|
uchar = bidiBase.text[m]; |
|
if (!BidiBase.IsBidiControlChar(uchar)) { |
|
indexMap[k++] = m; |
|
} |
|
} |
|
} |
|
} |
|
if (allocLength == bidiBase.resultLength) { |
|
return indexMap; |
|
} |
|
int[] newMap = new int[bidiBase.resultLength]; |
|
System.arraycopy(indexMap, 0, newMap, 0, bidiBase.resultLength); |
|
return newMap; |
|
} |
|
|
|
} |