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

de.larssh.utils.io.PeekableReader Maven / Gradle / Ivy

// Generated by delombok at Fri Dec 30 22:57:48 UTC 2022
package de.larssh.utils.io;

import java.io.IOException;
import java.io.Reader;
import java.util.NoSuchElementException;
import de.larssh.utils.Nullables;
import edu.umd.cs.findbugs.annotations.Nullable;

/**
 * A {@link Reader} that allows peeking up to one character without removing it,
 * from the logical I/O stream, supporting a one-element lookahead.
 *
 * 

* The methods {@link #hasNext()} and {@link #next()} are implemented similar to * the methods of {@link java.util.Iterator} to simplify working with character * based readers. */ public class PeekableReader extends Reader { /** * The wrapped reader */ private final Reader reader; /** * The next character read if {@link #state} is {@link ReaderState#PEEKED}, else * undefined. */ private int peekedCharacter = -1; /** * The reader's current inner state */ private ReaderState state = ReaderState.CALL_FOR_NEXT; /** * The peeked character at the time of calling {@link #mark(int)} the last time. */ private int markedPeekedCharacter = -1; /** * The reader's current inner state at the time of calling {@link #mark(int)} * the last time. */ private ReaderState markedState = ReaderState.CALL_FOR_NEXT; /** * {@inheritDoc} */ @Override @SuppressWarnings("PMD.CloseResource") public void close() throws IOException { reader.close(); } /** * Returns {@code true} if the reader has more characters. (In other words, * returns {@code true} if {@link #next()} would return a character rather than * throwing an exception.) * * @return {@code true} if the reader has more characters, else {@code false} * @throws IOException if an I/O error occurs */ public final boolean hasNext() throws IOException { if (state == ReaderState.CALL_FOR_NEXT) { peekedCharacter = reader.read(); state = peekedCharacter == -1 ? ReaderState.END_OF_DATA : ReaderState.PEEKED; } return state == ReaderState.PEEKED; } /** * {@inheritDoc} */ @Override public void mark(final int readAheadLimit) throws IOException { reader.mark(readAheadLimit); markedPeekedCharacter = peekedCharacter; markedState = state; } /** * {@inheritDoc} */ @Override public boolean markSupported() { return reader.markSupported(); } /** * Returns the next character of the reader. * * @return the next character of the reader * @throws IOException if an I/O error occurs * @throws NoSuchElementException if the reader has no more characters */ public final char next() throws IOException { // Method "hasNext" peeks the next character (if required) if (!hasNext()) { throw new NoSuchElementException(); } if (state == ReaderState.PEEKED) { state = ReaderState.CALL_FOR_NEXT; } final char next = (char) peekedCharacter; peekedCharacter = -1; return next; } /** * Returns the next character in the reader, returned by {@link #read()}, * without removing it from the I/O stream. * * @return the next element in the iteration * @throws IOException if an I/O error occurs * @throws NoSuchElementException if the iteration has no more elements */ public char peek() throws IOException { // Method "hasNext" peeks the next character (if required) if (!hasNext()) { throw new NoSuchElementException(); } return (char) peekedCharacter; } /** * {@inheritDoc} */ @Override public int read() throws IOException { if (state == ReaderState.PEEKED) { state = ReaderState.CALL_FOR_NEXT; return (char) peekedCharacter; } return super.read(); } /** * {@inheritDoc} */ @Override @SuppressWarnings("PMD.CyclomaticComplexity") public int read(@Nullable final char[] buffer, final int offset, final int length) throws IOException { if (state == ReaderState.CALL_FOR_NEXT) { return reader.read(buffer, offset, length); } if (state == ReaderState.END_OF_DATA) { return -1; } // Error handling as of JavaDoc final char[] nonNullableBuffer = Nullables.orElseThrow(buffer); if (offset < 0 || length < 0 || offset + length > nonNullableBuffer.length) { throw new IndexOutOfBoundsException(); } // Early exit: Avoid further processing in case no character were requested if (length == 0) { return 0; } // Insert peeked character as first character nonNullableBuffer[offset] = (char) peekedCharacter; state = ReaderState.CALL_FOR_NEXT; // No need to read further characters if just one character were requested if (length == 1) { return 1; } // Read further characters and handle possible end of data final int noOfCharacters = reader.read(buffer, offset + 1, length - 1); if (noOfCharacters == -1) { state = ReaderState.END_OF_DATA; return 1; } return noOfCharacters + 1; } /** * {@inheritDoc} */ @Override public void reset() throws IOException { reader.reset(); this.peekedCharacter = markedPeekedCharacter; state = markedState; } /** * This enumeration contains the possible inner states of * {@link PeekableReader}. */ @SuppressWarnings("PMD.UnnecessaryModifier") private enum ReaderState { /** * Status of a reader, that needs to read once information about the next * character is required. */ CALL_FOR_NEXT, /** * Status representing the end of data. The reader must not be called any * longer. This is an end state and must not change once reached. */ END_OF_DATA, /** * Status of a reader, that peeked the next character already. */ PEEKED; } @java.lang.SuppressWarnings("all") @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code") @lombok.Generated public PeekableReader(final Reader reader) { this.reader = reader; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy