Skip to content

Commit

Permalink
performance optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
kondorj committed Dec 26, 2022
1 parent f03a479 commit ff90257
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@
*/
package zakadabar.lib.xlsx.internal

internal typealias ContentMap = HashMap<String, ()->Any>
internal expect fun ContentMap.generateZip(zipContent: ByteArray.() -> Unit)

internal typealias ContentMap = HashMap<String, ((String)->Unit) -> Unit>
internal expect fun ContentMap.generateZip(zipContent: Any)
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ internal fun XlsxDocument.toXlsxFile() : File {
return f
}

private fun Part.content() : Any = when(this) {
is Node -> toXml()
else -> throw IllegalStateException("Content not supported: ${this::class}")
private fun Part.content(append: (String)->Unit) {
when (this) {
is Node -> toXml(append)
else -> throw IllegalStateException("Content not supported: ${this::class}")
}
}

internal fun File.toContentMap() : ContentMap {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,47 @@
*/
package zakadabar.lib.xlsx.internal.dom

internal fun Node.toXml() = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n$xml"
internal fun Node.toXml(append: (String)->Unit) {
append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n")
xml(append)
}

private val Node.xml : String
get() {
return if (attributes.isEmpty()) {
if (childNodes.isNotEmpty()) "<$name>$xmlChildNodes</$name>"
else if (text != null) "<$name>${text.xml}</$name>"
else "<$name/>"
} else {
if (childNodes.isNotEmpty()) "<$name$xmlAttributes>$xmlChildNodes</$name>"
else if (text != null) "<$name$xmlAttributes>${text.xml}</$name>"
else "<$name$xmlAttributes/>"
}
private fun Node.xml(append: (String)->Unit) {
append("<")
append(name)
xmlAttributes(append)
if (text != null) {
append(">")
append(text.xml)
append("</")
append(name)
append(">")
} else if (childNodes.isNotEmpty()) {
append(">")
xmlChildNodes(append)
append("</")
append(name)
append(">")
} else {
append("/>")
}
}

private val Node.xmlAttributes : CharSequence
get() {
val sb = StringBuilder()
for((key, value) in attributes) {
sb.append(" $key=\"${value.xml}\"")
}
return sb
private fun Node.xmlAttributes(append: (String)->Unit) {
for((key, value) in attributes) {
append(" ")
append(key)
append("=\"")
append(value.xml)
append("\"")
}
}

private val Node.xmlChildNodes : CharSequence
get() {
val sb = StringBuilder()
for (n in childNodes) {
sb.append(n.xml)
}
return sb
private fun Node.xmlChildNodes(append: (String)->Unit) {
for (n in childNodes) {
n.xml(append)
}
}

private val XML_CHARS = "[&<>'\"]".toRegex()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ internal fun Int.toColumnLetter() : String {
return col.toString()
}

internal fun XlsxDocument.buildFileContent(zipContent: ByteArray.()->Unit) {
internal fun XlsxDocument.buildFileContent(zipContent: Any) {
val f = toXlsxFile()
val cm = f.toContentMap()
cm.generateZip(zipContent)
val c = f.toContentMap()
c.generateZip(zipContent)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ class XlsxDocumentTest {

val doc = XlsxDocument(cfg)


val sheet = doc.newSheet("T2 Database & Task")

sheet.columns["A"].width = 18.5
Expand All @@ -35,6 +34,14 @@ class XlsxDocumentTest {
sheet.fillRow("A3", listOf("Sarah Connor", LocalDate(1964, 8, 13), true))


for(i in 8..100_000) {
sheet[1,i].value = "Alma-$i"
sheet[2,i].value = i % 2 == 0
sheet[3,i].value = Clock.System.now()
}



val summary = doc.newSheet("Summary")

summary.columns["A"].width = 18.2
Expand Down
6 changes: 4 additions & 2 deletions lib/xlsx/src/commonTest/kotlin/zakadabar/lib/xlsx/XmlTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ class XmlTest {
}
}

val xml = doc.toXml()
val sb = StringBuilder()
doc.toXml(sb::append)
val xml = sb.toString()

val ref = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<foo id=\"1\"><bars><bar name=\"Name1\"/><bar/></bars></foo>"
val ref = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<foo id=\"1\"><bars><bar name=\"Name1\"/><bar/></bars></foo>"
assertEquals(ref, xml)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import zakadabar.core.browser.util.downloadBlob
import zakadabar.core.data.BaseBo
import zakadabar.lib.xlsx.internal.buildFileContent
import zakadabar.lib.xlsx.conf.XlsxConfiguration
import zakadabar.lib.xlsx.internal.BlobConsumer
import zakadabar.lib.xlsx.model.XlsxDocument

private const val XLSX_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
Expand Down Expand Up @@ -73,9 +74,9 @@ val <T: BaseBo> ZkTable<T>.exportXlsxFileName : String
*/
fun downloadXlsX(fileName: String, doc: XlsxDocument) {
console.log("${Clock.System.now()} $fileName download triggered")
doc.buildFileContent {
val blob = Blob(arrayOf(this), BlobPropertyBag(XLSX_CONTENT_TYPE))
downloadBlob(fileName, blob)

doc.buildFileContent(BlobConsumer(XLSX_CONTENT_TYPE){
downloadBlob(fileName, this)
console.log("${Clock.System.now()} $fileName download completed")
}
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,34 @@ import kotlin.js.Json
import kotlin.js.Promise
import kotlin.js.json

internal actual fun ContentMap.generateZip(zipContent: ByteArray.() -> Unit) {
internal actual fun ContentMap.generateZip(zipContent: Any) {

val bc = (zipContent as? BlobConsumer)
?: throw IllegalArgumentException("Output type not supported : ${zipContent::class.simpleName}")

val zip = JSZip()

for ((path, content) in this) {
val data = content()
val blob = Blob(arrayOf(data))
val b = StringBuilder()
content(b::append)
val blob = Blob(arrayOf(b))
zip.file(path, blob, json("binary" to true))
}

zip.generateAsync(json(
zip.generateAsync<Blob>(json(
"type" to "blob",
"mimeType" to bc.contentType,
"compression" to "DEFLATE",
"compressionOptions" to json("level" to 9)
)).then(zipContent)
)).then(bc.blob)
}

internal class BlobConsumer(val contentType: String, val blob: Blob.()->Unit)

@JsModule("jszip")
@JsNonModule
private external class JSZip {
fun file(path: String, content: Blob, options: Json) : Unit = definedExternally
fun generateAsync(options: Json) : Promise<ByteArray> = definedExternally
fun <T> file(path: String, content: T, options: Json) : Unit = definedExternally
fun <T> generateAsync(options: Json) : Promise<T> = definedExternally

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,28 @@
*/
package zakadabar.lib.xlsx.internal

import java.io.ByteArrayOutputStream
import java.io.OutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream

internal actual fun ContentMap.generateZip(zipContent: ByteArray.() -> Unit) {
val os = ByteArrayOutputStream()
internal actual fun ContentMap.generateZip(zipContent: Any) {

val os = (zipContent as? OutputStream)
?: throw IllegalArgumentException("Output type not supported : ${zipContent::class.simpleName}")

ZipOutputStream(os).use { zip ->

zip.setLevel(9)

for((path, content) in this) {
val data = content()
val writer = zip.bufferedWriter()

for((path, content) in this) {
zip.putNextEntry(ZipEntry(path))

when(data) {
is String -> zip.write(data.encodeToByteArray())
is ByteArray -> zip.write(data)
else -> throw IllegalArgumentException("Content not supported: ${data::class}")
}

content(writer::write)
writer.flush()
zip.closeEntry()
}

}

zipContent(os.toByteArray())

}
12 changes: 4 additions & 8 deletions lib/xlsx/src/jvmMain/kotlin/zakadabar/lib/xlsx/save.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,19 @@
*/
import zakadabar.lib.xlsx.internal.buildFileContent
import zakadabar.lib.xlsx.model.XlsxDocument
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream

/**
* Save xlsx file.
*/
actual fun XlsxDocument.save(fileName: String) {
buildFileContent {
File(fileName).writeBytes(this)
FileOutputStream(fileName).use {
writeTo(it)
}
}

/**
* Generate XlsxFile and Write it to the OutputStream
*/
fun XlsxDocument.writeTo(os: OutputStream) {
buildFileContent {
os.write(this)
}
}
fun XlsxDocument.writeTo(os: OutputStream) = buildFileContent(os)

0 comments on commit ff90257

Please sign in to comment.