diff --git a/base/src/main/java/de/staropensource/engine/base/utility/FileAccess.java b/base/src/main/java/de/staropensource/engine/base/utility/FileAccess.java index 99bf117..5cece01 100644 --- a/base/src/main/java/de/staropensource/engine/base/utility/FileAccess.java +++ b/base/src/main/java/de/staropensource/engine/base/utility/FileAccess.java @@ -26,6 +26,7 @@ import de.staropensource.engine.base.utility.misc.Miscellaneous; import lombok.Getter; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FileNotFoundException; @@ -34,6 +35,7 @@ import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.PosixFilePermissions; import java.util.*; import java.util.stream.Stream; @@ -410,7 +412,7 @@ public final class FileAccess { * @throws IOException on an IO error * @since v1-alpha8 */ - public @NotNull String @NotNull [] listContents() throws UnsupportedOperationException, IOException { + public @NotNull String @NotNull [] list() throws UnsupportedOperationException, IOException { if (getType() != Type.DIRECTORY) throw new UnsupportedOperationException("The file '" + path + "' is not a directory"); @@ -422,6 +424,58 @@ public final class FileAccess { return list; } + /** + * Returns the names of all files + * in this directory. + * + * @return array of file names + * @throws UnsupportedOperationException if this file isn't a directory + * @throws IOException on an IO error + * @since v1-alpha8 + */ + public @NotNull String @NotNull [] listFiles() throws UnsupportedOperationException, IOException { + if (getType() != Type.DIRECTORY) + throw new UnsupportedOperationException("The file '" + path + "' is not a directory"); + + String[] listArray = file.list(); + List<@NotNull String> list = new ArrayList<>(); + + if (listArray == null) + throw new IOException("list is null (isn't a directory although it should be one)"); + + for (String item : listArray) + if (path.resolve(item).toFile().isFile()) + list.add(item); + + return list.toArray(new String[0]); + } + + /** + * Returns the names of all + * directories in this directory. + * + * @return array of directory names + * @throws UnsupportedOperationException if this file isn't a directory + * @throws IOException on an IO error + * @since v1-alpha8 + */ + public @NotNull String @NotNull [] listDirectories() throws UnsupportedOperationException, IOException { + if (getType() != Type.DIRECTORY) + throw new UnsupportedOperationException("The file '" + path + "' is not a directory"); + + String[] listArray = file.list(); + List<@NotNull String> list = new ArrayList<>(); + + if (listArray == null) + throw new IOException("list is null (isn't a directory although it should be one)"); + + for (String item : listArray) + if (path.resolve(item).toFile().isDirectory()) + list.add(item); + + return list.toArray(new String[0]); + } + /** * Returns the destination of the symbolic link. * @@ -699,6 +753,7 @@ public final class FileAccess { * @since v1-alpha9 */ public @NotNull FileAccess move(@NotNull FileAccess destination) throws IOException { + Logger.diag("Moving '" + path + "' to '" + destination.path + "'"); Files.move(path, destination.path, StandardCopyOption.REPLACE_EXISTING); return destination; } @@ -712,7 +767,11 @@ public final class FileAccess { * @since v1-alpha9 */ public @NotNull FileAccess copy(@NotNull FileAccess destination) throws IOException { - Files.copy(path, destination.path, StandardCopyOption.REPLACE_EXISTING); + Logger.diag("Copying '" + path + "' to '" + destination.path + "'"); + if (file.isDirectory()) + Files.walkFileTree(path, new CopyDirectoryVisitor(path, destination.path)); + else + Files.copy(path, destination.path, StandardCopyOption.REPLACE_EXISTING); return this; } @@ -721,11 +780,17 @@ public final class FileAccess { * If it doesn't exist, nothing will be done. * * @return this instance + * @throws IOException on an IO error * @since v1-alpha8 */ - public @NotNull FileAccess delete() { + public @NotNull FileAccess delete() throws IOException { if (exists()) { Logger.diag("Deleting '" + path + "'"); + + // Recursively delete if directory + if (getFile().isDirectory()) + Files.walkFileTree(path, new DeleteDirectoryVisitor(path)); + //noinspection ResultOfMethodCallIgnored file.delete(); } @@ -1013,4 +1078,93 @@ public final class FileAccess { */ UNKNOWN } + + /** + * {@link FileVisitor} instance for + * copying directories recursively. + * + * @param source source to copy from + * @param destination destination to copy to + * @since v1-alpha9 + */ + private record CopyDirectoryVisitor(@NotNull Path source, @NotNull Path destination) implements FileVisitor<@NotNull Path> { + /** + * Creates and initializes an + * instance of this class. + * + * @since v1-alpha9 + */ + private CopyDirectoryVisitor {} + @Override + public @NotNull FileVisitResult preVisitDirectory(Path path, @NotNull BasicFileAttributes attributes) throws IOException { + Files.createDirectories(destination.resolve(source.relativize(path))); + return FileVisitResult.CONTINUE; + } + + /** {@inheritDoc} */ + @Override + public @NotNull FileVisitResult visitFile(Path path, @NotNull BasicFileAttributes attributes) throws IOException { + Files.copy(path, destination.resolve(source.relativize(path))); + return FileVisitResult.CONTINUE; + } + + /** {@inheritDoc} */ + @Override + public @NotNull FileVisitResult visitFileFailed(Path path, @NotNull IOException exception) throws IOException { + throw exception; + } + + /** {@inheritDoc} */ + @Override + public @NotNull FileVisitResult postVisitDirectory(Path path, @Nullable IOException exception) throws IOException { + if (exception != null) + throw exception; + + return FileVisitResult.CONTINUE; + } + } + + /** + * {@link FileVisitor} instance for + * delete directories recursively. + * + * @param directory directory to delete + * @since v1-alpha9 + */ + private record DeleteDirectoryVisitor(@NotNull Path directory) implements FileVisitor<@NotNull Path> { + /** + * Creates and initializes an + * instance of this class. + * + * @since v1-alpha9 + */ + private DeleteDirectoryVisitor {} + @Override + public @NotNull FileVisitResult preVisitDirectory(Path path, @NotNull BasicFileAttributes attributes) { + return FileVisitResult.CONTINUE; + } + + /** {@inheritDoc} */ + @Override + public @NotNull FileVisitResult visitFile(Path path, @NotNull BasicFileAttributes attributes) throws IOException { + Files.delete(path); + return FileVisitResult.CONTINUE; + } + + /** {@inheritDoc} */ + @Override + public @NotNull FileVisitResult visitFileFailed(Path path, @NotNull IOException exception) throws IOException { + throw exception; + } + + /** {@inheritDoc} */ + @Override + public @NotNull FileVisitResult postVisitDirectory(Path path, @Nullable IOException exception) throws IOException { + if (exception != null) + throw exception; + + Files.delete(path); + return FileVisitResult.CONTINUE; + } + } }