com.goeuro.sync4j.fs.LocalFileSystem Maven / Gradle / Ivy
package com.goeuro.sync4j.fs;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.file.*;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static com.goeuro.sync4j.fs.Detail.Kind.file;
import static com.goeuro.sync4j.fs.Directory.directory;
import static com.goeuro.sync4j.fs.File.file;
import static com.goeuro.sync4j.fs.Local.localOf;
import static com.goeuro.sync4j.fs.Path.path;
import static com.goeuro.sync4j.utils.DateTimes.toOffsetDateTime;
import static com.goeuro.sync4j.utils.Digests.md5HashAsStringOf;
import static com.goeuro.sync4j.utils.IOStreams.copy;
import static java.math.BigInteger.valueOf;
import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.FileVisitResult.SKIP_SUBTREE;
import static java.nio.file.Files.*;
import static java.util.Optional.empty;
import static java.util.Optional.of;
@Immutable
public class LocalFileSystem implements FileSystem {
@Nonnull
private static final LocalFileSystem INSTANCE = new LocalFileSystem();
@Nonnull
public static LocalFileSystem defaultLocalFileSystem() {
return INSTANCE;
}
@Nonnull
public static Builder localFileSystem() {
return new Builder();
}
protected LocalFileSystem() {
}
@Override
public boolean couldHandle(@Nonnull LoosePath plainLoosePath) {
return plainLoosePath instanceof Local;
}
@Override
public boolean couldHandle(@Nonnull URI uri) {
return "file".equals(uri.getScheme());
}
@Nonnull
@Override
public Path resolve(@Nonnull URI uri) {
if (!couldHandle(uri)) {
throw new IllegalArgumentException("Could not handle uri: " + uri);
}
final java.nio.file.Path resolved = Paths.get(uri);
return path(this, localOf(resolved))
.build();
}
@Nonnull
@Override
public Path resolve(@Nonnull String path) {
return path(this, localOf(Paths.get(path)))
.build();
}
@Nonnull
@Override
public Path resolve(@Nonnull Local parent, @Nonnull String path) {
final java.nio.file.Path resolved = resolve(parent.delegate(), path);
return path(this, localOf(resolved))
.build();
}
@Nonnull
protected java.nio.file.Path resolve(@Nonnull java.nio.file.Path parent, @Nonnull String path) {
return parent.resolve(path);
}
@Nonnull
@Override
public String relativize(@Nonnull Local base, @Nonnull Local other) {
return relativize(base.delegate(), other.delegate());
}
@Nonnull
@Override
public Optional> normalize(@Nonnull Path input) {
final java.nio.file.Path newPlain = input.loose().delegate().normalize();
if (newPlain.getNameCount() == 0) {
return empty();
}
return of(toPath(newPlain));
}
@Nonnull
protected String relativize(@Nonnull java.nio.file.Path base, @Nonnull java.nio.file.Path other) {
return base.relativize(other).toString().replace('\\', '/');
}
@Nonnull
@Override
public Stream> list(@Nonnull Local path) {
return list(path.delegate());
}
@Nonnull
protected Stream> list(@Nonnull java.nio.file.Path path) {
try {
return Files.list(path)
.map(this::detailsOf);
} catch (final NotDirectoryException | NoSuchFileException ignored) {
return Stream.empty();
} catch (final IOException e) {
throw new UncheckedIOException("Could not list contents of '" + path + "'.", e);
}
}
@Nonnull
@Override
public Stream> listAll(@Nonnull Local path) {
return listAll(path.delegate());
}
@Nonnull
protected Stream> listAll(@Nonnull java.nio.file.Path path) {
try {
return Files.list(path)
.map(this::detailsOf)
.flatMap(candidate -> {
if (candidate.kind() == file) {
return Stream.of(candidate);
}
return Stream.concat(
Stream.of(candidate),
listAll(candidate.loose())
);
})
;
} catch (final NotDirectoryException | NoSuchFileException ignored) {
return Stream.empty();
} catch (final IOException e) {
throw new UncheckedIOException("Could not list contents of '" + path + "'.", e);
}
}
@Nonnull
@Override
public Optional> detailsOf(@Nonnull Local plain) {
return findDetailsOf(plain.delegate());
}
@Nonnull
protected Detail detailsOf(@Nonnull java.nio.file.Path plain) throws NoSuchElementException {
return findDetailsOf(plain)
.orElseThrow(() -> new NoSuchElementException("Provided path '" + plain + "' does not exist."));
}
@Nonnull
protected Optional> findDetailsOf(@Nonnull java.nio.file.Path plain) {
if (isDirectory(plain)) {
return of(toDirectory(plain));
}
if (isRegularFile(plain)) {
return of(toFile(plain));
}
return empty();
}
@Override
public boolean isTemporary(@Nonnull Exception e, @Nonnull Local source) {
return false;
}
@Nonnull
@Override
public Optional open(@Nonnull Local path) throws IOException {
return open(path.delegate());
}
@Nonnull
protected Optional open(@Nonnull java.nio.file.Path path) throws IOException {
if (!Files.isRegularFile(path)) {
return empty();
}
try {
return of(newInputStream(path));
} catch (final IOException e) {
throw new IOException("Could not open '" + path + "' for read.", e);
}
}
@Nonnull
@Override
public File write(@Nonnull Local path, @Nonnull InputStream is, @Nonnull WriteListener extends LoosePath> listener) throws IOException {
//noinspection unchecked
final WriteListener l = (WriteListener) listener;
createDirectories(path.delegate().getParent());
final long total;
try (final OutputStream os = newOutputStream(path.delegate())) {
total = copy(is, os, transferred -> l.onTransferProgress(path, valueOf(transferred)));
}
l.onTransferDone(path, valueOf(total));
return toFile(path);
}
@Nonnull
@Override
public Directory createDirectory(@Nonnull Local path) throws IOException {
createDirectories(path.delegate());
return toDirectory(path.delegate());
}
@Override
public void delete(@Nonnull Local path, @Nonnull Predicate extends Path> shouldBeDeleted) {
delete(path.delegate(), shouldBeDeleted);
}
protected void delete(@Nonnull java.nio.file.Path path, @Nonnull Predicate extends Path> shouldBeDeleted) {
//noinspection unchecked
final Predicate> predicate = (Predicate>) shouldBeDeleted;
try {
if (isDirectory(path)) {
walkFileTree(path, new SimpleFileVisitor() {
@Override
public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) {
if (predicate.test(toPath(dir))) {
return CONTINUE;
}
return SKIP_SUBTREE;
}
@Override
public FileVisitResult visitFile(java.nio.file.Path file, BasicFileAttributes attrs) throws IOException {
if (predicate.test(toPath(file))) {
try {
Files.delete(file);
} catch (final NoSuchFileException ignored) {
}
}
return CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) throws IOException {
if (exc != null) {
throw exc;
}
try {
Files.delete(dir);
} catch (final NoSuchFileException | DirectoryNotEmptyException ignored) {
}
return CONTINUE;
}
});
} else if (predicate.test(toPath(path))) {
Files.delete(path);
}
} catch (final NoSuchFileException ignored) {
} catch (final IOException e) {
throw new UncheckedIOException("Could not delete '" + path + "'.", e);
}
}
@Nonnull
protected Path toPath(@Nonnull java.nio.file.Path plain) {
return path(this, localOf(plain))
.build();
}
@Nonnull
protected Directory toDirectory(@Nonnull java.nio.file.Path plain) {
return directory(this, localOf(plain))
.build();
}
@Nonnull
protected File toFile(@Nonnull Local path) {
final java.nio.file.Path delegate = path.delegate();
try {
return file(this, path)
.ofSize(valueOf(size(delegate)))
.whichWasLastModifiedAt(toOffsetDateTime(getLastModifiedTime(delegate)))
.withMd5Hash(md5HashAsStringOf(delegate))
.build();
} catch (final IOException e) {
throw new UncheckedIOException("Could not get details of '" + path + "'.", e);
}
}
@Override
public boolean equals(Object o) {
return o instanceof LocalFileSystem;
}
@Override
public int hashCode() {
return 1;
}
@Nonnull
protected File toFile(@Nonnull java.nio.file.Path plain) {
return toFile(localOf(plain));
}
public static class Builder {
protected Builder() {
}
@Nonnull
public LocalFileSystem build() {
return new LocalFileSystem();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy