Skip to content

Instantly share code, notes, and snippets.

@malvinstn
Last active February 22, 2019 07:44
Show Gist options
  • Save malvinstn/f117a3c31b0a30658e72ed2cc95919da to your computer and use it in GitHub Desktop.
Save malvinstn/f117a3c31b0a30658e72ed2cc95919da to your computer and use it in GitHub Desktop.
@AutoService(Processor::class)
class AnalyticsEventProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils {
companion object {
private val ANNOTATION = AnalyticsEvent::class.java
}
...
override fun process(
annotations: Set<TypeElement>,
roundEnv: RoundEnvironment
): Boolean {
// Get all elements that has been annotated with our annotation
val annotatedElements = roundEnv.getElementsAnnotatedWith(ANNOTATION)
for (annotatedElement in annotatedElements) {
// Check if the annotatedElement is a Kotlin sealed class
val analyticsElement = getAnalyticsElement(annotatedElement) ?: continue
// Get all the declared inner class as our Analytics Event
val declaredAnalyticsEvents = getDeclaredAnalyticsEvents(analyticsElement)
}
return true
}
private fun getAnalyticsElement(element: Element): TypeElement? {
val kotlinMetadata = element.kotlinMetadata
if (kotlinMetadata !is KotlinClassMetadata || element !is TypeElement) {
// Not a Kotlin class
messager.printMessage(
Diagnostic.Kind.WARNING,
"$element is not a Kotlin class."
)
return null
}
val proto = kotlinMetadata.data.classProto
if (proto.modality != ProtoBuf.Modality.SEALED) {
// Is not a sealed class
messager.printMessage(
Diagnostic.Kind.WARNING,
"$element is not a sealed Kotlin class."
)
return null
}
return element
}
private fun getDeclaredAnalyticsEvents(
analyticsElement: TypeElement
): Map<ClassName, List<String>> {
val analyticsEvents = mutableMapOf<ClassName, List<String>>()
val supertype = analyticsElement.asType()
val enclosedElements = analyticsElement.enclosedElements
for (element in enclosedElements) {
val type = element.asType()
if (element !is TypeElement) {
// Inner element is not a class
messager.printMessage(
Diagnostic.Kind.WARNING,
"$element is not a kotlin class."
)
continue
} else if (!typeUtils.directSupertypes(type).contains(supertype)) {
// Inner class does not extend from the enclosing sealed class
messager.printMessage(
Diagnostic.Kind.WARNING,
"$element does not extend from $analyticsElement."
)
continue
}
val kotlinMetadata = element.kotlinMetadata as KotlinClassMetadata
// Make use of KotlinPoet's ClassName to easily get the class' name.
val eventClass = element.asClassName()
// Extract the primary constructor and its parameters as the event's parameters.
val proto = kotlinMetadata.data.classProto
val nameResolver = kotlinMetadata.data.nameResolver
if (proto.constructorCount == 0) {
messager.printMessage(
Diagnostic.Kind.WARNING,
"$element has no constructor."
)
continue
}
val mainConstructor = proto.constructorList[0]
val eventParameters = mainConstructor.valueParameterList
.map { valueParameter ->
// Resolve the constructor parameter's name
// using nameResolver.
nameResolver.getString(valueParameter.name)
}
analyticsEvents[eventClass] = eventParameters
}
return analyticsEvents
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment