jvmMain.com.lt.lazy_people_http.provider.LazyPeopleHttpVisitor.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of LazyPeopleHttp Show documentation
Show all versions of LazyPeopleHttp Show documentation
Lazy people http, Retrofit of kmp all targets
package com.lt.lazy_people_http.provider
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.symbol.*
import com.lt.lazy_people_http.annotations.*
import com.lt.lazy_people_http.appendText
import com.lt.lazy_people_http.getKSTypeInfo
import com.lt.lazy_people_http.getNewAnnotationString
import com.lt.lazy_people_http.options.KspOptions
import com.lt.lazy_people_http.options.MethodInfo
import com.lt.lazy_people_http.options.ParameterInfo
import com.lt.lazy_people_http.request.RequestMethod
import java.io.OutputStream
/**
* creator: lt 2022/10/20 [email protected]
* effect : 访问并处理相应符号
* warning:
*/
internal class LazyPeopleHttpVisitor(
private val environment: SymbolProcessorEnvironment,
) : KSVisitorVoid() {
private val options = KspOptions(environment)
private val isGetFunctionAnnotations = options.isGetFunctionAnnotations()
/**
* 访问class的声明
*/
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
//获取class信息并创建kt文件
val packageName = classDeclaration.packageName.asString()
val originalClassName = classDeclaration.simpleName.asString()
val className = "_${originalClassName}ServiceImpl"
if (classDeclaration.classKind != ClassKind.INTERFACE)
throw RuntimeException("LazyPeopleHttpService can only decorate interface")
val file = environment.codeGenerator.createNewFile(
Dependencies(
true,
classDeclaration.containingFile!!
), packageName, className
)
writeFile(file, packageName, className, originalClassName, classDeclaration)
file.close()
}
//向文件中写入完整内容
private fun writeFile(
file: OutputStream,
packageName: String,
className: String,
originalClassName: String,
classDeclaration: KSClassDeclaration
) {
file.appendText(
"package $packageName\n" +
"\n" +
"import com.lt.lazy_people_http.config.LazyPeopleHttpConfig\n" +
"import com.lt.lazy_people_http.call.CallAdapter\n" +
"import com.lt.lazy_people_http.request.RequestMethod\n" +
"import com.lt.lazy_people_http.service.HttpServiceImpl\n" +
"import kotlin.reflect.typeOf\n" +
"\n" +
"class $className(\n" +
" val config: LazyPeopleHttpConfig,\n" +
") : $originalClassName, HttpServiceImpl {\n" +
" inline fun T?.asString() = CallAdapter.parameterToJson(config, this)\n\n"
)
writeFunction(file, classDeclaration)
file.appendText(
"}\n\n" +
"fun kotlin.reflect.KClass<$originalClassName>.createService(config: LazyPeopleHttpConfig): $originalClassName =\n" +
" $className(config)"
)
}
//向文件中写入变换后的函数
private fun writeFunction(file: OutputStream, classDeclaration: KSClassDeclaration) {
classDeclaration.getAllFunctions().filter {
it.isAbstract
}.forEach {
val functionName = it.simpleName.asString()
val methodInfo = getMethodInfo(it, functionName)
val returnType = getKSTypeInfo(it.returnType!!).toString()
val typeOf =
getKSTypeInfo(it.returnType!!.element!!.typeArguments.first().type!!).toString()
val headers = getHeaders(it)
val parameterInfo = getParameters(it, methodInfo.method)
val isSuspendFun = Modifier.SUSPEND in it.modifiers
var url = methodInfo.url
parameterInfo.replaceUrlFunction?.forEach {
url = url.replace(it.key, "\$${it.value}")
}
val functionAnnotations = getFunctionAnnotations(it)
file.appendText(" override ${if (isSuspendFun) "suspend " else ""}fun $functionName(${parameterInfo.funParameter}): $returnType {\n")
if (functionAnnotations.isNotEmpty())
file.appendText(
" var annotations: Array? = null\n" +
" val getAnnotations: () -> Array = get@{\n" +
" annotations?.let { return@get it }\n" +
" val array = arrayOf($functionAnnotations)\n" +
" annotations = array\n" +
" array\n" +
" }\n"
)
file.appendText(
" return CallAdapter.createCall${if (isSuspendFun) "<$returnType>" else ""}(\n" +
" config,\n" +
" \"$url\",\n" +
" ${parameterInfo.queryParameter},\n" +
" ${parameterInfo.fieldParameter},\n" +
" ${parameterInfo.runtimeParameter},\n" +
" typeOf<${if (isSuspendFun) returnType else typeOf}>(),\n" +
" ${if (methodInfo.method == null) "null" else "RequestMethod.${methodInfo.method}"},\n" +
" $headers,\n" +
" ${if (functionAnnotations.isEmpty()) "null" else "getAnnotations"},\n" +
" )${if (isSuspendFun) ".await()" else ""}\n" +
" }\n\n"
)
}
}
//获取方法的参数和请求参数
private fun getParameters(it: KSFunctionDeclaration, method: RequestMethod?): ParameterInfo {
//如果没有参数
if (it.parameters.isEmpty()) return ParameterInfo("", "null", "null", "null", null)
//有参数的话就将参数拆为:方法参数,query参数,field参数,和只有运行时才能处理的参数
val funPList = ArrayList()
val queryPList = ArrayList()
val fieldPList = ArrayList()
val runtimePList = ArrayList()
val replaceUrlMap = HashMap()
it.parameters.forEach {
val funPName = it.name!!.asString()
val type = getKSTypeInfo(it.type).toString()
funPList.add("$funPName: $type")
getParameterInfo(it, funPName, queryPList, fieldPList, runtimePList, replaceUrlMap)
}
//处理方法加了注解,但参数没加注解的情况
when (method) {
RequestMethod.GET_QUERY -> {
queryPList.addAll(runtimePList)
runtimePList.clear()
}
RequestMethod.POST_FIELD -> {
fieldPList.addAll(runtimePList)
runtimePList.clear()
}
else -> {}
}
//将所有参数拼接成代码
return ParameterInfo(
if (funPList.isEmpty()) "" else funPList.joinToString(),
if (queryPList.isEmpty()) "null" else queryPList.joinToString(
prefix = "mapOf(",
postfix = ")"
),
if (fieldPList.isEmpty()) "null" else fieldPList.joinToString(
prefix = "mapOf(",
postfix = ")"
),
if (runtimePList.isEmpty()) "null" else runtimePList.joinToString(
prefix = "mapOf(",
postfix = ")"
),
replaceUrlMap,
)
}
//获取参数设置的参数的内容
@OptIn(KspExperimental::class)
private fun getParameterInfo(
it: KSValueParameter,
funPName: String,
queryPList: ArrayList,
fieldPList: ArrayList,
runtimePList: ArrayList,
replaceUrlMap: HashMap,
) {
val list =
(it.getAnnotationsByType(Query::class)
+ it.getAnnotationsByType(Field::class)
+ it.getAnnotationsByType(Url::class)
).toList()
if (list.isEmpty()) {
runtimePList.add("\"$funPName\" to $funPName.asString()")
return
}
when (val annotation = list.first()) {
is Query -> queryPList.add("\"${annotation.name}\" to $funPName.asString()")
is Field -> fieldPList.add("\"${annotation.name}\" to $funPName.asString()")
is Url -> replaceUrlMap["{${annotation.replaceUrlName}}"] = funPName
else -> throw RuntimeException("There is a problem with the getParameterInfo function")
}
}
//获取请求头的内容
@OptIn(KspExperimental::class)
private fun getHeaders(it: KSFunctionDeclaration): String {
val list = it.getAnnotationsByType(Header::class).toList()
if (list.isEmpty()) return "null"
return list.joinToString(
prefix = "mapOf(",
postfix = ")"
) { "\"${it.name}\" to \"${it.value}\"" }
}
//获取函数的请求方法相关数据
@OptIn(KspExperimental::class)
private fun getMethodInfo(it: KSFunctionDeclaration, functionName: String): MethodInfo {
val list =
(it.getAnnotationsByType(GET::class) + it.getAnnotationsByType(POST::class)).toList()
if (list.isEmpty())
return MethodInfo(null, functionName.replace("_", "/"))
if (list.size > 1)
throw RuntimeException("Function $functionName there are multiple http method annotations")
return when (val annotation = list.first()) {
is GET -> MethodInfo(RequestMethod.GET_QUERY, annotation.url)
is POST -> MethodInfo(RequestMethod.POST_FIELD, annotation.url)
else -> throw RuntimeException("There is a problem with the getMethodInfo function")
}
}
//获取方法和其参数以及返回值上的注解(不包含Type的注解)
private fun getFunctionAnnotations(it: KSFunctionDeclaration): String {
if (!isGetFunctionAnnotations) return ""
val annotations = StringBuilder()
it.annotations.forEach {
annotations.append(getNewAnnotationString(it))
.append(", ")
}
it.parameters.forEach {
it.annotations.forEach {
annotations.append(getNewAnnotationString(it))
.append(", ")
}
}
it.returnType?.annotations?.forEach {
annotations.append(getNewAnnotationString(it))
.append(", ")
}
return annotations.toString()
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy