diff --git a/src/main/java/sirius/kernel/commons/Files.java b/src/main/java/sirius/kernel/commons/Files.java index 7fa7d782..88ba06a2 100644 --- a/src/main/java/sirius/kernel/commons/Files.java +++ b/src/main/java/sirius/kernel/commons/Files.java @@ -18,8 +18,10 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import java.util.List; import java.util.Optional; import java.util.regex.Pattern; +import java.util.stream.Stream; /** * Helperclass for handling files in Java 8. @@ -28,6 +30,11 @@ public class Files { private static final Pattern NON_PATH_CHARACTERS = Pattern.compile("[^a-zA-Z0-9\\-.]"); + /** + * Contains a list of file names and endings which are considered to be metadata. + */ + private static final List METADATA_FILES = List.of("__MACOSX", ".DS_Store", "Thumbs.db"); + private Files() { } @@ -147,8 +154,39 @@ public static String getFilenameWithoutExtension(@Nullable String path) { } /** - * If the given file is not null and exists, tries to delete that file and logs when a file cannot be deleted. T - * his is useful for error reporting and to diagnose why a file cannot be deleted. + * Determines if the given path is hidden. + *

+ * A path is considered hidden if it starts with a dot. + * + * @param fileOrDirectoryName the path to check + * @return true if the path is hidden, false otherwise + */ + public static boolean isConsideredHidden(@Nullable String fileOrDirectoryName) { + if (Strings.isEmpty(fileOrDirectoryName)) { + return false; + } + return fileOrDirectoryName.startsWith("."); + } + + /** + * Determines if the given path is a metadata file. + *

+ * A metadata file is a file which is not part of the actual content but rather contains metadata or is used by + * the operating system or other tools. + * + * @param fileOrDirectoryName the path to check + * @return true if the path is a metadata file that is listed in the METADATA_FILES list, false otherwise + */ + public static boolean isConsideredMetadata(@Nullable String fileOrDirectoryName) { + if (Strings.isEmpty(fileOrDirectoryName)) { + return false; + } + return METADATA_FILES.stream().anyMatch(fileOrDirectoryName::startsWith); + } + + /** + * If the given file is not null and exists, tries to delete that file and logs when a file cannot be deleted. + * This is useful for error reporting and to diagnose why a file cannot be deleted. * * @param file the file to delete */ @@ -213,4 +251,27 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx } }); } + + /** + * Returns a stream of all parts of the given path. + *

+ * The stream will contain the filename, the parent directory, the parent of the parent and so on. + * + * @param path the path to split + * @return a stream of all parts of the given path + */ + public static Stream streamPath(String path) { + if (Strings.isEmpty(path)) { + return Stream.empty(); + } + + Stream.Builder builder = Stream.builder(); + builder.add(Files.getFilenameAndExtension(path)); + String parent = Files.getBasepath(path); + while (Strings.isFilled(parent)) { + builder.add(Files.getFilenameAndExtension(parent)); + parent = Files.getBasepath(parent); + } + return builder.build(); + } } diff --git a/src/test/kotlin/sirius/kernel/commons/FilesTest.kt b/src/test/kotlin/sirius/kernel/commons/FilesTest.kt index 92be5afd..5294cff3 100644 --- a/src/test/kotlin/sirius/kernel/commons/FilesTest.kt +++ b/src/test/kotlin/sirius/kernel/commons/FilesTest.kt @@ -84,4 +84,34 @@ internal class FilesTest { assertNull(Files.toSaneFileName(" ").orElse(null)) assertNull(Files.toSaneFileName("").orElse(null)) } + + @Test + fun `isConsideredHidden works as expected`(){ + assertEquals(true, Files.isConsideredHidden(".test")); + assertEquals(true, Files.isConsideredHidden(".test.tmp")); + assertEquals(false, Files.isConsideredHidden("test")); + assertEquals(false, Files.isConsideredHidden("test.tmp")); + assertEquals(false,Files.isConsideredHidden(null)); + } + + @Test + fun `isConsideredMetadata works as expected`(){ + assertEquals(true, Files.isConsideredMetadata("__MACOSX")); + assertEquals(true, Files.isConsideredMetadata("__MACOSX/test")); + assertEquals(true, Files.isConsideredMetadata("__MACOSX/folder1/test")); + assertEquals(true, Files.isConsideredMetadata(".DS_Store")); + assertEquals(true, Files.isConsideredMetadata("Thumbs.db")); + assertEquals(false, Files.isConsideredMetadata("test.tmp")); + assertEquals(false, Files.isConsideredMetadata("thumbs.db")); + assertEquals(false, Files.isConsideredMetadata(null)); + } + + @Test + fun `streamPath works as expected`() { + assert(Files.streamPath("test1").toList().equals(listOf("test1"))); + assertEquals(listOf("test2", "test1"), Files.streamPath("/test1/test2").toList()); + assertEquals(listOf("test3", ".test2", "test1"), Files.streamPath("/test1/.test2/test3").toList()); + assertEquals(listOf("test4.png", "test3", "test2", "test1"), Files.streamPath("/test1/test2/test3/test4.png").toList()); + assertEquals(listOf(null, "test2", "test1"), Files.streamPath("/test1/test2/").toList()); + } }