diff --git a/base/src/main/kotlin/de/staropensource/engine/base/utility/FileAccess.kt b/base/src/main/kotlin/de/staropensource/engine/base/utility/FileAccess.kt index dbe1e124a..224b03851 100644 --- a/base/src/main/kotlin/de/staropensource/engine/base/utility/FileAccess.kt +++ b/base/src/main/kotlin/de/staropensource/engine/base/utility/FileAccess.kt @@ -357,7 +357,7 @@ class FileAccess { */ @Throws(IOAccessException::class) fun getType(): Type { - return if (!exists()) Type.VOID + return if (!exists(noFollowSymbolicLink = true)) Type.VOID else if (Files.isRegularFile(path)) Type.FILE else if (Files.isDirectory(path)) Type.DIRECTORY else Type.UNKNOWN @@ -566,7 +566,7 @@ class FileAccess { */ @Throws(IOAccessException::class) fun createFile(): FileAccess { - if (!exists()) + if (!exists(noFollowSymbolicLink = true)) try { logger.diag("Creating a file at '${unformatFromPath(path)}'") file.parentFile.mkdirs() @@ -591,7 +591,7 @@ class FileAccess { */ @Throws(IOAccessException::class) fun createDirectory(): FileAccess { - if (!exists()) + if (!exists(noFollowSymbolicLink = true)) try { logger.diag("Creating a directory at '${unformatFromPath(path)}'") file.mkdirs() @@ -619,7 +619,7 @@ class FileAccess { */ @Throws(IOAccessException::class) fun createLink(destination: FileAccess, hard: Boolean): FileAccess { - if (!exists()) + if (!exists(noFollowSymbolicLink = true)) try { logger.diag("Creating a ${if (hard) "hard" else "symbolic"} link at '${unformatFromPath(path)}'") if (hard) Files.createLink(path, destination.path) @@ -707,7 +707,7 @@ class FileAccess { */ @Throws(IOAccessException::class) fun delete(): FileAccess { - if (exists()) + if (exists(noFollowSymbolicLink = true)) try { logger.diag("Deleting '${unformatFromPath(path)}'") @@ -825,12 +825,17 @@ class FileAccess { @Throws(IOAccessException::class) fun writeBytes(bytes: ByteArray, async: Boolean = false): FileAccess { try { - if (getType() != Type.FILE) - return this + when (getType()) { + Type.UNKNOWN, Type.DIRECTORY -> return this + else -> {} + } logger.diag("Writing to file '${path}' (bytes, ${if (async) "async" else ""})") createFile() - Files.write(path, bytes, StandardOpenOption.WRITE, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) + if (async) + Files.write(path, bytes, StandardOpenOption.WRITE) + else + Files.write(path, bytes, StandardOpenOption.WRITE, StandardOpenOption.SYNC) } catch (exception: Exception) { throw IOAccessException("Unable to read file '${path}' (bytes, ${if (async) "async" else ""})", exception) } @@ -855,12 +860,17 @@ class FileAccess { @Throws(IOAccessException::class) fun writeLines(lines: List, async: Boolean = false): FileAccess { try { - if (getType() != Type.FILE) - return this + when (getType()) { + Type.UNKNOWN, Type.DIRECTORY -> return this + else -> {} + } logger.diag("Writing to file '${path}' (lines, ${if (async) "async" else ""})") createFile() - Files.write(path, lines, StandardOpenOption.WRITE, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) + if (async) + Files.write(path, lines, StandardOpenOption.WRITE) + else + Files.write(path, lines, StandardOpenOption.WRITE, StandardOpenOption.SYNC) } catch (exception: Exception) { throw IOAccessException("Unable to read file '${path}' (lines, ${if (async) "async" else ""})", exception) } @@ -885,12 +895,17 @@ class FileAccess { @Throws(IOAccessException::class) fun writeString(string: String, async: Boolean = false): FileAccess { try { - if (getType() != Type.FILE) - return this + when (getType()) { + Type.UNKNOWN, Type.DIRECTORY -> return this + else -> {} + } logger.diag("Writing to file '${path}' (string, ${if (async) "async" else ""})") createFile() - Files.writeString(path, string, StandardOpenOption.WRITE, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) + if (async) + Files.writeString(path, string, StandardOpenOption.WRITE) + else + Files.writeString(path, string, StandardOpenOption.WRITE, StandardOpenOption.SYNC) } catch (exception: Exception) { throw IOAccessException("Unable to read file '${path}' (string, ${if (async) "async" else ""})", exception) } @@ -915,12 +930,17 @@ class FileAccess { @Throws(IOAccessException::class) fun appendBytes(bytes: ByteArray, async: Boolean = false): FileAccess { try { - if (getType() != Type.FILE) - return this + when (getType()) { + Type.UNKNOWN, Type.DIRECTORY -> return this + else -> {} + } logger.diag("Appending to file '${path}' (bytes, ${if (async) "async" else ""})") createFile() - Files.write(path, bytes, StandardOpenOption.APPEND, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) + if (async) + Files.write(path, bytes, StandardOpenOption.APPEND) + else + Files.write(path, bytes, StandardOpenOption.APPEND, StandardOpenOption.SYNC) } catch (exception: Exception) { throw IOAccessException("Unable to read file '${path}' (bytes, ${if (async) "async" else ""})", exception) } @@ -945,12 +965,17 @@ class FileAccess { @Throws(IOAccessException::class) fun appendLines(lines: List, async: Boolean = false): FileAccess { try { - if (getType() != Type.FILE) - return this + when (getType()) { + Type.UNKNOWN, Type.DIRECTORY -> return this + else -> {} + } logger.diag("Appending to file '${path}' (lines, ${if (async) "async" else ""})") createFile() - Files.write(path, lines, StandardOpenOption.APPEND, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) + if (async) + Files.write(path, lines, StandardOpenOption.APPEND) + else + Files.write(path, lines, StandardOpenOption.APPEND, StandardOpenOption.SYNC) } catch (exception: Exception) { throw IOAccessException("Unable to read file '${path}' (lines, ${if (async) "async" else ""})", exception) } @@ -975,12 +1000,17 @@ class FileAccess { @Throws(IOAccessException::class) fun appendString(string: String, async: Boolean = false): FileAccess { try { - if (getType() != Type.FILE) - return this + when (getType()) { + Type.UNKNOWN, Type.DIRECTORY -> return this + else -> {} + } logger.diag("Appending to file '${path}' (string, ${if (async) "async" else ""})") createFile() - Files.writeString(path, string, StandardOpenOption.APPEND, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) + if (async) + Files.writeString(path, string, StandardOpenOption.APPEND) + else + Files.writeString(path, string, StandardOpenOption.APPEND, StandardOpenOption.SYNC) } catch (exception: Exception) { throw IOAccessException("Unable to read file '${path}' (string, ${if (async) "async" else ""})", exception) } @@ -1105,14 +1135,21 @@ class FileAccess { * Verifies that something * exists at this location. * + * This method does not follow symbolic links + * and instead looks at the file directly. This + * method should likely be used to check if + * something exists at that exact location. + * In case you may need to follow symbolic links, + * use [verifyExistsLink] instead. + * * @return this instance * @throws IOAccessException on IO error * @throws VerificationFailedException if the verification fails * @since v1-alpha10 */ @Throws(IOAccessException::class, VerificationFailedException::class) - fun verifyExists(noFollowSymbolicLink: Boolean = false): FileAccess { - if (!exists(noFollowSymbolicLink = noFollowSymbolicLink)) + fun verifyExists(): FileAccess { + if (!exists(noFollowSymbolicLink = true)) throw VerificationFailedException("Expected that something exists at '${unformatFromPath(path)}'") return this @@ -1122,19 +1159,76 @@ class FileAccess { * Verifies that something does * not exist at this location. * + * This method does not follow symbolic links + * and instead looks at the file directly. This + * method should likely be used to check if + * something exists at that exact location. + * In case you may need to follow symbolic links, + * use [verifyNotExistsLink] instead. + * * @return this instance * @throws IOAccessException on IO error * @throws VerificationFailedException if the verification fails * @since v1-alpha10 */ @Throws(IOAccessException::class, VerificationFailedException::class) - fun verifyNotExists(noFollowSymbolicLink: Boolean = false): FileAccess { - if (exists(noFollowSymbolicLink = noFollowSymbolicLink)) + fun verifyNotExists(): FileAccess { + if (exists(noFollowSymbolicLink = true)) throw VerificationFailedException("Expected that nothing exists at '${unformatFromPath(path)}'") return this } + /** + * Verifies that something + * exists at this location. + * + * This method follows symbolic links during + * the verification process, which should + * only be used to check if you can read/write + * from a file. In case you need to verify the + * existence of this very file and do not + * require following symbolic links, use the + * [verifyNotExists] method instead. + * + * @return this instance + * @throws IOAccessException on IO error + * @throws VerificationFailedException if the verification fails + * @since v1-alpha10 + */ + @Throws(IOAccessException::class, VerificationFailedException::class) + fun verifyExistsLink(): FileAccess { + if (!exists(noFollowSymbolicLink = false)) + throw VerificationFailedException("Expected the link destination of '${unformatFromPath(path)}' is dead") + + return this + } + + /** + * Verifies that something does + * not exist at this location. + * + * This method follows symbolic links during + * the verification process, which should + * only be used to check if you can read/write + * from a file. In case you need to verify the + * existence of this very file and do not + * require following symbolic links, use the + * [verifyExists] method instead. + * + * @return this instance + * @throws IOAccessException on IO error + * @throws VerificationFailedException if the verification fails + * @since v1-alpha10 + */ + @Throws(IOAccessException::class, VerificationFailedException::class) + fun verifyNotExistsLink(): FileAccess { + if (exists(noFollowSymbolicLink = false)) + throw VerificationFailedException("Expected that the link destination of '${unformatFromPath(path)}' exists") + + return this + } + /** * Verifies that a file * is at this location. @@ -1214,7 +1308,7 @@ class FileAccess { */ @Throws(IOAccessException::class, VerificationFailedException::class) fun verifyIsLink(): FileAccess { - if (exists() && isSymbolicLink()) + if (exists(noFollowSymbolicLink = true) && isSymbolicLink()) throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is a link") return this @@ -1231,7 +1325,7 @@ class FileAccess { */ @Throws(IOAccessException::class, VerificationFailedException::class) fun verifyIsNotLink(): FileAccess { - if (exists() && !isSymbolicLink()) + if (exists(noFollowSymbolicLink = true) && !isSymbolicLink()) throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is not a link") return this @@ -1248,7 +1342,7 @@ class FileAccess { */ @Throws(IOAccessException::class, VerificationFailedException::class) fun verifyIsValidLink(): FileAccess { - if (exists() && (isSymbolicLink() || getLinkDestination() != null)) + if (exists(noFollowSymbolicLink = true) && (isSymbolicLink() || getLinkDestination() != null)) throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is a link") return this @@ -1265,7 +1359,7 @@ class FileAccess { */ @Throws(IOAccessException::class, VerificationFailedException::class) fun verifyIsNotValidLink(): FileAccess { - if (exists() && !(isSymbolicLink() || getLinkDestination() != null)) + if (exists(noFollowSymbolicLink = true) && !(isSymbolicLink() || getLinkDestination() != null)) throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is not a link") return this @@ -1387,6 +1481,7 @@ class FileAccess { } override fun visitFile(path: Path, attributes: BasicFileAttributes): FileVisitResult { + path.toFile().delete() return FileVisitResult.CONTINUE }