private void write()

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);
    }