package io.gitlab.jfronny.inceptum.launcher.util.gitignore; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.*; import java.util.*; import java.util.stream.Stream; import java.util.stream.StreamSupport; public class IgnoringWalk implements Iterator { public static Stream walk(Path repositoryRoot) throws IOException { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new IgnoringWalk(repositoryRoot), 0), false); } private static final String GITIGNORE = ".gitignore"; private static final String ICEIGNORE = ".iceignore"; private static final String GIT = ".git"; private final Path ref; private final Queue toScan = new ArrayDeque<>(); // A queue of absolute Paths which must still be scanned/returned private final Map ignores = new HashMap<>(); private Path next; private IgnoringWalk(Path repositoryRoot) throws IOException { this.ref = repositoryRoot; enqueueDirectory(repositoryRoot); } @Override public Path next() { if (!hasNext()) throw new NoSuchElementException(); Path ret = next; next = null; return ret; } @Override public boolean hasNext() { if (next != null) return true; next = toScan.poll(); if (next == null) return false; if (Files.isDirectory(next)) { try { enqueueDirectory(next); } catch (IOException e) { throw new UncheckedIOException(e); } } return true; } private void enqueueDirectory(Path directory) throws IOException { Path gitignorePath = directory.resolve(GITIGNORE); Path iceignorePath = directory.resolve(ICEIGNORE); Ignore ignore = null; if (Files.exists(gitignorePath)) { if (ignore == null) ignore = new Ignore(); ignore.add(Files.readAllLines(gitignorePath)); } if (Files.exists(iceignorePath)) { if (ignore == null) ignore = new Ignore(); ignore.add(Files.readAllLines(iceignorePath)); } if (ignore != null) ignores.put(directory, ignore); try (DirectoryStream files = Files.newDirectoryStream(directory)) { for (Path path : files) { String fileName = path.getFileName().toString(); if (!fileName.equals(GITIGNORE) && !fileName.equals(ICEIGNORE) && !fileName.equals(GIT) && !isIgnored(ref.relativize(path))) { toScan.add(path); } } } } private boolean isIgnored(Path relativePath) { Path constructed = ref; for (Path segment : relativePath) { Ignore ignore = ignores.get(constructed); if (ignore != null && ignore.isIgnored(relativePath.toString())) return true; constructed = constructed.resolve(segment); } return false; } }