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

edu.hm.hafner.util.LookaheadStream Maven / Gradle / Ivy

package edu.hm.hafner.util;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;

/**
 * A stream of lines with a lookahead of one line. Useful to parse a stream of lines when it is required to check if the
 * next line matches a given regular expression.
 *
 * @author Ullrich Hafner
 */
public class LookaheadStream implements AutoCloseable {
    private final Stream stream;
    private final Iterator lineIterator;
    private final String fileName;

    private boolean isLookaheadFilled = false;
    private String lookaheadLine = StringUtils.EMPTY;
    private int line = 0;

    /**
     * Wraps the specified stream of lines into a {@link LookaheadStream}.
     *
     * @param stream
     *         the lines to wrap
     */
    public LookaheadStream(final Stream stream) {
        this(stream, StringUtils.EMPTY);
    }

    /**
     * Wraps the specified stream of lines into a {@link LookaheadStream}.
     *
     * @param stream
     *         the lines to wrap
     * @param fileName
     *         the file name of the stream
     */
    public LookaheadStream(final Stream stream, final String fileName) {
        this.stream = stream;
        lineIterator = stream.iterator();
        this.fileName = fileName;
    }

    public String getFileName() {
        return fileName;
    }

    @Override
    public void close() {
        stream.close();
    }

    /**
     * Returns {@code true} if the stream has more elements. (In other words, returns {@code true} if {@link #next}
     * would return an element rather than throwing an exception.)
     *
     * @return {@code true} if the stream has more elements
     */
    public boolean hasNext() {
        return lineIterator.hasNext() || isLookaheadFilled;
    }

    /**
     * Returns {@code true} if the stream has at least one more element that matches the given regular expression.
     *
     * @param regexp
     *         the regular expression
     *
     * @return {@code true} if the stream has more elements that match the regexp
     */
    public boolean hasNext(final String regexp) {
        if (!isLookaheadFilled) {
            if (!hasNext()) {
                return false;
            }
            fillLookahead();
        }

        return Pattern.compile(regexp).matcher(lookaheadLine).find();
    }

    /**
     * Peeks the next element in the stream. I.e., the next element is returned but not removed from the stream so that
     * the next call of {@link #next()} will again return this value.
     *
     * @return the next element in the stream
     * @throws NoSuchElementException
     *         if the stream has no more elements
     */
    public String peekNext() {
        if (!isLookaheadFilled) {
            fillLookahead();
        }
        return lookaheadLine;
    }

    private void fillLookahead() {
        lookaheadLine = lineIterator.next();
        isLookaheadFilled = true;
    }

    /**
     * Returns the next element in the stream.
     *
     * @return the next element in the stream
     * @throws NoSuchElementException
     *         if the stream has no more elements
     */
    public String next() {
        line++;

        if (isLookaheadFilled) {
            isLookaheadFilled = false;
            return lookaheadLine;
        }
        return lineIterator.next();
    }

    /**
     * Returns the line number of the line that has been handed out using the {@link #next()} method.
     *
     * @return the current line, or 0 if no line has been handed out yet
     */
    public int getLine() {
        return line;
    }

    @Override @Generated
    public String toString() {
        return String.format("[%d] -> '%s'", line, lookaheadLine);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy