|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
/* |
|
******************************************************************************* |
|
* Copyright (C) 2001-2010, International Business Machines |
|
* Corporation and others. All Rights Reserved. |
|
******************************************************************************* |
|
*/ |
|
/* Written by Simon Montagu, Matitiahu Allouche |
|
* (ported from C code written by Markus W. Scherer) |
|
*/ |
|
|
|
package sun.text.bidi; |
|
|
|
import sun.text.normalizer.UCharacter; |
|
import sun.text.normalizer.UTF16; |
|
|
|
final class BidiWriter { |
|
|
|
|
|
static final char LRM_CHAR = 0x200e; |
|
static final char RLM_CHAR = 0x200f; |
|
static final int MASK_R_AL = (1 << UCharacter.RIGHT_TO_LEFT | |
|
1 << UCharacter.RIGHT_TO_LEFT_ARABIC); |
|
|
|
private static boolean IsCombining(int type) { |
|
return ((1<<type & |
|
(1<<UCharacter.NON_SPACING_MARK | |
|
1<<UCharacter.COMBINING_SPACING_MARK | |
|
1<<UCharacter.ENCLOSING_MARK)) != 0); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static String doWriteForward(String src, int options) { |
|
|
|
switch(options&(BidiBase.REMOVE_BIDI_CONTROLS|BidiBase.DO_MIRRORING)) { |
|
case 0: { |
|
|
|
return src; |
|
} |
|
case BidiBase.DO_MIRRORING: { |
|
StringBuffer dest = new StringBuffer(src.length()); |
|
|
|
|
|
int i=0; |
|
int c; |
|
|
|
do { |
|
c = UTF16.charAt(src, i); |
|
i += UTF16.getCharCount(c); |
|
UTF16.append(dest, UCharacter.getMirror(c)); |
|
} while(i < src.length()); |
|
return dest.toString(); |
|
} |
|
case BidiBase.REMOVE_BIDI_CONTROLS: { |
|
StringBuilder dest = new StringBuilder(src.length()); |
|
|
|
|
|
int i = 0; |
|
char c; |
|
do { |
|
c = src.charAt(i++); |
|
if(!BidiBase.IsBidiControlChar(c)) { |
|
dest.append(c); |
|
} |
|
} while(i < src.length()); |
|
return dest.toString(); |
|
} |
|
default: { |
|
StringBuffer dest = new StringBuffer(src.length()); |
|
|
|
|
|
int i = 0; |
|
int c; |
|
do { |
|
c = UTF16.charAt(src, i); |
|
i += UTF16.getCharCount(c); |
|
if(!BidiBase.IsBidiControlChar(c)) { |
|
UTF16.append(dest, UCharacter.getMirror(c)); |
|
} |
|
} while(i < src.length()); |
|
return dest.toString(); |
|
} |
|
} /* end of switch */ |
|
} |
|
|
|
private static String doWriteForward(char[] text, int start, int limit, |
|
int options) { |
|
return doWriteForward(new String(text, start, limit - start), options); |
|
} |
|
|
|
static String writeReverse(String src, int options) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
StringBuffer dest = new StringBuffer(src.length()); |
|
|
|
|
|
switch (options & |
|
(BidiBase.REMOVE_BIDI_CONTROLS | |
|
BidiBase.DO_MIRRORING | |
|
BidiBase.KEEP_BASE_COMBINING)) { |
|
|
|
case 0: |
|
/* |
|
* With none of the "complicated" options set, the destination |
|
* run will have the same length as the source run, |
|
* and there is no mirroring and no keeping combining characters |
|
* with their base characters. |
|
* |
|
* XXX: or dest = UTF16.reverse(new StringBuffer(src)); |
|
*/ |
|
|
|
int srcLength = src.length(); |
|
|
|
|
|
do { |
|
|
|
* in this segment */ |
|
int i = srcLength; |
|
|
|
|
|
srcLength -= UTF16.getCharCount(UTF16.charAt(src, |
|
srcLength - 1)); |
|
|
|
|
|
dest.append(src.substring(srcLength, i)); |
|
} while(srcLength > 0); |
|
break; |
|
|
|
case BidiBase.KEEP_BASE_COMBINING: |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
srcLength = src.length(); |
|
|
|
|
|
do { |
|
|
|
* in this segment */ |
|
int c; |
|
int i = srcLength; |
|
|
|
|
|
* character */ |
|
do { |
|
c = UTF16.charAt(src, srcLength - 1); |
|
srcLength -= UTF16.getCharCount(c); |
|
} while(srcLength > 0 && IsCombining(UCharacter.getType(c))); |
|
|
|
|
|
dest.append(src.substring(srcLength, i)); |
|
} while(srcLength > 0); |
|
break; |
|
|
|
default: |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
srcLength = src.length(); |
|
|
|
|
|
do { |
|
|
|
* in this segment */ |
|
int i = srcLength; |
|
|
|
|
|
int c = UTF16.charAt(src, srcLength - 1); |
|
srcLength -= UTF16.getCharCount(c); |
|
if ((options & BidiBase.KEEP_BASE_COMBINING) != 0) { |
|
|
|
while(srcLength > 0 && IsCombining(UCharacter.getType(c))) { |
|
c = UTF16.charAt(src, srcLength - 1); |
|
srcLength -= UTF16.getCharCount(c); |
|
} |
|
} |
|
|
|
if ((options & BidiBase.REMOVE_BIDI_CONTROLS) != 0 && |
|
BidiBase.IsBidiControlChar(c)) { |
|
|
|
continue; |
|
} |
|
|
|
|
|
int j = srcLength; |
|
if((options & BidiBase.DO_MIRRORING) != 0) { |
|
|
|
c = UCharacter.getMirror(c); |
|
UTF16.append(dest, c); |
|
j += UTF16.getCharCount(c); |
|
} |
|
dest.append(src.substring(j, i)); |
|
} while(srcLength > 0); |
|
break; |
|
} /* end of switch */ |
|
|
|
return dest.toString(); |
|
} |
|
|
|
static String doWriteReverse(char[] text, int start, int limit, int options) { |
|
return writeReverse(new String(text, start, limit - start), options); |
|
} |
|
|
|
static String writeReordered(BidiBase bidi, int options) { |
|
int run, runCount; |
|
StringBuilder dest; |
|
char[] text = bidi.text; |
|
runCount = bidi.countRuns(); |
|
|
|
|
|
|
|
|
|
*/ |
|
if ((bidi.reorderingOptions & BidiBase.OPTION_INSERT_MARKS) != 0) { |
|
options |= BidiBase.INSERT_LRM_FOR_NUMERIC; |
|
options &= ~BidiBase.REMOVE_BIDI_CONTROLS; |
|
} |
|
|
|
|
|
|
|
*/ |
|
if ((bidi.reorderingOptions & BidiBase.OPTION_REMOVE_CONTROLS) != 0) { |
|
options |= BidiBase.REMOVE_BIDI_CONTROLS; |
|
options &= ~BidiBase.INSERT_LRM_FOR_NUMERIC; |
|
} |
|
|
|
|
|
|
|
*/ |
|
if ((bidi.reorderingMode != BidiBase.REORDER_INVERSE_NUMBERS_AS_L) && |
|
(bidi.reorderingMode != BidiBase.REORDER_INVERSE_LIKE_DIRECT) && |
|
(bidi.reorderingMode != BidiBase.REORDER_INVERSE_FOR_NUMBERS_SPECIAL) && |
|
(bidi.reorderingMode != BidiBase.REORDER_RUNS_ONLY)) { |
|
options &= ~BidiBase.INSERT_LRM_FOR_NUMERIC; |
|
} |
|
dest = new StringBuilder((options & BidiBase.INSERT_LRM_FOR_NUMERIC) != 0 ? |
|
bidi.length * 2 : bidi.length); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if ((options & BidiBase.OUTPUT_REVERSE) == 0) { |
|
|
|
if ((options & BidiBase.INSERT_LRM_FOR_NUMERIC) == 0) { |
|
|
|
for (run = 0; run < runCount; ++run) { |
|
BidiRun bidiRun = bidi.getVisualRun(run); |
|
if (bidiRun.isEvenRun()) { |
|
dest.append(doWriteForward(text, bidiRun.start, |
|
bidiRun.limit, |
|
options & ~BidiBase.DO_MIRRORING)); |
|
} else { |
|
dest.append(doWriteReverse(text, bidiRun.start, |
|
bidiRun.limit, options)); |
|
} |
|
} |
|
} else { |
|
|
|
byte[] dirProps = bidi.dirProps; |
|
char uc; |
|
int markFlag; |
|
|
|
for (run = 0; run < runCount; ++run) { |
|
BidiRun bidiRun = bidi.getVisualRun(run); |
|
markFlag=0; |
|
|
|
markFlag = bidi.runs[run].insertRemove; |
|
if (markFlag < 0) { |
|
markFlag = 0; |
|
} |
|
if (bidiRun.isEvenRun()) { |
|
if (bidi.isInverse() && |
|
dirProps[bidiRun.start] != BidiBase.L) { |
|
markFlag |= BidiBase.LRM_BEFORE; |
|
} |
|
if ((markFlag & BidiBase.LRM_BEFORE) != 0) { |
|
uc = LRM_CHAR; |
|
} else if ((markFlag & BidiBase.RLM_BEFORE) != 0) { |
|
uc = RLM_CHAR; |
|
} else { |
|
uc = 0; |
|
} |
|
if (uc != 0) { |
|
dest.append(uc); |
|
} |
|
dest.append(doWriteForward(text, |
|
bidiRun.start, bidiRun.limit, |
|
options & ~BidiBase.DO_MIRRORING)); |
|
|
|
if (bidi.isInverse() && |
|
dirProps[bidiRun.limit - 1] != BidiBase.L) { |
|
markFlag |= BidiBase.LRM_AFTER; |
|
} |
|
if ((markFlag & BidiBase.LRM_AFTER) != 0) { |
|
uc = LRM_CHAR; |
|
} else if ((markFlag & BidiBase.RLM_AFTER) != 0) { |
|
uc = RLM_CHAR; |
|
} else { |
|
uc = 0; |
|
} |
|
if (uc != 0) { |
|
dest.append(uc); |
|
} |
|
} else { |
|
if (bidi.isInverse() && |
|
!bidi.testDirPropFlagAt(MASK_R_AL, |
|
bidiRun.limit - 1)) { |
|
markFlag |= BidiBase.RLM_BEFORE; |
|
} |
|
if ((markFlag & BidiBase.LRM_BEFORE) != 0) { |
|
uc = LRM_CHAR; |
|
} else if ((markFlag & BidiBase.RLM_BEFORE) != 0) { |
|
uc = RLM_CHAR; |
|
} else { |
|
uc = 0; |
|
} |
|
if (uc != 0) { |
|
dest.append(uc); |
|
} |
|
dest.append(doWriteReverse(text, bidiRun.start, |
|
bidiRun.limit, options)); |
|
|
|
if(bidi.isInverse() && |
|
(MASK_R_AL & BidiBase.DirPropFlag(dirProps[bidiRun.start])) == 0) { |
|
markFlag |= BidiBase.RLM_AFTER; |
|
} |
|
if ((markFlag & BidiBase.LRM_AFTER) != 0) { |
|
uc = LRM_CHAR; |
|
} else if ((markFlag & BidiBase.RLM_AFTER) != 0) { |
|
uc = RLM_CHAR; |
|
} else { |
|
uc = 0; |
|
} |
|
if (uc != 0) { |
|
dest.append(uc); |
|
} |
|
} |
|
} |
|
} |
|
} else { |
|
|
|
if((options & BidiBase.INSERT_LRM_FOR_NUMERIC) == 0) { |
|
|
|
for(run = runCount; --run >= 0; ) { |
|
BidiRun bidiRun = bidi.getVisualRun(run); |
|
if (bidiRun.isEvenRun()) { |
|
dest.append(doWriteReverse(text, |
|
bidiRun.start, bidiRun.limit, |
|
options & ~BidiBase.DO_MIRRORING)); |
|
} else { |
|
dest.append(doWriteForward(text, bidiRun.start, |
|
bidiRun.limit, options)); |
|
} |
|
} |
|
} else { |
|
/* insert Bidi controls for "inverse Bidi" */ |
|
|
|
byte[] dirProps = bidi.dirProps; |
|
|
|
for (run = runCount; --run >= 0; ) { |
|
|
|
BidiRun bidiRun = bidi.getVisualRun(run); |
|
if (bidiRun.isEvenRun()) { |
|
if (dirProps[bidiRun.limit - 1] != BidiBase.L) { |
|
dest.append(LRM_CHAR); |
|
} |
|
|
|
dest.append(doWriteReverse(text, bidiRun.start, |
|
bidiRun.limit, options & ~BidiBase.DO_MIRRORING)); |
|
|
|
if (dirProps[bidiRun.start] != BidiBase.L) { |
|
dest.append(LRM_CHAR); |
|
} |
|
} else { |
|
if ((MASK_R_AL & BidiBase.DirPropFlag(dirProps[bidiRun.start])) == 0) { |
|
dest.append(RLM_CHAR); |
|
} |
|
|
|
dest.append(doWriteForward(text, bidiRun.start, |
|
bidiRun.limit, options)); |
|
|
|
if ((MASK_R_AL & BidiBase.DirPropFlag(dirProps[bidiRun.limit - 1])) == 0) { |
|
dest.append(RLM_CHAR); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
return dest.toString(); |
|
} |
|
} |