All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.atlan.pkg.serde.cell.CellXformer.kt Maven / Gradle / Ivy

There is a newer version: 3.1.2
Show newest version
/* SPDX-License-Identifier: Apache-2.0
   Copyright 2023 Atlan Pte. Ltd. */
package com.atlan.pkg.serde.cell

import com.atlan.cache.ReflectionCache
import com.atlan.model.assets.Asset
import com.atlan.model.core.AtlanTag
import com.atlan.model.enums.AtlanEnum
import com.atlan.model.structs.AtlanStruct
import com.atlan.pkg.PackageContext
import java.io.IOException
import java.util.SortedSet
import java.util.TreeSet

object CellXformer {
    const val LIST_DELIMITER = "\n"
    private const val NEWLINE_SENTINEL = "§LF±"

    fun encode(
        ctx: PackageContext<*>,
        value: Any?,
        guid: String? = null,
        dates: Boolean = false,
    ): String {
        return when (value) {
            is String -> encodeString(value)
            is Collection<*> -> {
                val list = mutableListOf()
                for (element in value) {
                    val encoded = encode(ctx, element, guid, dates)
                    list.add(encoded)
                }
                return getDelimitedList(list)
            }
            is Map<*, *> -> {
                val list = mutableListOf()
                for ((key, embeddedValue) in value) {
                    list.add(key.toString() + "=" + encode(ctx, embeddedValue, guid, dates))
                }
                return getDelimitedList(list)
            }
            is Asset -> AssetRefXformer.encode(ctx, value)
            is AtlanEnum -> EnumXformer.encode(value)
            is AtlanStruct -> StructXformer.encode(ctx.client, value)
            is AtlanTag -> AtlanTagXformer.encode(ctx.client, guid!!, value)
            is Long -> {
                if (dates) {
                    TimestampXformer.encode(value)
                } else {
                    value.toString()
                }
            }
            is Any -> value.toString()
            else -> ""
        }
    }

    @Suppress("UNCHECKED_CAST")
    fun decode(
        ctx: PackageContext<*>,
        assetClass: Class<*>?,
        value: String?,
        type: Class<*>,
        innerType: Class<*>?,
        fieldName: String,
    ): Any? {
        return if (value.isNullOrEmpty()) {
            null
        } else if (String::class.java.isAssignableFrom(type)) {
            when (fieldName) {
                in UserXformer.FIELDS -> UserXformer.decode(ctx.client, value, fieldName)
                in GroupXformer.FIELDS -> GroupXformer.decode(ctx.client, value, fieldName)
                in RoleXformer.FIELDS -> RoleXformer.decode(ctx.client, value, fieldName)
                in DataTypeXformer.FIELDS -> DataTypeXformer.decode(value, fieldName)
                else -> decodeString(value)
            }
        } else if (Boolean::class.java.isAssignableFrom(type) || java.lang.Boolean::class.java.isAssignableFrom(type)) {
            decodeBoolean(value)
        } else if (Integer::class.java.isAssignableFrom(type) || java.lang.Integer::class.java.isAssignableFrom(type)) {
            decodeInt(value)
        } else if (Long::class.java.isAssignableFrom(type) || java.lang.Long::class.java.isAssignableFrom(type)) {
            if (ReflectionCache.isDate(assetClass, fieldName)) {
                TimestampXformer.decode(value, fieldName)
            } else {
                decodeLong(value)
            }
        } else if (Double::class.java.isAssignableFrom(type) || java.lang.Double::class.java.isAssignableFrom(type)) {
            decodeDouble(value)
        } else if (Collection::class.java.isAssignableFrom(type)) {
            // Start by checking whether the list is simple or complex
            val values = parseDelimitedList(value)
            val list = mutableListOf()
            if (innerType != null) {
                for (element in values) {
                    val decoded = decode(ctx, assetClass, element, innerType, null, fieldName)
                    if (decoded != null) {
                        list.add(decoded)
                    }
                }
            }
            when (type) {
                Collection::class.java, List::class.java -> list
                Set::class.java, SortedSet::class.java -> TreeSet(list)
                else -> throw IOException("Unable to deserialize cell to Java class (in $fieldName): $type")
            }
        } else if (Map::class.java.isAssignableFrom(type)) {
            TODO("Not yet implemented for import")
        } else if (Asset::class.java.isAssignableFrom(type)) {
            AssetRefXformer.decode(ctx, value, fieldName)
        } else if (AtlanEnum::class.java.isAssignableFrom(type)) {
            EnumXformer.decode(value, type as Class)
        } else if (AtlanStruct::class.java.isAssignableFrom(type)) {
            StructXformer.decode(ctx.client, value, type as Class)
        } else if (AtlanTag::class.java.isAssignableFrom(type)) {
            AtlanTagXformer.decode(ctx.client, value)
        } else if (type.isInterface) {
            // Relationships between assets are defined via interfaces, so this would mean
            // there should be asset references
            AssetRefXformer.decode(ctx, value, fieldName)
        } else {
            throw IOException("Unhandled data type (in $fieldName): $type")
        }
    }

    private fun getDelimitedList(values: List?): String {
        return if (values.isNullOrEmpty()) {
            ""
        } else {
            values.joinToString(LIST_DELIMITER)
        }
    }

    private fun parseDelimitedList(values: String?): List {
        return if (values.isNullOrEmpty()) {
            listOf()
        } else {
            values.split(LIST_DELIMITER).map { it.trim() }
        }
    }

    fun encodeString(value: String): String {
        return if (value.contains(LIST_DELIMITER)) {
            value.replace(LIST_DELIMITER, NEWLINE_SENTINEL)
        } else {
            value
        }
    }

    fun decodeString(value: String): String {
        return if (value.contains(NEWLINE_SENTINEL)) {
            value.replace(NEWLINE_SENTINEL, LIST_DELIMITER)
        } else {
            value
        }
    }

    fun decodeBoolean(value: String): Boolean {
        return value.toBoolean()
    }

    fun decodeInt(value: String): Int {
        return value.toInt()
    }

    fun decodeLong(value: String): Long {
        return value.toLong()
    }

    fun decodeDouble(value: String): Double {
        return value.toDouble()
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy