Загрузка данных


package ru.notesapp.util

import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.primaryConstructor

// ---------- toJson ----------

fun Any.toJson(): String {
    val klass = this::class
    if (klass.annotations.none { it is MiniSerializable }) {
        throw IllegalArgumentException(
            "Class ${klass.simpleName} is not annotated with @MiniSerializable"
        )
    }

    val sb = StringBuilder("{")
    var first = true
    for (prop in klass.declaredMemberProperties) {
        val value = prop.getter.call(this)
        val ann = prop.annotations.filterIsInstance<MiniSerializable>().firstOrNull()
        val key = ann?.name?.takeIf { it.isNotEmpty() } ?: prop.name

        if (!first) sb.append(",")
        first = false
        sb.append("\"").append(key).append("\":").append(valueToJson(value))
    }
    sb.append("}")
    return sb.toString()
}

private fun valueToJson(value: Any?): String = when (value) {
    null -> "null"
    is String -> "\"${escapeJson(value)}\""
    is Number, is Boolean -> value.toString()
    is Enum<*> -> "\"${value.name}\""
    else -> value.toJson()
}

private fun escapeJson(s: String): String {
    val sb = StringBuilder()
    for (c in s) {
        when (c) {
            '\\' -> sb.append("\\\\")
            '"' -> sb.append("\\\"")
            '\n' -> sb.append("\\n")
            '\r' -> sb.append("\\r")
            '\t' -> sb.append("\\t")
            else -> sb.append(c)
        }
    }
    return sb.toString()
}

// ---------- fromJson ----------

fun <T : Any> fromJson(json: String, klass: KClass<T>): T {
    if (klass.annotations.none { it is MiniSerializable }) {
        throw IllegalArgumentException(
            "Class ${klass.simpleName} is not annotated with @MiniSerializable"
        )
    }

    val ctor = klass.primaryConstructor
        ?: throw IllegalArgumentException("No primary constructor for ${klass.simpleName}")

    val map = parseSimpleJson(json)
    val propsByName = klass.declaredMemberProperties.associateBy { it.name }

    val args = mutableMapOf<KParameter, Any?>()
    for (param in ctor.parameters) {
        val prop = propsByName[param.name]
        val ann = prop?.annotations?.filterIsInstance<MiniSerializable>()?.firstOrNull()
            ?: param.annotations.filterIsInstance<MiniSerializable>().firstOrNull()
        val key = ann?.name?.takeIf { it.isNotEmpty() } ?: param.name

        if (map.containsKey(key)) {
            args[param] = convertValue(map[key], param)
        } else if (!param.isOptional) {
            throw IllegalArgumentException("Missing required parameter '$key'")
        }
    }
    return ctor.callBy(args)
}

@Suppress("UNCHECKED_CAST")
private fun convertValue(value: Any?, param: KParameter): Any? {
    return when (param.type.classifier) {
        String::class -> value?.toString()
        Long::class -> when (value) {
            null -> 0L
            is Long -> value
            is Number -> value.toLong()
            else -> value.toString().toLong()
        }
        Int::class -> when (value) {
            null -> 0
            is Int -> value
            is Number -> value.toInt()
            else -> value.toString().toInt()
        }
        Double::class -> when (value) {
            null -> 0.0
            is Double -> value
            is Number -> value.toDouble()
            else -> value.toString().toDouble()
        }
        Boolean::class -> when (value) {
            null -> false
            is Boolean -> value
            else -> value.toString().toBoolean()
        }
        else -> value
    }
}

// ---------- простейший парсер JSON ----------

private fun parseSimpleJson(json: String): Map<String, Any?> {
    val s = json.trim()
    if (!s.startsWith("{") || !s.endsWith("}")) {
        throw IllegalArgumentException("JSON must be an object")
    }
    val inner = s.substring(1, s.length - 1)
    val result = mutableMapOf<String, Any?>()
    var i = 0

    while (i < inner.length) {
        while (i < inner.length && (inner[i].isWhitespace() || inner[i] == ',')) i++
        if (i >= inner.length) break

        if (inner[i] != '"') throw IllegalArgumentException("Expected '\"' at $i")
        i++
        val keySb = StringBuilder()
        while (i < inner.length && inner[i] != '"') {
            if (inner[i] == '\\' && i + 1 < inner.length) {
                i++
                keySb.append(unescape(inner[i]))
            } else {
                keySb.append(inner[i])
            }
            i++
        }
        if (i >= inner.length) throw IllegalArgumentException("Unterminated key")
        i++
        val key = keySb.toString()

        while (i < inner.length && inner[i].isWhitespace()) i++
        if (i >= inner.length || inner[i] != ':') throw IllegalArgumentException("Expected ':' at $i")
        i++
        while (i < inner.length && inner[i].isWhitespace()) i++
        if (i >= inner.length) throw IllegalArgumentException("Missing value")

        val value: Any? = when (inner[i]) {
            '"' -> {
                i++
                val sb = StringBuilder()
                while (i < inner.length && inner[i] != '"') {
                    if (inner[i] == '\\' && i + 1 < inner.length) {
                        i++
                        sb.append(unescape(inner[i]))
                    } else {
                        sb.append(inner[i])
                    }
                    i++
                }
                if (i >= inner.length) throw IllegalArgumentException("Unterminated string")
                i++
                sb.toString()
            }
            'n' -> {
                if (!inner.startsWith("null", i)) throw IllegalArgumentException("Invalid value at $i")
                i += 4
                null
            }
            't' -> {
                if (!inner.startsWith("true", i)) throw IllegalArgumentException("Invalid value at $i")
                i += 4
                true
            }
            'f' -> {
                if (!inner.startsWith("false", i)) throw IllegalArgumentException("Invalid value at $i")
                i += 5
                false
            }
            else -> {
                val start = i
                while (i < inner.length && inner[i] != ',' && inner[i] != '}') i++
                val numStr = inner.substring(start, i).trim()
                if (numStr.contains('.') || numStr.contains('e') || numStr.contains('E')) {
                    numStr.toDouble()
                } else {
                    numStr.toLong()
                }
            }
        }
        result[key] = value
    }
    return result
}

private fun unescape(c: Char): Char = when (c) {
    'n' -> '\n'
    't' -> '\t'
    'r' -> '\r'
    '"' -> '"'
    '\\' -> '\\'
    '/' -> '/'
    else -> c
}