in endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java [382:551]
private void write(final int c) throws IOException {
/*
* If the character to write is a EOL sequence, then:
*
* 1) Trim trailing whitespaces in the buffer.
* 2) Remove unused soft-hyphens (otherwise some consoles display them).
* 3) Flush the buffer to the underlying appendable.
* 4) Write the line separator.
*/
if (Characters.isLineOrParagraphSeparator(c)) {
final boolean skip;
switch (c) {
case '\r': skip = false; skipLF = true; break;
case '\n': skip = skipLF; skipLF = false; break;
default: skip = false; skipLF = false; break;
}
if (!skip) {
endOfLine();
}
if (!isEndOfLineReplaced) {
appendCodePoint(c); // Forward EOL sequences "as-is".
} else if (!skip) {
writeLineSeparator(); // Replace EOL sequences by the unique line separator.
}
return;
}
skipLF = false;
/*
* If the character to write is a whitespace, then write any pending characters from
* the buffer to the underlying appendable since we know that those characters didn't
* exceed the line length limit.
*
* We use `Character.isWhitespace(…)` instead of `Character.isSpaceChar(…)` because
* the former returns `true` for tabulations (which we want), and returns `false`
* for non-breaking spaces (which we also want).
*/
if (Character.isWhitespace(c)) {
if (printableLength != 0) {
deleteSoftHyphen(printableLength);
transfer(printableLength);
printableLength = 0;
}
if (c != '\t') {
codePointCount++;
} else {
final int width = tabulationWidth - (codePointCount % tabulationWidth);
codePointCount += width;
if (isTabulationExpanded) {
buffer.append(CharSequences.spaces(width));
return;
}
}
buffer.appendCodePoint(c);
return;
}
buffer.appendCodePoint(c);
printableLength = buffer.length();
/*
* Special handling of ANSI X3.64 escape sequences. Since they are not visible
* characters (they are used for controlling the colors), do not count them in
* `codePointCount` (but still count them as "printable" characters, since we
* don't want to trim them). The sequence pattern is "CSI <digits> <command>"
* where <command> is a single letter.
*/
if (c == X364.ESCAPE) {
isEscapeSequence = true;
return;
} else if (isEscapeSequence) {
final char previous = buffer.charAt(printableLength - 2);
if (previous != X364.ESCAPE) {
isEscapeSequence = (c >= '0' && c <= '9');
return; // The letter after the digits will be the last character to skip.
} else if (c == X364.BRACKET) {
return; // Found the second part of the Control Sequence Introducer (CSI).
}
// [ESC] was not followed by '['. Proceed as a normal character.
isEscapeSequence = false;
}
/*
* The remaining of this method is executed only if we exceeded the maximal line length.
* First, search for a dash character (hyphen) for splitting the line after it. If we do
* not find a dash character, as a fallback split on any non-letter or digit characters
* except the punctuation starts.
*/
if (++codePointCount < maximalLineLength) {
return;
}
int splitAt = buffer.length(); // Where to separate the line as two lines.
int fallback = splitAt; // Fallback to use if we could not find a value for `splitAt`.
boolean hasFallback = false; // Whether the `fallback` value has been defined.
split: for (;;) {
if (splitAt <= 0) {
splitAt = fallback;
break;
}
int b = buffer.codePointBefore(splitAt);
int n = Character.charCount(b);
switch (Character.getType(b)) {
case Character.UPPERCASE_LETTER:
case Character.LOWERCASE_LETTER:
case Character.TITLECASE_LETTER:
case Character.MODIFIER_LETTER:
case Character.OTHER_LETTER:
case Character.DECIMAL_DIGIT_NUMBER:
case Character.INITIAL_QUOTE_PUNCTUATION:
case Character.START_PUNCTUATION: break; // Do nothing (search another character).
case Character.PARAGRAPH_SEPARATOR:
case Character.SPACE_SEPARATOR:
case Character.LINE_SEPARATOR:
case Character.CONTROL: {
/*
* Split the line before a space (except no-break space) and discard trailing spaces.
* The `isWhitespace(b)` check is necessary for excluding the no-break spaces.
*/
final int end = splitAt;
while (Character.isWhitespace(b)) {
if ((splitAt -= n) <= 0) break;
b = buffer.codePointBefore(splitAt);
n = Character.charCount(b);
}
if (splitAt == end) break; // No-break space. Search another character.
buffer.delete(splitAt, end);
break split; // Split here (before the space character).
}
/*
* Split the line after a dash character.
* The "letter before" condition is a way to avoid splitting at the minus sign
* of negative numbers, assuming that the minus sign is preceeded by a space.
* We cannot look at the character after because it may not be in the buffer yet.
*/
case Character.DASH_PUNCTUATION: {
if (b == '-') {
b = splitAt - n;
if (b > 0 && !Character.isLetter(buffer.codePointBefore(b))) {
break; // Continue the search in previous characters.
}
}
break split; // Split here (after the dash character).
}
/*
* Soft hyphen are not in the dash category, so they need to be checked here.
* Replace soft-hyphen by ordinary (visible) hyphen since the hyphen is used.
*/
case Character.FORMAT: {
if (b == Characters.SOFT_HYPHEN) {
buffer.setCharAt(splitAt - n, Characters.HYPHEN);
break split; // Split here (after the dash character).
}
break; // Do nothing (search another character).
}
/*
* All other categories (e.g. punctuations) may be used as a split point
* if no better location is found.
*/
default: {
if (!hasFallback && b != '<') {
hasFallback = true;
fallback = splitAt;
}
break;
}
}
splitAt -= n;
}
transfer(splitAt);
writeLineSeparator();
printableLength = buffer.length(); // Remaining characters will be on next line.
codePointCount = buffer.codePointCount(0, printableLength);
onLineBegin(true);
}