All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.pwall.util.ReaderBuffer Maven / Gradle / Ivy

The newest version!
/*
 * @(#) ReaderBuffer.java
 *
 * javautil Java Utility Library
 * Copyright (c) 2017 Peter Wall
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.pwall.util;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;

/**
 * An implementation of {@link CharSequence} that provides read-only random access to the data
 * input from a {@link Reader}.
 *
 * @author Peter Wall
 */
public class ReaderBuffer implements CharSequence {

    /** Default buffer size = 4096 */
    public static final int defaultBufferSize = 4096;

    private int bufferSize;
    private char[][] buffers;
    private int length;
    private String str;

    /**
     * Create a {@code ReaderBuffer} with the contents taken from the nominated {@link Reader},
     * using the specified buffer size.  Read the data into a set of character arrays in an
     * {@link ArrayList}, so that it can be accessed by the {@link CharSequence} interface.
     *
     * @param   rdr         the {@link Reader}
     * @param   bufferSize  the buffer size to use
     * @throws  IOException if thrown by the {@link Reader}
     */
    public ReaderBuffer(Reader rdr, int bufferSize) throws IOException {
        if (bufferSize <= 0)
            throw new IllegalArgumentException("Buffer size must be positive");

        // initialise fields

        this.bufferSize = bufferSize;
        str = null;
        length = 0;
        ArrayList bufferList = new ArrayList<>();

        // outer loop - read one buffer at a time

        for (;;) {

            // inner loop - read from Reader until buffer is filled

            char[] buffer = new char[bufferSize];
            int len = bufferSize;
            int offset = 0;
            do {
                int n = rdr.read(buffer, offset, len);
                if (n < 0) // EOF
                    break;
                offset += n;
                len -= n;
            } while (len > 0);
            if (offset == 0) // no data was read in inner loop
                break;

            // add buffer to list

            bufferList.add(buffer);
            length += bufferSize - len;
            if (len > 0) // inner loop terminated early - EOF
                break;
        }

        // get buffer list as array

        buffers = bufferList.toArray(new char[bufferList.size()][]);
    }

    /**
     * Create a {@code ReaderBuffer} with the contents taken from the nominated {@link Reader},
     * using the default buffer size.
     *
     * @param   rdr         the {@link Reader}
     * @throws  IOException if thrown by the {@link Reader}
     */
    public ReaderBuffer(Reader rdr) throws IOException {
        this(rdr, defaultBufferSize);
    }

    /**
     * Get the total length of the {@link CharSequence}.
     *
     * @return  the length
     * @see     CharSequence#length()
     */
    @Override
    public int length() {
        return length;
    }

    /**
     * Get the character at the specified index.
     *
     * @param   index   the index
     * @return  the character
     * @throws  IndexOutOfBoundsException if index less than 0 or greater than length
     * @see     CharSequence#charAt(int)
     */
    @Override
    public char charAt(int index) {
        if (index < 0 || index >= length)
            throw new IndexOutOfBoundsException(String.valueOf(index));
        return buffers[index / bufferSize][index % bufferSize];
    }

    /**
     * Get a sub-sequence of this {@link CharSequence}.
     *
     * @param   start   the start index
     * @param   end     the end index
     * @return  the sub-sequence
     * @throws  IllegalArgumentException if the start or end index is invalid
     * @see     CharSequence#subSequence(int, int)
     */
    @Override
    public CharSequence subSequence(int start, int end) {
        return new SubSequence(this, start, end);
    }

    /**
     * Get the string from this {@link CharSequence}.  The result is stored for subsequent
     * re-use.
     *
     * @return  the string containing all the characters of this {@link CharSequence}
     * @see     Object#toString()
     */
    @Override
    public String toString() {
        if (str == null) {

            // create output StringBuffer

            StringBuilder sb = new StringBuilder(length);

            // append buffers 0 to n-1

            int lastbuffer = buffers.length - 1;
            for (int i = 0; i < lastbuffer; i++)
                sb.append(buffers[i]);

            // append last buffer (if there is one)

            if (lastbuffer >= 0)
                sb.append(buffers[lastbuffer], 0, length - lastbuffer * bufferSize);
            str = sb.toString();
        }
        return str;
    }

    /**
     * Convenience method to convert the contents of a {@link Reader} to a {@link String}.
     *
     * @param   rdr     the {@link Reader}
     * @return          the {@link String}
     * @throws IOException if thrown by the {@link Reader}
     */
    public static String toString(Reader rdr) throws IOException {
        return (new ReaderBuffer(rdr)).toString();
    }

    /**
     * Convenience method to convert the contents of a {@link Reader} to a {@link String} using
     * a specified buffer size.
     *
     * @param   rdr         the {@link Reader}
     * @param   bufferSize  the buffer size
     * @return              the {@link String}
     * @throws IOException if thrown by the {@link Reader}
     */
    public static String toString(Reader rdr, int bufferSize) throws IOException {
        return (new ReaderBuffer(rdr, bufferSize)).toString();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy