All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 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> shouldBeDeleted) {
        delete(path.delegate(), shouldBeDeleted);
    }

    protected void delete(@Nonnull java.nio.file.Path path, @Nonnull Predicate> 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