mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
archive state to custom binary format
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.util.containers.isNullOrEmpty
|
||||
import org.jdom.*
|
||||
import java.io.DataOutputStream
|
||||
|
||||
private enum class TypeMarker {
|
||||
ELEMENT, CDATA, TEXT, ELEMENT_END
|
||||
}
|
||||
|
||||
fun output(doc: Document, out: DataOutputStream) {
|
||||
val content = doc.content
|
||||
val size = content.size
|
||||
for (i in 0..size - 1) {
|
||||
val obj = content[i]
|
||||
when (obj) {
|
||||
is Element -> printElement(out, doc.rootElement)
|
||||
}
|
||||
}
|
||||
|
||||
out.flush()
|
||||
}
|
||||
|
||||
fun writeElement(element: Element, out: DataOutputStream) {
|
||||
printElement(out, element)
|
||||
out.flush()
|
||||
}
|
||||
|
||||
private fun printElement(out: DataOutputStream, element: Element) {
|
||||
out.writeByte(TypeMarker.ELEMENT.ordinal)
|
||||
out.writeUTF(element.name)
|
||||
|
||||
val content = element.content
|
||||
printAttributes(out, element.attributes)
|
||||
|
||||
for (item in content) {
|
||||
if (item is Element) {
|
||||
printElement(out, item)
|
||||
}
|
||||
else if (item is Text) {
|
||||
if (!isAllWhitespace(item)) {
|
||||
out.writeByte(TypeMarker.TEXT.ordinal)
|
||||
out.writeUTF(item.text)
|
||||
}
|
||||
}
|
||||
else if (item is CDATA) {
|
||||
out.writeByte(TypeMarker.CDATA.ordinal)
|
||||
out.writeUTF(item.text)
|
||||
}
|
||||
}
|
||||
out.writeByte(TypeMarker.ELEMENT_END.ordinal)
|
||||
}
|
||||
|
||||
private fun printAttributes(out: DataOutputStream, attributes: List<Attribute>?) {
|
||||
if (attributes.isNullOrEmpty()) {
|
||||
val size = attributes?.size ?: 0
|
||||
if (size > 255) {
|
||||
throw UnsupportedOperationException("attributes size > 255")
|
||||
}
|
||||
out.writeByte(size)
|
||||
return
|
||||
}
|
||||
|
||||
for (attribute in attributes!!) {
|
||||
out.writeUTF(attribute.name)
|
||||
out.writeUTF(attribute.value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAllWhitespace(obj: Content): Boolean {
|
||||
val str = (obj as? Text)?.text ?: return false
|
||||
for (i in 0..str.length - 1) {
|
||||
if (!Verifier.isXMLWhitespace(str[i])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.JDOMUtil
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.util.ArrayUtil
|
||||
@@ -27,30 +29,63 @@ import org.iq80.snappy.SnappyOutputStream
|
||||
import org.jdom.Element
|
||||
import org.jdom.output.Format
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.io.OutputStreamWriter
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray
|
||||
|
||||
private val XML_FORMAT = Format.getRawFormat().setTextMode(Format.TextMode.TRIM).setOmitEncoding(true).setOmitDeclaration(true)
|
||||
|
||||
private fun archiveState(state: Element): ByteArray {
|
||||
// must be mot modified during app life
|
||||
private val isUseNewSaving by lazy { ApplicationManager.getApplication().isUnitTestMode || Registry.`is`("configuration.saving.v3", false) }
|
||||
|
||||
private fun archiveState(state: Element): BufferExposingByteArrayOutputStream {
|
||||
if (isUseNewSaving) {
|
||||
return archiveStateBinary(state)
|
||||
}
|
||||
else {
|
||||
return archiveStateXml(state)
|
||||
}
|
||||
}
|
||||
|
||||
fun archiveStateBinary(state: Element): BufferExposingByteArrayOutputStream {
|
||||
val byteOut = BufferExposingByteArrayOutputStream()
|
||||
DataOutputStream(SnappyOutputStream(byteOut)).use {
|
||||
writeElement(state, it)
|
||||
}
|
||||
return byteOut
|
||||
}
|
||||
|
||||
fun archiveStateXml(state: Element): BufferExposingByteArrayOutputStream {
|
||||
val byteOut = BufferExposingByteArrayOutputStream()
|
||||
OutputStreamWriter(SnappyOutputStream(byteOut), CharsetToolkit.UTF8_CHARSET).use {
|
||||
val xmlOutputter = JDOMUtil.MyXMLOutputter()
|
||||
xmlOutputter.format = XML_FORMAT
|
||||
xmlOutputter.output(state, it)
|
||||
}
|
||||
return ArrayUtil.realloc(byteOut.internalBuffer, byteOut.size())
|
||||
return byteOut
|
||||
}
|
||||
|
||||
private fun unarchiveState(state: ByteArray) = JDOMUtil.load(SnappyInputStream(ByteArrayInputStream(state)))
|
||||
|
||||
fun getNewByteIfDiffers(key: String, newState: Any, oldState: ByteArray): ByteArray? {
|
||||
val newBytes = if (newState is Element) archiveState(newState) else newState as ByteArray
|
||||
if (Arrays.equals(newBytes, oldState)) {
|
||||
return null
|
||||
val newBytes: ByteArray
|
||||
if (newState is Element) {
|
||||
val byteOut = archiveState(newState)
|
||||
if (arrayEquals(byteOut.internalBuffer, oldState, byteOut.size())) {
|
||||
return null
|
||||
}
|
||||
|
||||
newBytes = ArrayUtil.realloc(byteOut.internalBuffer, byteOut.size())
|
||||
}
|
||||
else if (SystemProperties.getBooleanProperty("idea.log.changed.components", false)) {
|
||||
else {
|
||||
newBytes = newState as ByteArray
|
||||
if (Arrays.equals(newBytes, oldState)) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
if (SystemProperties.getBooleanProperty("idea.log.changed.components", false)) {
|
||||
fun stateToString(state: Any) = JDOMUtil.writeParent(state as? Element ?: unarchiveState(state as ByteArray), "\n")
|
||||
|
||||
val before = stateToString(oldState)
|
||||
@@ -75,8 +110,7 @@ fun stateToElement(key: String, state: Any?, newLiveStates: Map<String, Element>
|
||||
}
|
||||
|
||||
class StateMap private constructor(private val names: Array<String>, private val states: AtomicReferenceArray<Any?>) {
|
||||
override fun toString(): String =
|
||||
if (this == EMPTY) "EMPTY" else states.toString();
|
||||
override fun toString() = if (this == EMPTY) "EMPTY" else states.toString()
|
||||
|
||||
companion object {
|
||||
val EMPTY = StateMap(emptyArray(), AtomicReferenceArray(0))
|
||||
@@ -225,5 +259,24 @@ internal fun updateState(states: MutableMap<String, Any>, key: String, newState:
|
||||
}
|
||||
|
||||
states.put(key, newBytes ?: newState)
|
||||
return true
|
||||
}
|
||||
|
||||
fun arrayEquals(a: ByteArray, a2: ByteArray, aSize: Int = a.size): Boolean {
|
||||
if (a == a2) {
|
||||
return true
|
||||
}
|
||||
|
||||
val length = aSize
|
||||
if (a2.size != length) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (i in 0..length - 1) {
|
||||
if (a[i] != a2[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -26,6 +26,8 @@ import org.xml.sax.EntityResolver
|
||||
import org.xml.sax.InputSource
|
||||
import java.io.CharArrayReader
|
||||
import java.io.Reader
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
private val cachedSaxBuilder = ThreadLocal<SoftReference<SAXBuilder>>()
|
||||
|
||||
@@ -44,6 +46,8 @@ private fun getSaxBuilder(): SAXBuilder {
|
||||
|
||||
fun loadElement(chars: CharSequence) = loadDocument(CharSequenceReader(chars)).detachRootElement()
|
||||
|
||||
fun loadElement(path: Path) = loadDocument(Files.newInputStream(path).bufferedReader()).detachRootElement()
|
||||
|
||||
private fun loadDocument(reader: Reader): Document {
|
||||
if (Registry.`is`("jdom.ignoring.whitespace", false) || (ApplicationManager.getApplication()?.isUnitTestMode ?: false)) {
|
||||
try {
|
||||
|
||||
@@ -686,6 +686,7 @@ ruby.remote.debugger.supports.catchpoint.removal=true
|
||||
use.read.action.to.init.service=false
|
||||
use.loaded.state.as.existing=true
|
||||
saving.state.in.new.format.is.allowed=false
|
||||
configuration.saving.v3=false
|
||||
jdom.ignoring.whitespace=false
|
||||
|
||||
ide.mac.new.color.picker=false
|
||||
|
||||
Reference in New Issue
Block a user