RecursivePathIterator.java

/*-
 * #%L
 * io.earcam.instrumental.io
 * %%
 * Copyright (C) 2018 earcam
 * %%
 * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
 * 
 * You <b>must</b> choose to accept, in full - any individual or combination of 
 * the following licenses:
 * <ul>
 * 	<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
 * 	<li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
 * 	<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
 * 	<li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
 * </ul>
 * #L%
 */
package io.earcam.utilitarian.io;

import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.function.Function;

import io.earcam.unexceptional.Exceptional;

class RecursivePathIterator implements Iterator<Path> {

	private static final Function<Path, DirectoryStream<Path>> DEFAULT_STREAMER = Exceptional.uncheckFunction(Files::newDirectoryStream);
	private final Deque<Iterator<Path>> pending = new ArrayDeque<>();
	private final Function<Path, DirectoryStream<Path>> streamer;
	private Iterator<Path> current;


	RecursivePathIterator(Path root)
	{
		this(root, DEFAULT_STREAMER);
	}


	RecursivePathIterator(Path root, Function<Path, DirectoryStream<Path>> directoryStreamer)
	{
		this(directoryStreamer.apply(root).iterator(), directoryStreamer);
	}


	RecursivePathIterator(Iterator<Path> iterator, Function<Path, DirectoryStream<Path>> directoryStreamer)
	{
		this.current = iterator;
		this.streamer = directoryStreamer;
	}


	@Override
	public boolean hasNext()
	{
		return current.hasNext()
				|| popPending();
	}


	private boolean popPending()
	{
		if(pending.isEmpty()) {
			return false;
		}
		current = pending.pop();
		return hasNext();
	}


	@Override
	public Path next()
	{
		Path next = current.next();
		if(next.toFile().isDirectory()) {
			pending.push(current);
			current = streamer.apply(next).iterator();
		}
		return next;
	}

}