1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 }