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

name.remal.java.io.File.kt Maven / Gradle / Ivy

There is a newer version: 1.26.147
Show newest version
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()
                }
            }
        })
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy