|
| 1 | +package kategory.common.utils |
| 2 | + |
| 3 | +import kategory.implicits.implicitAnnotationName |
| 4 | +import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata |
| 5 | +import me.eugeniomarletti.kotlin.metadata.KotlinMetadata |
| 6 | +import me.eugeniomarletti.kotlin.metadata.KotlinMetadataUtils |
| 7 | +import me.eugeniomarletti.kotlin.metadata.KotlinPackageMetadata |
| 8 | +import me.eugeniomarletti.kotlin.metadata.getJvmMethodSignature |
| 9 | +import me.eugeniomarletti.kotlin.metadata.kotlinMetadata |
| 10 | +import me.eugeniomarletti.kotlin.metadata.kotlinPropertyAnnotationsFunPostfix |
| 11 | +import org.jetbrains.kotlin.serialization.ProtoBuf |
| 12 | +import javax.lang.model.element.Element |
| 13 | +import javax.lang.model.element.ExecutableElement |
| 14 | +import javax.lang.model.element.TypeElement |
| 15 | +import javax.lang.model.element.VariableElement |
| 16 | + |
| 17 | +interface ProcessorUtils : KotlinMetadataUtils { |
| 18 | + |
| 19 | + fun KotlinMetadata.asClassOrPackageDataWrapper(classElement: TypeElement): ClassOrPackageDataWrapper? { |
| 20 | + val `package` = elementUtils.getPackageOf(classElement).toString() |
| 21 | + return when (this) { |
| 22 | + is KotlinClassMetadata -> data.asClassOrPackageDataWrapper(`package`) |
| 23 | + is KotlinPackageMetadata -> data.asClassOrPackageDataWrapper(`package`) |
| 24 | + else -> null |
| 25 | + } |
| 26 | + } |
| 27 | + |
| 28 | + fun getClassOrPackageDataWrapper(classElement: TypeElement): ClassOrPackageDataWrapper { |
| 29 | + val metadata = classElement.kotlinMetadata ?: knownError("These annotations can only be used in Kotlin") |
| 30 | + return metadata.asClassOrPackageDataWrapper(classElement) ?: knownError("This annotation can't be used on this element") |
| 31 | + } |
| 32 | + |
| 33 | + fun ClassOrPackageDataWrapper.getFunction(methodElement: ExecutableElement) = |
| 34 | + methodElement.jvmMethodSignature.let { methodSignature -> |
| 35 | + functionList |
| 36 | + .firstOrNull { methodSignature == it.getJvmMethodSignature(nameResolver) } |
| 37 | + ?: knownError("Can't find annotated method $methodSignature") |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +fun knownError(message: String, element: Element? = null): Nothing = throw KnownException(message, element) |
| 42 | + |
| 43 | +fun String.plusIfNotBlank( |
| 44 | + postfix: String = "", |
| 45 | + prefix: String = "" |
| 46 | +) = if (this.isNotBlank()) prefix + this + postfix else this |
| 47 | + |
| 48 | +val String.escapedClassName |
| 49 | + get() = split('/', '.').joinToString("`.`").plusIfNotBlank(prefix = "`", postfix = "`") |
| 50 | + |
| 51 | +val ProtoBuf.Class.Kind.isCompanionOrObject get() = when (this) { |
| 52 | + ProtoBuf.Class.Kind.OBJECT, |
| 53 | + ProtoBuf.Class.Kind.COMPANION_OBJECT -> true |
| 54 | + else -> false |
| 55 | +} |
| 56 | + |
| 57 | +fun ClassOrPackageDataWrapper.getParameter(function: ProtoBuf.Function, parameterElement: VariableElement) = |
| 58 | + parameterElement.simpleName.toString().let { parameterName -> |
| 59 | + function.valueParameterList |
| 60 | + .firstOrNull { parameterName == nameResolver.getString(it.name) } |
| 61 | + ?: knownError("Can't find annotated parameter $parameterName in ${function.getJvmMethodSignature(nameResolver)}") |
| 62 | + } |
| 63 | + |
| 64 | +fun ClassOrPackageDataWrapper.getPropertyOrNull(methodElement: ExecutableElement) = |
| 65 | + methodElement.simpleName.toString() |
| 66 | + .takeIf { it.endsWith(kotlinPropertyAnnotationsFunPostfix) } |
| 67 | + ?.substringBefore(kotlinPropertyAnnotationsFunPostfix) |
| 68 | + ?.let { propertyName -> propertyList.firstOrNull { propertyName == nameResolver.getString(it.name) } } |
| 69 | + |
| 70 | +fun ProtoBuf.Type.extractFullName( |
| 71 | + classData: ClassOrPackageDataWrapper, |
| 72 | + outputTypeAlias: Boolean = true, |
| 73 | + failOnGeneric: Boolean = true |
| 74 | +): String { |
| 75 | + val nameResolver = classData.nameResolver |
| 76 | + |
| 77 | + if (failOnGeneric && !hasClassName()) knownError("Generic $implicitAnnotationName types are not yet supported") |
| 78 | + |
| 79 | + val name = when { |
| 80 | + hasTypeParameter() -> classData.getTypeParameter(typeParameter)!!.name |
| 81 | + hasTypeParameterName() -> typeParameterName |
| 82 | + outputTypeAlias && hasAbbreviatedType() -> abbreviatedType.typeAliasName |
| 83 | + else -> className |
| 84 | + }.let { nameResolver.getString(it).escapedClassName } |
| 85 | + |
| 86 | + val argumentList = if (outputTypeAlias && hasAbbreviatedType()) abbreviatedType.argumentList else argumentList |
| 87 | + val arguments = argumentList |
| 88 | + .takeIf { it.isNotEmpty() } |
| 89 | + ?.joinToString(prefix = "<", postfix = ">") { |
| 90 | + when { |
| 91 | + it.hasType() -> it.type.extractFullName(classData, outputTypeAlias, failOnGeneric) |
| 92 | + !failOnGeneric -> "*" |
| 93 | + else -> knownError("Wildcard $implicitAnnotationName types are not yet supported") |
| 94 | + } |
| 95 | + } |
| 96 | + ?: "" |
| 97 | + |
| 98 | + val nullability = if (nullable) "?" else "" |
| 99 | + |
| 100 | + return name + arguments + nullability |
| 101 | +} |
0 commit comments