add Files walk alternative without AccessDeniedException
This commit is contained in:
parent
720640f8aa
commit
94cea9ae79
6 changed files with 101 additions and 147 deletions
|
@ -5,4 +5,5 @@ module org.xbib.files {
|
||||||
exports org.xbib.files;
|
exports org.xbib.files;
|
||||||
uses FileServiceProvider;
|
uses FileServiceProvider;
|
||||||
provides FileServiceProvider with DefaultFileServiceProvider;
|
provides FileServiceProvider with DefaultFileServiceProvider;
|
||||||
|
requires java.logging;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public interface FileService {
|
public interface FileService {
|
||||||
|
@ -115,4 +117,29 @@ public interface FileService {
|
||||||
Stream<Path> walk(String path, FileVisitOption... options) throws IOException;
|
Stream<Path> walk(String path, FileVisitOption... options) throws IOException;
|
||||||
|
|
||||||
Stream<Path> walk(String path, int maxdepth, FileVisitOption... options) throws IOException;
|
Stream<Path> walk(String path, int maxdepth, FileVisitOption... options) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement for Files.walk() without AccessDeniedException.
|
||||||
|
* @param p the path
|
||||||
|
* @return a stream of readable files under the path
|
||||||
|
*/
|
||||||
|
static Stream<Path> walk(Path p) {
|
||||||
|
if (Files.isReadable(p)) {
|
||||||
|
if (Files.isDirectory(p)) {
|
||||||
|
try (Stream<Path> stream = Files.list(p)) {
|
||||||
|
return stream.flatMap(FileService::walk);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.getLogger(FileService.class.getName())
|
||||||
|
.log(Level.FINE,"skipped " + p + " because of " + e.getMessage());
|
||||||
|
return Stream.empty();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Stream.of(p);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger.getLogger(FileService.class.getName())
|
||||||
|
.log(Level.WARNING, "unreadable: " + p);
|
||||||
|
}
|
||||||
|
return Stream.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import org.xbib.files.FileTreeWalker.Event;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This source is a copy taken from java.nio.file.FileTreeIterator which is inaccessible.
|
* This source is a copy taken from java.nio.file.FileTreeIterator which is inaccessible.
|
||||||
*
|
|
||||||
* An {@code Iterator} to iterate over the nodes of a file tree.
|
* An {@code Iterator} to iterate over the nodes of a file tree.
|
||||||
*
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
|
@ -27,7 +26,9 @@ import org.xbib.files.FileTreeWalker.Event;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class FileTreeIterator implements Iterator<Event>, Closeable {
|
class FileTreeIterator implements Iterator<Event>, Closeable {
|
||||||
|
|
||||||
private final FileTreeWalker walker;
|
private final FileTreeWalker walker;
|
||||||
|
|
||||||
private Event next;
|
private Event next;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,21 +38,15 @@ class FileTreeIterator implements Iterator<Event>, Closeable {
|
||||||
* if {@code maxDepth} is negative
|
* if {@code maxDepth} is negative
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* if an I/O errors occurs opening the starting file
|
* if an I/O errors occurs opening the starting file
|
||||||
* @throws SecurityException
|
|
||||||
* if the security manager denies access to the starting file
|
|
||||||
* @throws NullPointerException
|
* @throws NullPointerException
|
||||||
* if {@code start} or {@code options} is {@code null} or
|
* if {@code start} or {@code options} is {@code null} or
|
||||||
* the options array contains a {@code null} element
|
* the options array contains a {@code null} element
|
||||||
*/
|
*/
|
||||||
FileTreeIterator(Path start, int maxDepth, FileVisitOption... options)
|
FileTreeIterator(Path start, int maxDepth, FileVisitOption... options) throws IOException {
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
this.walker = new FileTreeWalker(Arrays.asList(options), maxDepth);
|
this.walker = new FileTreeWalker(Arrays.asList(options), maxDepth);
|
||||||
this.next = walker.walk(start);
|
this.next = walker.walk(start);
|
||||||
assert next.type() == FileTreeWalker.EventType.ENTRY ||
|
assert next.type() == FileTreeWalker.EventType.ENTRY ||
|
||||||
next.type() == FileTreeWalker.EventType.START_DIRECTORY;
|
next.type() == FileTreeWalker.EventType.START_DIRECTORY;
|
||||||
|
|
||||||
// IOException if there a problem accessing the starting file
|
|
||||||
IOException ioe = next.ioeException();
|
IOException ioe = next.ioeException();
|
||||||
if (ioe != null)
|
if (ioe != null)
|
||||||
throw ioe;
|
throw ioe;
|
||||||
|
@ -62,10 +57,9 @@ class FileTreeIterator implements Iterator<Event>, Closeable {
|
||||||
FileTreeWalker.Event ev = walker.next();
|
FileTreeWalker.Event ev = walker.next();
|
||||||
while (ev != null) {
|
while (ev != null) {
|
||||||
IOException ioe = ev.ioeException();
|
IOException ioe = ev.ioeException();
|
||||||
if (ioe != null)
|
if (ioe != null) {
|
||||||
throw new UncheckedIOException(ioe);
|
throw new UncheckedIOException(ioe);
|
||||||
|
}
|
||||||
// END_DIRECTORY events are ignored
|
|
||||||
if (ev.type() != FileTreeWalker.EventType.END_DIRECTORY) {
|
if (ev.type() != FileTreeWalker.EventType.END_DIRECTORY) {
|
||||||
next = ev;
|
next = ev;
|
||||||
return;
|
return;
|
||||||
|
@ -77,19 +71,22 @@ class FileTreeIterator implements Iterator<Event>, Closeable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
if (!walker.isOpen())
|
if (!walker.isOpen()) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
fetchNextIfNeeded();
|
fetchNextIfNeeded();
|
||||||
return next != null;
|
return next != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Event next() {
|
public Event next() {
|
||||||
if (!walker.isOpen())
|
if (!walker.isOpen()) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
fetchNextIfNeeded();
|
fetchNextIfNeeded();
|
||||||
if (next == null)
|
if (next == null) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
Event result = next;
|
Event result = next;
|
||||||
next = null;
|
next = null;
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -12,11 +12,12 @@ import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This source is a copy taken from java.nio.file.FileTreeWalker which is inaccessible.
|
* This source is a copy taken from java.nio.file.FileTreeWalker which is inaccessible.
|
||||||
*
|
|
||||||
* Walks a file tree, generating a sequence of events corresponding to the files
|
* Walks a file tree, generating a sequence of events corresponding to the files
|
||||||
* in the tree.
|
* in the tree.
|
||||||
*
|
*
|
||||||
|
@ -38,10 +39,11 @@ import java.util.Iterator;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class FileTreeWalker implements Closeable {
|
class FileTreeWalker implements Closeable {
|
||||||
|
|
||||||
private final boolean followLinks;
|
private final boolean followLinks;
|
||||||
private final LinkOption[] linkOptions;
|
private final LinkOption[] linkOptions;
|
||||||
private final int maxDepth;
|
private final int maxDepth;
|
||||||
private final ArrayDeque<DirectoryNode> stack = new ArrayDeque<>();
|
private final Deque<DirectoryNode> stack = new ArrayDeque<>();
|
||||||
private boolean closed;
|
private boolean closed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,7 +91,7 @@ class FileTreeWalker implements Closeable {
|
||||||
/**
|
/**
|
||||||
* The event types.
|
* The event types.
|
||||||
*/
|
*/
|
||||||
static enum EventType {
|
enum EventType {
|
||||||
/**
|
/**
|
||||||
* Start of a directory
|
* Start of a directory
|
||||||
*/
|
*/
|
||||||
|
@ -109,13 +111,13 @@ class FileTreeWalker implements Closeable {
|
||||||
*/
|
*/
|
||||||
static class Event {
|
static class Event {
|
||||||
private final EventType type;
|
private final EventType type;
|
||||||
private final Path file;
|
private final Path path;
|
||||||
private final BasicFileAttributes attrs;
|
private final BasicFileAttributes attrs;
|
||||||
private final IOException ioe;
|
private final IOException ioe;
|
||||||
|
|
||||||
private Event(EventType type, Path file, BasicFileAttributes attrs, IOException ioe) {
|
private Event(EventType type, Path path, BasicFileAttributes attrs, IOException ioe) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.file = file;
|
this.path = path;
|
||||||
this.attrs = attrs;
|
this.attrs = attrs;
|
||||||
this.ioe = ioe;
|
this.ioe = ioe;
|
||||||
}
|
}
|
||||||
|
@ -132,8 +134,8 @@ class FileTreeWalker implements Closeable {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path file() {
|
Path path() {
|
||||||
return file;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicFileAttributes attributes() {
|
BasicFileAttributes attributes() {
|
||||||
|
@ -161,18 +163,17 @@ class FileTreeWalker implements Closeable {
|
||||||
boolean fl = false;
|
boolean fl = false;
|
||||||
for (FileVisitOption option: options) {
|
for (FileVisitOption option: options) {
|
||||||
// will throw NPE if options contains null
|
// will throw NPE if options contains null
|
||||||
switch (option) {
|
if (Objects.requireNonNull(option) == FileVisitOption.FOLLOW_LINKS) {
|
||||||
case FOLLOW_LINKS : fl = true; break;
|
fl = true;
|
||||||
default:
|
} else {
|
||||||
throw new AssertionError("Should not get here");
|
throw new AssertionError("Should not get here");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (maxDepth < 0)
|
if (maxDepth < 0)
|
||||||
throw new IllegalArgumentException("'maxDepth' is negative");
|
throw new IllegalArgumentException("'maxDepth' is negative");
|
||||||
|
|
||||||
this.followLinks = fl;
|
this.followLinks = fl;
|
||||||
this.linkOptions = (fl) ? new LinkOption[0] :
|
this.linkOptions = (fl) ? new LinkOption[0] : new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
|
||||||
new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
|
|
||||||
this.maxDepth = maxDepth;
|
this.maxDepth = maxDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,22 +182,18 @@ class FileTreeWalker implements Closeable {
|
||||||
* the walk is following sym links is not. The {@code canUseCached}
|
* the walk is following sym links is not. The {@code canUseCached}
|
||||||
* argument determines whether this method can use cached attributes.
|
* argument determines whether this method can use cached attributes.
|
||||||
*/
|
*/
|
||||||
private BasicFileAttributes getAttributes(Path file, boolean canUseCached)
|
private BasicFileAttributes getAttributes(Path file)
|
||||||
throws IOException
|
throws IOException {
|
||||||
{
|
|
||||||
// attempt to get attributes of file. If fails and we are following
|
// attempt to get attributes of file. If fails and we are following
|
||||||
// links then a link target might not exist so get attributes of link
|
// links then a link target might not exist so get attributes of link
|
||||||
BasicFileAttributes attrs;
|
BasicFileAttributes attrs;
|
||||||
try {
|
try {
|
||||||
attrs = Files.readAttributes(file, BasicFileAttributes.class, linkOptions);
|
attrs = Files.readAttributes(file, BasicFileAttributes.class, linkOptions);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
if (!followLinks)
|
if (!followLinks) {
|
||||||
throw ioe;
|
throw ioe;
|
||||||
|
}
|
||||||
// attempt to get attrmptes without following links
|
attrs = Files.readAttributes(file, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
|
||||||
attrs = Files.readAttributes(file,
|
|
||||||
BasicFileAttributes.class,
|
|
||||||
LinkOption.NOFOLLOW_LINKS);
|
|
||||||
}
|
}
|
||||||
return attrs;
|
return attrs;
|
||||||
}
|
}
|
||||||
|
@ -232,70 +229,39 @@ class FileTreeWalker implements Closeable {
|
||||||
/**
|
/**
|
||||||
* Visits the given file, returning the {@code Event} corresponding to that
|
* Visits the given file, returning the {@code Event} corresponding to that
|
||||||
* visit.
|
* visit.
|
||||||
*
|
|
||||||
* The {@code ignoreSecurityException} parameter determines whether
|
|
||||||
* any SecurityException should be ignored or not. If a SecurityException
|
|
||||||
* is thrown, and is ignored, then this method returns {@code null} to
|
|
||||||
* mean that there is no event corresponding to a visit to the file.
|
|
||||||
*
|
|
||||||
* The {@code canUseCached} parameter determines whether cached attributes
|
|
||||||
* for the file can be used or not.
|
|
||||||
*/
|
*/
|
||||||
private Event visit(Path entry, boolean ignoreSecurityException, boolean canUseCached) {
|
private Event visit(Path entry) {
|
||||||
// need the file attributes
|
|
||||||
BasicFileAttributes attrs;
|
BasicFileAttributes attrs;
|
||||||
try {
|
try {
|
||||||
attrs = getAttributes(entry, canUseCached);
|
attrs = getAttributes(entry);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
return new Event(EventType.ENTRY, entry, ioe);
|
return new Event(EventType.ENTRY, entry, ioe);
|
||||||
} catch (SecurityException se) {
|
|
||||||
if (ignoreSecurityException)
|
|
||||||
return null;
|
|
||||||
throw se;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// at maximum depth or file is not a directory
|
|
||||||
int depth = stack.size();
|
int depth = stack.size();
|
||||||
if (depth >= maxDepth || !attrs.isDirectory()) {
|
if (depth >= maxDepth || !attrs.isDirectory()) {
|
||||||
return new Event(EventType.ENTRY, entry, attrs);
|
return new Event(EventType.ENTRY, entry, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for cycles when following links
|
|
||||||
if (followLinks && wouldLoop(entry, attrs.fileKey())) {
|
if (followLinks && wouldLoop(entry, attrs.fileKey())) {
|
||||||
return new Event(EventType.ENTRY, entry,
|
return new Event(EventType.ENTRY, entry, new FileSystemLoopException(entry.toString()));
|
||||||
new FileSystemLoopException(entry.toString()));
|
|
||||||
}
|
}
|
||||||
|
DirectoryStream<Path> stream;
|
||||||
// file is a directory, attempt to open it
|
|
||||||
DirectoryStream<Path> stream = null;
|
|
||||||
try {
|
try {
|
||||||
stream = Files.newDirectoryStream(entry);
|
stream = Files.newDirectoryStream(entry);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
return new Event(EventType.ENTRY, entry, ioe);
|
return new Event(EventType.ENTRY, entry, ioe);
|
||||||
} catch (SecurityException se) {
|
|
||||||
if (ignoreSecurityException)
|
|
||||||
return null;
|
|
||||||
throw se;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// push a directory node to the stack and return an event
|
|
||||||
stack.push(new DirectoryNode(entry, attrs.fileKey(), stream));
|
stack.push(new DirectoryNode(entry, attrs.fileKey(), stream));
|
||||||
return new Event(EventType.START_DIRECTORY, entry, attrs);
|
return new Event(EventType.START_DIRECTORY, entry, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start walking from the given file.
|
* Start walking from the given file.
|
||||||
*/
|
*/
|
||||||
Event walk(Path file) {
|
Event walk(Path file) {
|
||||||
if (closed)
|
if (closed) {
|
||||||
throw new IllegalStateException("Closed");
|
throw new IllegalStateException("Closed");
|
||||||
|
}
|
||||||
Event ev = visit(file,
|
return visit(file);
|
||||||
false, // ignoreSecurityException
|
|
||||||
false); // canUseCached
|
|
||||||
assert ev != null;
|
|
||||||
return ev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -304,51 +270,35 @@ class FileTreeWalker implements Closeable {
|
||||||
*/
|
*/
|
||||||
Event next() {
|
Event next() {
|
||||||
DirectoryNode top = stack.peek();
|
DirectoryNode top = stack.peek();
|
||||||
if (top == null)
|
if (top == null) {
|
||||||
return null; // stack is empty, we are done
|
return null;
|
||||||
|
}
|
||||||
// continue iteration of the directory at the top of the stack
|
Path entry = null;
|
||||||
Event ev;
|
IOException ioe = null;
|
||||||
do {
|
if (!top.skipped()) {
|
||||||
Path entry = null;
|
Iterator<Path> iterator = top.iterator();
|
||||||
IOException ioe = null;
|
try {
|
||||||
|
if (iterator.hasNext()) {
|
||||||
// get next entry in the directory
|
entry = iterator.next();
|
||||||
if (!top.skipped()) {
|
}
|
||||||
Iterator<Path> iterator = top.iterator();
|
} catch (DirectoryIteratorException x) {
|
||||||
try {
|
ioe = x.getCause();
|
||||||
if (iterator.hasNext()) {
|
}
|
||||||
entry = iterator.next();
|
}
|
||||||
}
|
if (entry == null) {
|
||||||
} catch (DirectoryIteratorException x) {
|
try {
|
||||||
ioe = x.getCause();
|
top.stream().close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (ioe == null) {
|
||||||
|
ioe = e;
|
||||||
|
} else {
|
||||||
|
ioe.addSuppressed(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stack.pop();
|
||||||
// no next entry so close and pop directory,
|
return new Event(EventType.END_DIRECTORY, top.directory(), ioe);
|
||||||
// creating corresponding event
|
}
|
||||||
if (entry == null) {
|
return visit(entry);
|
||||||
try {
|
|
||||||
top.stream().close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (ioe == null) {
|
|
||||||
ioe = e;
|
|
||||||
} else {
|
|
||||||
ioe.addSuppressed(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stack.pop();
|
|
||||||
return new Event(EventType.END_DIRECTORY, top.directory(), ioe);
|
|
||||||
}
|
|
||||||
|
|
||||||
// visit the entry
|
|
||||||
ev = visit(entry,
|
|
||||||
true, // ignoreSecurityException
|
|
||||||
true); // canUseCached
|
|
||||||
|
|
||||||
} while (ev == null);
|
|
||||||
|
|
||||||
return ev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -362,17 +312,9 @@ class FileTreeWalker implements Closeable {
|
||||||
DirectoryNode node = stack.pop();
|
DirectoryNode node = stack.pop();
|
||||||
try {
|
try {
|
||||||
node.stream().close();
|
node.stream().close();
|
||||||
} catch (IOException ignore) { }
|
} catch (IOException ignore) {
|
||||||
}
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Skips the remaining entries in the directory at the top of the stack.
|
|
||||||
* This method is a no-op if the stack is empty or the walker is closed.
|
|
||||||
*/
|
|
||||||
void skipRemainingSiblings() {
|
|
||||||
if (!stack.isEmpty()) {
|
|
||||||
stack.peek().skip();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import java.nio.file.DirectoryIteratorException;
|
||||||
import java.nio.file.DirectoryStream;
|
import java.nio.file.DirectoryStream;
|
||||||
import java.nio.file.FileSystemLoopException;
|
import java.nio.file.FileSystemLoopException;
|
||||||
import java.nio.file.FileVisitOption;
|
import java.nio.file.FileVisitOption;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.NotDirectoryException;
|
import java.nio.file.NotDirectoryException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
@ -64,10 +63,6 @@ public class FileWalker {
|
||||||
* a directory <i>(optional specific exception)</i>
|
* a directory <i>(optional specific exception)</i>
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* if an I/O error occurs when opening the directory
|
* if an I/O error occurs when opening the directory
|
||||||
* @throws SecurityException
|
|
||||||
* In the case of the default provider, and a security manager is
|
|
||||||
* installed, the {@link SecurityManager#checkRead(String) checkRead}
|
|
||||||
* method is invoked to check read access to the directory.
|
|
||||||
*/
|
*/
|
||||||
public static Stream<Path> list(DirectoryStream<Path> ds) throws IOException {
|
public static Stream<Path> list(DirectoryStream<Path> ds) throws IOException {
|
||||||
try {
|
try {
|
||||||
|
@ -90,10 +85,8 @@ public class FileWalker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Spliterator<Path> spliterator =
|
Spliterator<Path> spliterator = Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT);
|
||||||
Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT);
|
return StreamSupport.stream(spliterator, false).onClose(asUncheckedRunnable(ds));
|
||||||
return StreamSupport.stream(spliterator, false)
|
|
||||||
.onClose(asUncheckedRunnable(ds));
|
|
||||||
} catch (Error|RuntimeException e) {
|
} catch (Error|RuntimeException e) {
|
||||||
try {
|
try {
|
||||||
ds.close();
|
ds.close();
|
||||||
|
@ -179,11 +172,6 @@ public class FileWalker {
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException
|
* @throws IllegalArgumentException
|
||||||
* if the {@code maxDepth} parameter is negative
|
* if the {@code maxDepth} parameter is negative
|
||||||
* @throws SecurityException
|
|
||||||
* If the security manager denies access to the starting file.
|
|
||||||
* In the case of the default provider, the {@link
|
|
||||||
* SecurityManager#checkRead(String) checkRead} method is invoked
|
|
||||||
* to check read access to the directory.
|
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* if an I/O error is thrown when accessing the starting file.
|
* if an I/O error is thrown when accessing the starting file.
|
||||||
*/
|
*/
|
||||||
|
@ -204,7 +192,7 @@ public class FileWalker {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(FileTreeWalker.Event::file);
|
.map(FileTreeWalker.Event::path);
|
||||||
} catch (Error|RuntimeException e) {
|
} catch (Error|RuntimeException e) {
|
||||||
iterator.close();
|
iterator.close();
|
||||||
closeable.close();
|
closeable.close();
|
||||||
|
@ -213,8 +201,7 @@ public class FileWalker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a Closeable to a Runnable by converting checked IOException
|
* Convert a Closeable to a Runnable by converting checked IOException to UncheckedIOException.
|
||||||
* to UncheckedIOException
|
|
||||||
*/
|
*/
|
||||||
private static Runnable asUncheckedRunnable(Closeable c) {
|
private static Runnable asUncheckedRunnable(Closeable c) {
|
||||||
return () -> {
|
return () -> {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
group = org.xbib
|
group = org.xbib
|
||||||
name = files
|
name = files
|
||||||
version = 4.1.0
|
version = 4.2.0
|
||||||
|
|
||||||
org.gradle.warning.mode = ALL
|
org.gradle.warning.mode = ALL
|
||||||
|
|
Loading…
Reference in a new issue