View Javadoc
1   /*-
2    * #%L
3    * io.earcam.instrumental.io
4    * %%
5    * Copyright (C) 2018 earcam
6    * %%
7    * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
8    * 
9    * You <b>must</b> choose to accept, in full - any individual or combination of 
10   * the following licenses:
11   * <ul>
12   * 	<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
13   * 	<li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
14   * 	<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
15   * 	<li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
16   * </ul>
17   * #L%
18   */
19  package io.earcam.utilitarian.io;
20  
21  import java.nio.file.DirectoryStream;
22  import java.nio.file.Files;
23  import java.nio.file.Path;
24  import java.util.ArrayDeque;
25  import java.util.Deque;
26  import java.util.Iterator;
27  import java.util.function.Function;
28  
29  import io.earcam.unexceptional.Exceptional;
30  
31  class RecursivePathIterator implements Iterator<Path> {
32  
33  	private static final Function<Path, DirectoryStream<Path>> DEFAULT_STREAMER = Exceptional.uncheckFunction(Files::newDirectoryStream);
34  	private final Deque<Iterator<Path>> pending = new ArrayDeque<>();
35  	private final Function<Path, DirectoryStream<Path>> streamer;
36  	private Iterator<Path> current;
37  
38  
39  	RecursivePathIterator(Path root)
40  	{
41  		this(root, DEFAULT_STREAMER);
42  	}
43  
44  
45  	RecursivePathIterator(Path root, Function<Path, DirectoryStream<Path>> directoryStreamer)
46  	{
47  		this(directoryStreamer.apply(root).iterator(), directoryStreamer);
48  	}
49  
50  
51  	RecursivePathIterator(Iterator<Path> iterator, Function<Path, DirectoryStream<Path>> directoryStreamer)
52  	{
53  		this.current = iterator;
54  		this.streamer = directoryStreamer;
55  	}
56  
57  
58  	@Override
59  	public boolean hasNext()
60  	{
61  		return current.hasNext()
62  				|| popPending();
63  	}
64  
65  
66  	private boolean popPending()
67  	{
68  		if(pending.isEmpty()) {
69  			return false;
70  		}
71  		current = pending.pop();
72  		return hasNext();
73  	}
74  
75  
76  	@Override
77  	public Path next()
78  	{
79  		Path next = current.next();
80  		if(next.toFile().isDirectory()) {
81  			pending.push(current);
82  			current = streamer.apply(next).iterator();
83  		}
84  		return next;
85  	}
86  
87  }