Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/text/bidi/BidiLine.java
38918 views
/*1* Copyright (c) 2009, 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*/24/*25*******************************************************************************26* (C) Copyright IBM Corp. and others, 1996-2009 - All Rights Reserved *27* *28* The original version of this source code and documentation is copyrighted *29* and owned by IBM, These materials are provided under terms of a License *30* Agreement between IBM and Sun. This technology is protected by multiple *31* US and International patents. This notice and attribution to IBM may not *32* to removed. *33*******************************************************************************34*/35/* Written by Simon Montagu, Matitiahu Allouche36* (ported from C code written by Markus W. Scherer)37*/3839package sun.text.bidi;4041import java.text.Bidi;42import java.util.Arrays;4344public final class BidiLine {4546/*47* General remarks about the functions in this file:48*49* These functions deal with the aspects of potentially mixed-directional50* text in a single paragraph or in a line of a single paragraph51* which has already been processed according to52* the Unicode 3.0 Bidi algorithm as defined in53* http://www.unicode.org/unicode/reports/tr9/ , version 13,54* also described in The Unicode Standard, Version 4.0.1 .55*56* This means that there is a Bidi object with a levels57* and a dirProps array.58* paraLevel and direction are also set.59* Only if the length of the text is zero, then levels==dirProps==NULL.60*61* The overall directionality of the paragraph62* or line is used to bypass the reordering steps if possible.63* Even purely RTL text does not need reordering there because64* the getLogical/VisualIndex() methods can compute the65* index on the fly in such a case.66*67* The implementation of the access to same-level-runs and of the reordering68* do attempt to provide better performance and less memory usage compared to69* a direct implementation of especially rule (L2) with an array of70* one (32-bit) integer per text character.71*72* Here, the levels array is scanned as soon as necessary, and a vector of73* same-level-runs is created. Reordering then is done on this vector.74* For each run of text positions that were resolved to the same level,75* only 8 bytes are stored: the first text position of the run and the visual76* position behind the run after reordering.77* One sign bit is used to hold the directionality of the run.78* This is inefficient if there are many very short runs. If the average run79* length is <2, then this uses more memory.80*81* In a further attempt to save memory, the levels array is never changed82* after all the resolution rules (Xn, Wn, Nn, In).83* Many methods have to consider the field trailingWSStart:84* if it is less than length, then there is an implicit trailing run85* at the paraLevel,86* which is not reflected in the levels array.87* This allows a line Bidi object to use the same levels array as88* its paragraph parent object.89*90* When a Bidi object is created for a line of a paragraph, then the91* paragraph's levels and dirProps arrays are reused by way of setting92* a pointer into them, not by copying. This again saves memory and forbids to93* change the now shared levels for (L1).94*/9596/* handle trailing WS (L1) -------------------------------------------------- */9798/*99* setTrailingWSStart() sets the start index for a trailing100* run of WS in the line. This is necessary because we do not modify101* the paragraph's levels array that we just point into.102* Using trailingWSStart is another form of performing (L1).103*104* To make subsequent operations easier, we also include the run105* before the WS if it is at the paraLevel - we merge the two here.106*107* This method is called only from setLine(), so paraLevel is108* set correctly for the line even when contextual multiple paragraphs.109*/110111static void setTrailingWSStart(BidiBase bidiBase)112{113byte[] dirProps = bidiBase.dirProps;114byte[] levels = bidiBase.levels;115int start = bidiBase.length;116byte paraLevel = bidiBase.paraLevel;117118/* If the line is terminated by a block separator, all preceding WS etc...119are already set to paragraph level.120Setting trailingWSStart to pBidi->length will avoid changing the121level of B chars from 0 to paraLevel in getLevels when122orderParagraphsLTR==TRUE123*/124if (BidiBase.NoContextRTL(dirProps[start - 1]) == BidiBase.B) {125bidiBase.trailingWSStart = start; /* currently == bidiBase.length */126return;127}128/* go backwards across all WS, BN, explicit codes */129while (start > 0 &&130(BidiBase.DirPropFlagNC(dirProps[start - 1]) & BidiBase.MASK_WS) != 0) {131--start;132}133134/* if the WS run can be merged with the previous run then do so here */135while (start > 0 && levels[start - 1] == paraLevel) {136--start;137}138139bidiBase.trailingWSStart=start;140}141142public static Bidi setLine(Bidi bidi, BidiBase paraBidi,143Bidi newBidi, BidiBase newBidiBase,144int start, int limit) {145int length;146147BidiBase lineBidi = newBidiBase;148149/* set the values in lineBidi from its paraBidi parent */150/* class members are already initialized to 0 */151// lineBidi.paraBidi = null; /* mark unfinished setLine */152// lineBidi.flags = 0;153// lineBidi.controlCount = 0;154155length = lineBidi.length = lineBidi.originalLength =156lineBidi.resultLength = limit - start;157158lineBidi.text = new char[length];159System.arraycopy(paraBidi.text, start, lineBidi.text, 0, length);160lineBidi.paraLevel = paraBidi.GetParaLevelAt(start);161lineBidi.paraCount = paraBidi.paraCount;162lineBidi.runs = new BidiRun[0];163if (paraBidi.controlCount > 0) {164int j;165for (j = start; j < limit; j++) {166if (BidiBase.IsBidiControlChar(paraBidi.text[j])) {167lineBidi.controlCount++;168}169}170lineBidi.resultLength -= lineBidi.controlCount;171}172/* copy proper subset of DirProps */173lineBidi.getDirPropsMemory(length);174lineBidi.dirProps = lineBidi.dirPropsMemory;175System.arraycopy(paraBidi.dirProps, start, lineBidi.dirProps, 0,176length);177/* copy proper subset of Levels */178lineBidi.getLevelsMemory(length);179lineBidi.levels = lineBidi.levelsMemory;180System.arraycopy(paraBidi.levels, start, lineBidi.levels, 0,181length);182lineBidi.runCount = -1;183184if (paraBidi.direction != BidiBase.MIXED) {185/* the parent is already trivial */186lineBidi.direction = paraBidi.direction;187188/*189* The parent's levels are all either190* implicitly or explicitly ==paraLevel;191* do the same here.192*/193if (paraBidi.trailingWSStart <= start) {194lineBidi.trailingWSStart = 0;195} else if (paraBidi.trailingWSStart < limit) {196lineBidi.trailingWSStart = paraBidi.trailingWSStart - start;197} else {198lineBidi.trailingWSStart = length;199}200} else {201byte[] levels = lineBidi.levels;202int i, trailingWSStart;203byte level;204205setTrailingWSStart(lineBidi);206trailingWSStart = lineBidi.trailingWSStart;207208/* recalculate lineBidi.direction */209if (trailingWSStart == 0) {210/* all levels are at paraLevel */211lineBidi.direction = (byte)(lineBidi.paraLevel & 1);212} else {213/* get the level of the first character */214level = (byte)(levels[0] & 1);215216/* if there is anything of a different level, then the line217is mixed */218if (trailingWSStart < length &&219(lineBidi.paraLevel & 1) != level) {220/* the trailing WS is at paraLevel, which differs from221levels[0] */222lineBidi.direction = BidiBase.MIXED;223} else {224/* see if levels[1..trailingWSStart-1] have the same225direction as levels[0] and paraLevel */226for (i = 1; ; i++) {227if (i == trailingWSStart) {228/* the direction values match those in level */229lineBidi.direction = level;230break;231} else if ((levels[i] & 1) != level) {232lineBidi.direction = BidiBase.MIXED;233break;234}235}236}237}238239switch(lineBidi.direction) {240case Bidi.DIRECTION_LEFT_TO_RIGHT:241/* make sure paraLevel is even */242lineBidi.paraLevel = (byte)243((lineBidi.paraLevel + 1) & ~1);244245/* all levels are implicitly at paraLevel (important for246getLevels()) */247lineBidi.trailingWSStart = 0;248break;249case Bidi.DIRECTION_RIGHT_TO_LEFT:250/* make sure paraLevel is odd */251lineBidi.paraLevel |= 1;252253/* all levels are implicitly at paraLevel (important for254getLevels()) */255lineBidi.trailingWSStart = 0;256break;257default:258break;259}260}261262newBidiBase.paraBidi = paraBidi; /* mark successful setLine */263return newBidi;264}265266static byte getLevelAt(BidiBase bidiBase, int charIndex)267{268/* return paraLevel if in the trailing WS run, otherwise the real level */269if (bidiBase.direction != BidiBase.MIXED || charIndex >= bidiBase.trailingWSStart) {270return bidiBase.GetParaLevelAt(charIndex);271} else {272return bidiBase.levels[charIndex];273}274}275276static byte[] getLevels(BidiBase bidiBase)277{278int start = bidiBase.trailingWSStart;279int length = bidiBase.length;280281if (start != length) {282/* the current levels array does not reflect the WS run */283/*284* After the previous if(), we know that the levels array285* has an implicit trailing WS run and therefore does not fully286* reflect itself all the levels.287* This must be a Bidi object for a line, and288* we need to create a new levels array.289*/290/* bidiBase.paraLevel is ok even if contextual multiple paragraphs,291since bidiBase is a line object */292Arrays.fill(bidiBase.levels, start, length, bidiBase.paraLevel);293294/* this new levels array is set for the line and reflects the WS run */295bidiBase.trailingWSStart = length;296}297if (length < bidiBase.levels.length) {298byte[] levels = new byte[length];299System.arraycopy(bidiBase.levels, 0, levels, 0, length);300return levels;301}302return bidiBase.levels;303}304305static BidiRun getLogicalRun(BidiBase bidiBase, int logicalPosition)306{307/* this is done based on runs rather than on levels since levels have308a special interpretation when REORDER_RUNS_ONLY309*/310BidiRun newRun = new BidiRun(), iRun;311getRuns(bidiBase);312int runCount = bidiBase.runCount;313int visualStart = 0, logicalLimit = 0;314iRun = bidiBase.runs[0];315316for (int i = 0; i < runCount; i++) {317iRun = bidiBase.runs[i];318logicalLimit = iRun.start + iRun.limit - visualStart;319if ((logicalPosition >= iRun.start) &&320(logicalPosition < logicalLimit)) {321break;322}323visualStart = iRun.limit;324}325newRun.start = iRun.start;326newRun.limit = logicalLimit;327newRun.level = iRun.level;328return newRun;329}330331/* in trivial cases there is only one trivial run; called by getRuns() */332private static void getSingleRun(BidiBase bidiBase, byte level) {333/* simple, single-run case */334bidiBase.runs = bidiBase.simpleRuns;335bidiBase.runCount = 1;336337/* fill and reorder the single run */338bidiBase.runs[0] = new BidiRun(0, bidiBase.length, level);339}340341/* reorder the runs array (L2) ---------------------------------------------- */342343/*344* Reorder the same-level runs in the runs array.345* Here, runCount>1 and maxLevel>=minLevel>=paraLevel.346* All the visualStart fields=logical start before reordering.347* The "odd" bits are not set yet.348*349* Reordering with this data structure lends itself to some handy shortcuts:350*351* Since each run is moved but not modified, and since at the initial maxLevel352* each sequence of same-level runs consists of only one run each, we353* don't need to do anything there and can predecrement maxLevel.354* In many simple cases, the reordering is thus done entirely in the355* index mapping.356* Also, reordering occurs only down to the lowest odd level that occurs,357* which is minLevel|1. However, if the lowest level itself is odd, then358* in the last reordering the sequence of the runs at this level or higher359* will be all runs, and we don't need the elaborate loop to search for them.360* This is covered by ++minLevel instead of minLevel|=1 followed361* by an extra reorder-all after the reorder-some loop.362* About a trailing WS run:363* Such a run would need special treatment because its level is not364* reflected in levels[] if this is not a paragraph object.365* Instead, all characters from trailingWSStart on are implicitly at366* paraLevel.367* However, for all maxLevel>paraLevel, this run will never be reordered368* and does not need to be taken into account. maxLevel==paraLevel is only reordered369* if minLevel==paraLevel is odd, which is done in the extra segment.370* This means that for the main reordering loop we don't need to consider371* this run and can --runCount. If it is later part of the all-runs372* reordering, then runCount is adjusted accordingly.373*/374private static void reorderLine(BidiBase bidiBase, byte minLevel, byte maxLevel) {375376/* nothing to do? */377if (maxLevel<=(minLevel|1)) {378return;379}380381BidiRun[] runs;382BidiRun tempRun;383byte[] levels;384int firstRun, endRun, limitRun, runCount;385386/*387* Reorder only down to the lowest odd level388* and reorder at an odd minLevel in a separate, simpler loop.389* See comments above for why minLevel is always incremented.390*/391++minLevel;392393runs = bidiBase.runs;394levels = bidiBase.levels;395runCount = bidiBase.runCount;396397/* do not include the WS run at paraLevel<=old minLevel except in the simple loop */398if (bidiBase.trailingWSStart < bidiBase.length) {399--runCount;400}401402while (--maxLevel >= minLevel) {403firstRun = 0;404405/* loop for all sequences of runs */406for ( ; ; ) {407/* look for a sequence of runs that are all at >=maxLevel */408/* look for the first run of such a sequence */409while (firstRun < runCount && levels[runs[firstRun].start] < maxLevel) {410++firstRun;411}412if (firstRun >= runCount) {413break; /* no more such runs */414}415416/* look for the limit run of such a sequence (the run behind it) */417for (limitRun = firstRun; ++limitRun < runCount &&418levels[runs[limitRun].start]>=maxLevel; ) {}419420/* Swap the entire sequence of runs from firstRun to limitRun-1. */421endRun = limitRun - 1;422while (firstRun < endRun) {423tempRun = runs[firstRun];424runs[firstRun] = runs[endRun];425runs[endRun] = tempRun;426++firstRun;427--endRun;428}429430if (limitRun == runCount) {431break; /* no more such runs */432} else {433firstRun = limitRun + 1;434}435}436}437438/* now do maxLevel==old minLevel (==odd!), see above */439if ((minLevel & 1) == 0) {440firstRun = 0;441442/* include the trailing WS run in this complete reordering */443if (bidiBase.trailingWSStart == bidiBase.length) {444--runCount;445}446447/* Swap the entire sequence of all runs. (endRun==runCount) */448while (firstRun < runCount) {449tempRun = runs[firstRun];450runs[firstRun] = runs[runCount];451runs[runCount] = tempRun;452++firstRun;453--runCount;454}455}456}457458/* compute the runs array --------------------------------------------------- */459460static int getRunFromLogicalIndex(BidiBase bidiBase, int logicalIndex) {461BidiRun[] runs = bidiBase.runs;462int runCount = bidiBase.runCount, visualStart = 0, i, length, logicalStart;463464for (i = 0; i < runCount; i++) {465length = runs[i].limit - visualStart;466logicalStart = runs[i].start;467if ((logicalIndex >= logicalStart) && (logicalIndex < (logicalStart+length))) {468return i;469}470visualStart += length;471}472/* we should never get here */473throw new IllegalStateException("Internal ICU error in getRunFromLogicalIndex");474}475476/*477* Compute the runs array from the levels array.478* After getRuns() returns true, runCount is guaranteed to be >0479* and the runs are reordered.480* Odd-level runs have visualStart on their visual right edge and481* they progress visually to the left.482* If option OPTION_INSERT_MARKS is set, insertRemove will contain the483* sum of appropriate LRM/RLM_BEFORE/AFTER flags.484* If option OPTION_REMOVE_CONTROLS is set, insertRemove will contain the485* negative number of BiDi control characters within this run.486*/487static void getRuns(BidiBase bidiBase) {488/*489* This method returns immediately if the runs are already set. This490* includes the case of length==0 (handled in setPara)..491*/492if (bidiBase.runCount >= 0) {493return;494}495if (bidiBase.direction != BidiBase.MIXED) {496/* simple, single-run case - this covers length==0 */497/* bidiBase.paraLevel is ok even for contextual multiple paragraphs */498getSingleRun(bidiBase, bidiBase.paraLevel);499} else /* BidiBase.MIXED, length>0 */ {500/* mixed directionality */501int length = bidiBase.length, limit;502byte[] levels = bidiBase.levels;503int i, runCount;504byte level = BidiBase.INTERNAL_LEVEL_DEFAULT_LTR; /* initialize with no valid level */505/*506* If there are WS characters at the end of the line507* and the run preceding them has a level different from508* paraLevel, then they will form their own run at paraLevel (L1).509* Count them separately.510* We need some special treatment for this in order to not511* modify the levels array which a line Bidi object shares512* with its paragraph parent and its other line siblings.513* In other words, for the trailing WS, it may be514* levels[]!=paraLevel but we have to treat it like it were so.515*/516limit = bidiBase.trailingWSStart;517/* count the runs, there is at least one non-WS run, and limit>0 */518runCount = 0;519for (i = 0; i < limit; ++i) {520/* increment runCount at the start of each run */521if (levels[i] != level) {522++runCount;523level = levels[i];524}525}526527/*528* We don't need to see if the last run can be merged with a trailing529* WS run because setTrailingWSStart() would have done that.530*/531if (runCount == 1 && limit == length) {532/* There is only one non-WS run and no trailing WS-run. */533getSingleRun(bidiBase, levels[0]);534} else /* runCount>1 || limit<length */ {535/* allocate and set the runs */536BidiRun[] runs;537int runIndex, start;538byte minLevel = BidiBase.MAX_EXPLICIT_LEVEL + 1;539byte maxLevel=0;540541/* now, count a (non-mergeable) WS run */542if (limit < length) {543++runCount;544}545546/* runCount > 1 */547bidiBase.getRunsMemory(runCount);548runs = bidiBase.runsMemory;549550/* set the runs */551/* FOOD FOR THOUGHT: this could be optimized, e.g.:552* 464->444, 484->444, 575->555, 595->555553* However, that would take longer. Check also how it would554* interact with BiDi control removal and inserting Marks.555*/556runIndex = 0;557558/* search for the run limits and initialize visualLimit values with the run lengths */559i = 0;560do {561/* prepare this run */562start = i;563level = levels[i];564if (level < minLevel) {565minLevel = level;566}567if (level > maxLevel) {568maxLevel = level;569}570571/* look for the run limit */572while (++i < limit && levels[i] == level) {}573574/* i is another run limit */575runs[runIndex] = new BidiRun(start, i - start, level);576++runIndex;577} while (i < limit);578579if (limit < length) {580/* there is a separate WS run */581runs[runIndex] = new BidiRun(limit, length - limit, bidiBase.paraLevel);582/* For the trailing WS run, bidiBase.paraLevel is ok even583if contextual multiple paragraphs. */584if (bidiBase.paraLevel < minLevel) {585minLevel = bidiBase.paraLevel;586}587}588589/* set the object fields */590bidiBase.runs = runs;591bidiBase.runCount = runCount;592593reorderLine(bidiBase, minLevel, maxLevel);594595/* now add the direction flags and adjust the visualLimit's to be just that */596/* this loop will also handle the trailing WS run */597limit = 0;598for (i = 0; i < runCount; ++i) {599runs[i].level = levels[runs[i].start];600limit = (runs[i].limit += limit);601}602603/* Set the embedding level for the trailing WS run. */604/* For a RTL paragraph, it will be the *first* run in visual order. */605/* For the trailing WS run, bidiBase.paraLevel is ok even if606contextual multiple paragraphs. */607if (runIndex < runCount) {608int trailingRun = ((bidiBase.paraLevel & 1) != 0)? 0 : runIndex;609runs[trailingRun].level = bidiBase.paraLevel;610}611}612}613614/* handle insert LRM/RLM BEFORE/AFTER run */615if (bidiBase.insertPoints.size > 0) {616BidiBase.Point point;617int runIndex, ip;618for (ip = 0; ip < bidiBase.insertPoints.size; ip++) {619point = bidiBase.insertPoints.points[ip];620runIndex = getRunFromLogicalIndex(bidiBase, point.pos);621bidiBase.runs[runIndex].insertRemove |= point.flag;622}623}624625/* handle remove BiDi control characters */626if (bidiBase.controlCount > 0) {627int runIndex, ic;628char c;629for (ic = 0; ic < bidiBase.length; ic++) {630c = bidiBase.text[ic];631if (BidiBase.IsBidiControlChar(c)) {632runIndex = getRunFromLogicalIndex(bidiBase, ic);633bidiBase.runs[runIndex].insertRemove--;634}635}636}637}638639static int[] prepareReorder(byte[] levels, byte[] pMinLevel, byte[] pMaxLevel)640{641int start;642byte level, minLevel, maxLevel;643644if (levels == null || levels.length <= 0) {645return null;646}647648/* determine minLevel and maxLevel */649minLevel = BidiBase.MAX_EXPLICIT_LEVEL + 1;650maxLevel = 0;651for (start = levels.length; start>0; ) {652level = levels[--start];653if (level > BidiBase.MAX_EXPLICIT_LEVEL + 1) {654return null;655}656if (level < minLevel) {657minLevel = level;658}659if (level > maxLevel) {660maxLevel = level;661}662}663pMinLevel[0] = minLevel;664pMaxLevel[0] = maxLevel;665666/* initialize the index map */667int[] indexMap = new int[levels.length];668for (start = levels.length; start > 0; ) {669--start;670indexMap[start] = start;671}672673return indexMap;674}675676static int[] reorderVisual(byte[] levels)677{678byte[] aMinLevel = new byte[1];679byte[] aMaxLevel = new byte[1];680int start, end, limit, temp;681byte minLevel, maxLevel;682683int[] indexMap = prepareReorder(levels, aMinLevel, aMaxLevel);684if (indexMap == null) {685return null;686}687688minLevel = aMinLevel[0];689maxLevel = aMaxLevel[0];690691/* nothing to do? */692if (minLevel == maxLevel && (minLevel & 1) == 0) {693return indexMap;694}695696/* reorder only down to the lowest odd level */697minLevel |= 1;698699/* loop maxLevel..minLevel */700do {701start = 0;702703/* loop for all sequences of levels to reorder at the current maxLevel */704for ( ; ; ) {705/* look for a sequence of levels that are all at >=maxLevel */706/* look for the first index of such a sequence */707while (start < levels.length && levels[start] < maxLevel) {708++start;709}710if (start >= levels.length) {711break; /* no more such runs */712}713714/* look for the limit of such a sequence (the index behind it) */715for (limit = start; ++limit < levels.length && levels[limit] >= maxLevel; ) {}716717/*718* Swap the entire interval of indexes from start to limit-1.719* We don't need to swap the levels for the purpose of this720* algorithm: the sequence of levels that we look at does not721* move anyway.722*/723end = limit - 1;724while (start < end) {725temp = indexMap[start];726indexMap[start] = indexMap[end];727indexMap[end] = temp;728729++start;730--end;731}732733if (limit == levels.length) {734break; /* no more such sequences */735} else {736start = limit + 1;737}738}739} while (--maxLevel >= minLevel);740741return indexMap;742}743744static int[] getVisualMap(BidiBase bidiBase)745{746/* fill a visual-to-logical index map using the runs[] */747BidiRun[] runs = bidiBase.runs;748int logicalStart, visualStart, visualLimit;749int allocLength = bidiBase.length > bidiBase.resultLength ? bidiBase.length750: bidiBase.resultLength;751int[] indexMap = new int[allocLength];752753visualStart = 0;754int idx = 0;755for (int j = 0; j < bidiBase.runCount; ++j) {756logicalStart = runs[j].start;757visualLimit = runs[j].limit;758if (runs[j].isEvenRun()) {759do { /* LTR */760indexMap[idx++] = logicalStart++;761} while (++visualStart < visualLimit);762} else {763logicalStart += visualLimit - visualStart; /* logicalLimit */764do { /* RTL */765indexMap[idx++] = --logicalStart;766} while (++visualStart < visualLimit);767}768/* visualStart==visualLimit; */769}770771if (bidiBase.insertPoints.size > 0) {772int markFound = 0, runCount = bidiBase.runCount;773int insertRemove, i, j, k;774runs = bidiBase.runs;775/* count all inserted marks */776for (i = 0; i < runCount; i++) {777insertRemove = runs[i].insertRemove;778if ((insertRemove & (BidiBase.LRM_BEFORE|BidiBase.RLM_BEFORE)) > 0) {779markFound++;780}781if ((insertRemove & (BidiBase.LRM_AFTER|BidiBase.RLM_AFTER)) > 0) {782markFound++;783}784}785/* move back indexes by number of preceding marks */786k = bidiBase.resultLength;787for (i = runCount - 1; i >= 0 && markFound > 0; i--) {788insertRemove = runs[i].insertRemove;789if ((insertRemove & (BidiBase.LRM_AFTER|BidiBase.RLM_AFTER)) > 0) {790indexMap[--k] = BidiBase.MAP_NOWHERE;791markFound--;792}793visualStart = i > 0 ? runs[i-1].limit : 0;794for (j = runs[i].limit - 1; j >= visualStart && markFound > 0; j--) {795indexMap[--k] = indexMap[j];796}797if ((insertRemove & (BidiBase.LRM_BEFORE|BidiBase.RLM_BEFORE)) > 0) {798indexMap[--k] = BidiBase.MAP_NOWHERE;799markFound--;800}801}802}803else if (bidiBase.controlCount > 0) {804int runCount = bidiBase.runCount, logicalEnd;805int insertRemove, length, i, j, k, m;806char uchar;807boolean evenRun;808runs = bidiBase.runs;809visualStart = 0;810/* move forward indexes by number of preceding controls */811k = 0;812for (i = 0; i < runCount; i++, visualStart += length) {813length = runs[i].limit - visualStart;814insertRemove = runs[i].insertRemove;815/* if no control found yet, nothing to do in this run */816if ((insertRemove == 0) && (k == visualStart)) {817k += length;818continue;819}820/* if no control in this run */821if (insertRemove == 0) {822visualLimit = runs[i].limit;823for (j = visualStart; j < visualLimit; j++) {824indexMap[k++] = indexMap[j];825}826continue;827}828logicalStart = runs[i].start;829evenRun = runs[i].isEvenRun();830logicalEnd = logicalStart + length - 1;831for (j = 0; j < length; j++) {832m = evenRun ? logicalStart + j : logicalEnd - j;833uchar = bidiBase.text[m];834if (!BidiBase.IsBidiControlChar(uchar)) {835indexMap[k++] = m;836}837}838}839}840if (allocLength == bidiBase.resultLength) {841return indexMap;842}843int[] newMap = new int[bidiBase.resultLength];844System.arraycopy(indexMap, 0, newMap, 0, bidiBase.resultLength);845return newMap;846}847848}849850851