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

com.simiacryptus.util.describe.ApiFunctionDescriber.kt Maven / Gradle / Ivy

There is a newer version: 1.0.33
Show newest version
package com.simiacryptus.util.describe

import com.fasterxml.jackson.module.kotlin.isKotlinClass
import com.google.common.reflect.TypeToken
import com.simiacryptus.util.describe.DescriptorUtil.componentType
import com.simiacryptus.util.describe.DescriptorUtil.isArray
import com.simiacryptus.util.describe.TypeDescriber.Companion.primitives
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.lang.reflect.Parameter
import java.lang.reflect.Type
import kotlin.reflect.full.memberProperties
import java.util.*
import kotlin.reflect.KClass
import kotlin.reflect.KVisibility
import kotlin.reflect.full.functions
import kotlin.reflect.jvm.javaType

class ApiFunctionDescriber : TypeDescriber {

    open val includeMethods: Boolean = true
    private val truncation = "..."

    override fun describe(self: Method, stackMax: Int): String {
        if (stackMax <= 0) return truncation
        val parameters = self.parameters.joinToString("\n") {
            "  ${describe(it, stackMax - 1).replace("\n", "\n  ")}"
        }
        if (parameters.isBlank()) return "${self.name}()"
        return "${self.name}(\n$parameters\n)"
    }

    override fun describe(rawType: Class, stackMax: Int): String {
        if (isAbbreviated(rawType)) return rawType.simpleName
        if (stackMax <= 0) return truncation
        return if (!rawType.isKotlinClass()) {
            describeJavaClass(rawType, stackMax)
        } else {
            describeKotlinClass(rawType.kotlin, stackMax)
        }
    }

    fun describe(self: Parameter, stackMax: Int): String {
        if (stackMax <= 0) return truncation
        return "${self.name}: ${toApiFunctionFormat(self.parameterizedType, stackMax - 1)}"
    }

    private fun toApiFunctionFormat(self: Type, stackMax: Int = 10): String {
        if (stackMax <= 0) return truncation
        val typeName = self.typeName.substringAfterLast('.').replace('$', '.').lowercase(Locale.getDefault())
        return when {
            typeName in primitives -> typeName
            self.isArray -> "${toApiFunctionFormat(self.componentType!!, stackMax - 1)}[]"
            else -> describe(TypeToken.of(self).rawType, stackMax - 1)
        }
    }

    override val methodBlacklist = setOf("equals", "hashCode", "copy", "toString", "valueOf")

    private fun describeKotlinClass(kClass: KClass, stackMax: Int): String {
        val properties = try {
            kClass.memberProperties.filter { it.visibility == KVisibility.PUBLIC }
                .joinToString("\n") {
                    "  val ${it.name}: ${
                        toApiFunctionFormat(it.returnType.javaType, stackMax - 1).replace(
                            "\n",
                            "\n  "
                        )
                    }"
                }
        } catch (e: Throwable) {
            ""
        }
        val methods = try {
            kClass.functions.filter {
                it.visibility == KVisibility.PUBLIC
                        && !methodBlacklist.contains(it.name)
                        && !it.isOperator && !it.isInfix && !it.isAbstract
            }.toList().sortedBy { it.toString() }.joinToString("\n") {
                val kParameters = it.parameters.filter { it.name != null }
                if (kParameters.isEmpty()) return@joinToString "  fun ${it.name}()"
                "  fun ${it.name}(\n${
                    kParameters.joinToString("\n") {
                        "    ${it.name}: ${
                            toApiFunctionFormat(
                                it.type.javaType,
                                stackMax - 1
                            ).replace("\n", "\n    ")
                        }"
                    }
                })"
            }
        } catch (e: Throwable) {
            ""
        }
        if (kClass.isData) {
            if (methods.isBlank()) return "data class ${kClass.simpleName} (\n  $properties\n)"
            return "data class ${kClass.simpleName} (\n$properties\n){\n$methods\n}"
        } else {
            if (methods.isBlank()) return "class ${kClass.simpleName} {\n  $properties\n}"
            return "class ${kClass.simpleName} {\n$properties\n$methods\n}"
        }
    }

    private fun describeJavaClass(rawType: Class, stackMax: Int): String {
        val typeName = rawType.typeName.substringAfterLast('.').replace('$', '.').lowercase(Locale.getDefault())
        if (typeName in primitives) return typeName
        if (!includeMethods) return rawType.simpleName
        val methods =
            rawType.methods.toList()
                .filter {
                    Modifier.isPublic(it.modifiers) && !it.isSynthetic && !it.name.contains("$") && !methodBlacklist.contains(
                        it.name
                    )
                }
                .sortedBy { it.toString() }
                .joinToString("\n") { "  ${describe(it, stackMax - 1).replace("\n", "\n  ")}" }
        if (methods.isBlank()) return rawType.simpleName
        return "class ${rawType.simpleName} {\n$methods\n}"
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy