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

com.therouter.ksp.TheRouterSymbolProcessor.kt Maven / Gradle / Ivy

There is a newer version: 1.2.3-rc17
Show newest version
package com.therouter.ksp

import com.google.devtools.ksp.containingFile
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.FunctionKind
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.KSFile
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSType
import com.therouter.app.flowtask.lifecycle.FlowTask
import com.therouter.apt.ActionInterceptorItem
import com.therouter.apt.AutowiredItem
import com.therouter.apt.BuildConfig
import com.therouter.apt.FlowTaskItem
import com.therouter.apt.KEY_USE_EXTEND
import com.therouter.apt.PACKAGE
import com.therouter.apt.PREFIX_ROUTER_MAP
import com.therouter.apt.PREFIX_SERVICE_PROVIDER
import com.therouter.apt.PROPERTY_FILE
import com.therouter.apt.RouteItem
import com.therouter.apt.STR_TRUE
import com.therouter.apt.SUFFIX_AUTOWIRED
import com.therouter.apt.ServiceProviderItem
import com.therouter.apt.duplicateRemove
import com.therouter.apt.gson
import com.therouter.inject.ServiceProvider
import com.therouter.router.Autowired
import com.therouter.router.Route
import com.therouter.router.action.ActionInterceptor
import java.io.FileInputStream
import java.io.PrintStream
import java.lang.StringBuilder
import java.util.HashMap
import java.util.Locale
import java.util.Properties
import kotlin.collections.ArrayList

/**
 * Created by ZhangTao on 17/8/11.
 */
class TheRouterSymbolProcessor(
    private val codeGenerator: CodeGenerator,
    private val logger: KSPLogger
) : SymbolProcessor {
    private var sourcePath = ""

    private val routeDependencies = mutableSetOf()
    private val autoWiredDependencies = mutableSetOf()
    private val serviceProviderDependencies = mutableSetOf()

    override fun process(resolver: Resolver): List {
        routeDependencies.clear()
        autoWiredDependencies.clear()
        serviceProviderDependencies.clear()

        genRouterMapFile(parseRoute(resolver))
        genAutowiredFile(parseAutowired(resolver))
        val providerItemList = parseServiceProvider(resolver)
        val flowTaskList = parseFlowTask(resolver)
        val actionInterceptorList = parseActionInterceptor(resolver)
        genServiceProviderFile(providerItemList, flowTaskList, actionInterceptorList)
        return emptyList()
    }

    private fun parseRoute(resolver: Resolver): List {
        val list: ArrayList = ArrayList()
        resolver.getSymbolsWithAnnotation(Route::class.java.name).forEach {
            it.containingFile?.let { file ->
                routeDependencies.add(file)
            }
            it.accept(RouteVisitor(list), Unit)
        }
        return list
    }

    inner class RouteVisitor(private val list: ArrayList) : TheRouterVisitor(logger) {
        override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
            super.visitClassDeclaration(classDeclaration, data)
            sourcePath = getSourcePath(classDeclaration)
            classDeclaration.annotations.forEach { annotation ->
                val routeItem = RouteItem()
                routeItem.className = classDeclaration.qualifiedName?.asString()
                annotation.arguments.forEach { arg ->
                    when (arg.name?.asString()) {
                        "path" -> routeItem.path = "${arg.value}"
                        "action" -> routeItem.action = "${arg.value}"
                        "description" -> routeItem.description = "${arg.value}"
                        "params" -> {
                            if (arg.value is Array<*>) {
                                require((arg.value as Array<*>).size % 2 == 0) { "${routeItem.className} @Route(params) is not key value pairs" }
                                var k: String? = null
                                for (kv in arg.value as Array<*>) {
                                    if (k == null) {
                                        require(kv != null) { "${routeItem.className} @Route(params) key is null" }
                                        k = "$kv"
                                    } else {
                                        routeItem.params[k] = "$kv"
                                        k = null
                                    }
                                }
                            } else if (arg.value is java.util.ArrayList<*>) {
                                require((arg.value as java.util.ArrayList<*>).size % 2 == 0) { "${routeItem.className} @Route(params) is not key value pairs" }
                                var k: String? = null
                                for (kv in arg.value as java.util.ArrayList<*>) {
                                    if (k == null) {
                                        require(kv != null) { "${routeItem.className} @Route(params) key is null" }
                                        k = "$kv"
                                    } else {
                                        routeItem.params[k] = "$kv"
                                        k = null
                                    }
                                }
                            }
                        }
                    }
                }
                if (routeItem.path.isNotEmpty() && !routeItem.className.isNullOrEmpty()) {
                    list.add(routeItem)
                }
            }
        }
    }

    private fun genRouterMapFile(pageList: List) {
        if (pageList.isEmpty()) {
            return
        }
        val routePagelist = duplicateRemove(pageList)
        // 确保只要编译的软硬件环境不变,类名就不会改变
        val className = PREFIX_ROUTER_MAP + kotlin.math.abs(
            if (sourcePath.isEmpty()) {
                routePagelist[0].className?.hashCode() ?: routePagelist[0].path.hashCode()
            } else {
                sourcePath.hashCode()
            }
        )
        val json = gson.toJson(routePagelist)
        var ps: PrintStream? = null
        try {
            val dependencies = Dependencies(aggregating = true, *routeDependencies.toTypedArray())
            ps = PrintStream(codeGenerator.createNewFile(dependencies, PACKAGE, className))
            ps.println("@file:JvmName(\"$className\")")
            ps.println("package $PACKAGE")
            ps.println()
            ps.println("/**")
            ps.println(" * Generated code, Don't modify!!!")
            ps.println(" * Created by kymjs, and KSP Version is ${BuildConfig.VERSION}.")
            ps.println(" * JDK Version is ${System.getProperty("java.version")}.")
            ps.println(" */")
            ps.println("@androidx.annotation.Keep")
            ps.println("class $className : com.therouter.router.IRouterMapAPT {")
            ps.println()
            ps.println("\tcompanion object { ")
            ps.println()
            ps.println("\tconst val TAG = \"Created by kymjs, and KSP Version is ${BuildConfig.VERSION}.\"")
            ps.println("\tconst val THEROUTER_APT_VERSION = \"${BuildConfig.VERSION}\"")
            val routeMapJson = json.replace("\"", "\\\"")
            ps.println("\tconst val ROUTERMAP = \"$routeMapJson\"")
            ps.println()
            ps.println("\t@JvmStatic")
            ps.println("\tfun addRoute() {")
            var i = 0
            for (item in routePagelist) {
                i++
                ps.println("\t\tval item$i = com.therouter.router.RouteItem(\"${item.path}\",\"${item.className}\",\"${item.action}\",\"${item.description}\")")
                item.params.keys.forEach {
                    ps.println("\t\titem$i.addParams(\"$it\", \"${item.params[it]}\")")
                }
                ps.println("\t\tcom.therouter.router.addRouteItem(item$i)")
            }
            ps.println("\t}")
            ps.println("\t}")
            ps.println("}")
            ps.flush()
        } finally {
            ps?.close()
        }
    }

    private fun parseAutowired(resolver: Resolver): Map> {
        val map = HashMap>()
        resolver.getSymbolsWithAnnotation(Autowired::class.java.name).forEach {
            it.containingFile?.let { file ->
                autoWiredDependencies.add(file)
            }
            it.accept(AutowiredVisitor(map), Unit)
        }
        return map
    }

    inner class AutowiredVisitor(private val map: HashMap>) :
        TheRouterVisitor(logger) {

        override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: Unit) {
            super.visitPropertyDeclaration(property, data)
            sourcePath = getSourcePath(property)
            property.annotations.forEach { annotation ->
                val autowiredItem = AutowiredItem()
                autowiredItem.fieldName = property.simpleName.asString()
                autowiredItem.className = property.parentDeclaration?.qualifiedName?.asString() ?: property.packageName.asString()
                autowiredItem.classNameAndTypeParameters = autowiredItem.className
                property.parentDeclaration?.typeParameters?.size?.let { size ->
                    if (size > 0) {
                        val classNameBuilder = StringBuilder(autowiredItem.className).append("<")
                        for (i in 0 until size) {
                            classNameBuilder.append("*")
                            if (i != size - 1) {
                                classNameBuilder.append(",")
                            }
                        }
                        classNameBuilder.append(">")
                        autowiredItem.classNameAndTypeParameters = classNameBuilder.toString()
                    }
                }

                autowiredItem.type = getFieldType(property.type.resolve())

                annotation.arguments.forEach { arg ->
                    when (arg.name?.asString()) {
                        "name" -> {
                            var key = "${arg.value}"
                            if (key.isBlank()) {
                                key = property.simpleName.asString()
                            }
                            autowiredItem.key = key
                        }

                        "args" -> autowiredItem.args = "${arg.value}"
                        "id" -> autowiredItem.id = "${arg.value ?: 0}".toInt()
                        "required" -> {
                            autowiredItem.required = "${arg.value}".equals("true", ignoreCase = true)
                        }

                        "description" -> autowiredItem.description = "${arg.value}"
                    }
                }
                var list = map[autowiredItem.className]
                if (list == null) {
                    list = ArrayList()
                }
                list.add(autowiredItem)
                list.sort()
                map[autowiredItem.className] = list
            }
        }
    }

    private fun getFieldType(type: KSType?): String =
        if (type != null && type.arguments.isNotEmpty()) {
            val classNameBuilder = StringBuilder(type.declaration.qualifiedName?.asString()).append("<")
            type.arguments.forEach {
                classNameBuilder.append(getFieldType(it.type?.resolve())).append(",")
            }
            classNameBuilder.deleteCharAt(classNameBuilder.length - 1)
            classNameBuilder.append(">")
            classNameBuilder.toString()
        } else {
            type?.declaration?.qualifiedName?.asString() ?: ""
        }

    private fun genAutowiredFile(pageMap: Map>) {
        val keyList = ArrayList(pageMap.keys)
        // 做一次排序,确保只要map成员没有变化,输出文件内容的顺序就没有变化
        keyList.sort()
        for (key in keyList) {
            val fullClassName = key + SUFFIX_AUTOWIRED
            val fullClassNameAndTypeParameters = pageMap[key]?.get(0)?.classNameAndTypeParameters ?: key
            val simpleName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1)
            val pkgName = fullClassName.substring(0, fullClassName.lastIndexOf('.'))
            var ps: PrintStream? = null
            try {
                val dependencies = Dependencies(aggregating = false, *autoWiredDependencies.toTypedArray())
                ps = PrintStream(codeGenerator.createNewFile(dependencies, pkgName, simpleName))
                ps.println("@file:JvmName(\"$simpleName\")")
                ps.println(String.format("package %s", pkgName))
                ps.println()
                ps.println("/**")
                ps.println(" * Generated code, Don't modify!!!")
                ps.println(" * Created by kymjs, and KSP Version is ${BuildConfig.VERSION}.")
                ps.println(" * JDK Version is ${System.getProperty("java.version")}.")
                ps.println(" */")
                ps.println("@androidx.annotation.Keep")
                ps.println(String.format("object %s {", simpleName))
                ps.println("\t@JvmStatic")
                ps.println("\tval TAG = \"Created by kymjs, and KSP Version is ${BuildConfig.VERSION}.\"")
                ps.println("\t@JvmStatic")
                ps.println("\tval THEROUTER_APT_VERSION = \"${BuildConfig.VERSION}\"")
                ps.println()
                ps.println("\t@JvmStatic")
                ps.println("\tfun autowiredInject(obj: Any) {")
                ps.println(String.format("\t\tif (obj is %s) {", fullClassNameAndTypeParameters))
                ps.println(String.format("\t\tval target = obj as %s", fullClassNameAndTypeParameters))
                ps.println()
                ps.println("\t\tfor (parser in com.therouter.TheRouter.parserList) {")
                for ((i, item) in pageMap[key]!!.withIndex()) {
                    var type = transformNumber(item.type)
                    if (!type.endsWith('?')) {
                        type += "?"
                    }
                    val variableName = "variableName$i"
                    ps.println("\t\t\ttry {")
                    ps.println(
                        String.format(
                            "\t\t\t\tval %s: %s = parser.parse(\"%s\", target, com.therouter.router.AutowiredItem(\"%s\",\"%s\",%s,\"%s\",\"%s\",\"%s\",%s,\"%s\"))",
                            variableName, type,
                            item.type,
                            item.type,
                            item.key,
                            item.id,
                            item.args,
                            item.className,
                            item.fieldName,
                            item.required,
                            item.description
                        )
                    )
                    ps.println("\t\t\t\tif ($variableName != null){")
                    ps.println("\t\t\t\t\t// ${item.description}")
                    ps.println(String.format("\t\t\t\t\ttarget.%s = $variableName", item.fieldName))
                    ps.println("\t\t\t\t}")
                    ps.println("\t\t\t} catch (e: Exception) {")
                    ps.println("\t\t\t\tif (com.therouter.TheRouter.isDebug) { e.printStackTrace() }")
                    ps.println("\t\t\t}")
                }
                ps.println("\t\t} // for end")
                ps.println()
                ps.println("\t\t}")
                ps.println("\t}")
                ps.println("}")
                ps.flush()
            } finally {
                ps?.close()
            }
        }
    }

    private fun parseServiceProvider(resolver: Resolver): ArrayList {
        val list: ArrayList = ArrayList()
        resolver.getSymbolsWithAnnotation(ServiceProvider::class.java.name).forEach {
            it.containingFile?.let { file ->
                serviceProviderDependencies.add(file)
            }
            it.accept(ServiceProviderVisitor(list), Unit)
        }
        return list
    }

    inner class ServiceProviderVisitor(private val list: ArrayList) :
        TheRouterVisitor(logger) {

        override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
            super.visitClassDeclaration(classDeclaration, data)
            sourcePath = getSourcePath(classDeclaration)
            classDeclaration.annotations.forEach { annotation ->
                val serviceProviderItem = ServiceProviderItem(false)
                serviceProviderItem.className =
                    classDeclaration.qualifiedName?.asString().toString()
                serviceProviderItem.methodName = ""
                annotation.arguments.forEach { arg ->
                    when (arg.name?.asString()) {
                        "returnType" -> {
                            if (arg.value is KSType) {
                                serviceProviderItem.returnType =
                                    "${(arg.value as KSType).declaration.qualifiedName?.asString()}"
                                if (serviceProviderItem.returnType == ServiceProvider::class.java.name) {
                                    serviceProviderItem.returnType = ""
                                }
                            }
                        }

                        "params" -> {
                            val params = ArrayList()
                            for (kv in arg.value as List<*>) {
                                if (kv is KSType) {
                                    params.add(
                                        transformNumber(
                                            kv.declaration.qualifiedName?.asString() ?: ""
                                        )
                                    )
                                }
                            }
                            serviceProviderItem.params = params
                        }
                    }
                }

                if (serviceProviderItem.returnType.isBlank()) {
                    val list = classDeclaration.superTypes.toList()
                    if (list.isEmpty()) {
                        serviceProviderItem.returnType = serviceProviderItem.className
                    } else if (list.size == 1) {
                        list.forEach {
                            serviceProviderItem.returnType =
                                it.resolve().declaration.qualifiedName?.asString().toString()
                        }
                    } else {
                        val prop = Properties()
                        try {
                            val gradleProperties = FileInputStream(PROPERTY_FILE)
                            prop.load(gradleProperties)
                        } catch (e: Exception) {
                        }
                        if (!STR_TRUE.equals(
                                prop.getProperty(KEY_USE_EXTEND),
                                ignoreCase = true
                            )
                        ) {
                            throw IllegalArgumentException(
                                serviceProviderItem.className +
                                        " has multiple interfaces. Must to be specified returnType=XXX," +
                                        " or configuration KEY_USE_EXTEND=true in gradle.properties"
                            )
                        } else {
                            serviceProviderItem.returnType = serviceProviderItem.className
                        }
                    }
                }
                list.add(serviceProviderItem)
            }
        }

        override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
            super.visitFunctionDeclaration(function, data)
            sourcePath = getSourcePath(function)

            if (function.functionKind != FunctionKind.STATIC && function.functionKind != FunctionKind.TOP_LEVEL) {
                logger.error("The modifiers of the " + function.qualifiedName?.asString() + "() must have static!")
            }
            function.annotations.forEach { annotation ->
                val serviceProviderItem = ServiceProviderItem(true)
                serviceProviderItem.methodName = function.simpleName.asString()
                serviceProviderItem.className =
                    function.parentDeclaration?.qualifiedName?.asString() ?: function.packageName.asString()

                annotation.arguments.forEach { arg ->
                    when (arg.name?.asString()) {
                        "returnType" -> {
                            if (arg.value is KSType) {
                                serviceProviderItem.returnType =
                                    "${(arg.value as KSType).declaration.qualifiedName?.asString()}"
                                if (serviceProviderItem.returnType == ServiceProvider::class.java.name) {
                                    serviceProviderItem.returnType =
                                        function.returnType?.resolve()?.declaration?.qualifiedName?.asString()
                                            ?: ""
                                }
                            }
                        }

                        "params" -> {
                            val params = ArrayList()
                            for (kv in arg.value as List<*>) {
                                if (kv is KSType) {
                                    params.add(
                                        transformNumber(
                                            kv.declaration.qualifiedName?.asString() ?: ""
                                        )
                                    )
                                }
                            }
                            if (params.size == 0) {
                                function.parameters.forEach {
                                    params.add(
                                        transformNumber(
                                            it.type.resolve().declaration.qualifiedName?.asString()
                                                ?: ""
                                        )
                                    )
                                }
                            } else if (params.size != function.parameters.size) {
                                val log = StringBuilder(function.qualifiedName?.asString())
                                    .append("() parameter list required (")
                                function.parameters.forEach {
                                    log.append(it.type.resolve().declaration.qualifiedName?.asString())
                                        .append(",")
                                }
                                log.append("), But the declared parameter is")
                                repeat(params.size) {
                                    log.append("$it,")
                                }
                                log.append(")")
                                logger.error(log.toString())
                            }
                            serviceProviderItem.params = params
                        }
                    }
                }
                list.add(serviceProviderItem)
            }
        }
    }

    private fun parseFlowTask(resolver: Resolver): ArrayList {
        val list = ArrayList()
        resolver.getSymbolsWithAnnotation(FlowTask::class.java.name).forEach {
            it.containingFile?.let { file ->
                serviceProviderDependencies.add(file)
            }
            it.accept(FlowTaskVisitor(list), Unit)
        }
        return list
    }

    inner class FlowTaskVisitor(private val list: ArrayList) :
        TheRouterVisitor(logger) {

        override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
            super.visitFunctionDeclaration(function, data)
            sourcePath = getSourcePath(function)

            if (function.functionKind != FunctionKind.STATIC && function.functionKind != FunctionKind.TOP_LEVEL) {
                logger.error("The modifiers of the " + function.qualifiedName?.asString() + "() must have static!")
            }

            val type = function.returnType?.resolve()?.declaration?.qualifiedName?.asString() ?: ""
            if (type != Unit.javaClass.name) {
                logger.error("The return type of the " + function.qualifiedName?.asString() + "() must be void")
            }
            if (function.parameters.size != 1) {
                logger.error("=========================\n\n\n\n" + function.qualifiedName?.asString() + "() must only has Context parameter")
            }
            function.parameters.forEach {
                if (it.type.resolve().declaration.qualifiedName?.asString() != "android.content.Context") {
                    logger.error(
                        "=========================\n\n\n\n" + function.qualifiedName?.asString() + "(" + it.type.resolve().declaration.qualifiedName?.asString() + ") must only has Context parameter"
                    )
                }
            }

            function.annotations.forEach { annotation ->
                val flowTaskItem = FlowTaskItem()
                flowTaskItem.methodName = function.simpleName.asString()
                flowTaskItem.className = function.parentDeclaration?.qualifiedName?.asString() ?: function.packageName.asString()
                annotation.arguments.forEach { arg ->
                    when (arg.name?.asString()) {
                        "taskName" -> flowTaskItem.taskName = "${arg.value}"
                        "async" -> flowTaskItem.async = "${arg.value}".toBoolean()
                        "dependsOn" -> flowTaskItem.dependencies = "${arg.value}"
                    }
                }
                list.add(flowTaskItem)
            }
        }
    }

    private fun parseActionInterceptor(resolver: Resolver): ArrayList {
        val list = ArrayList()
        resolver.getSymbolsWithAnnotation(ActionInterceptor::class.java.name).forEach {
            it.containingFile?.let { file ->
                serviceProviderDependencies.add(file)
            }
            it.accept(ActionInterceptorVisitor(list), Unit)
        }
        return list
    }

    inner class ActionInterceptorVisitor(private val list: ArrayList) :
        TheRouterVisitor(logger) {
        override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
            super.visitClassDeclaration(classDeclaration, data)
            sourcePath = getSourcePath(classDeclaration)
            classDeclaration.annotations.forEach { annotation ->
                val item = ActionInterceptorItem()
                item.className = classDeclaration.qualifiedName?.asString().toString()
                annotation.arguments.forEach { arg ->
                    when (arg.name?.asString()) {
                        "actionName" -> item.actionName = "${arg.value}"
                    }
                }
                list.add(item)
            }
        }
    }

    private fun genServiceProviderFile(
        pageList: ArrayList,
        flowTaskList: ArrayList,
        actionInterceptorList: ArrayList
    ) {
        if (pageList.isEmpty() && flowTaskList.isEmpty() && actionInterceptorList.isEmpty()) {
            return
        }

        val stringBuilder = StringBuilder()
        stringBuilder.append("{")
        var isFirst = true
        flowTaskList.sort()
        pageList.sort()
        actionInterceptorList.sort()
        flowTaskList.forEach {
            if (!isFirst) {
                stringBuilder.append(",")
            }
            stringBuilder.append("\\\"").append(it.taskName).append("\\\":\\\"")
                .append(it.dependencies).append("\\\"")
            isFirst = false
        }
        stringBuilder.append("}")

        // 确保只要编译的软硬件环境不变,类名就不会改变
        val className = PREFIX_SERVICE_PROVIDER + kotlin.math.abs(
            if (sourcePath.isEmpty()) {
                if (pageList.isNotEmpty()) {
                    pageList[0].className.hashCode()
                } else {
                    flowTaskList[0].className.hashCode()
                }
            } else {
                sourcePath.hashCode()
            }
        )
        var ps: PrintStream? = null
        try {
            val dependencies = Dependencies(aggregating = true, *serviceProviderDependencies.toTypedArray())
            ps = PrintStream(codeGenerator.createNewFile(dependencies, PACKAGE, className))
            ps.println("@file:JvmName(\"$className\")")
            ps.println(String.format("package %s", PACKAGE))
            ps.println()
            ps.println("/**")
            ps.println(" * Generated code, Don't modify!!!")
            ps.println(" * Created by kymjs, and KSP Version is ${BuildConfig.VERSION}.")
            ps.println(" * JDK Version is ${System.getProperty("java.version")}.")
            ps.println(" */")
            ps.println("@androidx.annotation.Keep")
            ps.println(
                String.format(
                    "public class %s : com.therouter.inject.Interceptor {",
                    className
                )
            )
            ps.println()
            ps.println("\toverride fun  interception(clazz: Class?, vararg params: Any?): T? {")
            ps.println("\t\tvar obj: T? = null")
            ps.print("\t\t")
            val prop = Properties()
            try {
                val gradleProperties = FileInputStream(PROPERTY_FILE)
                prop.load(gradleProperties)
            } catch (e: Exception) {
            }
            for (serviceProviderItem in pageList) {
                //处理 USE_EXTEND 开关
                if (STR_TRUE.equals(prop.getProperty(KEY_USE_EXTEND), ignoreCase = true)) {
                    ps.print(
                        String.format(
                            "if (%s::class.javaObjectType.isAssignableFrom(clazz)",
                            serviceProviderItem.returnType
                        )
                    )
                } else {
                    ps.print(
                        String.format(
                            "if (%s::class.java.equals(clazz)",
                            serviceProviderItem.returnType
                        )
                    )
                }
                // 多参数判断
                ps.print(" && params.size == ")
                if (serviceProviderItem.params.size == 1) {
                    if (serviceProviderItem.params[0].trim { it <= ' ' }.isEmpty()) {
                        ps.print(0)
                    } else {
                        ps.print(1)
                    }
                } else {
                    ps.print(serviceProviderItem.params.size)
                }
                //参数类型判断
                for (count in serviceProviderItem.params.indices) {
                    if (!serviceProviderItem.params[count].trim { it <= ' ' }.isEmpty()) {
                        ps.print(
                            String.format(
                                Locale.getDefault(),
                                "\n\t\t\t\t&& params[%d] is %s",
                                count,
                                serviceProviderItem.params[count]
                            )
                        )
                    }
                }
                ps.println(") {")
                if (serviceProviderItem.isMethod) {
                    ps.print(
                        String.format(
                            "\t\t\tval returnType: %s = %s.%s(",
                            serviceProviderItem.returnType,
                            serviceProviderItem.className,
                            serviceProviderItem.methodName
                        )
                    )
                } else {
                    ps.print(
                        String.format(
                            "\t\t\tval returnType: %s = %s(",
                            serviceProviderItem.returnType,
                            serviceProviderItem.className
                        )
                    )
                }

                for (count in serviceProviderItem.params.indices) {
                    if (!serviceProviderItem.params[count].trim { it <= ' ' }.isEmpty()) {
                        //参数强转
                        ps.print(
                            String.format(
                                Locale.getDefault(),
                                "params[%d] as %s",
                                count,
                                serviceProviderItem.params[count]
                            )
                        )
                        if (count != serviceProviderItem.params.size - 1) {
                            ps.print(", ")
                        }
                    }
                }
                ps.println(")")
                ps.println("\t\t\tobj = returnType as T?")
                ps.print("\t\t} else ")
            }
            ps.println("{\n")
            ps.println("        }")
            ps.println("        return obj")
            ps.println("    }")
            ps.println()
            ps.println("\tcompanion object { ")
            ps.println()
            ps.println("\tconst val TAG = \"Created by kymjs, and KSP Version is ${BuildConfig.VERSION}.\"")
            ps.println("\tconst val THEROUTER_APT_VERSION = \"${BuildConfig.VERSION}\"")
            ps.println("\tconst val FLOW_TASK_JSON = \"${stringBuilder}\"")
            ps.println()
            ps.println("\t\[email protected]")
            ps.println("\t\tfun addFlowTask(context: android.content.Context, digraph: com.therouter.flow.Digraph) {")
            for (item in flowTaskList) {
                ps.println("\t\t\tdigraph.addTask(com.therouter.flow.Task(${item.async}, \"${item.taskName}\", \"${item.dependencies}\", object : com.therouter.flow.FlowTaskRunnable {")
                ps.println("\t\t\t\toverride fun run() = ${item.className}.${item.methodName}(context)")
                ps.println()
                ps.println("\t\t\t\toverride fun log() = \"${item.className}.${item.methodName}(context)\"")
                ps.println("\t\t\t}))")
            }
            for (item in actionInterceptorList) {
                ps.println("\t\t\tcom.therouter.TheRouter.addActionInterceptor(\"${item.actionName}\", ${item.className}());")
            }
            ps.println("\t\t}")
            ps.println("\t}")
            ps.println("}")
            ps.flush()
        } finally {
            ps?.close()
        }
    }
}

fun transformNumber(type: String): String {
    return when (type) {
        "byte" -> "kotlin.Byte"
        "short" -> "kotlin.Short"
        "int" -> "kotlin.Integer"
        "long" -> "kotlin.Long"
        "float" -> "kotlin.Float"
        "double" -> "kotlin.Double"
        "boolean" -> "kotlin.Boolean"
        "char" -> "kotlin.Character"
        "String" -> "kotlin.String"
        "java.lang.String" -> "kotlin.String"
        else -> type
    }
}

fun getSourcePath(ks: KSDeclaration): String {
    val pkgPath = ks.packageName.asString().replace(".", "/")
    val filePath = ks.containingFile?.filePath
    var sourcePath = filePath?.replace("/$pkgPath", "") ?: ""
    val endIndex = sourcePath.indexOfLast { it == '/' }
    if (endIndex > 0) {
        sourcePath = sourcePath.substring(0, endIndex)
    }
    return sourcePath
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy