name.remal.java.io.File.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common Show documentation
Show all versions of common Show documentation
Java & Kotlin tools: common
package name.remal
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import name.remal.io.LockingFileOperations
import java.io.File
import java.io.IOException
import java.lang.System.currentTimeMillis
import java.nio.channels.FileChannel
import java.nio.file.Path
import java.time.Duration
import java.util.zip.Deflater.BEST_COMPRESSION
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
val TEMP_DIR: File get() = File(System.getProperty("java.io.tmpdir")).absoluteFile.createDirectories()
val WORKING_DIR: File get() = File(System.getProperty("user.dir")).absoluteFile
val USER_HOME_DIR: File get() = File(System.getProperty("user.home")).absoluteFile
@JvmOverloads
fun newTempFile(prefix: String = "temp-", suffix: String = ".temp", doDeleteOnExit: Boolean = true, baseTempDir: File = TEMP_DIR): File {
val now = currentTimeMillis()
var counter = 0
while (true) {
val file = File(baseTempDir, encodeURIComponent("$prefix$now-$counter$suffix"))
if (file.createNewFile()) {
return file.absoluteFile.also {
if (doDeleteOnExit) it.deleteOnExit()
}
}
++counter
}
}
@JvmOverloads
fun newTempDir(prefix: String = "temp-", doDeleteOnExit: Boolean = true, baseTempDir: File = TEMP_DIR): File {
val now = currentTimeMillis()
var counter = 0
while (true) {
val dir = File(baseTempDir, encodeURIComponent("$prefix$now-$counter"))
if (dir.mkdir()) {
return dir.absoluteFile.also {
if (doDeleteOnExit) it.deleteOnExitRecursively()
}
}
++counter
}
}
fun File.forceDelete() = apply { toPath().delete() }
fun File.forceDeleteRecursively() = apply { toPath().deleteRecursively() }
fun File.createDirectories() = apply { toPath().createDirectories() }
fun File.createParentDirectories() = apply { toPath().createParentDirectories() }
fun File.permitRead() = apply { if (!setReadable(true, false)) throw IOException("$this can't permit read access") }
fun File.permitWrite() = apply { if (!setWritable(true, false)) throw IOException("$this can't permit write access") }
fun File.permitExecute() = apply { if (!setExecutable(true, false)) throw IOException("$this can't permit execute access") }
fun File.permitAll() = permitRead().permitWrite().permitExecute()
fun File.allowReadOnlyForOwner() = apply {
if (!setReadable(false, false)) throw IOException("$this can't forbid read access")
if (!setReadable(true, true)) throw IOException("$this can't permit read access for owner only")
}
fun File.allowWriteOnlyForOwner() = apply {
if (!setWritable(false, false)) throw IOException("$this can't permit forbid access")
if (!setWritable(true, true)) throw IOException("$this can't permit write access for owner only")
}
fun File.allowExecuteOnlyForOwner() = apply {
if (!setExecutable(false, false)) throw IOException("$this can't forbid execute access")
if (!setExecutable(true, true)) throw IOException("$this can't permit execute access for owner only")
}
fun File.allowAllOnlyForOwner() = allowReadOnlyForOwner().allowWriteOnlyForOwner().allowExecuteOnlyForOwner()
fun File.forbidRead() = apply { if (!setReadable(false, false)) throw IOException("$this can't forbid read access") }
fun File.forbidWrite() = apply { if (!setWritable(false, false)) throw IOException("$this can't forbid write access") }
fun File.forbidExecute() = apply { if (!setExecutable(false, false)) throw IOException("$this can't forbid execute access") }
fun File.forbidAll() = forbidRead().forbidWrite().forbidExecute()
private val defaultLockAcquiringTimeout: Duration = Duration.ofMinutes(1);
fun File.lockAndReadBytes(lockAcquiringTimeout: Duration = defaultLockAcquiringTimeout): ByteArray = LockingFileOperations.lockAndReadBytes(this, lockAcquiringTimeout)
fun File.lockAndWriteBytes(bytes: ByteArray, lockAcquiringTimeout: Duration = defaultLockAcquiringTimeout) = LockingFileOperations.lockAndWriteBytes(this, lockAcquiringTimeout, bytes)
fun File.lockAndDelete(lockAcquiringTimeout: Duration = defaultLockAcquiringTimeout) = LockingFileOperations.lockAndDelete(this, lockAcquiringTimeout)
fun File.forLockedFileChannel(mode: String = "rw", lockAcquiringTimeout: Duration = defaultLockAcquiringTimeout, action: (fileChannel: FileChannel) -> R): R {
return LockingFileOperations.forLockedFileChannel(this, mode, lockAcquiringTimeout) { fileChannel ->
action(fileChannel)
}
}
inline fun File.forSelfAndEachParent(action: (file: File) -> Unit) {
var file: File? = this.absoluteFile
while (file != null) {
action(file)
file = file.parentFile
}
}
private fun File.zipToImpl(target: File, overwrite: Boolean, zipOnlyContent: Boolean) {
if (!this.exists()) throw NoSuchFileException(file = this, reason = "The source file doesn't exist.")
if (target.exists()) {
val stillExists = if (!overwrite) true else !target.deleteRecursively()
if (stillExists) {
throw FileAlreadyExistsException(file = this, other = target, reason = "The destination file already exists.")
}
}
target.createParentDirectories().toPath().newOutputStream().use {
ZipOutputStream(it).use { zipOutputStream ->
zipOutputStream.setLevel(BEST_COMPRESSION)
if (isDirectory) {
val entryNamePrefix: String
if (!zipOnlyContent) {
entryNamePrefix = name + "/"
zipOutputStream.putNextEntry(ZipEntry(entryNamePrefix))
zipOutputStream.closeEntry()
} else {
entryNamePrefix = ""
}
val paths = sortedMapOf()
val sourcePath = toPath().toAbsolutePath()
sourcePath.walk().forEach { path ->
val entryName = sourcePath.relativize(path)
.let { if ("/" != it.fileSystem.separator) it.toString().replace(it.fileSystem.separator, "/") else it.toString() }
.let { if (path.isDirectory) "$it/" else it }
.let { entryNamePrefix + it }
paths[entryName] = path
}
paths.forEach { entryName, path ->
zipOutputStream.putNextEntry(ZipEntry(entryName))
if (!entryName.endsWith("/")) {
path.newInputStream().use { it.copyTo(zipOutputStream) }
}
zipOutputStream.closeEntry()
}
} else {
zipOutputStream.putNextEntry(ZipEntry(name))
toPath().newInputStream().use { it.copyTo(zipOutputStream) }
zipOutputStream.closeEntry()
}
}
}
}
fun File.zipTo(target: File, overwrite: Boolean = false) = zipToImpl(target, overwrite, false)
fun File.zipContentTo(target: File, overwrite: Boolean = false) = zipToImpl(target, overwrite, true)
fun File.deleteOnExitRecursively() = apply { FilesToDeleteContainer.files.add(this.absoluteFile) }
@SuppressFBWarnings("SEC_SIDE_EFFECT_CONSTRUCTOR", "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
private object FilesToDeleteContainer {
val files = concurrentSetOf()
init {
Runtime.getRuntime().addShutdownHook(Thread {
while (files.isNotEmpty()) {
val iterator = files.iterator()
while (iterator.hasNext()) {
iterator.next().deleteRecursively()
iterator.remove()
}
}
})
}
}