blob: d366cbe4180ba29f38ba220931fc81b8c948771e [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2008-2011, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.charset;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.util.Arrays;
import com.ibm.icu.charset.CharsetMBCS.CharsetDecoderMBCS;
import com.ibm.icu.charset.CharsetMBCS.CharsetEncoderMBCS;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.text.UTF16;
import com.ibm.icu.text.UnicodeSet;
class CharsetISO2022 extends CharsetICU {
private UConverterDataISO2022 myConverterData;
private int variant; // one of enum {ISO_2022_JP, ISO_2022_KR, or ISO_2022_CN}
private static final byte[] SHIFT_IN_STR = { 0x0f };
// private static final byte[] SHIFT_OUT_STR = { 0x0e };
private static final byte CR = 0x0D;
private static final byte LF = 0x0A;
/*
private static final byte H_TAB = 0x09;
private static final byte SPACE = 0x20;
*/
private static final char HWKANA_START = 0xff61;
private static final char HWKANA_END = 0xff9f;
/*
* 94-character sets with native byte values A1..FE are encoded in ISO 2022
* as bytes 21..7E. (Subtract 0x80.)
* 96-character sets with native bit values A0..FF are encoded in ISO 2022
* as bytes 20..7F. (Subtract 0x80.)
* Do not encode C1 control codes with native bytes 80..9F
* as bytes 00..1F (C0 control codes).
*/
/*
private static final char GR94_START = 0xa1;
private static final char GR94_END = 0xfe;
*/
private static final char GR96_START = 0xa0;
private static final char GR96_END = 0xff;
/* for ISO-2022-JP and -CN implementations */
// typedef enum {
/* shared values */
private static final byte INVALID_STATE = -1;
private static final byte ASCII = 0;
private static final byte SS2_STATE = 0x10;
private static final byte SS3_STATE = 0x11;
/* JP */
private static final byte ISO8859_1 = 1;
private static final byte ISO8859_7 = 2;
private static final byte JISX201 = 3;
private static final byte JISX208 = 4;
private static final byte JISX212 = 5;
private static final byte GB2312 = 6;
private static final byte KSC5601 = 7;
private static final byte HWKANA_7BIT = 8; /* Halfwidth Katakana 7 bit */
/* CN */
/* the first few enum constants must keep their values because they corresponds to myConverterArray[] */
private static final byte GB2312_1 = 1;
private static final byte ISO_IR_165= 2;
private static final byte CNS_11643 = 3;
/*
* these are used in StateEnum and ISO2022State variables,
* but CNS_11643 must be used to index into myConverterArray[]
*/
private static final byte CNS_11643_0 = 0x20;
private static final byte CNS_11643_1 = 0x21;
private static final byte CNS_11643_2 = 0x22;
private static final byte CNS_11643_3 = 0x23;
private static final byte CNS_11643_4 = 0x24;
private static final byte CNS_11643_5 = 0x25;
private static final byte CNS_11643_6 = 0x26;
private static final byte CNS_11643_7 = 0x27;
// } StateEnum;
public CharsetISO2022(String icuCanonicalName, String javaCanonicalName, String[] aliases) {
super(icuCanonicalName, javaCanonicalName, aliases);
myConverterData = new UConverterDataISO2022();
int versionIndex = icuCanonicalName.indexOf("version=");
int version = Integer.decode(icuCanonicalName.substring(versionIndex+8, versionIndex+9)).intValue();
myConverterData.version = version;
if (icuCanonicalName.indexOf("locale=ja") > 0) {
ISO2022InitJP(version);
} else if (icuCanonicalName.indexOf("locale=zh") > 0) {
ISO2022InitCN(version);
} else /* if (icuCanonicalName.indexOf("locale=ko") > 0) */ {
ISO2022InitKR(version);
}
myConverterData.currentEncoder = (CharsetEncoderMBCS)myConverterData.currentConverter.newEncoder();
myConverterData.currentDecoder = (CharsetDecoderMBCS)myConverterData.currentConverter.newDecoder();
}
private void ISO2022InitJP(int version) {
variant = ISO_2022_JP;
maxBytesPerChar = 6;
minBytesPerChar = 1;
maxCharsPerByte = 1;
// open the required converters and cache them
if((jpCharsetMasks[version]&CSM(ISO8859_7)) != 0) {
myConverterData.myConverterArray[ISO8859_7] = ((CharsetMBCS)CharsetICU.forNameICU("ISO8859_7")).sharedData;
}
// myConverterData.myConverterArray[JISX201] = ((CharsetMBCS)CharsetICU.forNameICU("jisx-201")).sharedData;
myConverterData.myConverterArray[JISX208] = ((CharsetMBCS)CharsetICU.forNameICU("Shift-JIS")).sharedData;
if ((jpCharsetMasks[version]&CSM(JISX212)) != 0) {
myConverterData.myConverterArray[JISX212] = ((CharsetMBCS)CharsetICU.forNameICU("jisx-212")).sharedData;
}
if ((jpCharsetMasks[version]&CSM(GB2312)) != 0) {
myConverterData.myConverterArray[GB2312] = ((CharsetMBCS)CharsetICU.forNameICU("ibm-5478")).sharedData;
}
if ((jpCharsetMasks[version]&CSM(KSC5601)) != 0) {
myConverterData.myConverterArray[KSC5601] = ((CharsetMBCS)CharsetICU.forNameICU("ksc_5601")).sharedData;
}
// create a generic CharsetMBCS object
myConverterData.currentConverter = (CharsetMBCS)CharsetICU.forNameICU("icu-internal-25546");
}
private void ISO2022InitCN(int version) {
variant = ISO_2022_CN;
maxBytesPerChar = 8;
minBytesPerChar = 1;
maxCharsPerByte = 1;
// open the required coverters and cache them.
myConverterData.myConverterArray[GB2312_1] = ((CharsetMBCS)CharsetICU.forNameICU("ibm-5478")).sharedData;
if (version == 1) {
myConverterData.myConverterArray[ISO_IR_165] = ((CharsetMBCS)CharsetICU.forNameICU("iso-ir-165")).sharedData;
}
myConverterData.myConverterArray[CNS_11643] = ((CharsetMBCS)CharsetICU.forNameICU("cns-11643-1992")).sharedData;
// create a generic CharsetMBCS object
myConverterData.currentConverter = (CharsetMBCS)CharsetICU.forNameICU("icu-internal-25546");
}
private void ISO2022InitKR(int version) {
variant = ISO_2022_KR;
maxBytesPerChar = 3;
minBytesPerChar = 1;
maxCharsPerByte = 1;
if (version == 1) {
myConverterData.currentConverter = (CharsetMBCS)CharsetICU.forNameICU("icu-internal-25546");
myConverterData.currentConverter.subChar1 = fromUSubstitutionChar[0][0];
} else {
myConverterData.currentConverter = (CharsetMBCS)CharsetICU.forNameICU("ibm-949");
}
myConverterData.currentEncoder = (CharsetEncoderMBCS)myConverterData.currentConverter.newEncoder();
myConverterData.currentDecoder = (CharsetDecoderMBCS)myConverterData.currentConverter.newDecoder();
}
/*
* ISO 2022 control codes must not be converted from Unicode
* because they would mess up the byte stream.
* The bit mask 0x0800c000 has bits set at bit positions 0xe, 0xf, 0x1b
* corresponding to SO, SI, and ESC.
*/
private static boolean IS_2022_CONTROL(int c) {
return (c<0x20) && (((1<<c) & 0x0800c000) != 0);
}
/*
* Check that the result is a 2-byte value with each byte in the range A1..FE
* (strict EUC DBCS) before accepting it and subtracting 0x80 from each byte
* to move it to the ISO 2022 range 21..7E.
* return 0 if out of range.
*/
private static int _2022FromGR94DBCS(int value) {
if ((value <= 0xfefe && value >= 0xa1a1) &&
((short)(value&UConverterConstants.UNSIGNED_BYTE_MASK) <= 0xfe && ((short)(value&UConverterConstants.UNSIGNED_BYTE_MASK) >= 0xa1))) {
return (value - 0x8080); /* shift down to 21..7e byte range */
} else {
return 0; /* not valid for ISO 2022 */
}
}
/*
* Commented out because Ticket 5691: Call sites now check for validity. They can just += 0x8080 after that.
*
* This method does the reverse of _2022FromGR94DBCS(). Given the 2022 code point, it returns the
* 2 byte value that is in the range A1..FE for each byte. Otherwise it returns the 2022 code point
* unchanged.
*
private static int _2022ToGR94DBCS(int value) {
int returnValue = value + 0x8080;
if ((returnValue <= 0xfefe && returnValue >= 0xa1a1) &&
((short)(returnValue&UConverterConstants.UNSIGNED_BYTE_MASK) <= 0xfe && ((short)(returnValue&UConverterConstants.UNSIGNED_BYTE_MASK) >= 0xa1))) {
return returnValue;
} else {
return value;
}
}*/
/* is the StateEnum charset value for a DBCS charset? */
private static boolean IS_JP_DBCS(byte cs) {
return ((JISX208 <= cs) && (cs <= KSC5601));
}
private static short CSM(short cs) {
return (short)(1<<cs);
}
/* This gets the valid index of the end of buffer when decoding. */
private static int getEndOfBuffer_2022(ByteBuffer source) {
int sourceIndex = source.position();
byte mySource = 0;
mySource = source.get(sourceIndex);
while (source.hasRemaining() && mySource != ESC_2022) {
mySource = source.get();
if (mySource == ESC_2022) {
break;
}
sourceIndex++;
}
return sourceIndex;
}
/*
* This is a simple version of _MBCSGetNextUChar() calls the method in CharsetDecoderMBCS and returns
* the value given.
*
* Return value:
* U+fffe unassigned
* U+ffff illegal
* otherwise the Unicode code point
*/
private int MBCSSimpleGetNextUChar(UConverterSharedData sharedData,
ByteBuffer source,
boolean useFallback) {
int returnValue;
UConverterSharedData tempSharedData = myConverterData.currentConverter.sharedData;
myConverterData.currentConverter.sharedData = sharedData;
returnValue = myConverterData.currentDecoder.simpleGetNextUChar(source, useFallback);
myConverterData.currentConverter.sharedData = tempSharedData;
return returnValue;
}
/*
* @param is the the output byte
* @return 1 roundtrip byte 0 no mapping -1 fallback byte
*/
static int MBCSSingleFromUChar32(UConverterSharedData sharedData, int c, int[] retval, boolean useFallback) {
char[] table;
int value;
/* BMP-only codepages are stored without stage 1 entries for supplementary code points */
if (c >= 0x10000 && (sharedData.mbcs.unicodeMask&UConverterConstants.HAS_SUPPLEMENTARY) == 0) {
return 0;
}
/* convert the Unicode code point in c into codepage bytes */
table = sharedData.mbcs.fromUnicodeTable;
/* get the byte for the output */
value = CharsetMBCS.MBCS_SINGLE_RESULT_FROM_U(table, sharedData.mbcs.fromUnicodeBytes, c);
/* get the byte for the output */
retval[0] = value & 0xff;
if (value >= 0xf00) {
return 1; /* roundtrip */
} else if (useFallback ? value>=0x800 : value>=0xc00) {
return -1; /* fallback taken */
} else {
return 0; /* no mapping */
}
}
/*
* Each of these charset masks (with index x) contains a bit for a charset in exact correspondence
* to whether that charset is used in the corresponding version x of ISO_2022, locale=ja,version=x
*
* Note: The converter uses some leniency:
* - The escape sequence ESC ( I for half-width 7-bit Katakana is recognized in
* all versions, not just JIS7 and JIS8.
* - ICU does not distinguish between different version so of JIS X 0208.
*/
private static final short jpCharsetMasks[] = {
(short)(CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)),
(short)(CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212)),
(short)(CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212)|CSM(GB2312)|CSM(KSC5601)|CSM(ISO8859_1)|CSM(ISO8859_7)),
(short)(CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212)|CSM(GB2312)|CSM(KSC5601)|CSM(ISO8859_1)|CSM(ISO8859_7)),
(short)(CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212)|CSM(GB2312)|CSM(KSC5601)|CSM(ISO8859_1)|CSM(ISO8859_7))
};
/*
// typedef enum {
private static final byte ASCII1 = 0;
private static final byte LATIN1 = 1;
private static final byte SBCS = 2;
private static final byte DBCS = 3;
private static final byte MBCS = 4;
private static final byte HWKANA = 5;
// } Cnv2002Type;
*/
private static class ISO2022State {
private byte []cs; /* Charset number for SI (G0)/SO (G1)/SS2 (G2)/SS3 (G3) */
private byte g; /* 0..3 for G0..G3 (SI/SO/SS2/SS3) */
private byte prevG; /* g before single shift (SS2 or SS3) */
ISO2022State() {
cs = new byte[4];
}
void reset() {
Arrays.fill(cs, (byte)0);
g = 0;
prevG = 0;
}
}
// private static final byte UCNV_OPTIONS_VERSION_MASK = 0xf;
private static final byte UCNV_2022_MAX_CONVERTERS = 10;
@SuppressWarnings("unused")
private static class UConverterDataISO2022 {
UConverterSharedData []myConverterArray;
CharsetEncoderMBCS currentEncoder;
CharsetDecoderMBCS currentDecoder;
CharsetMBCS currentConverter;
int currentType; // Cnv2022Type;
ISO2022State toU2022State;
ISO2022State fromU2022State;
int key;
int version;
boolean isEmptySegment;
UConverterDataISO2022() {
myConverterArray = new UConverterSharedData[UCNV_2022_MAX_CONVERTERS];
toU2022State = new ISO2022State();
fromU2022State = new ISO2022State();
currentType = 0;
key = 0;
version = 0;
isEmptySegment = false;
}
void reset() {
toU2022State.reset();
fromU2022State.reset();
isEmptySegment = false;
}
}
private static final byte ESC_2022 = 0x1B; /* ESC */
// typedef enum {
private static final byte INVALID_2022 = -1; /* Doesn't correspond to a valid iso 2022 escape sequence */
private static final byte VALID_NON_TERMINAL_2022 = 0; /* so far corresponds to a valid iso 2022 escape sequence */
private static final byte VALID_TERMINAL_2022 = 1; /* corresponds to a valid iso 2022 escape sequence */
private static final byte VALID_MAYBE_TERMINAL_2022 = 2; /* so far matches one iso 2022 escape sequence, but by adding
more characters might match another escape sequence */
// } UCNV_TableStates_2022;
/*
* The way these state transition arrays work is:
* ex : ESC$B is the sequence for JISX208
* a) First Iteration: char is ESC
* i) Get the value of ESC from normalize_esq_chars_2022[] with int value of ESC as index
* int x = normalize_esq_chars_2022[27] which is equal to 1
* ii) Search for this value in escSeqStateTable_Key_2022[]
* value of x is stored at escSeqStateTable_Key_2022[0]
* iii) Save this index as offset
* iv) Get state of this sequence from escSeqStateTable_Value_2022[]
* escSeqStateTable_value_2022[offset], which is VALID_NON_TERMINAL_2022
* b) Switch on this state and continue to next char
* i) Get the value of $ from normalize_esq_chars_2022[] with int value of $ as index
* which is normalize_esq_chars_2022[36] == 4
* ii) x is currently 1(from above)
* x<<=5 -- x is now 32
* x+=normalize_esq_chars_2022[36]
* now x is 36
* iii) Search for this value in escSeqStateTable_Key_2022[]
* value of x is stored at escSeqStateTable_Key_2022[2], so offset is 2
* iv) Get state of this sequence from escSeqStateTable_Value_2022[]
* escSeqStateTable_Value_2022[offset], which is VALID_NON_TERMINAL_2022
* c) Switch on this state and continue to next char
* i) Get the value of B from normalize_esq_chars_2022[] with int value of B as index
* ii) x is currently 36 (from above)
* x<<=5 -- x is now 1152
* x+= normalize_esq_chars_2022[66]
* now x is 1161
* iii) Search for this value in escSeqStateTable_Key_2022[]
* value of x is stored at escSeqStateTable_Key_2022[21], so offset is 21
* iv) Get state of this sequence from escSeqStateTable_Value_2022[1]
* escSeqStateTable_Value_2022[offset], which is VALID_TERMINAL_2022
* v) Get the converter name from escSeqStateTable_Result_2022[21] which is JISX208
*/
/* Below are the 3 arrays depicting a state transition table */
private static final byte normalize_esq_chars_2022[] = {
/* 0 1 2 3 4 5 6 7 8 9 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 4, 7, 29, 0,
2, 24, 26, 27, 0, 3, 23, 6, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 5, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 25, 28,
0, 0, 21, 0, 0, 0, 0, 0, 0, 0,
22, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0
};
private static final short MAX_STATES_2022 = 74;
private static final int escSeqStateTable_Key_2022[/* MAX_STATES_2022 */] = {
/* 0 1 2 3 4 5 6 7 8 9 */
1, 34, 36, 39, 55, 57, 60, 61, 1093, 1096,
1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106,
1109, 1154, 1157, 1160, 1161, 1176, 1178, 1179, 1254, 1257,
1768, 1773, 1957, 35105, 36933, 36936, 36937, 36938, 36939, 36940,
36942, 36943, 36944, 36945, 36946, 36947, 36948, 37640, 37642, 37644,
37646, 37711, 37744, 37745, 37746, 37747, 37748, 40133, 40136, 40138,
40139, 40140, 40141, 1123363, 35947624, 35947625, 35947626, 35947627, 35947629, 35947630,
35947631, 35947635, 35947636, 35947638
};
private static final byte escSeqStateTable_Value_2022[/* MAX_STATES_2022 */] = {
/* 0 1 2 3 4 */
VALID_NON_TERMINAL_2022, VALID_NON_TERMINAL_2022, VALID_NON_TERMINAL_2022, VALID_NON_TERMINAL_2022, VALID_NON_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_NON_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_MAYBE_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_NON_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_NON_TERMINAL_2022, VALID_NON_TERMINAL_2022, VALID_NON_TERMINAL_2022, VALID_NON_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_NON_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_NON_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022,
VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022, VALID_TERMINAL_2022
};
/* Type def for refactoring changeState_2022 code */
// typedef enum {
private static final byte ISO_2022_JP = 1;
private static final byte ISO_2022_KR = 2;
private static final byte ISO_2022_CN = 3;
// } Variant2022;
/* const UConverterSharedData _ISO2022Data; */
//private UConverterSharedData _ISO2022JPData;
//private UConverterSharedData _ISO2022KRData;
//private UConverterSharedData _ISO2022CNData;
/******************** to unicode ********************/
/****************************************************
* Recognized escape sequenes are
* <ESC>(B ASCII
* <ESC>.A ISO-8859-1
* <ESC>.F ISO-8859-7
* <ESC>(J JISX-201
* <ESC>(I JISX-201
* <ESC>$B JISX-208
* <ESC>$@ JISX-208
* <ESC>$(D JISX-212
* <ESC>$A GB2312
* <ESC>$(C KSC5601
*/
private final static byte nextStateToUnicodeJP[/* MAX_STATES_2022 */] = {
/* 0 1 2 3 4 5 6 7 8 9 */
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, SS2_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE,
ASCII, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, JISX201, HWKANA_7BIT, JISX201, INVALID_STATE,
INVALID_STATE, INVALID_STATE, JISX208, GB2312, JISX208, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE,
ISO8859_1, ISO8859_7, JISX208, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, KSC5601, JISX212, INVALID_STATE,
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE,
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE,
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE,
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE
};
private final static byte nextStateToUnicodeCN[/* MAX_STATES_2022 */] = {
/* 0 1 2 3 4 5 6 7 8 9 */
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, SS2_STATE, SS3_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE,
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE,
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE,
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE,
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, GB2312_1, INVALID_STATE, ISO_IR_165,
CNS_11643_1, CNS_11643_2, CNS_11643_3, CNS_11643_4, CNS_11643_5, CNS_11643_6, CNS_11643_7, INVALID_STATE, INVALID_STATE, INVALID_STATE,
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE,
INVALID_STATE, INVALID_STATE, INVALID_STATE, INVALID_STATE
};
/* runs through a state machine to determine the escape sequence - codepage correspondence */
@SuppressWarnings("fallthrough")
private CoderResult changeState_2022(CharsetDecoderICU decoder, ByteBuffer source, int var) {
CoderResult err = CoderResult.UNDERFLOW;
boolean DONE = false;
byte value;
int key[] = {myConverterData.key};
int offset[] = {0};
int initialToULength = decoder.toULength;
byte c;
int malformLength = 0;
value = VALID_NON_TERMINAL_2022;
while (source.hasRemaining()) {
c = source.get();
malformLength++;
decoder.toUBytesArray[decoder.toULength++] = c;
value = getKey_2022(c, key, offset);
switch(value) {
case VALID_NON_TERMINAL_2022:
/* continue with the loop */
break;
case VALID_TERMINAL_2022:
key[0] = 0;
DONE = true;
break;
case INVALID_2022:
DONE = true;
break;
case VALID_MAYBE_TERMINAL_2022:
/* not ISO_2022 itself, finish here */
value = VALID_TERMINAL_2022;
key[0] = 0;
DONE = true;
break;
}
if (DONE) {
break;
}
}
// DONE:
myConverterData.key = key[0];
if (value == VALID_NON_TERMINAL_2022) {
/* indicate that the escape sequence is incomplete: key !=0 */
return err;
} else if (value == INVALID_2022) {
err = CoderResult.malformedForLength(malformLength);
} else /* value == VALID_TERMINAL_2022 */ {
switch (var) {
case ISO_2022_JP: {
byte tempState = nextStateToUnicodeJP[offset[0]];
switch (tempState) {
case INVALID_STATE:
err = CoderResult.malformedForLength(malformLength);
break;
case SS2_STATE:
if (myConverterData.toU2022State.cs[2] != 0) {
if (myConverterData.toU2022State.g < 2) {
myConverterData.toU2022State.prevG = myConverterData.toU2022State.g;
}
myConverterData.toU2022State.g = 2;
} else {
/* illegal to have SS2 before a matching designator */
err = CoderResult.malformedForLength(malformLength);
}
break;
/* case SS3_STATE: not used in ISO-2022-JP-x */
case ISO8859_1:
case ISO8859_7:
if ((jpCharsetMasks[myConverterData.version] & CSM(tempState)) == 0) {
err = CoderResult.unmappableForLength(malformLength);
} else {
/* G2 charset for SS2 */
myConverterData.toU2022State.cs[2] = tempState;
}
break;
default:
if ((jpCharsetMasks[myConverterData.version] & CSM(tempState)) == 0) {
err = CoderResult.unmappableForLength(source.position() - 1);
} else {
/* G0 charset */
myConverterData.toU2022State.cs[0] = tempState;
}
break;
} // end of switch
break;
}
case ISO_2022_CN: {
byte tempState = nextStateToUnicodeCN[offset[0]];
switch (tempState) {
case INVALID_STATE:
err = CoderResult.unmappableForLength(malformLength);
break;
case SS2_STATE:
if (myConverterData.toU2022State.cs[2] != 0) {
if (myConverterData.toU2022State.g < 2) {
myConverterData.toU2022State.prevG = myConverterData.toU2022State.g;
}
myConverterData.toU2022State.g = 2;
} else {
/* illegal to have SS2 before a matching designator */
err = CoderResult.malformedForLength(malformLength);
}
break;
case SS3_STATE:
if (myConverterData.toU2022State.cs[3] != 0) {
if (myConverterData.toU2022State.g < 2) {
myConverterData.toU2022State.prevG = myConverterData.toU2022State.g;
}
myConverterData.toU2022State.g = 3;
} else {
/* illegal to have SS3 before a matching designator */
err = CoderResult.malformedForLength(malformLength);
}
break;
case ISO_IR_165:
if (myConverterData.version == 0) {
err = CoderResult.unmappableForLength(malformLength);
break;
}
/* fall through */
case GB2312_1:
/* fall through */
case CNS_11643_1:
myConverterData.toU2022State.cs[1] = tempState;
break;
case CNS_11643_2:
myConverterData.toU2022State.cs[2] = tempState;
break;
default:
/* other CNS 11643 planes */
if (myConverterData.version == 0) {
err = CoderResult.unmappableForLength(source.position() - 1);
} else {
myConverterData.toU2022State.cs[3] = tempState;
}
break;
} //end of switch
}
break;
case ISO_2022_KR:
if (offset[0] == 0x30) {
/* nothing to be done, just accept this one escape sequence */
} else {
err = CoderResult.unmappableForLength(malformLength);
}
break;
default:
err = CoderResult.malformedForLength(malformLength);
break;
} // end of switch
}
if (!err.isError()) {
decoder.toULength = 0;
} else if (err.isMalformed()) {
if (decoder.toULength > 1) {
/*
* Ticket 5691: consistent illegal sequences:
* - We include at least the first byte (ESC) in the illegal sequence.
* - If any of the non-initial bytes could be the start of a character,
* we stop the illegal sequece before the first one of those.
* In escape sequences, all following bytes are "printable", that is,
* unless they are completely illegal (>7f in SBCS, outside 21..7e in DBCS),
* they are valid single/lead bytes.
* For simplicity, we always only report the initial ESC byte as the
* illegal sequence and back out all other bytes we looked at.
*/
/* Back out some bytes. */
int backOutDistance = decoder.toULength - 1;
int bytesFromThisBuffer = decoder.toULength - initialToULength;
if (backOutDistance <= bytesFromThisBuffer) {
/* same as initialToULength<=1 */
source.position(source.position() - backOutDistance);
} else {
/* Back out bytes from the previous buffer: Need to replay them. */
decoder.preToULength = (byte)(bytesFromThisBuffer - backOutDistance);
/* same as -(initalToULength-1) */
/* preToULength is negative! */
for (int i = 0; i < -(decoder.preToULength); i++) {
decoder.preToUArray[i] = decoder.toUBytesArray[i+1];
}
source.position(source.position() - bytesFromThisBuffer);
}
decoder.toULength = 1;
}
}
return err;
}
private static byte getKey_2022(byte c, int[]key, int[]offset) {
int togo;
int low = 0;
int hi = MAX_STATES_2022;
int oldmid = 0;
togo = normalize_esq_chars_2022[(short)c&UConverterConstants.UNSIGNED_BYTE_MASK];
if (togo == 0) {
/* not a valid character anywhere in an escape sequence */
key[0] = 0;
offset[0] = 0;
return INVALID_2022;
}
togo = (key[0] << 5) + togo;
while (hi != low) { /* binary search */
int mid = (hi+low) >> 1; /* Finds median */
if (mid == oldmid) {
break;
}
if (escSeqStateTable_Key_2022[mid] > togo) {
hi = mid;
} else if (escSeqStateTable_Key_2022[mid] < togo) {
low = mid;
} else /* we found it */ {
key[0] = togo;
offset[0] = mid;
return escSeqStateTable_Value_2022[mid];
}
oldmid = mid;
}
return INVALID_2022;
}
/*
* To Unicode Callback helper function
*/
private static CoderResult toUnicodeCallback(CharsetDecoderICU cnv, int sourceChar, int targetUniChar) {
CoderResult err = CoderResult.UNDERFLOW;
if (sourceChar > 0xff) {
cnv.toUBytesArray[0] = (byte)(sourceChar>>8);
cnv.toUBytesArray[1] = (byte)sourceChar;
cnv.toULength = 2;
} else {
cnv.toUBytesArray[0] = (byte)sourceChar;
cnv.toULength = 1;
}
if (targetUniChar == (UConverterConstants.missingCharMarker-1/* 0xfffe */)) {
err = CoderResult.unmappableForLength(1);
} else {
err = CoderResult.malformedForLength(1);
}
return err;
}
/****************************ISO-2022-JP************************************/
private class CharsetDecoderISO2022JP extends CharsetDecoderICU {
public CharsetDecoderISO2022JP(CharsetICU cs) {
super(cs);
}
protected void implReset() {
super.implReset();
myConverterData.reset();
}
/*
* Map 00..7F to Unicode according to JIS X 0201.
* */
private int jisx201ToU(int value) {
if (value < 0x5c) {
return value;
} else if (value == 0x5c) {
return 0xa5;
} else if (value == 0x7e) {
return 0x203e;
} else { /* value <= 0x7f */
return value;
}
}
/*
* Convert a pair of JIS X 208 21..7E bytes to Shift-JIS.
* If either byte is outside 21..7E make sure that the result is not valid
* for Shift-JIS so that the converter catches it.
* Some invalid byte values already turn into equally invalid Shift-JIS
* byte values and need not be tested explicitly.
*/
private void _2022ToSJIS(char c1, char c2, byte []bytes) {
if ((c1&1) > 0) {
++c1;
if (c2 <= 0x5f) {
c2 += 0x1f;
} else if (c2 <= 0x7e) {
c2 += 0x20;
} else {
c2 = 0; /* invalid */
}
} else {
if ((c2 >= 0x21) && (c2 <= 0x7e)) {
c2 += 0x7e;
} else {
c2 = 0; /* invalid */
}
}
c1 >>=1;
if (c1 <= 0x2f) {
c1 += 0x70;
} else if (c1 <= 0x3f) {
c1 += 0xb0;
} else {
c1 = 0; /* invalid */
}
bytes[0] = (byte)(UConverterConstants.UNSIGNED_BYTE_MASK & c1);
bytes[1] = (byte)(UConverterConstants.UNSIGNED_BYTE_MASK & c2);
}
@SuppressWarnings("fallthrough")
protected CoderResult decodeLoop(ByteBuffer source, CharBuffer target, IntBuffer offsets, boolean flush) {
boolean gotoGetTrail = false;
boolean gotoEscape = false;
CoderResult err = CoderResult.UNDERFLOW;
byte []tempBuf = new byte[2];
int targetUniChar = 0x0000;
int mySourceChar = 0x0000;
int mySourceCharTemp = 0x0000; // use for getTrail label call.
byte cs; /* StateEnum */
byte csTemp= 0; // use for getTrail label call.
if (myConverterData.key != 0) {
/* continue with a partial escape sequence */
// goto escape;
gotoEscape = true;
} else if (toULength == 1 && source.hasRemaining() && target.hasRemaining()) {
/* continue with a partial double-byte character */
mySourceChar = (toUBytesArray[0] & UConverterConstants.UNSIGNED_BYTE_MASK);
toULength = 0;
cs = myConverterData.toU2022State.cs[myConverterData.toU2022State.g];
// goto getTrailByte;
mySourceCharTemp = 0x99;
gotoGetTrail = true;
}
while (source.hasRemaining() || gotoEscape || gotoGetTrail) {
// This code is here for the goto escape label call above.
if (gotoEscape) {
mySourceCharTemp = ESC_2022;
}
targetUniChar = UConverterConstants.missingCharMarker;
if (gotoEscape || gotoGetTrail || target.hasRemaining()) {
if (!gotoEscape && !gotoGetTrail) {
mySourceChar = source.get() & UConverterConstants.UNSIGNED_BYTE_MASK;
mySourceCharTemp = mySourceChar;
}
switch (mySourceCharTemp) {
case UConverterConstants.SI:
if (myConverterData.version == 3) {
myConverterData.toU2022State.g = 0;
continue;
} else {
/* only JIS7 uses SI/SO, not ISO-2022-JP-x */
myConverterData.isEmptySegment = false;
break;
}
case UConverterConstants.SO:
if (myConverterData.version == 3) {
/* JIS7: switch to G1 half-width Katakana */
myConverterData.toU2022State.cs[1] = HWKANA_7BIT;
myConverterData.toU2022State.g = 1;
continue;
} else {
/* only JIS7 uses SI/SO, not ISO-2022-JP-x */
myConverterData.isEmptySegment = false; /* reset this, we have a different error */
break;
}
case ESC_2022:
if (!gotoEscape) {
source.position(source.position() - 1);
} else {
gotoEscape = false;
}
// escape:
{
int mySourceBefore = source.position();
int toULengthBefore = this.toULength;
err = changeState_2022(this, source, variant);
/* If in ISO-2022-JP only and we successully completed an escape sequence, but previous segment was empty, create an error */
if(myConverterData.version == 0 && myConverterData.key == 0 && !err.isError() && myConverterData.isEmptySegment) {
err = CoderResult.malformedForLength(source.position() - mySourceBefore);
this.toULength = toULengthBefore + (source.position() - mySourceBefore);
}
}
/* invalid or illegal escape sequence */
if(err.isError()){
myConverterData.isEmptySegment = false; /* Reset to avoid future spurious errors */
return err;
}
/* If we successfully completed an escape sequence, we begin a new segment, empty so far */
if(myConverterData.key == 0) {
myConverterData.isEmptySegment = true;
}
continue;
/* ISO-2022-JP does not use single-byte (C1) SS2 and SS3 */
case CR:
/* falls through */
case LF:
/* automatically reset to single-byte mode */
if (myConverterData.toU2022State.cs[0] != ASCII && myConverterData.toU2022State.cs[0] != JISX201) {
myConverterData.toU2022State.cs[0] = ASCII;
}
myConverterData.toU2022State.cs[2] = 0;
myConverterData.toU2022State.g = 0;
/* falls through */
default :
/* convert one or two bytes */
myConverterData.isEmptySegment = false;
cs = myConverterData.toU2022State.cs[myConverterData.toU2022State.g];
csTemp = cs;
if (gotoGetTrail) {
csTemp = (byte)0x99;
}
if (!gotoGetTrail && ((mySourceChar >= 0xa1) && (mySourceChar <= 0xdf) && myConverterData.version == 4 && !IS_JP_DBCS(cs))) {
/* 8-bit halfwidth katakana in any single-byte mode for JIS8 */
targetUniChar = mySourceChar + (HWKANA_START - 0xa1);
/* return from a single-shift state to the previous one */
if (myConverterData.toU2022State.g >= 2) {
myConverterData.toU2022State.g = myConverterData.toU2022State.prevG;
}
} else {
switch(csTemp) {
case ASCII:
if (mySourceChar <= 0x7f) {
targetUniChar = mySourceChar;
}
break;
case ISO8859_1:
if (mySourceChar <= 0x7f) {
targetUniChar = mySourceChar + 0x80;
}
/* return from a single-shift state to the prevous one */
myConverterData.toU2022State.g = myConverterData.toU2022State.prevG;
break;
case ISO8859_7:
if (mySourceChar <= 0x7f) {
/* convert mySourceChar+0x80 to use a normal 8-bit table */
targetUniChar = CharsetMBCS.MBCS_SINGLE_SIMPLE_GET_NEXT_BMP(myConverterData.myConverterArray[cs].mbcs,
mySourceChar+0x80);
}
/* return from a single-shift state to the previous one */
myConverterData.toU2022State.g = myConverterData.toU2022State.prevG;
break;
case JISX201:
if (mySourceChar <= 0x7f) {
targetUniChar = jisx201ToU(mySourceChar);
}
break;
case HWKANA_7BIT:
if ((mySourceChar >= 0x21) && (mySourceChar <= 0x5f)) {
/* 7-bit halfwidth Katakana */
targetUniChar = mySourceChar + (HWKANA_START - 0x21);
break;
}
default :
/* G0 DBCS */
if (gotoGetTrail || source.hasRemaining()) {
// getTrailByte:
int tmpSourceChar;
gotoGetTrail = false;
short trailByte;
boolean leadIsOk, trailIsOk;
trailByte = (short)(source.get(source.position()) & UConverterConstants.UNSIGNED_BYTE_MASK);
/*
* Ticket 5691: consistent illegal sequences:
* - We include at least the first byte in the illegal sequence.
* - If any of the non-initial bytes could be the start of a character,
* we stop the illegal sequence before the first one of those.
*
* In ISO-2022 DBCS, if the second byte is in the 21..7e range or is
* an ESC/SO/SI, we report only the first byte as the illegal sequence.
* Otherwise we convert or report the pair of bytes.
*/
leadIsOk = (short)(UConverterConstants.UNSIGNED_BYTE_MASK & (mySourceChar - 0x21)) <= (0x7e - 0x21);
trailIsOk = (short)(UConverterConstants.UNSIGNED_BYTE_MASK & (trailByte - 0x21)) <= (0x7e - 0x21);
if (leadIsOk && trailIsOk) {
source.get();
tmpSourceChar = (mySourceChar << 8) | trailByte;
if (cs == JISX208) {
_2022ToSJIS((char)mySourceChar, (char)trailByte, tempBuf);
mySourceChar = tmpSourceChar;
} else {
/* Copy before we modify tmpSourceChar so toUnicodeCallback() sees the correct bytes. */
mySourceChar = tmpSourceChar;
if (cs == KSC5601) {
tmpSourceChar += 0x8080; /* = _2022ToGR94DBCS(tmpSourceChar) */
}
tempBuf[0] = (byte)(UConverterConstants.UNSIGNED_BYTE_MASK & (tmpSourceChar >> 8));
tempBuf[1] = (byte)(UConverterConstants.UNSIGNED_BYTE_MASK & tmpSourceChar);
}
targetUniChar = MBCSSimpleGetNextUChar(myConverterData.myConverterArray[cs], ByteBuffer.wrap(tempBuf), false);
} else if (!(trailIsOk || IS_2022_CONTROL(trailByte))) {
/* report a pair of illegal bytes if the second byte is not a DBCS starter */
source.get();
/* add another bit so that the code below writes 2 bytes in case of error */
mySourceChar = 0x10000 | (mySourceChar << 8) | trailByte;
}
} else {
toUBytesArray[0] = (byte)mySourceChar;
toULength = 1;
// goto endloop
return err;
}
} /* end of inner switch */
}
break;
} /* end of outer switch */
if (targetUniChar < (UConverterConstants.missingCharMarker-1/*0xfffe*/)) {
if (offsets != null) {
offsets.put(target.remaining(), source.remaining() - (mySourceChar <= 0xff ? 1 : 2));
}
target.put((char)targetUniChar);
} else if (targetUniChar > UConverterConstants.missingCharMarker) {
/* disassemble the surrogate pair and write to output */
targetUniChar -= 0x0010000;
target.put((char)(0xd800 + (char)(targetUniChar>>10)));
target.position(target.position()-1);
if (offsets != null) {
offsets.put(target.remaining(), source.remaining() - (mySourceChar <= 0xff ? 1 : 2));
}
target.get();
if (target.hasRemaining()) {
target.put((char)(0xdc00+(char)(targetUniChar&0x3ff)));
target.position(target.position()-1);
if (offsets != null) {
offsets.put(target.remaining(), source.remaining() - (mySourceChar <= 0xff ? 1 : 2));
}
target.get();
} else {
charErrorBufferArray[charErrorBufferLength++] =
(char)(0xdc00+(char)(targetUniChar&0x3ff));
}
} else {
/* Call the callback function */
err = toUnicodeCallback(this, mySourceChar, targetUniChar);
break;
}
} else { /* goes with "if (target.hasRemaining())" way up near the top of the function */
err = CoderResult.OVERFLOW;
break;
}
}
//endloop:
return err;
}
} // end of class CharsetDecoderISO2022JP
/****************************ISO-2022-CN************************************/
private class CharsetDecoderISO2022CN extends CharsetDecoderICU {
public CharsetDecoderISO2022CN(CharsetICU cs) {
super(cs);
}
protected void implReset() {
super.implReset();
myConverterData.reset();
}
@SuppressWarnings("fallthrough")
protected CoderResult decodeLoop(ByteBuffer source, CharBuffer target, IntBuffer offsets, boolean flush) {
CoderResult err = CoderResult.UNDERFLOW;
byte[] tempBuf = new byte[3];
int targetUniChar = 0x0000;
int mySourceChar = 0x0000;
int mySourceCharTemp = 0x0000;
boolean gotoEscape = false;
boolean gotoGetTrailByte = false;
if (myConverterData.key != 0) {
/* continue with a partial escape sequence */
// goto escape;
gotoEscape = true;
} else if (toULength == 1 && source.hasRemaining() && target.hasRemaining()) {
/* continue with a partial double-byte character */
mySourceChar = (toUBytesArray[0] & UConverterConstants.UNSIGNED_BYTE_MASK);
toULength = 0;
targetUniChar = UConverterConstants.missingCharMarker;
// goto getTrailByte
gotoGetTrailByte = true;
}
while (source.hasRemaining() || gotoGetTrailByte || gotoEscape) {
targetUniChar = UConverterConstants.missingCharMarker;
if (target.hasRemaining() || gotoEscape) {
if (gotoEscape) {
mySourceChar = ESC_2022; // goto escape label
mySourceCharTemp = mySourceChar;
} else if (gotoGetTrailByte) {
mySourceCharTemp = 0xff; // goto getTrailByte; set mySourceCharTemp to go to default
} else {
mySourceChar = UConverterConstants.UNSIGNED_BYTE_MASK & source.get();
mySourceCharTemp = mySourceChar;
}
switch (mySourceCharTemp) {
case UConverterConstants.SI:
myConverterData.toU2022State.g = 0;
if (myConverterData.isEmptySegment) {
myConverterData.isEmptySegment = false; /* we are handling it, reset to avoid future spurious errors */
err = CoderResult.malformedForLength(1);
this.toUBytesArray[0] = (byte)mySourceChar;
this.toULength = 1;
return err;
}
continue;
case UConverterConstants.SO:
if (myConverterData.toU2022State.cs[1] != 0) {
myConverterData.toU2022State.g = 1;
myConverterData.isEmptySegment = true; /* Begin a new segment, empty so far */
continue;
} else {
/* illegal to have SO before a matching designator */
myConverterData.isEmptySegment = false; /* Handling a different error, reset this to avoid future spurious errs */
break;
}
case ESC_2022:
if (!gotoEscape) {
source.position(source.position()-1);
}
// escape label
gotoEscape = false;
{
int mySourceBefore = source.position();
int toULengthBefore = this.toULength;
err = changeState_2022(this, source, ISO_2022_CN);
/* After SO there must be at least one character before a designator (designator error handled separately) */
if(myConverterData.key == 0 && !err.isError() && myConverterData.isEmptySegment) {
err = CoderResult.malformedForLength(source.position() - mySourceBefore);
this.toULength = toULengthBefore + (source.position() - mySourceBefore);
}
}
/* invalid or illegal escape sequence */
if(err.isError()){
myConverterData.isEmptySegment = false; /* Reset to avoid future spurious errors */
return err;
}
continue;
/*ISO-2022-CN does not use single-byte (C1) SS2 and SS3 */
case CR:
/* falls through */
case LF:
myConverterData.toU2022State.reset();
/* falls through */
default:
/* converter one or two bytes */
myConverterData.isEmptySegment = false;
if (myConverterData.toU2022State.g != 0 || gotoGetTrailByte) {
if (source.hasRemaining() || gotoGetTrailByte) {
UConverterSharedData cnv;
byte tempState;
int tempBufLen;
boolean leadIsOk, trailIsOk;
short trailByte;
// getTrailByte: label
gotoGetTrailByte = false; // reset gotoGetTrailByte
trailByte = (short)(source.get(source.position()) & UConverterConstants.UNSIGNED_BYTE_MASK);
/*
* Ticket 5691: consistent illegal sequences:
* - We include at least the first byte in the illegal sequence.
* - If any of the non-initial bytes could be the start of a character,
* we stop the illegal sequence before the first one of those.
*
* In ISO-2022 DBCS, if the second byte is in the range 21..7e range or is
* an ESC/SO/SI, we report only the first byte as the illegal sequence.
* Otherwise we convert or report the pair of bytes.
*/
leadIsOk = (short)(UConverterConstants.UNSIGNED_BYTE_MASK & (mySourceChar - 0x21)) <= (0x7e - 0x21);
trailIsOk = (short)(UConverterConstants.UNSIGNED_BYTE_MASK & (trailByte - 0x21)) <= (0x7e - 0x21);
if (leadIsOk && trailIsOk) {
source.get();
tempState = myConverterData.toU2022State.cs[myConverterData.toU2022State.g];
if (tempState > CNS_11643_0) {
cnv = myConverterData.myConverterArray[CNS_11643];
tempBuf[0] = (byte)(0x80 + (tempState - CNS_11643_0));
tempBuf[1] = (byte)mySourceChar;
tempBuf[2] = (byte)trailByte;
tempBufLen = 3;
} else {
cnv = myConverterData.myConverterArray[tempState];
tempBuf[0] = (byte)mySourceChar;
tempBuf[1] = (byte)trailByte;
tempBufLen = 2;
}
ByteBuffer tempBuffer = ByteBuffer.wrap(tempBuf);
tempBuffer.limit(tempBufLen);
targetUniChar = MBCSSimpleGetNextUChar(cnv, tempBuffer, false);
mySourceChar = (mySourceChar << 8) | trailByte;
} else if (!(trailIsOk || IS_2022_CONTROL(trailByte))) {
/* report a pair of illegal bytes if the second byte is not a DBCS starter */
source.get();
/* add another bit so that the code below writes 2 bytes in case of error */
mySourceChar = 0x10000 | (mySourceChar << 8) | trailByte;
}
if (myConverterData.toU2022State.g >= 2) {
/* return from a single-shift state to the previous one */
myConverterData.toU2022State.g = myConverterData.toU2022State.prevG;
}
} else {
toUBytesArray[0] = (byte)mySourceChar;
toULength = 1;
// goto endloop;
return err;
}
} else {
if (mySourceChar <= 0x7f) {
targetUniChar = (char)mySourceChar;
}
}
break;
}
if ((UConverterConstants.UNSIGNED_INT_MASK&targetUniChar) < (UConverterConstants.UNSIGNED_INT_MASK&(UConverterConstants.missingCharMarker-1))) {
if (offsets != null) {
offsets.array()[target.position()] = source.remaining() - (mySourceChar <= 0xff ? 1 : 2);
}
target.put((char)targetUniChar);
} else if ((UConverterConstants.UNSIGNED_INT_MASK&targetUniChar) > (UConverterConstants.UNSIGNED_INT_MASK&(UConverterConstants.missingCharMarker))) {
/* disassemble the surrogate pair and write to output */
targetUniChar -= 0x0010000;
target.put((char)(0xd800+(char)(targetUniChar>>10)));
if (offsets != null) {
offsets.array()[target.position()-1] = source.position() - (mySourceChar <= 0xff ? 1 : 2);
}
if (target.hasRemaining()) {
target.put((char)(0xdc00+(char)(targetUniChar&0x3ff)));
if (offsets != null) {
offsets.array()[target.position()-1] = source.position() - (mySourceChar <= 0xff ? 1 : 2);
}
} else {
charErrorBufferArray[charErrorBufferLength++] = (char)(0xdc00+(char)(targetUniChar&0x3ff));
}
} else {
/* Call the callback function */
err = toUnicodeCallback(this, mySourceChar, targetUniChar);
break;
}
} else {
err = CoderResult.OVERFLOW;
break;
}
}
return err;
}
}
/************************ ISO-2022-KR ********************/
private class CharsetDecoderISO2022KR extends CharsetDecoderICU {
public CharsetDecoderISO2022KR(CharsetICU cs) {
super(cs);
}
protected void implReset() {
super.implReset();
setInitialStateToUnicodeKR();
myConverterData.reset();
}
protected CoderResult decodeLoop(ByteBuffer source, CharBuffer target, IntBuffer offsets, boolean flush) {
CoderResult err = CoderResult.UNDERFLOW;
int mySourceChar = 0x0000;
int targetUniChar = 0x0000;
byte[] tempBuf = new byte[2];
boolean usingFallback;
boolean gotoGetTrailByte = false;
boolean gotoEscape = false;
if (myConverterData.version == 1) {
return decodeLoopIBM(myConverterData.currentDecoder, source, target, offsets, flush);
}
/* initialize state */
usingFallback = isFallbackUsed();
if (myConverterData.key != 0) {
/* continue with a partial escape sequence */
gotoEscape = true;
} else if (toULength == 1 && source.hasRemaining() && target.hasRemaining()) {
/* continue with a partial double-byte character */
mySourceChar = (toUBytesArray[0] & UConverterConstants.UNSIGNED_BYTE_MASK);
toULength = 0;
gotoGetTrailByte = true;
}
while (source.hasRemaining() || gotoGetTrailByte || gotoEscape) {
if (target.hasRemaining() || gotoGetTrailByte || gotoEscape) {
if (!gotoGetTrailByte && !gotoEscape) {
mySourceChar = (char)(source.get() & UConverterConstants.UNSIGNED_BYTE_MASK);
}
if (!gotoGetTrailByte && !gotoEscape && mySourceChar == UConverterConstants.SI) {
myConverterData.toU2022State.g = 0;
if (myConverterData.isEmptySegment) {
myConverterData.isEmptySegment = false; /* we are handling it, reset to avoid future spurious errors */
err = CoderResult.malformedForLength(1);
this.toUBytesArray[0] = (byte)mySourceChar;
this.toULength = 1;
return err;
}
/* consume the source */
continue;
} else if (!gotoGetTrailByte && !gotoEscape && mySourceChar == UConverterConstants.SO) {
myConverterData.toU2022State.g = 1;
myConverterData.isEmptySegment = true;
/* consume the source */
continue;
} else if (!gotoGetTrailByte && (gotoEscape || mySourceChar == ESC_2022)) {
if (!gotoEscape) {
source.position(source.position()-1);
}
// escape label
gotoEscape = false; // reset gotoEscape flag
myConverterData.isEmptySegment = false; /* Any invalid ESC sequences will be detected separately, so just reset this */
err = changeState_2022(this, source, ISO_2022_KR);
if (err.isError()) {
return err;
}
continue;
}
myConverterData.isEmptySegment = false; /* Any invalid char errors will be detected separately, so just reset this */
if (myConverterData.toU2022State.g == 1 || gotoGetTrailByte) {
if (source.hasRemaining() || gotoGetTrailByte) {
boolean leadIsOk, trailIsOk;
short trailByte;
// getTrailByte label
gotoGetTrailByte = false; // reset gotoGetTrailByte flag
trailByte = (short)(source.get(source.position()) & UConverterConstants.UNSIGNED_BYTE_MASK);
targetUniChar = UConverterConstants.missingCharMarker;
/*
* Ticket 5691: consistent illegal sequences:
* - We include at least the first byte in the illegal sequence.
* - If any of the non-initial bytes could be the start of a character,
* we stop the illegal sequence before the first one of those.
*
* In ISO-2022 DBCS, if the second byte is in the 21..7e range or is
* an ESC/SO/SI, we report only the first byte as the illegal sequence.
* Otherwise we convert or report the pair of bytes.
*/
leadIsOk = (short)(UConverterConstants.UNSIGNED_BYTE_MASK & (mySourceChar - 0x21)) <= (0x7e - 0x21);
trailIsOk = (short)(UConverterConstants.UNSIGNED_BYTE_MASK & (trailByte - 0x21)) <= (0x7e - 0x21);
if (leadIsOk && trailIsOk) {
source.get();
tempBuf[0] = (byte)(mySourceChar + 0x80);
tempBuf[1] = (byte)(trailByte + 0x80);
targetUniChar = MBCSSimpleGetNextUChar(myConverterData.currentConverter.sharedData, ByteBuffer.wrap(tempBuf), usingFallback);
mySourceChar = (char)((mySourceChar << 8) | trailByte);
} else if (!(trailIsOk || IS_2022_CONTROL(trailByte))) {
/* report a pair of illegal bytes if the second byte is not a DBCS starter */
source.get();
/* add another bit so that the code below writes 2 bytes in case of error */
mySourceChar = (char)(0x10000 | (mySourceChar << 8) | trailByte);
}
} else {
toUBytesArray[0] = (byte)mySourceChar;
toULength = 1;
break;
}
} else if (mySourceChar <= 0x7f) {
int savedSourceLimit = source.limit();
int savedSourcePosition = source.position();
source.limit(source.position());
source.position(source.position()-1);
targetUniChar = MBCSSimpleGetNextUChar(myConverterData.currentConverter.sharedData, source, usingFallback);
source.limit(savedSourceLimit);
source.position(savedSourcePosition);
} else {
targetUniChar = 0xffff;
}
if (targetUniChar < 0xfffe) {
target.put((char)targetUniChar);
if (offsets != null) {
offsets.array()[target.position()] = source.position() - (mySourceChar <= 0xff ? 1 : 2);
}
} else {
/* Call the callback function */
err = toUnicodeCallback(this, mySourceChar, targetUniChar);
break;
}
} else {
err = CoderResult.OVERFLOW;
break;
}
}
return err;
}
protected CoderResult decodeLoopIBM(CharsetDecoderMBCS cnv, ByteBuffer source, CharBuffer target, IntBuffer offsets, boolean flush) {
CoderResult err = CoderResult.UNDERFLOW;
int sourceStart;
int sourceLimit;
int argSource;
int argTarget;
boolean gotoEscape = false;
int oldSourceLimit;
/* remember the original start of the input for offsets */
sourceStart = argSource = source.position();
if (myConverterData.key != 0) {
/* continue with a partial escape sequence */
gotoEscape = true;
}
while (gotoEscape || (!err.isError() && source.hasRemaining())) {
if (!gotoEscape) {
/* Find the end of the buffer e.g : Next Escape Seq | end of Buffer */
int oldSourcePos = source.position();
sourceLimit = getEndOfBuffer_2022(source);
source.position(oldSourcePos);
if (source.position() != sourceLimit) {
/*
* get the current partial byte sequence
*
* it needs to be moved between the public and the subconverter
* so that the conversion frameword, which only sees the public
* converter, can handle truncated and illegal input etc.
*/
if (toULength > 0) {
cnv.toUBytesArray = toUBytesArray.clone();
}
cnv.toULength = toULength;
/*
* Convert up to the end of the input, or to before the next escape character.
* Does not handle conversion extensions because the preToU[] state etc.
* is not copied.
*/
argTarget = target.position();
oldSourceLimit = source.limit(); // save the old source limit change to new one
source.limit(sourceLimit);
err = myConverterData.currentDecoder.cnvMBCSToUnicodeWithOffsets(source, target, offsets, flush);
source.limit(oldSourceLimit); // restore source limit;
if (offsets != null && sourceStart != argSource) {
/* update offsets to base them on the actual start of the input */
int delta = argSource - sourceStart;
while (argTarget < target.position()) {
int currentOffset = offsets.get();
offsets.position(offsets.position()-1);
if (currentOffset >= 0) {
offsets.put(currentOffset + delta);
offsets.position(offsets.position()-1);
}
offsets.get();
target.get();
}
}
argSource = source.position();
/* copy input/error/overflow buffers */
if (cnv.toULength > 0) {
toUBytesArray = cnv.toUBytesArray.clone();
}
toULength = cnv.toULength;
if (err.isOverflow()) {
if (cnv.charErrorBufferLength > 0) {
charErrorBufferArray = cnv.charErrorBufferArray.clone();
}
charErrorBufferLength = cnv.charErrorBufferLength;
cnv.charErrorBufferLength = 0;
}
}
if (err.isError() || err.isOverflow() || (source.position() == source.limit())) {
return err;
}
}
// escape label
gotoEscape = false;
err = changeState_2022(this, source, ISO_2022_KR);
}
return err;
}
}
/******************** from unicode **********************/
/* preference order of JP charsets */
private final static byte []jpCharsetPref = {
ASCII,
JISX201,
ISO8859_1,
ISO8859_7,
JISX208,
JISX212,
GB2312,
KSC5601,
HWKANA_7BIT
};
/*
* The escape sequences must be in order of the enum constants like JISX201 = 3,
* not in order of jpCharsetPref[]!
*/
private final static byte [][]escSeqChars = {
{ 0x1B, 0x28, 0x42}, /* <ESC>(B ASCII */
{ 0x1B, 0x2E, 0x41}, /* <ESC>.A ISO-8859-1 */
{ 0x1B, 0x2E, 0x46}, /* <ESC>.F ISO-8859-7 */
{ 0x1B, 0x28, 0x4A}, /* <ESC>(J JISX-201 */
{ 0x1B, 0x24, 0x42}, /* <ESC>$B JISX-208 */
{ 0x1B, 0x24, 0x28, 0x44}, /* <ESC>$(D JISX-212 */
{ 0x1B, 0x24, 0x41}, /* <ESC>$A GB2312 */
{ 0x1B, 0x24, 0x28, 0x43}, /* <ESC>$(C KSC5601 */
{ 0x1B, 0x28, 0x49} /* <ESC>(I HWKANA_7BIT */
};
/*
* JIS X 0208 has fallbacks from Unicode half-width Katakana to full-width (DBCS)
* Katakana.
* Now that we use a Shift-JIS table for JIS X 0208 we need to hardcode these fallbacks
* because Shift-JIS roundtrips half-width Katakana to single bytes.
* These were the only fallbacks in ICU's jisx-208.ucm file.
*/
private final static char []hwkana_fb = {
0x2123, /* U+FF61 */
0x2156,
0x2157,
0x2122,
0x2126,
0x2572,
0x2521,
0x2523,
0x2525,
0x2527,
0x2529,
0x2563,
0x2565,
0x2567,
0x2543,
0x213C, /* U+FF70 */
0x2522,
0x2524,
0x2526,
0x2528,
0x252A,
0x252B,
0x252D,
0x252F,
0x2531,
0x2533,
0x2535,
0x2537,
0x2539,
0x253B,
0x253D,
0x253F, /* U+FF80 */
0x2541,
0x2544,
0x2546,
0x2548,
0x254A,
0x254B,
0x254C,
0x254D,
0x254E,
0x254F,
0x2552,
0x2555,
0x2558,
0x255B,
0x255E,
0x255F, /* U+FF90 */
0x2560,
0x2561,
0x2562,
0x2564,
0x2566,
0x2568,
0x2569,
0x256A,
0x256B,
0x256C,
0x256D,
0x256F,
0x2573,
0x212B,
0x212C /* U+FF9F */
};
protected byte [][]fromUSubstitutionChar = new byte[][]{ { (byte)0x1A }, { (byte)0x2F, (byte)0x7E} };
/****************************ISO-2022-JP************************************/
private class CharsetEncoderISO2022JP extends CharsetEncoderICU {
public CharsetEncoderISO2022JP(CharsetICU cs) {
super(cs, fromUSubstitutionChar[0]);
}
protected void implReset() {
super.implReset();
myConverterData.reset();
}
/* Map Unicode to 00..7F according to JIS X 0201. Return U+FFFE if unmappable. */
private int jisx201FromU(int value) {
if (value <= 0x7f) {
if (value != 0x5c && value != 0x7e) {
return value;
}
} else if (value == 0xa5) {
return 0x5c;
} else if (value == 0x203e) {
return 0x7e;
}
return (int)(UConverterConstants.UNSIGNED_INT_MASK & 0xfffe);
}
/*
* Take a valid Shift-JIS byte pair, check that it is in the range corresponding
* to JIS X 0208, and convert it to a pair of 21..7E bytes.
* Return 0 if the byte pair is out of range.
*/
private int _2022FromSJIS(int value) {
short trail;
if (value > 0xEFFC) {
return 0; /* beyond JIS X 0208 */
}
trail = (short)(value & UConverterConstants.UNSIGNED_BYTE_MASK);
value &= 0xff00; /* lead byte */
if (value <= 0x9f00) {
value -= 0x7000;
} else { /* 0xe000 <= value <= 0xef00 */
value -= 0xb000;
}
value <<= 1;
if (trail <= 0x9e) {
value -= 0x100;
if (trail <= 0x7e) {
value |= ((trail - 0x1f) & UConverterConstants.UNSIGNED_BYTE_MASK);
} else {
value |= ((trail - 0x20) & UConverterConstants.UNSIGNED_BYTE_MASK);
}
} else { /* trail <= 0xfc */
value |= ((trail - 0x7e) & UConverterConstants.UNSIGNED_BYTE_MASK);
}
return value;
}
/* This overrides the cbFromUWriteSub method in CharsetEncoderICU */
CoderResult cbFromUWriteSub (CharsetEncoderICU encoder,
CharBuffer source, ByteBuffer target, IntBuffer offsets){
CoderResult err = CoderResult.UNDERFLOW;
byte[] buffer = new byte[8];
int i = 0;
byte[] subchar;
subchar = encoder.replacement();
byte cs;
if (myConverterData.fromU2022State.g == 1) {
/* JIS7: switch from G1 to G0 */
myConverterData.fromU2022State.g = 0;
buffer[i++] = UConverterConstants.SI;
}
cs = myConverterData.fromU2022State.cs[0];
if (cs != ASCII && cs != JISX201) {
/* not in ASCII or JIS X 0201: switch to ASCII */
myConverterData.fromU2022State.cs[0] = ASCII;
buffer[i++] = 0x1B;
buffer[i++] = 0x28;
buffer[i++] = 0x42;
}
buffer[i++] = subchar[0];
err = CharsetEncoderICU.fromUWriteBytes(this, buffer, 0, i, target, offsets, source.position() - 1);
return err;
}
protected CoderResult encodeLoop(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush) {
CoderResult err = CoderResult.UNDERFLOW;
int sourceChar;
byte cs, g;
int choiceCount;
int len, outLen;
byte[] choices = new byte[10];
int targetValue = 0;
boolean usingFallback;
byte[] buffer = new byte[8];
boolean getTrail = false; // use for getTrail label
int oldSourcePos; // for proper error handling
choiceCount = 0;
/* check if the last codepoint of previous buffer was a lead surrogate */
if ((sourceChar = fromUChar32) != 0 && target.hasRemaining()) {
getTrail = true;
}
while (getTrail || source.hasRemaining()) {
if (getTrail || target.hasRemaining()) {
oldSourcePos = source.position();
if (!getTrail) { /* skip if going to getTrail label */
sourceChar = source.get();
}
/* check if the char is a First surrogate */
if (getTrail || UTF16.isSurrogate((char)sourceChar)) {
if (getTrail || UTF16.isLeadSurrogate((char)sourceChar)) {
// getTrail:
if (getTrail) {
getTrail = false;
}
/* look ahead to find the trail surrogate */
if (source.hasRemaining()) {
/* test the following code unit */
char trail = source.get();
/* go back to the previous position */
source.position(source.position()-1);
if (UTF16.isTrailSurrogate(trail)) {
source.get();
sourceChar = UCharacter.getCodePoint((char)sourceChar, trail);
fromUChar32 = 0x00;
/* convert this supplementary code point */
/* exit this condition tree */
} else {
/* this is an unmatched lead code unit (1st surrogate) */
/* callback(illegal) */
err = CoderResult.malformedForLength(1);
fromUChar32 = sourceChar;
break;
}
} else {
/* no more input */
fromUChar32 = sourceChar;
break;
}
} else {
/* this is an unmatched trail code unit (2nd surrogate) */
/* callback(illegal) */
err = CoderResult.malformedForLength(1);
fromUChar32 = sourceChar;
break;
}
}
/* do not convert SO/SI/ESC */
if (IS_2022_CONTROL(sourceChar)) {
/* callback(illegal) */
err = CoderResult.malformedForLength(1);
fromUChar32 = sourceChar;
break;
}
/* do the conversion */
if (choiceCount == 0) {
char csm;
/*
* The csm variable keeps track of which charsets are allowed
* and not used yet while building the choices[].
*/
csm = (char)jpCharsetMasks[myConverterData.version];
choiceCount = 0;
/* JIS7/8: try single-byte half-width Katakana before JISX208 */
if (myConverterData.version == 3 || myConverterData.version == 4) {
choices[choiceCount++] = HWKANA_7BIT;
}
/* Do not try single-bit half-width Katakana for other versions. */
csm &= ~CSM(HWKANA_7BIT);
/* try the current G0 charset */
choices[choiceCount++] = cs = myConverterData.fromU2022State.cs[0];
csm &= ~CSM(cs);
/* try the current G2 charset */
if ((cs = myConverterData.fromU2022State.cs[2]) != 0) {
choices[choiceCount++] = cs;
csm &= ~CSM(cs);
}
/* try all the other charsets */
for (int i = 0; i < jpCharsetPref.length; i++) {
cs = jpCharsetPref[i];
if ((CSM(cs) & csm) != 0) {
choices[choiceCount++] = cs;
csm &= ~CSM(cs);
}
}
}
cs = g = 0;
/*
* len==0: no mapping found yet
* len<0: found a fallback result: continue looking for a roundtrip but no further fallbacks
* len>0: found a roundtrip result, done
*/
len = 0;
/*
* We will turn off usingFallBack after finding a fallback,
* but we still get fallbacks from PUA code points as usual.
* Therefore, we will also need to check that we don't overwrite
* an early fallback with a later one.
*/
usingFallback = useFallback;
for (int i = 0; i < choiceCount && len <= 0; i++) {
int[] value = new int[1];
int len2;
byte cs0 = choices[i];
switch (cs0) {
case ASCII:
if (sourceChar <= 0x7f) {
targetValue = sourceChar;
len = 1;
cs = cs0;
g = 0;
}
break;
case ISO8859_1:
if (GR96_START <= sourceChar && sourceChar <= GR96_END) {
targetValue = sourceChar - 0x80;
len = 1;
cs = cs0;
g = 2;
}
break;
case HWKANA_7BIT:
if (sourceChar <= HWKANA_END && sourceChar >= HWKANA_START) {
if (myConverterData.version == 3) {
/* JIS7: use G1 (SO) */
/* Shift U+FF61..U+FF9F to bytes 21..5F. */
targetValue = (int)(UConverterConstants.UNSIGNED_INT_MASK & (sourceChar - (HWKANA_START - 0x21)));
len = 1;
myConverterData.fromU2022State.cs[1] = cs = cs0; /* do not output an escape sequence */
g = 1;
} else if (myConverterData.version == 4) {
/* JIS8: use 8-bit bytes with any single-byte charset, see escape sequence output below */
/* Shift U+FF61..U+FF9F to bytes A1..DF. */
targetValue = (int)(UConverterConstants.UNSIGNED_INT_MASK & (sourceChar - (HWKANA_START - 0xa1)));
len = 1;
cs = myConverterData.fromU2022State.cs[0];
if (IS_JP_DBCS(cs)) {
/* switch from a DBCS charset to JISX201 */
cs = JISX201;
}
/* else stay in the current G0 charset */
g = 0;
}
/* else do not use HWKANA_7BIT with other versions */
}
break;
case JISX201:
/* G0 SBCS */
value[0] = jisx201FromU(sourceChar);
if (value[0] <= 0x7f) {
targetValue = value[0];
len = 1;
cs = cs0;
g = 0;
usingFallback = false;
}
break;
case JISX208:
/* G0 DBCS from JIS table */
myConverterData.currentConverter.sharedData = myConverterData.myConverterArray[cs0];
myConverterData.currentConverter.sharedData.mbcs.outputType = CharsetMBCS.MBCS_OUTPUT_2;
len2 = myConverterData.currentEncoder.fromUChar32(sourceChar, value, usingFallback);
//len2 = MBCSFromUChar32_ISO2022(myConverterData.myConverterArray[cs0], sourceChar, value, usingFallback, CharsetMBCS.MBCS_OUTPUT_2);
if (len2 == 2 || (len2 == -2 && len == 0)) { /* only accept DBCS: abs(len) == 2 */
value[0] = _2022FromSJIS(value[0]);
if (value[0] != 0) {
targetValue = value[0];
len = len2;
cs = cs0;
g = 0;
usingFallback = false;
}
} else if (len == 0 && usingFallback && sourceChar <= HWKANA_END && sourceChar >= HWKANA_START) {
targetValue = hwkana_fb[sourceChar - HWKANA_START];
len = -2;
cs = cs0;
g = 0;
usingFallback = false;
}
break;
case ISO8859_7:
/* G0 SBCS forced to 7-bit output */
len2 = MBCSSingleFromUChar32(myConverterData.myConverterArray[cs0], sourceChar, value, usingFallback);
if (len2 != 0 && !(len2 < 0 && len != 0) && GR96_START <= value[0] && value[0] <= GR96_END) {
targetValue = value[0] - 0x80;
len = len2;
cs = cs0;
g = 2;
usingFallback = false;
}
break;
default :
/* G0 DBCS */
myConverterData.currentConverter.sharedData = myConverterData.myConverterArray[cs0];
myConverterData.currentConverter.sharedData.mbcs.outputType = CharsetMBCS.MBCS_OUTPUT_2;
len2 = myConverterData.currentEncoder.fromUChar32(sourceChar, value, usingFallback);
//len2 = MBCSFromUChar32_ISO2022(myConverterData.myConverterArray[cs0], sourceChar, value, usingFallback, CharsetMBCS.MBCS_OUTPUT_2);
if (len2 == 2 || (len2 == -2 && len == 0)) { /* only accept DBCS: abs(len)==2 */
if (cs0 == KSC5601) {
/*
* Check for valid bytes for the encoding scheme.
* This is necessary because the sub-converter (windows-949)
* has a broader encoding scheme than is valid for 2022.
*/
value[0] = _2022FromGR94DBCS(value[0]);
if (value[0] == 0) {
break;
}
}
targetValue = value[0];
len = len2;
cs = cs0;
g = 0;
usingFallback = false;
}
break;
}
}
if (len != 0) {
if (len < 0) {
len = -len; /* fallback */
}
outLen = 0;
/* write SI if necessary (only for JIS7 */
if (myConverterData.fromU2022State.g == 1 && g == 0) {
buffer[outLen++] = UConverterConstants.SI;
myConverterData.fromU2022State.g = 0;
}
/* write the designation sequence if necessary */
if (cs != myConverterData.fromU2022State.cs[g]) {
for (int i = 0; i < escSeqChars[cs].length; i++) {
buffer[outLen++] = escSeqChars[cs][i];
}
myConverterData.fromU2022State.cs[g] = cs;
/* invalidate the choices[] */
choiceCount = 0;
}
/* write the shift sequence if necessary */
if (g != myConverterData.fromU2022State.g) {
switch (g) {
/* case 0 handled before writing escapes */
case 1:
buffer[outLen++] = UConverterConstants.SO;
myConverterData.fromU2022State.g = 1;
break;
default : /* case 2 */
buffer[outLen++] = 0x1b;
buffer[outLen++] = 0x4e;
break;
/* case 3: no SS3 in ISO-2022-JP-x */
}
}
/* write the output bytes */
if (len == 1) {
buffer[outLen++] = (byte)targetValue;
} else { /* len == 2 */
buffer[outLen++] = (byte)(targetValue >> 8);
buffer[outLen++] = (byte)targetValue;
}
}else {
/*
* if we cannot find the character after checking all codepages
* then this is an error.
*/
err = CoderResult.unmappableForLength(source.position()-oldSourcePos);
fromUChar32 = sourceChar;
break;
}
if (sourceChar == CR || sourceChar == LF) {
/* reset the G2 state at the end of a line (conversion got use into ASCII or JISX201 already) */
myConverterData.fromU2022State.cs[2] = 0;
choiceCount = 0;
}
/* output outLen>0 bytes in buffer[] */
if (outLen == 1) {
target.put(buffer[0]);
if (offsets != null) {
offsets.put(source.remaining() - 1); /* -1 known to be ASCII */
}
} else if (outLen == 2 && (target.position() + 2) <= target.limit()) {
target.put(buffer[0]);
target.put(buffer[1]);
if (offsets != null) {
int sourceIndex = source.position() - 1;
offsets.put(sourceIndex);
offsets.put(sourceIndex);
}
} else {
err = CharsetEncoderICU.fromUWriteBytes(this, buffer, 0, outLen, target, offsets, source.position()-1);
}
} else {
err = CoderResult.OVERFLOW;
break;
}
}
/*
* the end of the input stream and detection of truncated input
* are handled by the framework, but for ISO-2022-JP conversion
* we need to be in ASCII mode at the very end
*
* conditions:
* successful
* in SO mode or not in ASCII mode
* end of input and no truncated input
*/
if (!err.isError() &&
(myConverterData.fromU2022State.g != 0 || myConverterData.fromU2022State.cs[0] != ASCII) &&
flush && !source.hasRemaining() && fromUChar32 == 0) {
int sourceIndex;
outLen = 0;
if (myConverterData.fromU2022State.g != 0) {
buffer[outLen++] = UConverterConstants.SI;
myConverterData.fromU2022State.g = 0;
}
if (myConverterData.fromU2022State.cs[0] != ASCII) {
for (int i = 0; i < escSeqChars[ASCII].length; i++) {
buffer[outLen++] = escSeqChars[ASCII][i];
}
myConverterData.fromU2022State.cs[0] = ASCII;
}
/* get the source index of the last input character */
sourceIndex = source.position();
if (sourceIndex > 0) {
--sourceIndex;
if (UTF16.isTrailSurrogate(source.get(sourceIndex)) &&
(sourceIndex == 0 || UTF16.isLeadSurrogate(source.get(sourceIndex-1)))) {
--sourceIndex;
}
} else {
sourceIndex = -1;
}
err = CharsetEncoderICU.fromUWriteBytes(this, buffer, 0, outLen, target, offsets, sourceIndex);
}
return err;
}
}
/****************************ISO-2022-CN************************************/
/*
* Rules for ISO-2022-CN Encoding:
* i) The designator sequence must appear once on a line before any instance
* of chracter set it designates.
* ii) If two lines contain characters from the same character set, both lines
* must include the designator sequence.
* iii) Once the designator sequence is known, a shifting sequence has to be found
* to invoke the shifting
* iv) All lines start in ASCII and end in ASCII.
* v) Four shifting sequences are employed for this purpose:
* Sequence ASCII Eq Charsets
* --------- --------- --------
* SI <SI> US-ASCII
* SO <SO> CNS-11643-1992 Plane 1, GB2312, ISO-IR-165
* SS2 <ESC>N CNS-11643-1992 Plane 2
* SS3 <ESC>O CNS-11643-1992 Planes 3-7
* vi)
* SOdesignator : ESC "$" ")" finalchar_for_SO
* SS2designator : ESC "$" "*" finalchar_for_SS2
* SS3designator : ESC "$" "+" finalchar_for_SS3
*
* ESC $ ) A Indicates the bytes following SO are Chinese
* characters as defined in GB 2312-80, until
* another SOdesignation appears
*
* ESC $ ) E Indicates the bytes following SO are as defined
* in ISO-IR-165 (for details, see section 2.1),
* until another SOdesignation appears
*
* ESC $ ) G Indicates the bytes following SO are as defined
* in CNS 11643-plane-1, until another SOdesignation appears
*
* ESC $ * H Indicates teh two bytes immediately following
* SS2 is a Chinese character as defined in CNS
* 11643-plane-2, until another SS2designation
* appears
* (Meaning <ESC>N must preceed ever 2 byte sequence.)
*
* ESC $ + I Indicates the immediate two bytes following SS3
* is a Chinese character as defined in CNS
* 11643-plane-3, until another SS3designation
* appears
* (Meaning <ESC>O must preceed every 2 byte sequence.)
*
* ESC $ + J Indicates the immediate two bytes following SS3
* is a Chinese character as defined in CNS
* 11643-plane-4, until another SS3designation
* appears
* (In English: <ESC>O must preceed every 2 byte sequence.)
*
* ESC $ + K Indicates the immediate two bytes following SS3
* is a Chinese character as defined in CNS
* 11643-plane-5, until another SS3designation
* appears
*
* ESC $ + L Indicates the immediate two bytes following SS3
* is a Chinese character as defined in CNS
* 11643-plane-6, until another SS3designation
* appears
*
* ESC $ + M Indicates the immediate two bytes following SS3
* is a Chinese character as defined in CNS
* 11643-plane-7, until another SS3designation
* appears
*
* As in ISO-2022-CN, each line starts in ASCII, and ends in ASCII, and
* has its own designation information before any Chinese chracters
* appears
*/
/* The following are defined this way to make strings truely readonly */
private final static byte[] GB_2312_80_STR = { 0x1B, 0x24, 0x29, 0x41 };
private final static byte[] ISO_IR_165_STR = { 0x1B, 0x24, 0x29, 0x45 };
private final static byte[] CNS_11643_1992_Plane_1_STR = { 0x1B, 0x24, 0x29, 0x47 };
private final static byte[] CNS_11643_1992_Plane_2_STR = { 0x1B, 0x24, 0x2A, 0x48 };
private final static byte[] CNS_11643_1992_Plane_3_STR = { 0x1B, 0x24, 0x2B, 0x49 };
private final static byte[] CNS_11643_1992_Plane_4_STR = { 0x1B, 0x24, 0x2B, 0x4A };
private final static byte[] CNS_11643_1992_Plane_5_STR = { 0x1B, 0x24, 0x2B, 0x4B };
private final static byte[] CNS_11643_1992_Plane_6_STR = { 0x1B, 0x24, 0x2B, 0x4C };
private final static byte[] CNS_11643_1992_Plane_7_STR = { 0x1B, 0x24, 0x2B, 0x4D };
/************************ ISO2022-CN Data *****************************/
private final static byte[][] escSeqCharsCN = {
SHIFT_IN_STR,
GB_2312_80_STR,
ISO_IR_165_STR,
CNS_11643_1992_Plane_1_STR,
CNS_11643_1992_Plane_2_STR,
CNS_11643_1992_Plane_3_STR,
CNS_11643_1992_Plane_4_STR,
CNS_11643_1992_Plane_5_STR,
CNS_11643_1992_Plane_6_STR,
CNS_11643_1992_Plane_7_STR,
};
private class CharsetEncoderISO2022CN extends CharsetEncoderICU {
public CharsetEncoderISO2022CN(CharsetICU cs) {
super(cs, fromUSubstitutionChar[0]);
}
protected void implReset() {
super.implReset();
myConverterData.reset();
}
/* This overrides the cbFromUWriteSub method in CharsetEncoderICU */
CoderResult cbFromUWriteSub (CharsetEncoderICU encoder,
CharBuffer source, ByteBuffer target, IntBuffer offsets){
CoderResult err = CoderResult.UNDERFLOW;
byte[] buffer = new byte[8];
int i = 0;
byte[] subchar;
subchar = encoder.replacement();
if (myConverterData.fromU2022State.g != 0) {
/* not in ASCII mode: switch to ASCII */
myConverterData.fromU2022State.g = 0;
buffer[i++] = UConverterConstants.SI;
}
buffer[i++] = subchar[0];
err = CharsetEncoderICU.fromUWriteBytes(this, buffer, 0, i, target, offsets, source.position() - 1);
return err;
}
protected CoderResult encodeLoop(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush) {
CoderResult err = CoderResult.UNDERFLOW;
int sourceChar;
byte[] buffer = new byte[8];
int len;
byte[] choices = new byte[3];
int choiceCount;
int targetValue = 0;
boolean usingFallback;
boolean gotoGetTrail = false;
int oldSourcePos; // For proper error handling
choiceCount = 0;
/* check if the last codepoint of previous buffer was a lead surrogate */
if ((sourceChar = fromUChar32) != 0 && target.hasRemaining()) {
// goto getTrail label
gotoGetTrail = true;
}
while (source.hasRemaining() || gotoGetTrail) {
if (target.hasRemaining() || gotoGetTrail) {
oldSourcePos = source.position();
if (!gotoGetTrail) {
sourceChar = source.get();
}
/* check if the char is a First surrogate */
if (UTF16.isSurrogate((char)sourceChar) || gotoGetTrail) {
if (UTF16.isLeadSurrogate((char)sourceChar) || gotoGetTrail) {
// getTrail label
/* reset gotoGetTrail flag*/
gotoGetTrail = false;
/* look ahead to find the trail surrogate */
if (source.hasRemaining()) {
/* test the following code unit */
char trail = source.get();
source.position(source.position()-1);
if (UTF16.isTrailSurrogate(trail)) {
source.get();
sourceChar = UCharacter.getCodePoint((char)sourceChar, trail);
fromUChar32 = 0x00;
/* convert this supplementary code point */
/* exit this condition tree */
} else {
/* this is an unmatched lead code unit (1st surrogate) */
/* callback(illegal) */
err = CoderResult.malformedForLength(1);
fromUChar32 = sourceChar;
break;
}
} else {
/* no more input */
fromUChar32 = sourceChar;
break;
}
} else {
/* this is an unmatched trail code unit (2nd surrogate) */
/* callback(illegal) */
err = CoderResult.malformedForLength(1);
fromUChar32 = sourceChar;
break;
}
}
/* do the conversion */
if (sourceChar <= 0x007f) {
/* do not converter SO/SI/ESC */
if (IS_2022_CONTROL(sourceChar)) {
/* callback(illegal) */
err = CoderResult.malformedForLength(1);
fromUChar32 = sourceChar;
break;
}
/* US-ASCII */
if (myConverterData.fromU2022State.g == 0) {
buffer[0] = (byte)sourceChar;
len = 1;
} else {
buffer[0] = UConverterConstants.SI;
buffer[1] = (byte)sourceChar;
len = 2;
myConverterData.fromU2022State.g = 0;
choiceCount = 0;
}
if (sourceChar == CR || sourceChar == LF) {
/* reset the state at the end of a line */
myConverterData.fromU2022State.reset();
choiceCount = 0;
}
} else {
/* convert U+0080..U+10ffff */
int i;
byte cs, g;
if (choiceCount == 0) {
/* try the current SO/G1 converter first */
choices[0] = myConverterData.fromU2022State.cs[1];
/* default to GB2312_1 if none is designated yet */
if (choices[0] == 0) {
choices[0] = GB2312_1;
}
if (myConverterData.version == 0) {
/* ISO-2022-CN */
/* try other SO/G1 converter; a CNS_11643_1 lookup may result in any plane */
if (choices[0] == GB2312_1) {
choices[1] = CNS_11643_1;
} else {
choices[1] = GB2312_1;
}
choiceCount = 2;
} else if (myConverterData.version == 1) {
/* ISO-2022-CN-EXT */
/* try one of the other converters */
switch (choices[0]) {
case GB2312_1:
choices[1] = CNS_11643_1;
choices[2] = ISO_IR_165;
break;
case ISO_IR_165:
choices[1] = GB2312_1;
choices[2] = CNS_11643_1;
break;
default :
choices[1] = GB2312_1;
choices[2] = ISO_IR_165;
break;
}
choiceCount = 3;
} else {
/* ISO-2022-CN-CNS */
choices[0] = CNS_11643_1;
choices[1] = GB2312_1;
choiceCount = 2;
}
}
cs = g = 0;
/*
* len==0: no mapping found yet
* len<0: found a fallback result: continue looking for a roundtrip but no further fallbacks
* len>0: found a roundtrip result, done
*/
len = 0;
/*
* We will turn off usingFallback after finding a fallback,
* but we still get fallbacks from PUA code points as usual.
* Therefore, we will also need to check that we don't overwrite
* an early fallback with a later one.
*/
usingFallback = useFallback;
for (i = 0; i < choiceCount && len <= 0; ++i) {
byte cs0 = choices[i];
if (cs0 > 0) {
int[] value = new int[1];
int len2;
if (cs0 > CNS_11643_0) {
myConverterData.currentConverter.sharedData = myConverterData.myConverterArray[CNS_11643];
myConverterData.currentConverter.sharedData.mbcs.outputType = CharsetMBCS.MBCS_OUTPUT_3;
len2 = myConverterData.currentEncoder.fromUChar32(sourceChar, value, usingFallback);
//len2 = MBCSFromUChar32_ISO2022(myConverterData.myConverterArray[CNS_11643],
// sourceChar, value, usingFallback, CharsetMBCS.MBCS_OUTPUT_3);
if (len2 == 3 || (len2 == -3 && len == 0)) {
targetValue = value[0];
cs = (byte)(CNS_11643_0 + (value[0] >> 16) - 0x80);
if (len2 >= 0) {
len = 2;
} else {
len = -2;
usingFallback = false;
}
if (cs == CNS_11643_1) {
g = 1;
} else if (cs == CNS_11643_2) {
g = 2;
} else if (myConverterData.version == 1) { /* plane 3..7 */
g = 3;
} else {
/* ISO-2022-CN (without -EXT) does not support plane 3..7 */
len = 0;
}
}
} else {
/* GB2312_1 or ISO-IR-165 */
myConverterData.currentConverter.sharedData = myConverterData.myConverterArray[cs0];
myConverterData.currentConverter.sharedData.mbcs.outputType = CharsetMBCS.MBCS_OUTPUT_2;
len2 = myConverterData.currentEncoder.fromUChar32(sourceChar, value, usingFallback);
//len2 = MBCSFromUChar32_ISO2022(myConverterData.myConverterArray[cs0],
// sourceChar, value, usingFallback, CharsetMBCS.MBCS_OUTPUT_2);
if (len2 == 2 || (len2 == -2 && len == 0)) {
targetValue = value[0];
len = len2;
cs = cs0;
g = 1;
usingFallback = false;
}
}
}
}
if (len != 0) {
len = 0; /* count output bytes; it must have ben abs(len) == 2 */
/* write the designation sequence if necessary */
if (cs != myConverterData.fromU2022State.cs[g]) {
if (cs < CNS_11643) {
for (int n = 0; n < escSeqCharsCN[cs].length; n++) {
buffer[n] = escSeqCharsCN[cs][n];
}
} else {
for (int n = 0; n < escSeqCharsCN[CNS_11643 + (cs - CNS_11643_1)].length; n++) {
buffer[n] = escSeqCharsCN[CNS_11643 + (cs - CNS_11643_1)][n];
}
}
len = 4;
myConverterData.fromU2022State.cs[g] = cs;
if (g == 1) {
/* changing the SO/G1 charset invalidates the choices[] */
choiceCount = 0;
}
}
/* write the shift sequence if necessary */
if (g != myConverterData.fromU2022State.g) {
switch (g) {
case 1:
buffer[len++] = UConverterConstants.SO;
/* set the new state only if it is the locking shift SO/G1, not for SS2 or SS3 */
myConverterData.fromU2022State.g = 1;
break;
case 2:
buffer[len++] = 0x1b;
buffer[len++] = 0x4e;
break;
default: /* case 3 */
buffer[len++] = 0x1b;
buffer[len++] = 0x4f;
break;
}
}
/* write the two output bytes */
buffer[len++] = (byte)(targetValue >> 8);
buffer[len++] = (byte)targetValue;
} else {
/* if we cannot find the character after checking all codepages
* then this is an error
*/
err = CoderResult.unmappableForLength(source.position()-oldSourcePos);
fromUChar32 = sourceChar;
break;
}
}
/* output len>0 bytes in buffer[] */
if (len == 1) {
target.put(buffer[0]);
if (offsets != null) {
offsets.put(source.position()-1);
}
} else if (len == 2 && (target.remaining() >= 2)) {
target.put(buffer[0]);
target.put(buffer[1]);
if (offsets != null) {
int sourceIndex = source.position();
offsets.put(sourceIndex);
offsets.put(sourceIndex);
}
} else {
err = CharsetEncoderICU.fromUWriteBytes(this, buffer, 0, len, target, offsets, source.position()-1);
if (err.isError()) {
break;
}
}
} else {
err = CoderResult.OVERFLOW;
break;
}
} /* end while (source.hasRemaining() */
/*
* the end of the input stream and detection of truncated input
* are handled by the framework, but for ISO-2022-CN conversion
* we need to be in ASCII mode at the very end
*
* condtions:
* succesful
* not in ASCII mode
* end of input and no truncated input
*/
if (!err.isError() && myConverterData.fromU2022State.g != 0 && flush && !source.hasRemaining() && fromUChar32 == 0) {
int sourceIndex;
/* we are switching to ASCII */
myConverterData.fromU2022State.g = 0;
/* get the source index of the last input character */
sourceIndex = source.position();
if (sourceIndex > 0) {
--sourceIndex;
if (UTF16.isTrailSurrogate(source.get(sourceIndex)) &&
(sourceIndex == 0 || UTF16.isLeadSurrogate(source.get(sourceIndex-1)))) {
--sourceIndex;
}
} else {
sourceIndex = -1;
}
err = CharsetEncoderICU.fromUWriteBytes(this, SHIFT_IN_STR, 0, 1, target, offsets, sourceIndex);
}
return err;
}
}
/******************************** ISO-2022-KR *****************************/
/*
* Rules for ISO-2022-KR encoding
* i) The KSC5601 designator sequence should appear only once in a file,
* at the begining of a line before any KSC5601 characters. This usually
* means that it appears by itself on the first line of the file
* ii) There are only 2 shifting sequences SO to shift into double byte mode
* and SI to shift into single byte mode
*/
private class CharsetEncoderISO2022KR extends CharsetEncoderICU {
public CharsetEncoderISO2022KR(CharsetICU cs) {
super(cs, fromUSubstitutionChar[myConverterData.version]);
}
protected void implReset() {
super.implReset();
myConverterData.reset();
setInitialStateFromUnicodeKR(this);
}
/* This overrides the cbFromUWriteSub method in CharsetEncoderICU */
CoderResult cbFromUWriteSub (CharsetEncoderICU encoder,
CharBuffer source, ByteBuffer target, IntBuffer offsets){
CoderResult err = CoderResult.UNDERFLOW;
byte[] buffer = new byte[8];
int length, i = 0;
byte[] subchar;
subchar = encoder.replacement();
length = subchar.length;
if (myConverterData.version == 0) {
if (length == 1) {
if (encoder.fromUnicodeStatus != 0) {
/* in DBCS mode: switch to SBCS */
encoder.fromUnicodeStatus = 0;
buffer[i++] = UConverterConstants.SI;
}
buffer[i++] = subchar[0];
} else { /* length == 2 */
if (encoder.fromUnicodeStatus == 0) {
/* in SBCS mode: switch to DBCS */
encoder.fromUnicodeStatus = 1;
buffer[i++] = UConverterConstants.SO;
}
buffer[i++] = subchar[0];
buffer[i++] = subchar[1];
}
err = CharsetEncoderICU.fromUWriteBytes(this, buffer, 0, i, target, offsets, source.position() - 1);
} else {
/* save the subvonverter's substitution string */
byte[] currentSubChars = myConverterData.currentEncoder.replacement();
/* set our substitution string into the subconverter */
myConverterData.currentEncoder.replaceWith(subchar);
myConverterData.currentConverter.subChar1 = fromUSubstitutionChar[0][0];
/* let the subconverter write the subchar, set/retrieve fromUChar32 state */
myConverterData.currentEncoder.fromUChar32 = encoder.fromUChar32;
err = myConverterData.currentEncoder.cbFromUWriteSub(myConverterData.currentEncoder, source, target, offsets);
encoder.fromUChar32 = myConverterData.currentEncoder.fromUChar32;
/* restore the subconverter's substitution string */
myConverterData.currentEncoder.replaceWith(currentSubChars);
if (err.isOverflow()) {
if (myConverterData.currentEncoder.errorBufferLength > 0) {
encoder.errorBuffer = myConverterData.currentEncoder.errorBuffer.clone();
}
encoder.errorBufferLength = myConverterData.currentEncoder.errorBufferLength;
myConverterData.currentEncoder.errorBufferLength = 0;
}
}
return err;
}
private CoderResult encodeLoopIBM(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush) {
CoderResult err = CoderResult.UNDERFLOW;
myConverterData.currentEncoder.fromUChar32 = fromUChar32;
err = myConverterData.currentEncoder.cnvMBCSFromUnicodeWithOffsets(source, target, offsets, flush);
fromUChar32 = myConverterData.currentEncoder.fromUChar32;
if (err.isOverflow()) {
if (myConverterData.currentEncoder.errorBufferLength > 0) {
errorBuffer = myConverterData.currentEncoder.errorBuffer.clone();
}
errorBufferLength = myConverterData.currentEncoder.errorBufferLength;
myConverterData.currentEncoder.errorBufferLength = 0;
}
return err;
}
protected CoderResult encodeLoop(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush) {
CoderResult err = CoderResult.UNDERFLOW;
int[] targetByteUnit = { 0x0000 };
int sourceChar = 0x0000;
boolean isTargetByteDBCS;
boolean oldIsTargetByteDBCS;
boolean usingFallback;
int length = 0;
boolean gotoGetTrail = false; // for goto getTrail label call
/*
* if the version is 1 then the user is requesting
* conversion with ibm-25546 pass the argument to
* MBCS converter and return
*/
if (myConverterData.version == 1) {
return encodeLoopIBM(source, target, offsets, flush);
}
usingFallback = useFallback;
isTargetByteDBCS = fromUnicodeStatus == 0 ? false : true;
if ((sourceChar = fromUChar32) != 0 && target.hasRemaining()) {
gotoGetTrail = true;
}
while (source.hasRemaining() || gotoGetTrail) {
targetByteUnit[0] = UConverterConstants.missingCharMarker;
if (target.hasRemaining() || gotoGetTrail) {
if (!gotoGetTrail) {
sourceChar = source.get();
/* do not convert SO/SI/ESC */
if (IS_2022_CONTROL(sourceChar)) {
/* callback(illegal) */
err = CoderResult.malformedForLength(1);
fromUChar32 = sourceChar;
break;
}
myConverterData.currentConverter.sharedData.mbcs.outputType = CharsetMBCS.MBCS_OUTPUT_2;
length = myConverterData.currentEncoder.fromUChar32(sourceChar, targetByteUnit, usingFallback);
//length = MBCSFromUChar32_ISO2022(myConverterData.currentConverter.sharedData, sourceChar, targetByteUnit, usingFallback, CharsetMBCS.MBCS_OUTPUT_2);
if (length < 0) {
length = -length; /* fallback */
}
/* only DBCS or SBCS characters are expected */
/* DB characters with high bit set to 1 are expected */
if (length > 2 || length == 0 ||
(length == 1 && targetByteUnit[0] > 0x7f) ||
(length ==2 &&
((char)(targetByteUnit[0] - 0xa1a1) > (0xfefe - 0xa1a1) ||
((targetByteUnit[0] - 0xa1) & UConverterConstants.UNSIGNED_BYTE_MASK) > (0xfe - 0xa1)))) {
targetByteUnit[0] = UConverterConstants.missingCharMarker;
}
}
if (!gotoGetTrail && targetByteUnit[0] != UConverterConstants.missingCharMarker) {
oldIsTargetByteDBCS = isTargetByteDBCS;
isTargetByteDBCS = (targetByteUnit[0] > 0x00FF);
/* append the shift sequence */
if (oldIsTargetByteDBCS != isTargetByteDBCS) {
if (isTargetByteDBCS) {
target.put((byte)UConverterConstants.SO);
} else {
target.put((byte)UConverterConstants.SI);
}
if (offsets != null) {
offsets.put(source.position()-1);
}
}
/* write the targetUniChar to target */
if (targetByteUnit[0] <= 0x00FF) {
if (target.hasRemaining()) {
target.put((byte)targetByteUnit[0]);
if (offsets != null) {
offsets.put(source.position()-1);
}
} else {
errorBuffer[errorBufferLength++] = (byte)targetByteUnit[0];
err = CoderResult.OVERFLOW;
}
} else {
if (target.hasRemaining()) {
target.put((byte)(UConverterConstants.UNSIGNED_BYTE_MASK & ((targetByteUnit[0]>>8) - 0x80)));
if (offsets != null) {
offsets.put(source.position()-1);
}
if (target.hasRemaining()) {
target.put((byte)(UConverterConstants.UNSIGNED_BYTE_MASK & (targetByteUnit[0]- 0x80)));
if (offsets != null) {
offsets.put(source.position()-1);
}
} else {
errorBuffer[errorBufferLength++] = (byte)(UConverterConstants.UNSIGNED_BYTE_MASK & (targetByteUnit[0] - 0x80));
err = CoderResult.OVERFLOW;
}
} else {
errorBuffer[errorBufferLength++] = (byte)(UConverterConstants.UNSIGNED_BYTE_MASK & ((targetByteUnit[0]>>8) - 0x80));
errorBuffer[errorBufferLength++] = (byte)(UConverterConstants.UNSIGNED_BYTE_MASK & (targetByteUnit[0]- 0x80));
err = CoderResult.OVERFLOW;
}
}
} else {
/* oops.. the code point is unassigned
* set the error and reason
*/
/* check if the char is a First surrogate */
if (gotoGetTrail || UTF16.isSurrogate((char)sourceChar)) {
if (gotoGetTrail || UTF16.isLeadSurrogate((char)sourceChar)) {
// getTrail label
// reset gotoGetTrail flag
gotoGetTrail = false;
/* look ahead to find the trail surrogate */
if (source.hasRemaining()) {
/* test the following code unit */
char trail = source.get();
source.position(source.position()-1);
if (UTF16.isTrailSurrogate(trail)) {
source.get();
sourceChar = UCharacter.getCodePoint((char)sourceChar, trail);
err = CoderResult.unmappableForLength(2);
/* convert this surrogate code point */
/* exit this condition tree */
} else {
/* this is an unmatched lead code unit (1st surrogate) */
/* callback(illegal) */
err = CoderResult.malformedForLength(1);
}
} else {
/* no more input */
err = CoderResult.UNDERFLOW;
}
} else {
/* this is an unmatched trail code unit (2nd surrogate ) */
/* callback(illegal) */
err = CoderResult.malformedForLength(1);
}
} else {
/* callback(unassigned) for a BMP code point */
err = CoderResult.unmappableForLength(1);
}
fromUChar32 = sourceChar;
break;
}
} else {
err = CoderResult.OVERFLOW;
break;
}
}
/*
* the end of the input stream and detection of truncated input
* are handled by the framework, but for ISO-2022-KR conversion
* we need to be inASCII mode at the very end
*
* conditions:
* successful
* not in ASCII mode
* end of input and no truncated input
*/
if (!err.isError() && isTargetByteDBCS && flush && !source.hasRemaining() && fromUChar32 == 0) {
int sourceIndex;
/* we are switching to ASCII */
isTargetByteDBCS = false;
/* get the source index of the last input character */
sourceIndex = source.position();
if (sourceIndex > 0) {
--sourceIndex;
if (UTF16.isTrailSurrogate(source.get(sourceIndex)) && UTF16.isLeadSurrogate(source.get(sourceIndex-1))) {
--sourceIndex;
}
} else {
sourceIndex = -1;
}
CharsetEncoderICU.fromUWriteBytes(this, SHIFT_IN_STR, 0, 1, target, offsets, sourceIndex);
}
/*save the state and return */
fromUnicodeStatus = isTargetByteDBCS ? 1 : 0;
return err;
}
}
public CharsetDecoder newDecoder() {
switch (variant) {
case ISO_2022_JP:
return new CharsetDecoderISO2022JP(this);
case ISO_2022_CN:
return new CharsetDecoderISO2022CN(this);
case ISO_2022_KR:
setInitialStateToUnicodeKR();
return new CharsetDecoderISO2022KR(this);
default: /* should not happen */
return null;
}
}
public CharsetEncoder newEncoder() {
CharsetEncoderICU cnv;
switch (variant) {
case ISO_2022_JP:
return new CharsetEncoderISO2022JP(this);
case ISO_2022_CN:
return new CharsetEncoderISO2022CN(this);
case ISO_2022_KR:
cnv = new CharsetEncoderISO2022KR(this);
setInitialStateFromUnicodeKR(cnv);
return cnv;
default: /* should not happen */
return null;
}
}
private void setInitialStateToUnicodeKR() {
if (myConverterData.version == 1) {
myConverterData.currentDecoder.toUnicodeStatus = 0; /* offset */
myConverterData.currentDecoder.mode = 0; /* state */
myConverterData.currentDecoder.toULength = 0; /* byteIndex */
}
}
private void setInitialStateFromUnicodeKR(CharsetEncoderICU cnv) {
/* ISO-2022-KR the designator sequence appears only once
* in a file so we append it only once
*/
if (cnv.errorBufferLength == 0) {
cnv.errorBufferLength = 4;
cnv.errorBuffer[0] = 0x1b;
cnv.errorBuffer[1] = 0x24;
cnv.errorBuffer[2] = 0x29;
cnv.errorBuffer[3] = 0x43;
}
if (myConverterData.version == 1) {
((CharsetMBCS)myConverterData.currentEncoder.charset()).subChar1 = 0x1A;
myConverterData.currentEncoder.fromUChar32 = 0;
myConverterData.currentEncoder.fromUnicodeStatus = 1; /* prevLength */
}
}
void getUnicodeSetImpl(UnicodeSet setFillIn, int which) {
int i;
/*open a set and initialize it with code points that are algorithmically round-tripped */
switch(variant){
case ISO_2022_JP:
/*include JIS X 0201 which is hardcoded */
setFillIn.add(0xa5);
setFillIn.add(0x203e);
if((jpCharsetMasks[myConverterData.version]&CSM(ISO8859_1))!=0){
/*include Latin-1 some variants of JP */
setFillIn.add(0, 0xff);
}
else {
/* include ASCII for JP */
setFillIn.add(0, 0x7f);
}
if(myConverterData.version==3 || myConverterData.version==4 ||which == ROUNDTRIP_AND_FALLBACK_SET){
/*
* Do not test(jpCharsetMasks[myConverterData.version]&CSM(HWKANA_7BIT))!=0 because the bit
* is on for all JP versions although version 3 & 4 (JIS7 and JIS8) use half-width Katakana.
* This is because all ISO_2022_JP variant are lenient in that they accept (in toUnicode) half-width
* Katakana via ESC.
* However, we only emit (fromUnicode) half-width Katakana according to the
* definition of each variant.
*
* When including fallbacks,
* we need to include half-width Katakana Unicode code points for all JP variants because
* JIS X 0208 has hardcoded fallbacks for them (which map to full-width Katakana).
*/
/* include half-width Katakana for JP */
setFillIn.add(HWKANA_START, HWKANA_END);
}
break;
case ISO_2022_CN:
/* Include ASCII for CN */
setFillIn.add(0, 0x7f);
break;
case ISO_2022_KR:
/* there is only one converter for KR */
myConverterData.currentConverter.getUnicodeSetImpl(setFillIn, which);
break;
default:
break;
}
//TODO Replaced by ucnv_MBCSGetFilteredUnicodeSetForUnicode() until
for(i=0; i<UCNV_2022_MAX_CONVERTERS;i++){
int filter;
if(myConverterData.myConverterArray[i]!=null){
if(variant==ISO_2022_CN && myConverterData.version==0 && i==CNS_11643){
/*
*
* version -specific for CN:
* CN version 0 does not map CNS planes 3..7 although
* they are all available in the CNS conversion table;
* CN version 1 (-EXT) does map them all.
* The two versions create different Unicode sets.
*/
filter=CharsetMBCS.UCNV_SET_FILTER_2022_CN;
} else if(variant==ISO_2022_JP && i == JISX208){
/*
* Only add code points that map to Shift-JIS codes
* corrosponding to JIS X 208
*/
filter=CharsetMBCS.UCNV_SET_FILTER_SJIS;
} else if(i==KSC5601){
/*
* Some of the KSC 5601 tables (Convrtrs.txt has this aliases on multiple tables)
* are broader than GR94.
*/
filter=CharsetMBCS.UCNV_SET_FILTER_GR94DBCS;
} else {
filter=CharsetMBCS.UCNV_SET_FILTER_NONE;
}
myConverterData.currentConverter.MBCSGetFilteredUnicodeSetForUnicode(myConverterData.myConverterArray[i],setFillIn, which, filter);
}
}
/*
* ISO Converter must not convert SO/SI/ESC despite what sub-converters do by themselves
* Remove these characters from the set.
*/
setFillIn.remove(0x0e);
setFillIn.remove(0x0f);
setFillIn.remove(0x1b);
/* ISO 2022 converter do not convert C! controls either */
setFillIn.remove(0x80, 0x9f);
}
}