Path: blob/master/src/java.base/share/classes/jdk/internal/icu/text/BidiLine.java
41161 views
/*1* Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26*******************************************************************************27* Copyright (C) 2001-2014, International Business Machines28* Corporation and others. All Rights Reserved.29*******************************************************************************30*/31/* Written by Simon Montagu, Matitiahu Allouche32* (ported from C code written by Markus W. Scherer)33*/3435package jdk.internal.icu.text;3637import java.text.Bidi;38import java.util.Arrays;3940final class BidiLine {4142/*43* General remarks about the functions in this file:44*45* These functions deal with the aspects of potentially mixed-directional46* text in a single paragraph or in a line of a single paragraph47* which has already been processed according to48* the Unicode 3.0 Bidi algorithm as defined in49* <a href="http://www.unicode.org/reports/tr9/">Unicode Standard Annex #9:50* Unicode Bidirectional Algorithm</a>, version 13,51* also described in The Unicode Standard, Version 4.0.1 .52*53* This means that there is a Bidi object with a levels54* and a dirProps array.55* paraLevel and direction are also set.56* Only if the length of the text is zero, then levels==dirProps==NULL.57*58* The overall directionality of the paragraph59* or line is used to bypass the reordering steps if possible.60* Even purely RTL text does not need reordering there because61* the getLogical/VisualIndex() methods can compute the62* index on the fly in such a case.63*64* The implementation of the access to same-level-runs and of the reordering65* do attempt to provide better performance and less memory usage compared to66* a direct implementation of especially rule (L2) with an array of67* one (32-bit) integer per text character.68*69* Here, the levels array is scanned as soon as necessary, and a vector of70* same-level-runs is created. Reordering then is done on this vector.71* For each run of text positions that were resolved to the same level,72* only 8 bytes are stored: the first text position of the run and the visual73* position behind the run after reordering.74* One sign bit is used to hold the directionality of the run.75* This is inefficient if there are many very short runs. If the average run76* length is <2, then this uses more memory.77*78* In a further attempt to save memory, the levels array is never changed79* after all the resolution rules (Xn, Wn, Nn, In).80* Many methods have to consider the field trailingWSStart:81* if it is less than length, then there is an implicit trailing run82* at the paraLevel,83* which is not reflected in the levels array.84* This allows a line Bidi object to use the same levels array as85* its paragraph parent object.86*87* When a Bidi object is created for a line of a paragraph, then the88* paragraph's levels and dirProps arrays are reused by way of setting89* a pointer into them, not by copying. This again saves memory and forbids to90* change the now shared levels for (L1).91*/9293/* handle trailing WS (L1) -------------------------------------------------- */9495/*96* setTrailingWSStart() sets the start index for a trailing97* run of WS in the line. This is necessary because we do not modify98* the paragraph's levels array that we just point into.99* Using trailingWSStart is another form of performing (L1).100*101* To make subsequent operations easier, we also include the run102* before the WS if it is at the paraLevel - we merge the two here.103*104* This method is called only from setLine(), so paraLevel is105* set correctly for the line even when contextual multiple paragraphs.106*/107108static void setTrailingWSStart(BidiBase bidiBase)109{110byte[] dirProps = bidiBase.dirProps;111byte[] levels = bidiBase.levels;112int start = bidiBase.length;113byte paraLevel = bidiBase.paraLevel;114115/* If the line is terminated by a block separator, all preceding WS etc...116are already set to paragraph level.117Setting trailingWSStart to pBidi->length will avoid changing the118level of B chars from 0 to paraLevel in getLevels when119orderParagraphsLTR==TRUE120*/121if (dirProps[start - 1] == BidiBase.B) {122bidiBase.trailingWSStart = start; /* currently == bidiBase.length */123return;124}125/* go backwards across all WS, BN, explicit codes */126while (start > 0 &&127(BidiBase.DirPropFlag(dirProps[start - 1]) & BidiBase.MASK_WS) != 0) {128--start;129}130131/* if the WS run can be merged with the previous run then do so here */132while (start > 0 && levels[start - 1] == paraLevel) {133--start;134}135136bidiBase.trailingWSStart=start;137}138139static Bidi setLine(BidiBase paraBidi,140Bidi newBidi, BidiBase lineBidi,141int start, int limit) {142int length;143144/* set the values in lineBidi from its paraBidi parent */145/* class members are already initialized to 0 */146// lineBidi.paraBidi = null; /* mark unfinished setLine */147// lineBidi.flags = 0;148// lineBidi.controlCount = 0;149150length = lineBidi.length = lineBidi.originalLength =151lineBidi.resultLength = limit - start;152153lineBidi.text = new char[length];154System.arraycopy(paraBidi.text, start, lineBidi.text, 0, length);155lineBidi.paraLevel = paraBidi.GetParaLevelAt(start);156lineBidi.paraCount = paraBidi.paraCount;157lineBidi.runs = new BidiRun[0];158lineBidi.reorderingMode = paraBidi.reorderingMode;159lineBidi.reorderingOptions = paraBidi.reorderingOptions;160if (paraBidi.controlCount > 0) {161int j;162for (j = start; j < limit; j++) {163if (BidiBase.IsBidiControlChar(paraBidi.text[j])) {164lineBidi.controlCount++;165}166}167lineBidi.resultLength -= lineBidi.controlCount;168}169/* copy proper subset of DirProps */170lineBidi.getDirPropsMemory(length);171lineBidi.dirProps = lineBidi.dirPropsMemory;172System.arraycopy(paraBidi.dirProps, start, lineBidi.dirProps, 0,173length);174/* copy proper subset of Levels */175lineBidi.getLevelsMemory(length);176lineBidi.levels = lineBidi.levelsMemory;177System.arraycopy(paraBidi.levels, start, lineBidi.levels, 0,178length);179lineBidi.runCount = -1;180181if (paraBidi.direction != BidiBase.MIXED) {182/* the parent is already trivial */183lineBidi.direction = paraBidi.direction;184185/*186* The parent's levels are all either187* implicitly or explicitly ==paraLevel;188* do the same here.189*/190if (paraBidi.trailingWSStart <= start) {191lineBidi.trailingWSStart = 0;192} else if (paraBidi.trailingWSStart < limit) {193lineBidi.trailingWSStart = paraBidi.trailingWSStart - start;194} else {195lineBidi.trailingWSStart = length;196}197} else {198byte[] levels = lineBidi.levels;199int i, trailingWSStart;200byte level;201202setTrailingWSStart(lineBidi);203trailingWSStart = lineBidi.trailingWSStart;204205/* recalculate lineBidiBase.direction */206if (trailingWSStart == 0) {207/* all levels are at paraLevel */208lineBidi.direction = (byte)(lineBidi.paraLevel & 1);209} else {210/* get the level of the first character */211level = (byte)(levels[0] & 1);212213/* if there is anything of a different level, then the line214is mixed */215if (trailingWSStart < length &&216(lineBidi.paraLevel & 1) != level) {217/* the trailing WS is at paraLevel, which differs from218levels[0] */219lineBidi.direction = BidiBase.MIXED;220} else {221/* see if levels[1..trailingWSStart-1] have the same222direction as levels[0] and paraLevel */223for (i = 1; ; i++) {224if (i == trailingWSStart) {225/* the direction values match those in level */226lineBidi.direction = level;227break;228} else if ((levels[i] & 1) != level) {229lineBidi.direction = BidiBase.MIXED;230break;231}232}233}234}235236switch(lineBidi.direction) {237case Bidi.DIRECTION_LEFT_TO_RIGHT:238/* make sure paraLevel is even */239lineBidi.paraLevel = (byte)240((lineBidi.paraLevel + 1) & ~1);241242/* all levels are implicitly at paraLevel (important for243getLevels()) */244lineBidi.trailingWSStart = 0;245break;246case Bidi.DIRECTION_RIGHT_TO_LEFT:247/* make sure paraLevel is odd */248lineBidi.paraLevel |= 1;249250/* all levels are implicitly at paraLevel (important for251getLevels()) */252lineBidi.trailingWSStart = 0;253break;254default:255break;256}257}258259lineBidi.paraBidi = paraBidi; /* mark successful setLine */260261return newBidi;262}263264static byte getLevelAt(BidiBase bidiBase, int charIndex)265{266/* return paraLevel if in the trailing WS run, otherwise the real level */267if (bidiBase.direction != BidiBase.MIXED || charIndex >= bidiBase.trailingWSStart) {268return bidiBase.GetParaLevelAt(charIndex);269} else {270return bidiBase.levels[charIndex];271}272}273274static byte[] getLevels(BidiBase bidiBase)275{276int start = bidiBase.trailingWSStart;277int length = bidiBase.length;278279if (start != length) {280/* the current levels array does not reflect the WS run */281/*282* After the previous if(), we know that the levels array283* has an implicit trailing WS run and therefore does not fully284* reflect itself all the levels.285* This must be a Bidi object for a line, and286* we need to create a new levels array.287*/288/* bidiBase.paraLevel is ok even if contextual multiple paragraphs,289since bidiBase is a line object */290Arrays.fill(bidiBase.levels, start, length, bidiBase.paraLevel);291292/* this new levels array is set for the line and reflects the WS run */293bidiBase.trailingWSStart = length;294}295if (length < bidiBase.levels.length) {296byte[] levels = new byte[length];297System.arraycopy(bidiBase.levels, 0, levels, 0, length);298return levels;299}300return bidiBase.levels;301}302303static BidiRun getVisualRun(BidiBase bidiBase, int runIndex) {304int start = bidiBase.runs[runIndex].start;305int limit;306byte level = bidiBase.runs[runIndex].level;307308if (runIndex > 0) {309limit = start +310bidiBase.runs[runIndex].limit -311bidiBase.runs[runIndex - 1].limit;312} else {313limit = start + bidiBase.runs[0].limit;314}315return new BidiRun(start, limit, level);316}317318/* in trivial cases there is only one trivial run; called by getRuns() */319private static void getSingleRun(BidiBase bidiBase, byte level) {320/* simple, single-run case */321bidiBase.runs = bidiBase.simpleRuns;322bidiBase.runCount = 1;323324/* fill and reorder the single run */325bidiBase.runs[0] = new BidiRun(0, bidiBase.length, level);326}327328/* reorder the runs array (L2) ---------------------------------------------- */329330/*331* Reorder the same-level runs in the runs array.332* Here, runCount>1 and maxLevel>=minLevel>=paraLevel.333* All the visualStart fields=logical start before reordering.334* The "odd" bits are not set yet.335*336* Reordering with this data structure lends itself to some handy shortcuts:337*338* Since each run is moved but not modified, and since at the initial maxLevel339* each sequence of same-level runs consists of only one run each, we340* don't need to do anything there and can predecrement maxLevel.341* In many simple cases, the reordering is thus done entirely in the342* index mapping.343* Also, reordering occurs only down to the lowest odd level that occurs,344* which is minLevel|1. However, if the lowest level itself is odd, then345* in the last reordering the sequence of the runs at this level or higher346* will be all runs, and we don't need the elaborate loop to search for them.347* This is covered by ++minLevel instead of minLevel|=1 followed348* by an extra reorder-all after the reorder-some loop.349* About a trailing WS run:350* Such a run would need special treatment because its level is not351* reflected in levels[] if this is not a paragraph object.352* Instead, all characters from trailingWSStart on are implicitly at353* paraLevel.354* However, for all maxLevel>paraLevel, this run will never be reordered355* and does not need to be taken into account. maxLevel==paraLevel is only reordered356* if minLevel==paraLevel is odd, which is done in the extra segment.357* This means that for the main reordering loop we don't need to consider358* this run and can --runCount. If it is later part of the all-runs359* reordering, then runCount is adjusted accordingly.360*/361private static void reorderLine(BidiBase bidiBase, byte minLevel, byte maxLevel) {362363/* nothing to do? */364if (maxLevel<=(minLevel|1)) {365return;366}367368BidiRun[] runs;369BidiRun tempRun;370byte[] levels;371int firstRun, endRun, limitRun, runCount;372373/*374* Reorder only down to the lowest odd level375* and reorder at an odd minLevel in a separate, simpler loop.376* See comments above for why minLevel is always incremented.377*/378++minLevel;379380runs = bidiBase.runs;381levels = bidiBase.levels;382runCount = bidiBase.runCount;383384/* do not include the WS run at paraLevel<=old minLevel except in the simple loop */385if (bidiBase.trailingWSStart < bidiBase.length) {386--runCount;387}388389while (--maxLevel >= minLevel) {390firstRun = 0;391392/* loop for all sequences of runs */393for ( ; ; ) {394/* look for a sequence of runs that are all at >=maxLevel */395/* look for the first run of such a sequence */396while (firstRun < runCount && levels[runs[firstRun].start] < maxLevel) {397++firstRun;398}399if (firstRun >= runCount) {400break; /* no more such runs */401}402403/* look for the limit run of such a sequence (the run behind it) */404for (limitRun = firstRun; ++limitRun < runCount &&405levels[runs[limitRun].start]>=maxLevel; ) {}406407/* Swap the entire sequence of runs from firstRun to limitRun-1. */408endRun = limitRun - 1;409while (firstRun < endRun) {410tempRun = runs[firstRun];411runs[firstRun] = runs[endRun];412runs[endRun] = tempRun;413++firstRun;414--endRun;415}416417if (limitRun == runCount) {418break; /* no more such runs */419} else {420firstRun = limitRun + 1;421}422}423}424425/* now do maxLevel==old minLevel (==odd!), see above */426if ((minLevel & 1) == 0) {427firstRun = 0;428429/* include the trailing WS run in this complete reordering */430if (bidiBase.trailingWSStart == bidiBase.length) {431--runCount;432}433434/* Swap the entire sequence of all runs. (endRun==runCount) */435while (firstRun < runCount) {436tempRun = runs[firstRun];437runs[firstRun] = runs[runCount];438runs[runCount] = tempRun;439++firstRun;440--runCount;441}442}443}444445/* compute the runs array --------------------------------------------------- */446447static int getRunFromLogicalIndex(BidiBase bidiBase, int logicalIndex) {448BidiRun[] runs = bidiBase.runs;449int runCount = bidiBase.runCount, visualStart = 0, i, length, logicalStart;450451for (i = 0; i < runCount; i++) {452length = runs[i].limit - visualStart;453logicalStart = runs[i].start;454if ((logicalIndex >= logicalStart) && (logicalIndex < (logicalStart+length))) {455return i;456}457visualStart += length;458}459/* we should never get here */460throw new IllegalStateException("Internal ICU error in getRunFromLogicalIndex");461}462463/*464* Compute the runs array from the levels array.465* After getRuns() returns true, runCount is guaranteed to be >0466* and the runs are reordered.467* Odd-level runs have visualStart on their visual right edge and468* they progress visually to the left.469* If option OPTION_INSERT_MARKS is set, insertRemove will contain the470* sum of appropriate LRM/RLM_BEFORE/AFTER flags.471* If option OPTION_REMOVE_CONTROLS is set, insertRemove will contain the472* negative number of BiDi control characters within this run.473*/474static void getRuns(BidiBase bidiBase) {475/*476* This method returns immediately if the runs are already set. This477* includes the case of length==0 (handled in setPara)..478*/479if (bidiBase.runCount >= 0) {480return;481}482if (bidiBase.direction != BidiBase.MIXED) {483/* simple, single-run case - this covers length==0 */484/* bidiBase.paraLevel is ok even for contextual multiple paragraphs */485getSingleRun(bidiBase, bidiBase.paraLevel);486} else /* BidiBase.MIXED, length>0 */ {487/* mixed directionality */488int length = bidiBase.length, limit;489byte[] levels = bidiBase.levels;490int i, runCount;491byte level = -1; /* initialize with no valid level */492/*493* If there are WS characters at the end of the line494* and the run preceding them has a level different from495* paraLevel, then they will form their own run at paraLevel (L1).496* Count them separately.497* We need some special treatment for this in order to not498* modify the levels array which a line Bidi object shares499* with its paragraph parent and its other line siblings.500* In other words, for the trailing WS, it may be501* levels[]!=paraLevel but we have to treat it like it were so.502*/503limit = bidiBase.trailingWSStart;504/* count the runs, there is at least one non-WS run, and limit>0 */505runCount = 0;506for (i = 0; i < limit; ++i) {507/* increment runCount at the start of each run */508if (levels[i] != level) {509++runCount;510level = levels[i];511}512}513514/*515* We don't need to see if the last run can be merged with a trailing516* WS run because setTrailingWSStart() would have done that.517*/518if (runCount == 1 && limit == length) {519/* There is only one non-WS run and no trailing WS-run. */520getSingleRun(bidiBase, levels[0]);521} else /* runCount>1 || limit<length */ {522/* allocate and set the runs */523BidiRun[] runs;524int runIndex, start;525byte minLevel = BidiBase.MAX_EXPLICIT_LEVEL + 1;526byte maxLevel=0;527528/* now, count a (non-mergeable) WS run */529if (limit < length) {530++runCount;531}532533/* runCount > 1 */534bidiBase.getRunsMemory(runCount);535runs = bidiBase.runsMemory;536537/* set the runs */538/* FOOD FOR THOUGHT: this could be optimized, e.g.:539* 464->444, 484->444, 575->555, 595->555540* However, that would take longer. Check also how it would541* interact with BiDi control removal and inserting Marks.542*/543runIndex = 0;544545/* search for the run limits and initialize visualLimit values with the run lengths */546i = 0;547do {548/* prepare this run */549start = i;550level = levels[i];551if (level < minLevel) {552minLevel = level;553}554if (level > maxLevel) {555maxLevel = level;556}557558/* look for the run limit */559while (++i < limit && levels[i] == level) {}560561/* i is another run limit */562runs[runIndex] = new BidiRun(start, i - start, level);563++runIndex;564} while (i < limit);565566if (limit < length) {567/* there is a separate WS run */568runs[runIndex] = new BidiRun(limit, length - limit, bidiBase.paraLevel);569/* For the trailing WS run, bidiBase.paraLevel is ok even570if contextual multiple paragraphs. */571if (bidiBase.paraLevel < minLevel) {572minLevel = bidiBase.paraLevel;573}574}575576/* set the object fields */577bidiBase.runs = runs;578bidiBase.runCount = runCount;579580reorderLine(bidiBase, minLevel, maxLevel);581582/* now add the direction flags and adjust the visualLimit's to be just that */583/* this loop will also handle the trailing WS run */584limit = 0;585for (i = 0; i < runCount; ++i) {586runs[i].level = levels[runs[i].start];587limit = (runs[i].limit += limit);588}589590/* Set the embedding level for the trailing WS run. */591/* For a RTL paragraph, it will be the *first* run in visual order. */592/* For the trailing WS run, bidiBase.paraLevel is ok even if593contextual multiple paragraphs. */594if (runIndex < runCount) {595int trailingRun = ((bidiBase.paraLevel & 1) != 0)? 0 : runIndex;596runs[trailingRun].level = bidiBase.paraLevel;597}598}599}600601/* handle insert LRM/RLM BEFORE/AFTER run */602if (bidiBase.insertPoints.size > 0) {603BidiBase.Point point;604int runIndex, ip;605for (ip = 0; ip < bidiBase.insertPoints.size; ip++) {606point = bidiBase.insertPoints.points[ip];607runIndex = getRunFromLogicalIndex(bidiBase, point.pos);608bidiBase.runs[runIndex].insertRemove |= point.flag;609}610}611612/* handle remove BiDi control characters */613if (bidiBase.controlCount > 0) {614int runIndex, ic;615char c;616for (ic = 0; ic < bidiBase.length; ic++) {617c = bidiBase.text[ic];618if (BidiBase.IsBidiControlChar(c)) {619runIndex = getRunFromLogicalIndex(bidiBase, ic);620bidiBase.runs[runIndex].insertRemove--;621}622}623}624}625626static int[] prepareReorder(byte[] levels, byte[] pMinLevel, byte[] pMaxLevel)627{628int start;629byte level, minLevel, maxLevel;630631if (levels == null || levels.length <= 0) {632return null;633}634635/* determine minLevel and maxLevel */636minLevel = BidiBase.MAX_EXPLICIT_LEVEL + 1;637maxLevel = 0;638for (start = levels.length; start>0; ) {639level = levels[--start];640if (level < 0 || level > (BidiBase.MAX_EXPLICIT_LEVEL + 1)) {641return null;642}643if (level < minLevel) {644minLevel = level;645}646if (level > maxLevel) {647maxLevel = level;648}649}650pMinLevel[0] = minLevel;651pMaxLevel[0] = maxLevel;652653/* initialize the index map */654int[] indexMap = new int[levels.length];655for (start = levels.length; start > 0; ) {656--start;657indexMap[start] = start;658}659660return indexMap;661}662663static int[] reorderVisual(byte[] levels)664{665byte[] aMinLevel = new byte[1];666byte[] aMaxLevel = new byte[1];667int start, end, limit, temp;668byte minLevel, maxLevel;669670int[] indexMap = prepareReorder(levels, aMinLevel, aMaxLevel);671if (indexMap == null) {672return null;673}674675minLevel = aMinLevel[0];676maxLevel = aMaxLevel[0];677678/* nothing to do? */679if (minLevel == maxLevel && (minLevel & 1) == 0) {680return indexMap;681}682683/* reorder only down to the lowest odd level */684minLevel |= 1;685686/* loop maxLevel..minLevel */687do {688start = 0;689690/* loop for all sequences of levels to reorder at the current maxLevel */691for ( ; ; ) {692/* look for a sequence of levels that are all at >=maxLevel */693/* look for the first index of such a sequence */694while (start < levels.length && levels[start] < maxLevel) {695++start;696}697if (start >= levels.length) {698break; /* no more such runs */699}700701/* look for the limit of such a sequence (the index behind it) */702for (limit = start; ++limit < levels.length && levels[limit] >= maxLevel; ) {}703704/*705* Swap the entire interval of indexes from start to limit-1.706* We don't need to swap the levels for the purpose of this707* algorithm: the sequence of levels that we look at does not708* move anyway.709*/710end = limit - 1;711while (start < end) {712temp = indexMap[start];713indexMap[start] = indexMap[end];714indexMap[end] = temp;715716++start;717--end;718}719720if (limit == levels.length) {721break; /* no more such sequences */722} else {723start = limit + 1;724}725}726} while (--maxLevel >= minLevel);727728return indexMap;729}730731static int[] getVisualMap(BidiBase bidiBase)732{733/* fill a visual-to-logical index map using the runs[] */734BidiRun[] runs = bidiBase.runs;735int logicalStart, visualStart, visualLimit;736int allocLength = bidiBase.length > bidiBase.resultLength ? bidiBase.length737: bidiBase.resultLength;738int[] indexMap = new int[allocLength];739740visualStart = 0;741int idx = 0;742for (int j = 0; j < bidiBase.runCount; ++j) {743logicalStart = runs[j].start;744visualLimit = runs[j].limit;745if (runs[j].isEvenRun()) {746do { /* LTR */747indexMap[idx++] = logicalStart++;748} while (++visualStart < visualLimit);749} else {750logicalStart += visualLimit - visualStart; /* logicalLimit */751do { /* RTL */752indexMap[idx++] = --logicalStart;753} while (++visualStart < visualLimit);754}755/* visualStart==visualLimit; */756}757758if (bidiBase.insertPoints.size > 0) {759int markFound = 0, runCount = bidiBase.runCount;760int insertRemove, i, j, k;761runs = bidiBase.runs;762/* count all inserted marks */763for (i = 0; i < runCount; i++) {764insertRemove = runs[i].insertRemove;765if ((insertRemove & (BidiBase.LRM_BEFORE|BidiBase.RLM_BEFORE)) > 0) {766markFound++;767}768if ((insertRemove & (BidiBase.LRM_AFTER|BidiBase.RLM_AFTER)) > 0) {769markFound++;770}771}772/* move back indexes by number of preceding marks */773k = bidiBase.resultLength;774for (i = runCount - 1; i >= 0 && markFound > 0; i--) {775insertRemove = runs[i].insertRemove;776if ((insertRemove & (BidiBase.LRM_AFTER|BidiBase.RLM_AFTER)) > 0) {777indexMap[--k] = BidiBase.MAP_NOWHERE;778markFound--;779}780visualStart = i > 0 ? runs[i-1].limit : 0;781for (j = runs[i].limit - 1; j >= visualStart && markFound > 0; j--) {782indexMap[--k] = indexMap[j];783}784if ((insertRemove & (BidiBase.LRM_BEFORE|BidiBase.RLM_BEFORE)) > 0) {785indexMap[--k] = BidiBase.MAP_NOWHERE;786markFound--;787}788}789}790else if (bidiBase.controlCount > 0) {791int runCount = bidiBase.runCount, logicalEnd;792int insertRemove, length, i, j, k, m;793char uchar;794boolean evenRun;795runs = bidiBase.runs;796visualStart = 0;797/* move forward indexes by number of preceding controls */798k = 0;799for (i = 0; i < runCount; i++, visualStart += length) {800length = runs[i].limit - visualStart;801insertRemove = runs[i].insertRemove;802/* if no control found yet, nothing to do in this run */803if ((insertRemove == 0) && (k == visualStart)) {804k += length;805continue;806}807/* if no control in this run */808if (insertRemove == 0) {809visualLimit = runs[i].limit;810for (j = visualStart; j < visualLimit; j++) {811indexMap[k++] = indexMap[j];812}813continue;814}815logicalStart = runs[i].start;816evenRun = runs[i].isEvenRun();817logicalEnd = logicalStart + length - 1;818for (j = 0; j < length; j++) {819m = evenRun ? logicalStart + j : logicalEnd - j;820uchar = bidiBase.text[m];821if (!BidiBase.IsBidiControlChar(uchar)) {822indexMap[k++] = m;823}824}825}826}827if (allocLength == bidiBase.resultLength) {828return indexMap;829}830int[] newMap = new int[bidiBase.resultLength];831System.arraycopy(indexMap, 0, newMap, 0, bidiBase.resultLength);832return newMap;833}834835}836837838