public int read()

in src/main/java/org/apache/commons/text/io/StringSubstitutorReader.java [175:305]


    public int read(final char[] target, final int targetIndexIn, final int targetLengthIn) throws IOException {
        // The whole thing is inefficient because we must look for a balanced suffix to match the starting prefix
        // Trying to substitute an incomplete expression can perform replacements when it should not.
        // At a high level:
        // - if draining, drain until empty or target length hit
        // - copy to target until we find a variable start
        // - buffer until a balanced suffix is read, then substitute.
        if (eos && buffer.isEmpty()) {
            return EOS;
        }
        if (targetLengthIn <= 0) {
            // short-circuit: ask nothing, give nothing
            return 0;
        }
        // drain check
        int targetIndex = targetIndexIn;
        int targetLength = targetLengthIn;
        if (isDraining()) {
            // drain as much as possible
            final int drainCount = drain(target, targetIndex, Math.min(toDrain, targetLength));
            if (drainCount == targetLength) {
                // drained length requested, target is full, can only do more in the next invocation
                return targetLength;
            }
            // drained less than requested, target not full.
            targetIndex += drainCount;
            targetLength -= drainCount;
        }
        // BUFFER from the underlying reader
        final int minReadLenPrefix = prefixEscapeMatcher.size();
        // READ enough to test for an [optionally escaped] variable start
        int readCount = buffer(readCount(minReadLenPrefix, 0));
        if (buffer.length() < minReadLenPrefix && targetLength < minReadLenPrefix) {
            // read less than minReadLenPrefix, no variable possible
            final int drainCount = drain(target, targetIndex, targetLength);
            targetIndex += drainCount;
            final int targetSize = targetIndex - targetIndexIn;
            return eos && targetSize <= 0 ? EOS : targetSize;
        }
        if (eos) {
            // EOS
            stringSubstitutor.replaceIn(buffer);
            toDrain = buffer.size();
            final int drainCount = drain(target, targetIndex, targetLength);
            targetIndex += drainCount;
            final int targetSize = targetIndex - targetIndexIn;
            return eos && targetSize <= 0 ? EOS : targetSize;
        }
        // PREFIX
        // buffer and drain until we find a variable start, escaped or plain.
        int balance = 0;
        final StringMatcher prefixMatcher = stringSubstitutor.getVariablePrefixMatcher();
        int pos = 0;
        while (targetLength > 0) {
            if (isBufferMatchAt(prefixMatcher, 0)) {
                balance = 1;
                pos = prefixMatcher.size();
                break;
            }
            if (isBufferMatchAt(prefixEscapeMatcher, 0)) {
                balance = 1;
                pos = prefixEscapeMatcher.size();
                break;
            }
            // drain first char
            final int drainCount = drain(target, targetIndex, 1);
            targetIndex += drainCount;
            targetLength -= drainCount;
            if (buffer.size() < minReadLenPrefix) {
                readCount = bufferOrDrainOnEos(minReadLenPrefix, target, targetIndex, targetLength);
                if (eos || isDraining()) {
                    // if draining, readCount is a drain count
                    if (readCount != EOS) {
                        targetIndex += readCount;
                        targetLength -= readCount;
                    }
                    final int actual = targetIndex - targetIndexIn;
                    return actual > 0 ? actual : EOS;
                }
            }
        }
        // we found a variable start
        if (targetLength <= 0) {
            // no more room in target
            return targetLengthIn;
        }
        // SUFFIX
        // buffer more to find a balanced suffix
        final StringMatcher suffixMatcher = stringSubstitutor.getVariableSuffixMatcher();
        final int minReadLenSuffix = Math.max(minReadLenPrefix, suffixMatcher.size());
        readCount = buffer(readCount(minReadLenSuffix, pos));
        if (eos) {
            // EOS
            stringSubstitutor.replaceIn(buffer);
            toDrain = buffer.size();
            final int drainCount = drain(target, targetIndex, targetLength);
            return targetIndex + drainCount - targetIndexIn;
        }
        // buffer and break out when we find the end or a balanced suffix
        while (true) {
            if (isBufferMatchAt(suffixMatcher, pos)) {
                balance--;
                pos++;
                if (balance == 0) {
                    break;
                }
            } else if (isBufferMatchAt(prefixMatcher, pos)) {
                balance++;
                pos += prefixMatcher.size();
            } else if (isBufferMatchAt(prefixEscapeMatcher, pos)) {
                balance++;
                pos += prefixEscapeMatcher.size();
            } else {
                pos++;
            }
            readCount = buffer(readCount(minReadLenSuffix, pos));
            if (readCount == EOS && pos >= buffer.size()) {
                break;
            }
        }
        // substitute
        final int endPos = pos + 1;
        final int leftover = Math.max(0, buffer.size() - pos);
        stringSubstitutor.replaceIn(buffer, 0, Math.min(buffer.size(), endPos));
        pos = buffer.size() - leftover;
        final int drainLen = Math.min(targetLength, pos);
        // only drain up to what we've substituted
        toDrain = pos;
        drain(target, targetIndex, drainLen);
        return targetIndex - targetIndexIn + drainLen;
    }