private int[] calculateNewlineIndicesAndCheckCodePoints(StringBuilder inputData) throws SnuggleParseException { List<Integer> newlineIndicesBuilder = new ArrayList<Integer>(); newlineIndicesBuilder.add(Integer.valueOf(-1)); char lastChar = 0; char thisChar; /* (16 bit char only) */ int codePoint; /* (Full Unicode code point */ for (int i = 0, length = inputData.length(); i < length; i++, lastChar = thisChar) { thisChar = inputData.charAt(i); if (thisChar == '\n') { newlineIndicesBuilder.add(Integer.valueOf(i)); } if (Character.isHighSurrogate(lastChar)) { if (Character.isLowSurrogate(thisChar)) { codePoint = Character.toCodePoint(lastChar, thisChar); } else { /* Error: last was bad surrogate character */ recordSurrogateError(inputData, i - 1, lastChar); continue; } } else if (Character.isLowSurrogate(thisChar)) { /* Error: this is bad surrogate character */ recordSurrogateError(inputData, i, thisChar); continue; } else { codePoint = thisChar; } /* Check that we allow this codepoint */ if (Character.isISOControl(codePoint) && !(codePoint == '\r' || codePoint == '\n' || codePoint == '\t')) { sessionContext.registerError( new InputError( CoreErrorCode.TTEG02, null, Integer.toHexString(codePoint), Integer.valueOf(i))); inputData.setCharAt(i, ' '); } } /* Make sure last character wasn't surrogate pair starter */ if (Character.isHighSurrogate(lastChar)) { recordSurrogateError(inputData, inputData.length() - 1, lastChar); } /* Finally store newline information */ int[] calculatedNewlineIndices = new int[newlineIndicesBuilder.size()]; for (int i = 0; i < calculatedNewlineIndices.length; i++) { calculatedNewlineIndices[i] = newlineIndicesBuilder.get(i); } return calculatedNewlineIndices; }
/** * 功能:cs串中是否一个都不包含字符数组searchChars中的字符。 * * @author Darlen * @param cs 字符串 * @param searchChars 字符数组 * @return boolean 都不包含返回true,否则返回false。 * @date 2014年06月24日 */ public static boolean containsNone(CharSequence cs, char... searchChars) { if (cs == null || searchChars == null) { return true; } int csLen = cs.length(); int csLast = csLen - 1; int searchLen = searchChars.length; int searchLast = searchLen - 1; for (int i = 0; i < csLen; i++) { char ch = cs.charAt(i); for (int j = 0; j < searchLen; j++) { if (searchChars[j] == ch) { if (Character.isHighSurrogate(ch)) { if (j == searchLast) { // missing low surrogate, fine, like // String.indexOf(String) return false; } if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) { return false; } } else { // ch is in the Basic Multilingual Plane return false; } } } } return true; }
/** {@inheritDoc} */ @Override public int read() throws IOException { int ic = buffer.get(bufferPosition); // End of input if (ic == -1) { buffer.freeBefore(bufferPosition); return ic; } char c = (char) ic; // Skip surrogate pair characters if (Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) { iterationMarkSpanEndPosition = bufferPosition + 1; } // Free rolling buffer on full stop if (c == FULL_STOP_PUNCTUATION) { buffer.freeBefore(bufferPosition); iterationMarkSpanEndPosition = bufferPosition + 1; } // Normalize iteration mark if (isIterationMark(c)) { c = normalizeIterationMark(c); } bufferPosition++; return c; }
public static int correctSubStringLen(String input, int len) { if (Character.isHighSurrogate(input.charAt(len - 1))) { assert input.length() >= len + 1 && Character.isLowSurrogate(input.charAt(len)); return len + 1; } return len; }
/** * Check if the given {@code index} is between UTF-16 surrogate pair. * * @param str The String. * @param index The index * @return True if the index is between UTF-16 surrogate pair, false otherwise. */ @VisibleForTesting static boolean isIndexBetweenUtf16SurrogatePair(CharSequence str, int index) { return index > 0 && index < str.length() && Character.isHighSurrogate(str.charAt(index - 1)) && Character.isLowSurrogate(str.charAt(index)); }
private void finishComposition() { int len = buffer.length(); if (len == 6 && format != SPECIAL_ESCAPE) { char codePoint = (char) getCodePoint(buffer, 2, 5); if (Character.isValidCodePoint(codePoint) && codePoint != 0xFFFF) { buffer.setLength(0); buffer.append(codePoint); sendCommittedText(); return; } } else if (len == 8 && format == SPECIAL_ESCAPE) { int codePoint = getCodePoint(buffer, 2, 7); if (Character.isValidCodePoint(codePoint) && codePoint != 0xFFFF) { buffer.setLength(0); buffer.appendCodePoint(codePoint); sendCommittedText(); return; } } else if (len == 12 && format == SURROGATE_PAIR) { char[] codePoint = {(char) getCodePoint(buffer, 2, 5), (char) getCodePoint(buffer, 8, 11)}; if (Character.isHighSurrogate(codePoint[0]) && Character.isLowSurrogate(codePoint[1])) { buffer.setLength(0); buffer.append(codePoint); sendCommittedText(); return; } } beep(); }
/** NOTE: The sourceX2 is exclusive. */ public void copyInterval(TerminalRow line, int sourceX1, int sourceX2, int destinationX) { final int x1 = line.findStartOfColumn(sourceX1); final int x2 = line.findStartOfColumn(sourceX2); boolean startingFromSecondHalfOfWideChar = (sourceX1 > 0 && line.wideDisplayCharacterStartingAt(sourceX1 - 1)); final char[] sourceChars = (this == line) ? Arrays.copyOf(line.mText, line.mText.length) : line.mText; int latestNonCombiningWidth = 0; for (int i = x1; i < x2; i++) { char sourceChar = sourceChars[i]; int codePoint = Character.isHighSurrogate(sourceChar) ? Character.toCodePoint(sourceChar, sourceChars[++i]) : sourceChar; if (startingFromSecondHalfOfWideChar) { // Just treat copying second half of wide char as copying whitespace. codePoint = ' '; startingFromSecondHalfOfWideChar = false; } int w = WcWidth.width(codePoint); if (w > 0) { destinationX += latestNonCombiningWidth; sourceX1 += latestNonCombiningWidth; latestNonCombiningWidth = w; } setChar(destinationX, codePoint, line.getStyle(sourceX1)); } }
protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) { int mark = src.position(); if (!doneBOM && src.hasRemaining()) { if (dst.remaining() < 4) return CoderResult.OVERFLOW; put(BOM_BIG, dst); doneBOM = true; } try { while (src.hasRemaining()) { char c = src.get(); if (!Character.isSurrogate(c)) { if (dst.remaining() < 4) return CoderResult.OVERFLOW; mark++; put(c, dst); } else if (Character.isHighSurrogate(c)) { if (!src.hasRemaining()) return CoderResult.UNDERFLOW; char low = src.get(); if (Character.isLowSurrogate(low)) { if (dst.remaining() < 4) return CoderResult.OVERFLOW; mark += 2; put(Character.toCodePoint(c, low), dst); } else { return CoderResult.malformedForLength(1); } } else { // assert Character.isLowSurrogate(c); return CoderResult.malformedForLength(1); } } return CoderResult.UNDERFLOW; } finally { src.position(mark); } }
public static void writeString(Writer out, String value) throws IOException { out.write('"'); char[] array = null; for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); switch (c) { case '"': out.write("\\\""); break; case '\\': out.write("\\\\"); break; case '\n': out.write("\\n"); break; case '\t': out.write("\\t"); break; case '\r': out.write("\\r"); break; case '\0': out.write("\\0"); break; default: if (Character.isISOControl(c)) { // Encode as: "x" + two hex digits. if (array == null) { array = new char[4]; array[0] = '\\'; } array[1] = 'x'; array[3] = Hex.charAt(c & 0xf); c >>= 4; array[2] = Hex.charAt(c & 0xf); out.write(array, 0, 4); } else if (Character.isHighSurrogate(c)) { // Surrogate pair i++; if (i >= value.length()) { throw new IllegalArgumentException("high surrogate not followed by anything"); } char c2 = value.charAt(i); if (!Character.isLowSurrogate(c2)) { throw new IllegalArgumentException("high surrogate not followed by low surrogate"); } out.write(value, i - 1, 2); } else if (Character.isLowSurrogate(c)) { throw new IllegalArgumentException("low surrogate without preceding high surrogate"); } else { // Basic Multilingual Plane (16 bits) out.write(c); } } } out.write('"'); }
public int next() { char ch = data.charAt(index++); if (Character.isHighSurrogate(ch)) { int ret = Character.toCodePoint(ch, data.charAt(index++)); index += 2; return ret; } else { return ch; } }
/** Returns the count of next character. */ private int getCurrentCodePointCount() { char c1 = text.current(); if (Character.isHighSurrogate(c1) && text.getIndex() < text.getEndIndex()) { char c2 = text.next(); text.previous(); if (Character.isLowSurrogate(c2)) { return 2; } } return 1; }
/** Returns current character */ int getCurrent() { char c1 = text.current(); if (Character.isHighSurrogate(c1) && text.getIndex() < text.getEndIndex()) { char c2 = text.next(); text.previous(); if (Character.isLowSurrogate(c2)) { return Character.toCodePoint(c1, c2); } } return (int) c1; }
/** Returns previous character */ private int getPrevious() { char c2 = text.previous(); if (Character.isLowSurrogate(c2) && text.getIndex() > text.getBeginIndex()) { char c1 = text.previous(); if (Character.isHighSurrogate(c1)) { return Character.toCodePoint(c1, c2); } else { text.next(); } } return (int) c2; }
/** * Refills the input buffer. * * @return <code>false</code>, iff there was new input. * @exception java.io.IOException if any I/O-Error occurs */ private boolean zzRefill() throws java.io.IOException { /* first: make room (if you can) */ if (zzStartRead > 0) { zzEndRead += zzFinalHighSurrogate; zzFinalHighSurrogate = 0; System.arraycopy(zzBuffer, zzStartRead, zzBuffer, 0, zzEndRead - zzStartRead); /* translate stored positions */ zzEndRead -= zzStartRead; zzCurrentPos -= zzStartRead; zzMarkedPos -= zzStartRead; zzStartRead = 0; } /* is the buffer big enough? */ if (zzCurrentPos >= zzBuffer.length - zzFinalHighSurrogate) { /* if not: blow it up */ char newBuffer[] = new char[zzBuffer.length * 2]; System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); zzBuffer = newBuffer; zzEndRead += zzFinalHighSurrogate; zzFinalHighSurrogate = 0; } /* fill the buffer with new input */ int requested = zzBuffer.length - zzEndRead; int numRead = zzReader.read(zzBuffer, zzEndRead, requested); /* not supposed to occur according to specification of java.io.Reader */ if (numRead == 0) { throw new java.io.IOException( "Reader returned 0 characters. See JFlex examples for workaround."); } if (numRead > 0) { zzEndRead += numRead; /* If numRead == requested, we might have requested to few chars to encode a full Unicode character. We assume that a Reader would otherwise never return half characters. */ if (numRead == requested) { if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { --zzEndRead; zzFinalHighSurrogate = 1; } } /* potentially more input available */ return false; } /* numRead < 0 ==> end of stream */ return true; }
/** * Refills the input buffer. * * @return <code>false</code>, iff there was new input. * @exception java.io.IOException if any I/O-Error occurs */ private boolean zzRefill() throws java.io.IOException { /* first: make room (if you can) */ if (zzStartRead > 0) { zzEndRead += zzFinalHighSurrogate; zzFinalHighSurrogate = 0; System.arraycopy(zzBuffer, zzStartRead, zzBuffer, 0, zzEndRead - zzStartRead); /* translate stored positions */ zzEndRead -= zzStartRead; zzCurrentPos -= zzStartRead; zzMarkedPos -= zzStartRead; zzStartRead = 0; } /* is the buffer big enough? */ if (zzCurrentPos >= zzBuffer.length - zzFinalHighSurrogate) { /* if not: blow it up */ char newBuffer[] = new char[zzBuffer.length * 2]; System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); zzBuffer = newBuffer; zzEndRead += zzFinalHighSurrogate; zzFinalHighSurrogate = 0; } /* fill the buffer with new input */ int requested = zzBuffer.length - zzEndRead; int totalRead = 0; while (totalRead < requested) { int numRead = zzReader.read(zzBuffer, zzEndRead + totalRead, requested - totalRead); if (numRead == -1) { break; } totalRead += numRead; } if (totalRead > 0) { zzEndRead += totalRead; if (totalRead == requested) { /* possibly more input available */ if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { --zzEndRead; zzFinalHighSurrogate = 1; } } return false; } // totalRead = 0: End of stream return true; }
/** * Converts a string to a UTF8 byte array. * * @param string string to be converted * @return byte array */ private static byte[] utf8(final String string) { final char[] arr = string.toCharArray(); final int al = arr.length; final TokenBuilder tb = new TokenBuilder(al << 1); for (int c = 0; c < al; ++c) { final char ch = arr[c]; tb.add( Character.isHighSurrogate(ch) && c < al - 1 && Character.isLowSurrogate(arr[c + 1]) ? Character.toCodePoint(ch, arr[++c]) : ch); } return tb.finish(); }
public int previousCodePoint() { int ch1 = previous(); if (Character.isLowSurrogate((char) ch1)) { int ch2 = previous(); if (Character.isHighSurrogate((char) ch2)) { return Character.toCodePoint((char) ch2, (char) ch1); } else if (ch2 != DONE) { // unmatched trail surrogate so back out next(); } } return ch1; }
/** Note that the column may end of second half of wide character. */ public int findStartOfColumn(int column) { if (column == mColumns) return getSpaceUsed(); int currentColumn = 0; int currentCharIndex = 0; while (true) { // 0<2 1 < 2 int newCharIndex = currentCharIndex; char c = mText[newCharIndex++]; // cci=1, cci=2 boolean isHigh = Character.isHighSurrogate(c); int codePoint = isHigh ? Character.toCodePoint(c, mText[newCharIndex++]) : c; int wcwidth = WcWidth.width(codePoint); // 1, 2 if (wcwidth > 0) { currentColumn += wcwidth; if (currentColumn == column) { while (newCharIndex < mSpaceUsed) { // Skip combining chars. if (Character.isHighSurrogate(mText[newCharIndex])) { if (WcWidth.width(Character.toCodePoint(mText[newCharIndex], mText[newCharIndex + 1])) <= 0) { newCharIndex += 2; } else { break; } } else if (WcWidth.width(mText[newCharIndex]) <= 0) { newCharIndex++; } else { break; } } return newCharIndex; } else if (currentColumn > column) { // Wide column going past end. return currentCharIndex; } } currentCharIndex = newCharIndex; } }
private boolean wideDisplayCharacterStartingAt(int column) { for (int currentCharIndex = 0, currentColumn = 0; currentCharIndex < mSpaceUsed; ) { char c = mText[currentCharIndex++]; int codePoint = Character.isHighSurrogate(c) ? Character.toCodePoint(c, mText[currentCharIndex++]) : c; int wcwidth = WcWidth.width(codePoint); if (wcwidth > 0) { if (currentColumn == column && wcwidth == 2) return true; currentColumn += wcwidth; if (currentColumn > column) return false; } } return false; }
/** * Adjusts entity indices for supplementary characters (Emoji being the most common example) in * UTF-8 (ones outside of U+0000 to U+FFFF range) are represented as a pair of char values, the * first from the high-surrogates range, and the second from the low-surrogates range. * * @param content The content of the tweet * @param formattedTweetText The formatted tweet text with entities that we need to adjust */ static void adjustIndicesForSupplementaryChars( StringBuilder content, FormattedTweetText formattedTweetText) { final List<Integer> highSurrogateIndices = new ArrayList<>(); final int len = content.length() - 1; for (int i = 0; i < len; ++i) { if (Character.isHighSurrogate(content.charAt(i)) && Character.isLowSurrogate(content.charAt(i + 1))) { highSurrogateIndices.add(i); } } adjustEntitiesWithOffsets(formattedTweetText.urlEntities, highSurrogateIndices); adjustEntitiesWithOffsets(formattedTweetText.mediaEntities, highSurrogateIndices); }
private void waitDigit2(char c) { if (Character.digit(c, 16) != -1) { buffer.insert(insertionPoint++, c); char codePoint = (char) getCodePoint(buffer, 2, 5); if (Character.isHighSurrogate(codePoint)) { format = SURROGATE_PAIR; buffer.append("\\u"); insertionPoint = 8; } else { format = ESCAPE; } sendComposedText(); } else { beep(); } }
/** * Get the length for UTF8-encoding a string without encoding it first * * @param s The string to calculate the length for * @return The length when serialized */ public static int utf8Length(CharSequence s) { int count = 0; for (int i = 0, len = s.length(); i < len; i++) { char ch = s.charAt(i); if (ch <= 0x7F) { count++; } else if (ch <= 0x7FF) { count += 2; } else if (Character.isHighSurrogate(ch)) { count += 4; ++i; } else { count += 3; } } return count; }
@Override public boolean fill(final CharacterBuffer buffer, final Reader reader) throws IOException { final char[] charBuffer = buffer.buffer; buffer.offset = 0; charBuffer[0] = buffer.lastTrailingHighSurrogate; final int offset = buffer.lastTrailingHighSurrogate == 0 ? 0 : 1; buffer.lastTrailingHighSurrogate = 0; final int read = reader.read(charBuffer, offset, charBuffer.length - offset); if (read == -1) { buffer.length = offset; return offset != 0; } buffer.length = read + offset; // special case if the read returns 0 and the lastTrailingHighSurrogate was set if (buffer.length > 1 && Character.isHighSurrogate(charBuffer[buffer.length - 1])) { buffer.lastTrailingHighSurrogate = charBuffer[--buffer.length]; } return true; }
public int encode(char[] src, int sp, int len, byte[] dst) { int dp = 0; int sl = sp + Math.min(len, dst.length); while (sp < sl) { char c = src[sp++]; int b = encode(c); if (b != UNMAPPABLE_ENCODING) { dst[dp++] = (byte) b; continue; } if (Character.isHighSurrogate(c) && sp < sl && Character.isLowSurrogate(src[sp])) { if (len > dst.length) { sl++; len--; } sp++; } dst[dp++] = repl; } return dp; }
private static CharBuffer _getRawCharBuffer(String rawString, int start) { int count = 0; for (int i = start; i < rawString.length(); i++) { char rawChar = rawString.charAt(i); if (!_validChars.get(rawChar)) { count++; if (Character.isHighSurrogate(rawChar)) { if (((i + 1) < rawString.length()) && Character.isLowSurrogate(rawString.charAt(i + 1))) { count++; } } } else { break; } } return CharBuffer.wrap(rawString, start, start + count); }
/** * Parses a UCS-4 character from the given source buffer, handling surrogates. * * @param c The first character * @param in The source buffer, from which one more character will be consumed if c is a high * surrogate * @returns Either a parsed UCS-4 character, in which case the isPair() and increment() methods * will return meaningful values, or -1, in which case error() will return a descriptive * result object */ public int parse(char c, CharBuffer in) { if (Character.isHighSurrogate(c)) { if (!in.hasRemaining()) { error = CoderResult.UNDERFLOW; return -1; } char d = in.get(); if (Character.isLowSurrogate(d)) { character = Character.toCodePoint(c, d); error = null; return character; } error = CoderResult.malformedForLength(1); return -1; } if (Character.isLowSurrogate(c)) { error = CoderResult.malformedForLength(1); return -1; } character = c; error = null; return character; }
/* */ protected CoderResult encodeLoop( CharBuffer paramCharBuffer, ByteBuffer paramByteBuffer) { /* 146 */ int i = paramCharBuffer.position(); /* 147 */ if ((!this.doneBOM) && (paramCharBuffer.hasRemaining())) { /* 148 */ if (paramByteBuffer.remaining() < 4) /* 149 */ return CoderResult.OVERFLOW; /* 150 */ put(65279, paramByteBuffer); /* 151 */ this.doneBOM = true; /* */ } /* */ try { /* 154 */ while (paramCharBuffer.hasRemaining()) { /* 155 */ char c1 = paramCharBuffer.get(); /* */ CoderResult localCoderResult2; /* 156 */ if (!Character.isSurrogate(c1)) { /* 157 */ if (paramByteBuffer.remaining() < 4) /* 158 */ return CoderResult.OVERFLOW; /* 159 */ i++; /* 160 */ put(c1, paramByteBuffer); /* 161 */ } else if (Character.isHighSurrogate(c1)) { /* 162 */ if (!paramCharBuffer.hasRemaining()) /* 163 */ return CoderResult.UNDERFLOW; /* 164 */ char c2 = paramCharBuffer.get(); /* */ CoderResult localCoderResult4; /* 165 */ if (Character.isLowSurrogate(c2)) { /* 166 */ if (paramByteBuffer.remaining() < 4) /* 167 */ return CoderResult.OVERFLOW; /* 168 */ i += 2; /* 169 */ put(Character.toCodePoint(c1, c2), paramByteBuffer); /* */ } else { /* 171 */ return CoderResult.malformedForLength(1); /* */ } /* */ } /* */ else { /* 175 */ return CoderResult.malformedForLength(1); /* */ } /* */ } /* 178 */ return CoderResult.UNDERFLOW; /* */ } finally { /* 180 */ paramCharBuffer.position(i); /* */ } /* */ }
/** * Parses a UCS-4 character from the given source buffer, handling surrogates. * * @param c The first character * @param ia The input array, from which one more character will be consumed if c is a high * surrogate * @param ip The input index * @param il The input limit * @returns Either a parsed UCS-4 character, in which case the isPair() and increment() methods * will return meaningful values, or -1, in which case error() will return a descriptive * result object */ public int parse(char c, char[] ia, int ip, int il) { assert (ia[ip] == c); if (Character.isHighSurrogate(c)) { if (il - ip < 2) { error = CoderResult.UNDERFLOW; return -1; } char d = ia[ip + 1]; if (Character.isLowSurrogate(d)) { character = Character.toCodePoint(c, d); error = null; return character; } error = CoderResult.malformedForLength(1); return -1; } if (Character.isLowSurrogate(c)) { error = CoderResult.malformedForLength(1); return -1; } character = c; error = null; return character; }
private void printString(String o) throws IOException { append('\"'); char[] chars = o.toCharArray(); for (int i = 0; i < chars.length; i++) { char ch = chars[i]; switch (ch) { case '\"': append('\\'); append('\"'); break; case '>': append('\\'); append('>'); break; case '<': append('\\'); append('<'); break; case '\'': append('\\'); append('\''); break; case '\\': append('\\'); append('\\'); break; case '\n': append('\\'); append('n'); break; case '\r': append('\\'); append('r'); break; case '\t': append('\\'); append('t'); break; case ' ': // needed because other space chars will be escaped in the default branch append(' '); break; default: int cp = Character.codePointAt(chars, i); if (Character.isSpaceChar(cp) || Character.isISOControl(cp) || Character.UnicodeBlock.SPECIALS.equals(Character.UnicodeBlock.of(cp))) { // these characters are invisible or otherwise unreadable and we escape them here // for clarity of the serialized string if (cp <= Byte.MAX_VALUE) { append("\\a" + String.format("%02x", (int) ch)); } else if (cp <= Character.MAX_VALUE) { append("\\u" + String.format("%04x", (int) ch)); } else { append("\\U" + String.format("%06x", (int) ch)); } if (Character.isHighSurrogate(ch)) { i++; // skip the next char } } else { append(ch); if (Character.isHighSurrogate(ch) && i + 1 < chars.length) { append(chars[++i]); } } } } append('\"'); }
// https://github.com/steven676/Android-Terminal-Emulator/commit/9a47042620bec87617f0b4f5d50568535668fe26 public void setChar(int columnToSet, int codePoint, long style) { mStyle[columnToSet] = style; final int newCodePointDisplayWidth = WcWidth.width(codePoint); final boolean newIsCombining = newCodePointDisplayWidth <= 0; boolean wasExtraColForWideChar = (columnToSet > 0) && wideDisplayCharacterStartingAt(columnToSet - 1); if (newIsCombining) { // When standing at second half of wide character and inserting combining: if (wasExtraColForWideChar) columnToSet--; } else { // Check if we are overwriting the second half of a wide character starting at the previous // column: if (wasExtraColForWideChar) setChar(columnToSet - 1, ' ', style); // Check if we are overwriting the first half of a wide character starting at the next column: boolean overwritingWideCharInNextColumn = newCodePointDisplayWidth == 2 && wideDisplayCharacterStartingAt(columnToSet + 1); if (overwritingWideCharInNextColumn) setChar(columnToSet + 1, ' ', style); } char[] text = mText; final int oldStartOfColumnIndex = findStartOfColumn(columnToSet); final int oldCodePointDisplayWidth = WcWidth.width(text, oldStartOfColumnIndex); // Get the number of elements in the mText array this column uses now int oldCharactersUsedForColumn; if (columnToSet + oldCodePointDisplayWidth < mColumns) { oldCharactersUsedForColumn = findStartOfColumn(columnToSet + oldCodePointDisplayWidth) - oldStartOfColumnIndex; } else { // Last character. oldCharactersUsedForColumn = mSpaceUsed - oldStartOfColumnIndex; } // Find how many chars this column will need int newCharactersUsedForColumn = Character.charCount(codePoint); if (newIsCombining) { // Combining characters are added to the contents of the column instead of overwriting them, // so that they // modify the existing contents. // FIXME: Put a limit of combining characters. // FIXME: Unassigned characters also get width=0. newCharactersUsedForColumn += oldCharactersUsedForColumn; } int oldNextColumnIndex = oldStartOfColumnIndex + oldCharactersUsedForColumn; int newNextColumnIndex = oldStartOfColumnIndex + newCharactersUsedForColumn; final int javaCharDifference = newCharactersUsedForColumn - oldCharactersUsedForColumn; if (javaCharDifference > 0) { // Shift the rest of the line right. int oldCharactersAfterColumn = mSpaceUsed - oldNextColumnIndex; if (mSpaceUsed + javaCharDifference > text.length) { // We need to grow the array char[] newText = new char[text.length + mColumns]; System.arraycopy(text, 0, newText, 0, oldStartOfColumnIndex + oldCharactersUsedForColumn); System.arraycopy( text, oldNextColumnIndex, newText, newNextColumnIndex, oldCharactersAfterColumn); mText = text = newText; } else { System.arraycopy( text, oldNextColumnIndex, text, newNextColumnIndex, oldCharactersAfterColumn); } } else if (javaCharDifference < 0) { // Shift the rest of the line left. System.arraycopy( text, oldNextColumnIndex, text, newNextColumnIndex, mSpaceUsed - oldNextColumnIndex); } mSpaceUsed += javaCharDifference; // Store char. A combining character is stored at the end of the existing contents so that it // modifies them: //noinspection ResultOfMethodCallIgnored - since we already now how many java chars is used. Character.toChars( codePoint, text, oldStartOfColumnIndex + (newIsCombining ? oldCharactersUsedForColumn : 0)); if (oldCodePointDisplayWidth == 2 && newCodePointDisplayWidth == 1) { // Replace second half of wide char with a space. Which mean that we actually add a ' ' java // character. if (mSpaceUsed + 1 > text.length) { char[] newText = new char[text.length + mColumns]; System.arraycopy(text, 0, newText, 0, newNextColumnIndex); System.arraycopy( text, newNextColumnIndex, newText, newNextColumnIndex + 1, mSpaceUsed - newNextColumnIndex); mText = text = newText; } else { System.arraycopy( text, newNextColumnIndex, text, newNextColumnIndex + 1, mSpaceUsed - newNextColumnIndex); } text[newNextColumnIndex] = ' '; ++mSpaceUsed; } else if (oldCodePointDisplayWidth == 1 && newCodePointDisplayWidth == 2) { if (columnToSet == mColumns - 1) { throw new IllegalArgumentException("Cannot put wide character in last column"); } else if (columnToSet == mColumns - 2) { // Truncate the line to the second part of this wide char: mSpaceUsed = (short) newNextColumnIndex; } else { // Overwrite the contents of the next column, which mean we actually remove java characters. // Due to the // check at the beginning of this method we know that we are not overwriting a wide char. int newNextNextColumnIndex = newNextColumnIndex + (Character.isHighSurrogate(mText[newNextColumnIndex]) ? 2 : 1); int nextLen = newNextNextColumnIndex - newNextColumnIndex; // Shift the array leftwards. System.arraycopy( text, newNextNextColumnIndex, text, newNextColumnIndex, mSpaceUsed - newNextNextColumnIndex); mSpaceUsed -= nextLen; } } }