mirror of
https://github.com/revanced/revanced-patcher.git
synced 2025-05-19 12:07:05 +02:00
feat: RwLock for opening files in DomFileEditor
This commit is contained in:
parent
4839f87519
commit
db4348c4fa
@ -30,10 +30,12 @@ class ResourceData(private val resourceCacheDirectory: File) : Data, Iterable<Fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DomFileEditor is a wrapper for a file that can be edited as a dom document.
|
* Wrapper for a file that can be edited as a dom document.
|
||||||
|
* Note: This constructor does not check for locks to the file when writing. Use the secondary constructor.
|
||||||
*
|
*
|
||||||
* @param inputStream the input stream to read the xml file from.
|
* @param inputStream the input stream to read the xml file from.
|
||||||
* @param outputStream the output stream to write the xml file to. If null, the file will not be written.
|
* @param outputStream the output stream to write the xml file to. If null, the file will be read only.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
class DomFileEditor internal constructor(
|
class DomFileEditor internal constructor(
|
||||||
private val inputStream: InputStream,
|
private val inputStream: InputStream,
|
||||||
@ -41,6 +43,7 @@ class DomFileEditor internal constructor(
|
|||||||
) : Closeable {
|
) : Closeable {
|
||||||
// path to the xml file to unlock the resource when closing the editor
|
// path to the xml file to unlock the resource when closing the editor
|
||||||
private var filePath: String? = null
|
private var filePath: String? = null
|
||||||
|
private var closed: Boolean = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The document of the xml file
|
* The document of the xml file
|
||||||
@ -48,37 +51,53 @@ class DomFileEditor internal constructor(
|
|||||||
val file: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream)
|
val file: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream)
|
||||||
.also(Document::normalize)
|
.also(Document::normalize)
|
||||||
|
|
||||||
|
|
||||||
// lazily open an output stream
|
// lazily open an output stream
|
||||||
// this is required because when constructing a DomFileEditor the output stream is created along with the input stream, which is not allowed
|
// this is required because when constructing a DomFileEditor the output stream is created along with the input stream, which is not allowed
|
||||||
// the workaround is to lazily create the output stream. This way it would be used after the input stream is closed, which happens in the constructor
|
// the workaround is to lazily create the output stream. This way it would be used after the input stream is closed, which happens in the constructor
|
||||||
constructor(file: File) : this(file.inputStream(), lazy { file.outputStream() }) {
|
constructor(file: File) : this(file.inputStream(), lazy { file.outputStream() }) {
|
||||||
|
// increase the lock
|
||||||
|
locks.merge(file.path, 1, Integer::sum)
|
||||||
filePath = file.path
|
filePath = file.path
|
||||||
|
|
||||||
// prevent sharing mutability of the same file between multiple instances of DomFileEditor
|
|
||||||
if (locks.contains(filePath))
|
|
||||||
throw IllegalStateException("Can not create a DomFileEditor for that file because it is already locked by another instance of DomFileEditor.")
|
|
||||||
locks.add(filePath!!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the editor. Write backs and decreases the lock count.
|
||||||
|
* Note: Will not write back to the file if the file is still locked.
|
||||||
|
*/
|
||||||
override fun close() {
|
override fun close() {
|
||||||
|
if (closed) return
|
||||||
|
|
||||||
inputStream.close()
|
inputStream.close()
|
||||||
|
|
||||||
// if the output stream is not null, do not close it
|
// if the output stream is not null, do not close it
|
||||||
outputStream?.let {
|
outputStream?.let {
|
||||||
val result = StreamResult(it.value)
|
// prevent writing to same file, if it is being locked
|
||||||
TransformerFactory.newInstance().newTransformer().transform(DOMSource(file), result)
|
// isLocked will be false if the editor was created through a stream
|
||||||
|
val isLocked = filePath?.let { path ->
|
||||||
|
val isLocked = locks[path]!! > 1
|
||||||
|
// decrease the lock count if the editor was opened for a file
|
||||||
|
locks.merge(path, -1, Integer::sum)
|
||||||
|
isLocked
|
||||||
|
} ?: false
|
||||||
|
|
||||||
it.value.close()
|
// if unlocked, write back to the file
|
||||||
|
if (!isLocked) {
|
||||||
|
it.value.use { stream ->
|
||||||
|
val result = StreamResult(stream)
|
||||||
|
TransformerFactory.newInstance().newTransformer().transform(DOMSource(file), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
it.value.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the lock, if it exists
|
closed = true
|
||||||
filePath?.let {
|
|
||||||
locks.remove(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
// list of locked file paths
|
// map of concurrent open files
|
||||||
val locks = mutableListOf<String>()
|
val locks = mutableMapOf<String, Int>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user