CharReader.java

package space.sunqian.fs.io;

import space.sunqian.annotation.Nonnull;
import space.sunqian.annotation.Nullable;

import java.io.Closeable;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.CharBuffer;

/**
 * This interface represents the char data reader to read char data as {@link CharSegment} from the data source, which
 * may be a char sequence or a stream.
 *
 * @author sunqian
 */
public interface CharReader extends Closeable {

    /**
     * Wraps the given reader as a new {@link CharReader}.
     * <p>
     * The result's support is as follows:
     * <ul>
     *     <li>mark/reset: based on the reader;</li>
     *     <li>close: closes the reader;</li>
     *     <li>thread safety: no;</li>
     * </ul>
     *
     * @param src the given reader
     * @return the given reader as a new {@link CharReader}
     */
    static @Nonnull CharReader from(@Nonnull Reader src) {
        return from(src, IOKit.bufferSize());
    }

    /**
     * Wraps the given reader as a new {@link CharReader} with the given buffer size.
     * <p>
     * The result's support is as follows:
     * <ul>
     *     <li>mark/reset: based on the reader;</li>
     *     <li>close: closes the reader;</li>
     *     <li>thread safety: no;</li>
     * </ul>
     *
     * @param src        the given reader
     * @param bufferSize the given buffer size, must {@code > 0}
     * @return the given reader as a new {@link CharReader}
     * @throws IllegalArgumentException if the given buffer size {@code <= 0}
     */
    static @Nonnull CharReader from(@Nonnull Reader src, int bufferSize) throws IllegalArgumentException {
        return CharReaderBack.of(src, bufferSize);
    }

    /**
     * Wraps the given array as a new {@link CharReader}.
     * <p>
     * The content of the segment returned from the {@link CharReader#read(int)} is shared with the array. Any changes
     * to the segment's content will be reflected in the array, and vice versa.
     * <p>
     * The result's support is as follows:
     * <ul>
     *     <li>mark/reset: supported;</li>
     *     <li>close: invoking has no effect;</li>
     *     <li>thread safety: no;</li>
     * </ul>
     *
     * @param src the given array
     * @return the given array as a new {@link CharReader}
     */
    static @Nonnull CharReader from(char @Nonnull [] src) {
        return from(src, 0, src.length);
    }

    /**
     * Wraps a specified length of data from the given array, starting at the specified offset, as a new
     * {@link CharReader}.
     * <p>
     * The content of the segment returned from the {@link CharReader#read(int)} is shared with the array. Any changes
     * to the segment's content will be reflected in the array, and vice versa.
     * <p>
     * The result's support is as follows:
     * <ul>
     *     <li>mark/reset: supported;</li>
     *     <li>close: invoking has no effect;</li>
     *     <li>thread safety: no;</li>
     * </ul>
     *
     * @param src the given array
     * @param off the specified offset
     * @param len the specified length
     * @return the given array as a new {@link CharReader}
     * @throws IndexOutOfBoundsException if the bounds arguments are out of bounds
     */
    static @Nonnull CharReader from(char @Nonnull [] src, int off, int len) throws IndexOutOfBoundsException {
        return CharReaderBack.of(src, off, len);
    }

    /**
     * Wraps the given char sequence as a new {@link CharReader}.
     * <p>
     * The content of the segment returned from the {@link CharReader#read(int)} is shared with the array. Any changes
     * in the array will be reflected to the segment's content, but not vice versa.
     * <p>
     * The result's support is as follows:
     * <ul>
     *     <li>mark/reset: supported;</li>
     *     <li>close: invoking has no effect;</li>
     *     <li>thread safety: no;</li>
     * </ul>
     *
     * @param src the char sequence
     * @return the char sequence as a new {@link CharReader}
     */
    static @Nonnull CharReader from(@Nonnull CharSequence src) {
        return from(src, 0, src.length());
    }

    /**
     * Wraps the given char sequence, starting at the specified start index inclusive and ending at the specified end
     * index exclusive, as a new {@link CharReader}.
     * <p>
     * The content of the segment returned from the {@link CharReader#read(int)} is shared with the array. Any changes
     * in the array will be reflected to the segment's content, but not vice versa.
     * <p>
     * The result's support is as follows:
     * <ul>
     *     <li>mark/reset: supported;</li>
     *     <li>close: invoking has no effect;</li>
     *     <li>thread safety: no;</li>
     * </ul>
     *
     * @param src   the given char sequence
     * @param start the specified start index inclusive
     * @param end   the specified end index exclusive
     * @return the given char sequence as a new {@link CharReader}
     * @throws IndexOutOfBoundsException if the bounds arguments are out of bounds
     */
    static @Nonnull CharReader from(@Nonnull CharSequence src, int start, int end) throws IndexOutOfBoundsException {
        return CharReaderBack.of(src, start, end);
    }

    /**
     * Wraps the given buffer as a new {@link CharReader}.
     * <p>
     * The content of the segment returned from the {@link CharReader#read(int)} is shared with the array. Any changes
     * to the segment's content will be reflected in the array (if it is not readonly), and vice versa.
     * <p>
     * The result's support is as follows:
     * <ul>
     *     <li>mark/reset: supported;</li>
     *     <li>close: invoking has no effect;</li>
     *     <li>thread safety: no;</li>
     * </ul>
     *
     * @param src the given buffer
     * @return the given buffer as a new {@link CharReader}
     */
    static @Nonnull CharReader from(@Nonnull CharBuffer src) {
        return CharReaderBack.of(src);
    }

    /**
     * Returns the chars number this reader is ready to be read, may be {@code 0}.
     * <p>
     * Note some data sources are unpredictable, so even if this method returns {@code 0}, it does not necessarily mean
     * that there is no data can be read out immediately
     *
     * @return the chars number this reader is ready to be read, may be {@code 0}
     * @throws IORuntimeException if an I/O error occurs
     */
    int ready() throws IORuntimeException;

    /**
     * Reads and returns the next specified length of data segment. This method reads continuously until the read number
     * reaches the specified length or reaches the end of this reader. It never returns {@code null}, but can return an
     * empty segment.
     * <p>
     * If the specified length is {@code 0}, returns an empty segment immediately without reading.
     * <p>
     * The content of the returned segment may be shared with the data source, depends on the implementation, such as
     * the instances obtained from the {@link #from(char[])}, {@link #from(char[], int, int)},
     * {@link #from(CharSequence)}, {@link #from(CharSequence, int, int)} and {@link #from(CharBuffer)}.
     * <p>
     * Note this method may allocate the specified length of space, and the excessive length may cause out of memory.
     *
     * @param len the specified length to read, must {@code >= 0}
     * @return the next data segment
     * @throws IllegalArgumentException if the specified length {@code < 0}
     * @throws IORuntimeException       if an I/O error occurs
     */
    @Nonnull
    CharSegment read(int len) throws IllegalArgumentException, IORuntimeException;

    /**
     * Reads all data from this reader, and returns an array contains the data. This method reads continuously until
     * reaches the end of this reader. It may return {@code null} if the reader has already reached the end of the
     * source and no data read out.
     * <p>
     * The content of the returned segment may be shared with the data source, depends on the implementation, such as
     * the instances obtained from the {@link #from(char[])}, {@link #from(char[], int, int)},
     * {@link #from(CharSequence)}, {@link #from(CharSequence, int, int)} and {@link #from(CharBuffer)}.
     *
     * @return the all data as buffer
     * @throws IORuntimeException if an I/O error occurs
     */
    @Nullable
    CharBuffer read() throws IORuntimeException;

    /**
     * Skips the next specified length of data segment. This method skips continuously until the skipped number reaches
     * the specified length or reaches the end of this reader.
     * <p>
     * If the specified length is {@code 0}, returns {@code 0} immediately without skipping.
     *
     * @param len the specified length to skip, must {@code >= 0}
     * @return the actual skipped length
     * @throws IllegalArgumentException if the specified length {@code < 0}
     * @throws IORuntimeException       if an I/O error occurs
     */
    long skip(long len) throws IllegalArgumentException, IORuntimeException;

    /**
     * Reads all data into the specified appender, until reaches the end of this reader, returns the actual number of
     * chars read to.
     * <p>
     * If reaches the end of this reader and no data is read, returns {@code -1}.
     * <p>
     * This method never invokes the {@link OutputStream#flush()} to force the backing buffer.
     *
     * @param dst the specified appender
     * @return the actual number of chars read to, or {@code -1} if reaches the end of this reader and no data is read
     * @throws IORuntimeException if an I/O error occurs
     */
    long readTo(@Nonnull Appendable dst) throws IORuntimeException;

    /**
     * Reads a specified length of data into the specified appender, until the read number reaches the specified length
     * or reaches the end of this reader, returns the actual number of chars read to.
     * <p>
     * If the specified length is {@code 0}, returns {@code 0} without reading; if reaches the end of this reader and no
     * data is read, returns {@code -1}.
     * <p>
     * This method never invokes the {@link OutputStream#flush()} to force the backing buffer.
     *
     * @param dst the specified appender
     * @param len the specified length, must {@code >= 0}
     * @return the actual number of chars read to, or {@code -1} if reaches the end of this reader and no data is read
     * @throws IllegalArgumentException if the specified length is illegal
     * @throws IORuntimeException       if an I/O error occurs
     */
    long readTo(@Nonnull Appendable dst, long len) throws IllegalArgumentException, IORuntimeException;

    /**
     * Reads data from this reader into the destination array, until the read number reaches the array's length or
     * reaches the end of this reader, and returns the actual number of chars read to.
     * <p>
     * If the array's length is {@code 0}, returns {@code 0} without reading. If reaches the end of this reader and no
     * data is read, returns {@code -1}.
     *
     * @param dst the destination array
     * @return the actual number of chars read to, or {@code -1} if reaches the end of this reader and no data is read
     * @throws IORuntimeException if an I/O error occurs
     */
    int readTo(char @Nonnull [] dst) throws IORuntimeException;

    /**
     * Reads a specified length of data from this reader into the destination array, starting at the specified offset,
     * until the read number reaches the specified length or reaches the end of this reader, and returns the actual
     * number of chars read to.
     * <p>
     * If the specified length is {@code 0}, returns {@code 0} without reading. If reaches the end of this reader and no
     * data is read, returns {@code -1}.
     *
     * @param dst the destination array
     * @param off the specified offset of the array
     * @param len the specified length to read
     * @return the actual number of chars read to, or {@code -1} if reaches the end of this reader and no data is read
     * @throws IndexOutOfBoundsException if the arguments are out of bounds
     * @throws IORuntimeException        if an I/O error occurs
     */
    int readTo(char @Nonnull [] dst, int off, int len) throws IndexOutOfBoundsException, IORuntimeException;

    /**
     * Reads data from this reader into the destination buffer, until reaches the end of the reader or buffer, and
     * returns the actual number of chars read to.
     * <p>
     * If the destination buffer's remaining is {@code 0}, returns {@code 0} without reading; if reaches the end of this
     * reader and no data is read, returns {@code -1}.
     * <p>
     * The buffer's position increments by the actual read number.
     *
     * @param dst the destination buffer
     * @return the actual number of chars read to, or {@code -1} if reaches the end of this reader and no data is read
     * @throws IORuntimeException if an I/O error occurs
     */
    int readTo(@Nonnull CharBuffer dst) throws IORuntimeException;

    /**
     * Reads a specified length of data from this reader into the destination buffer, until the read number reaches the
     * specified length or reaches the end of the reader or buffer, and returns the actual number of chars read to.
     * <p>
     * If the specified length or destination buffer's remaining is {@code 0}, returns {@code 0} without reading; if
     * reaches the end of this reader and no data is read, returns {@code -1}.
     * <p>
     * The buffer's position increments by the actual read number.
     *
     * @param dst the specified buffer
     * @param len the specified length, must {@code >= 0}
     * @return the actual number of chars read to, or {@code -1} if reaches the end of this reader and no data is read
     * @throws IllegalArgumentException if the specified read length is illegal
     * @throws IORuntimeException       if an I/O error occurs
     */
    int readTo(@Nonnull CharBuffer dst, int len) throws IllegalArgumentException, IORuntimeException;

    /**
     * Reads and returns the next specified length of data segment. This method reads continuously until the read number
     * reaches the specified length or no data is immediately available. It never returns {@code null}, but can return
     * an empty segment.
     * <p>
     * If the specified length is {@code 0}, returns an empty segment immediately without reading.
     * <p>
     * The content of the returned segment may be shared with the data source, depends on the implementation, such as
     * the instances obtained from the {@link #from(char[])}, {@link #from(char[], int, int)},
     * {@link #from(CharSequence)}, {@link #from(CharSequence, int, int)} and {@link #from(CharBuffer)}.
     * <p>
     * Note this method may allocate the specified length of space, and the excessive length may cause out of memory.
     *
     * @param len the specified length to read, must {@code >= 0}
     * @return the next data segment
     * @throws IllegalArgumentException if the specified length {@code < 0}
     * @throws IORuntimeException       if an I/O error occurs
     */
    @Nonnull
    CharSegment available(int len) throws IllegalArgumentException, IORuntimeException;

    /**
     * Reads and returns the next available data segment. This method reads continuously until no data is immediately
     * available. It never returns {@code null}, but can return an empty segment.
     * <p>
     * The content of the returned segment may be shared with the data source, depends on the implementation, such as
     * the instances obtained from the {@link #from(char[])}, {@link #from(char[], int, int)} and
     * {@link #from(CharBuffer)}.
     *
     * @return the next available data segment
     * @throws IORuntimeException if an I/O error occurs
     */
    @Nonnull
    CharSegment available() throws IORuntimeException;

    /**
     * Reads available data into the specified appender, until no data is immediately available, returns the actual
     * number of chars read to.
     * <p>
     * If reaches the end of this reader and no data is read, returns {@code -1}.
     * <p>
     * This method never invokes the {@link OutputStream#flush()} to force the backing buffer.
     *
     * @param dst the specified appender
     * @return the actual number of chars read to, possibly {@code 0}, or {@code -1} if reaches the end of this reader
     * and no data is read
     * @throws IORuntimeException if an I/O error occurs
     */
    long availableTo(@Nonnull Appendable dst) throws IORuntimeException;

    /**
     * Reads a specified length of data into the specified appender, until the read number reaches the specified length
     * or no data is immediately available, returns the actual number of chars read to.
     * <p>
     * If the specified length is {@code 0}, returns {@code 0} without reading; if reaches the end of this reader and no
     * data is read, returns {@code -1}.
     * <p>
     * This method never invokes the {@link OutputStream#flush()} to force the backing buffer.
     *
     * @param dst the specified appender
     * @param len the specified length, must {@code >= 0}
     * @return the actual number of chars read to, possibly {@code 0}, or {@code -1} if reaches the end of this reader
     * and no data is read
     * @throws IllegalArgumentException if the specified length is illegal
     * @throws IORuntimeException       if an I/O error occurs
     */
    long availableTo(@Nonnull Appendable dst, long len) throws IllegalArgumentException, IORuntimeException;

    /**
     * Reads available data from this reader into the destination array, until the read number reaches the array's
     * length or no data is immediately available, and returns the actual number of chars read to.
     * <p>
     * If the array's length is {@code 0}, returns {@code 0} without reading. If reaches the end of this reader and no
     * data is read, returns {@code -1}.
     *
     * @param dst the destination array
     * @return the actual number of chars read to, possibly {@code 0}, or {@code -1} if reaches the end of this reader
     * and no data is read
     * @throws IORuntimeException if an I/O error occurs
     */
    int availableTo(char @Nonnull [] dst) throws IORuntimeException;

    /**
     * Reads a specified length of data from this reader into the destination array, starting at the specified offset,
     * until the read number reaches the specified length or no data is immediately available, and returns the actual
     * number of chars read to.
     * <p>
     * If the specified length is {@code 0}, returns {@code 0} without reading. If reaches the end of this reader and no
     * data is read, returns {@code -1}.
     *
     * @param dst the destination array
     * @param off the specified offset of the array
     * @param len the specified length to read
     * @return the actual number of chars read to, possibly {@code 0}, or {@code -1} if reaches the end of this reader
     * and no data is read
     * @throws IndexOutOfBoundsException if the arguments are out of bounds
     * @throws IORuntimeException        if an I/O error occurs
     */
    int availableTo(char @Nonnull [] dst, int off, int len) throws IndexOutOfBoundsException, IORuntimeException;

    /**
     * Reads available data from this reader into the destination buffer, until reaches the end of the buffer or no data
     * is immediately available, and returns the actual number of chars read to.
     * <p>
     * If the destination buffer's remaining is {@code 0}, returns {@code 0} without reading; if reaches the end of this
     * reader and no data is read, returns {@code -1}.
     * <p>
     * The buffer's position increments by the actual read number.
     *
     * @param dst the destination buffer
     * @return the actual number of chars read to, possibly {@code 0}, or {@code -1} if reaches the end of this reader
     * and no data is read
     * @throws IORuntimeException if an I/O error occurs
     */
    int availableTo(@Nonnull CharBuffer dst) throws IORuntimeException;

    /**
     * Reads a specified length of data from this reader into the destination buffer, until the read number reaches the
     * specified length or reaches the end of the buffer or no data is immediately available, and returns the actual
     * number of chars read to.
     * <p>
     * If the specified length or destination buffer's remaining is {@code 0}, returns {@code 0} without reading; if
     * reaches the end of this reader and no data is read, returns {@code -1}.
     * <p>
     * The buffer's position increments by the actual read number.
     *
     * @param dst the specified buffer
     * @param len the specified length, must {@code >= 0}
     * @return the actual number of chars read to, possibly {@code 0}, or {@code -1} if reaches the end of this reader
     * and no data is read
     * @throws IllegalArgumentException if the specified read length is illegal
     * @throws IORuntimeException       if an I/O error occurs
     */
    int availableTo(@Nonnull CharBuffer dst, int len) throws IllegalArgumentException, IORuntimeException;

    /**
     * Returns whether this reader supports the {@link #mark()} and {@link #reset()} methods.
     *
     * @return whether this reader supports the {@link #mark()} and {@link #reset()} methods
     */
    boolean markSupported();

    /**
     * Marks the current position in this reader. This method can be used to mark a position for later
     * {@link #reset()}.
     *
     * @throws IORuntimeException if an I/O error occurs
     */
    void mark() throws IORuntimeException;

    /**
     * Resets this reader to the last marked position by {@link #mark()}. This method can be used to re-read the data
     * from last marked position.
     *
     * @throws IORuntimeException if an I/O error occurs
     */
    void reset() throws IORuntimeException;

    /**
     * Closes this reader and the data source if the data source is closable. If this reader is already closed, this
     * method has no effect.
     *
     * @throws IORuntimeException if an I/O error occurs
     */
    @Override
    void close() throws IORuntimeException;

    /**
     * Wraps this reader as a new {@link CharReader} of which readable number is limited to the specified limit. The
     * shareability of the content of the returned segment inherits this reader.
     * <p>
     * The result's support is as follows:
     * <ul>
     *     <li>mark/reset: based on this reader;</li>
     *     <li>close: closes this reader;</li>
     *     <li>thread safety: no;</li>
     * </ul>
     *
     * @param limit the specified limit, must {@code >= 0}
     * @return this reader as a new {@link CharReader} of which readable number is limited to the specified limit
     * @throws IllegalArgumentException if the limit argument is negative
     */
    default @Nonnull CharReader limit(long limit) throws IllegalArgumentException {
        return CharReaderBack.limit(this, limit);
    }

    /**
     * Returns a {@link Reader} represents this reader. Its content and status are shared with this reader.
     * <p>
     * The result's support is as follows:
     * <ul>
     *     <li>mark/reset: based on this reader;</li>
     *     <li>close: closes this reader;</li>
     *     <li>thread safety: no;</li>
     * </ul>
     *
     * @return a {@link Reader} represents this reader. Its content and status are shared with this reader
     */
    default @Nonnull Reader asReader() {
        return CharReaderBack.asReader(this);
    }
}