Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
soywiz committed Jul 25, 2024
1 parent 2440a60 commit edc0757
Show file tree
Hide file tree
Showing 24 changed files with 2,497 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# korlibs-library-template
# korlibs-serialization
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ aliases:
- jvmAndAndroid: [jvm, android]

dependencies:
- com.soywiz:korlibs-string:6.0.0
- com.soywiz:korlibs-math-core:6.0.0
- com.soywiz:korlibs-datastructure-core:6.0.0
- com.soywiz:korlibs-platform:6.0.0

test-dependencies:

44 changes: 44 additions & 0 deletions korlibs-serialization/src/korlibs/io/lang/Properties.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package korlibs.io.lang

import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set

open class Properties(map: Map<String, String>? = null) {
//private val map = FastStringMap<String>()
// This is required to work with K/N memory model
private val map = LinkedHashMap<String, String>().also {
if (map != null) it.putAll(map)
}

open operator fun contains(key: String): Boolean = get(key) != null
open operator fun get(key: String): String? = map[key]
open operator fun set(key: String, value: String) { map[key] = value }
open fun setAll(values: Map<String, String>) {
for ((key, value) in values) set(key, value)
}
open fun remove(key: String) { map.remove(key) }
open fun getAll(): Map<String, String> = map.toMap()

override fun toString(): String = buildString {
for ((key, value) in map) {
appendLine("$key=${value.replace("\n", "\\n")}")
}
}

companion object {
fun parseString(data: String): Properties {
val props = LinkedHashMap<String, String>()
for (line in data.lines()) {
val (rline) = line.trim().split('#')
if (rline.isEmpty()) continue
val key = rline.substringBefore('=', "").trim()
val value = rline.substringAfter('=', "").trim()
if (key.isNotEmpty() && value.isNotEmpty()) {
props[key] = value
}
}
return Properties(props)
}
}
}
3 changes: 3 additions & 0 deletions korlibs-serialization/src/korlibs/io/lang/SystemProperties.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package korlibs.io.lang

expect object SystemProperties : Properties
105 changes: 105 additions & 0 deletions korlibs-serialization/src/korlibs/io/serialization/csv/CSV.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
@file:Suppress("PackageDirectoryMismatch")

package korlibs.io.serialization.csv

import korlibs.util.*

class CSV(val lines: List<List<String>>, val names: List<String>? = null) : Collection<CSV.Record> {
val namesToIndex: Map<String, Int> = names?.withIndex()?.associate { it.value to it.index } ?: emptyMap()
val linesWithNames = if (names != null) listOf(names, *lines.toTypedArray()) else lines
val records: List<Record> = lines.map { Record(it) }

override val size: Int get() = records.size
operator fun get(index: Int) = records[index]

override fun iterator(): Iterator<Record> = records.iterator()
override fun contains(element: Record): Boolean = records.contains(element)
override fun containsAll(elements: Collection<Record>): Boolean = records.containsAll(elements)
override fun isEmpty(): Boolean = records.isEmpty()

inner class Record(val cells: List<String>) {
operator fun get(index: Int): String = getOrNull(index) ?: error("Can't find element at $index")
operator fun get(name: String): String = getOrNull(name) ?: error("Can't find element '$name'")

fun getOrNull(index: Int): String? = cells.getOrNull(index)
fun getOrNull(name: String): String? = namesToIndex[name]?.let { cells[it] }

fun toMap(): Map<String, String> = if (names != null) cells.zip(names).associate { it.first to it.second } else cells.mapIndexed { index, s -> index to s }.associate { "${it.first}" to it.second }
override fun toString(): String = if (names != null) "${toMap()}" else "$cells"
}

fun toString(separator: Char): String = linesWithNames.joinToString("\n") { serializeLine(it, separator) }
override fun toString(): String = toString(DEFAULT_SEPARATOR)

companion object {
const val DEFAULT_SEPARATOR = ','

internal fun serializeElement(value: String, separator: Char): String {
if (!value.contains('"') && !value.contains('\n') && !value.contains(separator)) return value
val out = StringBuilder(value.length)
for (n in 0 until value.length) {
out.append(value[n])
}
return out.toString()
}

fun serializeLine(values: List<String>, separator: Char = DEFAULT_SEPARATOR): String {
return values.joinToString("$separator") { serializeElement(it, separator) }
}

fun parseLine(line: String, separator: Char = DEFAULT_SEPARATOR): List<String> = parseLine(SimpleStrReader(line), separator)

fun parseLine(line: SimpleStrReader, separator: Char = DEFAULT_SEPARATOR): List<String> {
val out = arrayListOf<String>()
val str = StringBuilder()
while (line.hasMore) {
val c = line.readChar()
when (c) {
// Quoted string
'"' -> {
loop@while (line.hasMore) {
val c2 = line.readChar()
when (c2) {
'"' -> {
if (line.peekChar() == '"') {
line.readChar()
str.append('"')
} else {
break@loop
}
}
else -> str.append(c2)
}
}
}
// Line break
'\n' -> {
break
}
// Empty string
separator -> {
out.add(str.toString())
str.clear()
}
// Normal string
else -> {
str.append(c)
}
}
}
out.add(str.toString())
str.clear()
return out
}

fun parse(s: SimpleStrReader, separator: Char = DEFAULT_SEPARATOR, headerNames: Boolean = true): CSV {
val lines = arrayListOf<List<String>>()
while (s.hasMore) {
lines.add(parseLine(s, separator))
}
return if (headerNames) CSV(lines.drop(1), lines[0]) else CSV(lines, null)
}

fun parse(str: String, separator: Char = DEFAULT_SEPARATOR, headerNames: Boolean = true): CSV = parse(SimpleStrReader(str), separator, headerNames)
}
}
Loading

0 comments on commit edc0757

Please sign in to comment.