class Accumulator {
        final List<CharSequence> data = new ArrayList<>();
        int chars = 0, count = 0;
    }

    private static final int CUT_ANYWHERE = 0;
    private static final int CUT_CODEPOINT = 1;
    private static final int CUT_GRAPHEME = 2;
    private static final int CUT_WORD = 3;
    private static final int CUT_BEFORE_DELIMITER = 4;
    private static final int CUT_AFTER_DELIMITER = 5;

    private static final int LENGTH_CHARS = 0;
    private static final int LENGTH_CODEPOINTS = 1;
    private static final int LENGTH_GRAPHEMES = 2;
    private static final int LENGTH_ELEMENTS = 3;

    private final String delimiter, ellipsis, prefix, suffix;
    private final int cutStrategy, lenStrategy, maxLength;
    private int limit, delimCount = -1;

    private Joining(String delimiter, String ellipsis, String prefix, String suffix, int cutStrategy, int lenStrategy,
            int maxLength) {
        this.delimiter = delimiter;
        this.ellipsis = ellipsis;
        this.prefix = prefix;
        this.suffix = suffix;
        this.cutStrategy = cutStrategy;
        this.lenStrategy = lenStrategy;
        this.maxLength = maxLength;
    }

    private void init() {
        if (delimCount == -1) {
            limit = maxLength - length(prefix, false) - length(suffix, false);
            delimCount = length(delimiter, false);
        }
    }

    private int length(CharSequence s, boolean content) {
        switch (lenStrategy) {
            case LENGTH_CHARS:
                return s.length();
            case LENGTH_CODEPOINTS:
                if (s instanceof String)
                    return ((String) s).codePointCount(0, s.length());
                return (int) s.codePoints().count();
            case LENGTH_GRAPHEMES:
                BreakIterator bi = BreakIterator.getCharacterInstance();
                bi.setText(s.toString());
                int count = 0;
                for (int end = bi.next(); end != BreakIterator.DONE; end = bi.next())
                    count++;
                return count;
            case LENGTH_ELEMENTS:
                return content ? 1 : 0;
            default:
                throw new InternalError();
        }
    }

    private static int copy(char[] buf, int pos, String str) {
        str.getChars(0, str.length(), buf, pos);
        return pos + str.length();
    }

    private int copyCut(char[] buf, int pos, String str, int limit, int cutStrategy) {
        if (limit <= 0)
            return pos;
        int endPos = str.length();
        switch (lenStrategy) {
            case LENGTH_CHARS:
                if (limit < str.length())
                    endPos = limit;
                break;
            case LENGTH_CODEPOINTS:
                if (limit < str.codePointCount(0, str.length()))
                    endPos = str.offsetByCodePoints(0, limit);
                break;
            case LENGTH_GRAPHEMES:
                BreakIterator bi = BreakIterator.getCharacterInstance();
                bi.setText(str);
                int count = limit, end;
                while (true) {
                    end = bi.next();
                    if (end == BreakIterator.DONE)
                        break;
                    if (--count == 0) {
                        endPos = end;
                        break;
                    }
                }
                break;
            case LENGTH_ELEMENTS:
                break;
            default:
                throw new InternalError();
        }
        if (endPos < str.length()) {
            BreakIterator bi;
            switch (cutStrategy) {
            case CUT_BEFORE_DELIMITER:
            case CUT_AFTER_DELIMITER:
                endPos = 0;
                break;
            case CUT_WORD:
                bi = BreakIterator.getWordInstance();
                bi.setText(str);
                endPos = bi.preceding(endPos + 1);
                break;
            case CUT_GRAPHEME:
                bi = BreakIterator.getCharacterInstance();
                bi.setText(str);
                endPos = bi.preceding(endPos + 1);
                break;
            case CUT_ANYWHERE:
                break;
            case CUT_CODEPOINT:
                if (Character.isHighSurrogate(str.charAt(endPos - 1)) && Character.isLowSurrogate(str.charAt(endPos)))
                    endPos--;
                break;
            default:
                throw new InternalError();
            }
        }
        str.getChars(0, endPos, buf, pos);
        return pos + endPos;
    }

    private String finisherNoOverflow(Accumulator acc) {<FILL_FUNCTION_BODY>

        char[] buf = new char[acc.chars + prefix.length() + suffix.length()];
        int size = acc.data.size();
        int pos = copy(buf, 0, prefix);
        for (int i = 0; i < size; i++) {
            if (i > 0) {
                pos = copy(buf, pos, delimiter);
            }
            pos = copy(buf, pos, acc.data.get(i).toString());
        }
        copy(buf, pos, suffix);
        return new String(buf);
    
<no_super_class>