Skip to content

Instantly share code, notes, and snippets.

@malvinstn
Last active February 19, 2019 14:27
Show Gist options
  • Save malvinstn/80a68f632d692a6b5f646b0b4051776b to your computer and use it in GitHub Desktop.
Save malvinstn/80a68f632d692a6b5f646b0b4051776b to your computer and use it in GitHub Desktop.
@AutoService(Processor::class)
class AnalyticsEventProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils {
companion object {
private val ANNOTATION = AnalyticsEvent::class.java
private const val EVENT_PARAMETER_NAME = "event"
private const val EVENT_NAME_PARAMETER_NAME = "name"
private const val EVENT_PARAM_PARAMETER_NAME = "params"
private const val LOG_EVENT_FUNCTION_NAME = "logEvent"
private val EVENT_TRACKER_CLASS = ClassName("com.malvinstn.tracker", "EventTracker")
private val BUNDLE_CLASS = ClassName("android.os", "Bundle")
private val BUNDLE_OF_FUNCTION = ClassName("androidx.core.os", "bundleOf")
}
...
override fun process(
annotations: Set<TypeElement>,
roundEnv: RoundEnvironment
): Boolean {
val outputDir = generatedDir
if (outputDir == null) {
messager.printMessage(
Diagnostic.Kind.ERROR,
"Cannot find generated output dir."
)
return false
}
// 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)
if (declaredAnalyticsEvents.isEmpty()) {
// No declared Analytics Event, skip this class.
messager.printMessage(
Diagnostic.Kind.WARNING,
"$analyticsElement has no valid inner class."
)
continue
}
// Generate codes with KotlinPoet
generateCode(analyticsElement, declaredAnalyticsEvents, outputDir)
}
return true
}
private fun getAnalyticsElement(element: Element): TypeElement? {...}
private fun getDeclaredAnalyticsEvents(
analyticsElement: TypeElement
): Map<ClassName, List<String>> {...}
private fun generateCode(
analyticsElement: TypeElement,
analyticEvents: Map<ClassName, List<String>>,
outputDir: File
) {
val className = analyticsElement.asClassName()
val extensionFunSpecBuilder = FunSpec.builder(LOG_EVENT_FUNCTION_NAME)
.receiver(EVENT_TRACKER_CLASS)
.addParameter(EVENT_PARAMETER_NAME, className)
.addStatement("val %L: %T", EVENT_NAME_PARAMETER_NAME, String::class)
.addStatement("val %L: %T", EVENT_PARAM_PARAMETER_NAME, BUNDLE_CLASS)
.beginControlFlow("when (%L)", EVENT_PARAMETER_NAME)
for ((eventName, eventParamList) in analyticEvents) {
val codeBlock = CodeBlock.builder()
.addStatement("is %T -> {", eventName)
.indent()
.addStatement(
"%L = %S",
EVENT_NAME_PARAMETER_NAME,
eventName.simpleName.convertCase(
CaseFormat.UPPER_CAMEL,
CaseFormat.LOWER_UNDERSCORE
)
)
.apply {
if (eventParamList.isNotEmpty()) {
addStatement("%L = %T(", EVENT_PARAM_PARAMETER_NAME, BUNDLE_OF_FUNCTION)
indent()
for ((index, parameter) in eventParamList.withIndex()) {
val size = eventParamList.size
val separator = if (index == size - 1) { "" } else { "," }
addStatement(
"%S to %L.%L%L",
parameter.convertCase(
CaseFormat.LOWER_CAMEL,
CaseFormat.LOWER_UNDERSCORE
),
EVENT_PARAMETER_NAME,
parameter,
separator
)
}
unindent()
addStatement(")")
} else {
addStatement("%L = %T()", EVENT_PARAM_PARAMETER_NAME, BUNDLE_CLASS)
}
}
.unindent()
.addStatement("}")
.build()
extensionFunSpecBuilder.addCode(codeBlock)
}
extensionFunSpecBuilder.endControlFlow()
.addStatement(
"%L(%L, %L)",
LOG_EVENT_FUNCTION_NAME,
EVENT_NAME_PARAMETER_NAME,
EVENT_PARAM_PARAMETER_NAME
)
FileSpec.builder(className.packageName, className.simpleName)
.addFunction(extensionFunSpecBuilder.build())
.build()
.writeTo(outputDir)
}
}
/**
* Helper function to convert String case
* using guava's [CaseFormat].
*/
private fun String.convertCase(
fromCase: CaseFormat,
toCase: CaseFormat
): String {
return fromCase.to(toCase, this)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment