Skip to content

Commit

Permalink
Merge pull request #30 from Winfooz/mhamdan/handle-multiple-embedded/…
Browse files Browse the repository at this point in the history
…2018-11-11

Handle multiple embedded and add override to fields name
  • Loading branch information
winfoozmnayef authored Nov 11, 2018
2 parents db0edfb + 18455ba commit 7fa4e99
Show file tree
Hide file tree
Showing 22 changed files with 188 additions and 126 deletions.
2 changes: 1 addition & 1 deletion annotations/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ POM_DESCRIPTION=Annotations for use in your project
POM_BINTRAY_NAME=com.winfooz.winanalytics:annotations
POM_ARTIFACT_ID=annotations
POM_PACKAGING=jar
POM_VERSION=1.0.5-beta
POM_VERSION=1.0.6-beta
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ package com.winfooz.winanalytics.annotations
*/
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class Analytics(vararg val value: Event)
annotation class Analytics(val value: String, vararg val events: Event)
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ package com.winfooz.winanalytics.annotations
*/
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class AnalyticsEmbedded
annotation class AnalyticsEmbedded(val override: String = "")
2 changes: 1 addition & 1 deletion compiler/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ POM_DESCRIPTION=For proccess annotations and generate code
POM_BINTRAY_NAME=com.winfooz.winanalytics:compiler
POM_ARTIFACT_ID=compiler
POM_PACKAGING=jar
POM_VERSION=1.0.5-beta
POM_VERSION=1.0.6-beta
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ object AnnotationParsingUtils {
if (element.enclosingElement is TypeElement) {
val analytics = element.getAnnotation(Analytics::class.java)
if (Validator.validate(element, processingEnv, analytics)) {
annotationsSet.add(ElementUtils.parseAnnotationData(processingEnv, element, analytics))
annotationsSet.add(ElementUtils.parseAnalyticsAnnotationData(processingEnv, element, analytics))
} else {
MessagerUtils.analyticsError(processingEnv.messager, element, Analytics::class.java)
}
Expand All @@ -100,7 +100,7 @@ object AnnotationParsingUtils {
if (element.enclosingElement is TypeElement) {
val analyticsEmbedded = element.getAnnotation(AnalyticsEmbedded::class.java)
if (Validator.validate(element, processingEnv, analyticsEmbedded)) {
annotationsSet.add(ElementUtils.parseAnnotationData(processingEnv, element, analyticsEmbedded))
annotationsSet.add(ElementUtils.parseAnalyticsEmbeddedAnnotationData(processingEnv, element, analyticsEmbedded))
} else {
MessagerUtils.embeddedError(processingEnv.messager, element, AnalyticsEmbedded::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,30 @@ object BindingFactory {
* @param classBuilder The analytics class.
* @param pkg The package that contains class which has class fields annotated with [Analytics]
* annotation for import class in the generated analytics class.
* @param className The class name for add it as parameter for every generated method.
* @param className The class reference for add it as parameter for every generated method.
* @param methods The methods which need to be generated inside [classBuilder].
* @param configurationData The configuration for enable or disable multiple client generation.
*
* @see AnalyticsType
*/
fun bindAnalytics(classBuilder: TypeSpec.Builder, pkg: String, className: String,
methods: MutableMap<String, MutableList<String>>,
methods: MutableMap<String, Pair<String, MutableList<Pair<String, String>>>>,
configurationData: Configuration) {
methods.keys.forEach { key ->
val function = FunSpec.builder(MethodsUtil.getAnalyticsMethod(key))
function.addParameter("instance", ClassName(pkg, className))
if (isFirebaseMethod(key) || isFabricMethod(key) || isMixpanelMethod(key)) {
function.addStatement("val pair: Pair<%T, %N<%T, %T>> = %T(%S, %N)", String::class, "MutableMap", String::class, String::class, Pair::class, getEventName(key), "mutableMapOf()")
methods[key]?.forEach {
val index = it.indexOf(".")
function.addStatement("%N.%N.%N(%S, %N)", "pair", "second", "put", it.substring(if (index > -1) index + 1 else 0), "instance.$it.toString()")
function.addStatement(
"val pair: Pair<%T, %N<%T, %T>> = %T(%S, %N)",
String::class,
"MutableMap",
String::class,
String::class,
Pair::class, methods[key]?.first ?: "",
"mutableMapOf()"
)
methods[key]?.second?.forEach {
function.addStatement("%N.%N.%N(%S, %N)", "pair", "second", "put", it.first, "instance.${it.second}.toString()")
}
if (isFirebaseMethod(key)) {
function.addStatement("%N.log(%N)", FIREBASE_PREFIX.toLowerCase(), "pair")
Expand Down Expand Up @@ -103,8 +110,8 @@ object BindingFactory {
* @param typeSpec the [ConfigurationType].
* @param pkg The package that contains class which has class fields annotated with [Analytics]
* annotation for import class in the generated analytics class.
* @param className The class name for know which class in the [pkg] need to import.
* @param name The field name
* @param className The class reference for know which class in the [pkg] need to import.
* @param name The field reference
* @param configuration The configuration for enable or disable multiple client generation.
*/
fun bindAnalyticsConfigurationAnalyticsObjects(typeSpec: TypeSpec.Builder, pkg: String, name: String, className: String, configuration: Configuration) {
Expand Down Expand Up @@ -137,7 +144,7 @@ object BindingFactory {
* For add supported and enabled client to the Analytics class
*
* @param typeSpec the [ConfigurationType].
* @param name The client name
* @param name The client reference
* @param className The client class path for import.
*/
private fun addClient(typeSpec: TypeSpec.Builder, name: String, key: String, className: ClassName) {
Expand Down Expand Up @@ -172,7 +179,7 @@ object BindingFactory {
private fun isMixpanelMethod(key: String): Boolean = MethodsUtil.getAnalyticsMethod(key).contains(MIXPANEL_PREFIX, true)

/**
* For remove clients names from the method name
* For remove clients names from the method reference
*/
private fun getEventName(key: String): String = key.replace(FIREBASE_PREFIX, "", true)
.replace(MIXPANEL_PREFIX, "", true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,43 @@ class CodeGenerationUtils {
typeHelper = AnalyticsType(processingEnv, data.pkgName, data.className, configuration, configurationType)
classesMap[data.className] = typeHelper
}
parseEmbedded(typeHelper, processingEnv, data)
}

private fun parseEmbedded(typeHelper: AnalyticsType?, processingEnv: ProcessingEnvironment, data: FieldData) {
val elementMembers = processingEnv.elementUtils.getAllMembers((data.element.asType() as DeclaredType).asElement() as TypeElement)
val elementMethods = ElementFilter.fieldsIn(elementMembers)
elementMethods?.forEach {
elementMethods?.forEach { it ->
if (it.getAnnotation(Analytics::class.java) != null) {
val annotation = it.getAnnotation(Analytics::class.java)
val embedded = data.type as? AnalyticsEmbedded?
var value = annotation.value
if (embedded?.override?.isNotEmpty() == true) {
val overrides = embedded.override.split(",")
overrides.forEach {
if (it.split(":")[0].trim() == annotation.value.trim()) {
value = it.split(":")[1].trim()
}
}
}
val newData = FieldData(
data.element,
data.pkgName,
data.className,
data.name + "?." + it.simpleName.toString(),
it.getAnnotation(Analytics::class.java)
data.reference + "?." + it.simpleName.toString(),
annotation,
value
)
typeHelper?.addStatement(newData)
} else if (it.getAnnotation(AnalyticsEmbedded::class.java) != null) {
val newData = FieldData(
it,
data.pkgName,
data.className,
data.reference + "?." + it.simpleName.toString(),
it.getAnnotation(AnalyticsEmbedded::class.java)
)
typeHelper.addStatement(newData)
parseEmbedded(typeHelper, processingEnv, newData)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,37 @@ object ElementUtils {
*
* @return [FieldData] that contains the annotation values and other information about element.
*/
fun parseAnnotationData(processingEnv: ProcessingEnvironment, element: Element, type: Annotation): FieldData {
fun parseAnalyticsAnnotationData(processingEnv: ProcessingEnvironment, element: Element, type: Annotation): FieldData {
val enclosingElement = element.enclosingElement as TypeElement
val pkg = processingEnv.elementUtils.getPackageOf(element).toString()
return FieldData(
element,
pkg,
enclosingElement.qualifiedName.toString().substring(pkg.length + 1),
element.simpleName.toString(),
type
type,
(type as Analytics).value
)
}

/**
* @param processingEnv for use processing utils like messager for log errors or warnings
* or use filer to create kotlin or java files etc...
* @param element The element that annotated with [Analytics] or [AnalyticsEmbedded]
* @param type [AnalyticsEmbedded] or [Analytics]
*
* @return [FieldData] that contains the annotation values and other information about element.
*/
fun parseAnalyticsEmbeddedAnnotationData(processingEnv: ProcessingEnvironment, element: Element, type: Annotation): FieldData {
val enclosingElement = element.enclosingElement as TypeElement
val pkg = processingEnv.elementUtils.getPackageOf(element).toString()
return FieldData(
element,
pkg,
enclosingElement.qualifiedName.toString().substring(pkg.length + 1),
element.simpleName.toString(),
type,
""
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.winfooz.winanalytics.compiler
object MethodsUtil {

/**
* For get method name + Event suffix based on [methodName]
* For get method reference + Event suffix based on [methodName]
*/
fun getAnalyticsMethod(methodName: String): String {
return methodName[0].toLowerCase() + methodName.substring(1) + "Event"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.winfooz.winanalytics.compiler

/**
* Project: WinAnalytics
* Created: November 09, 2018
*
* @author Mohamed Hamdan
*/
fun String.toCamelCase(): String {
val parts = split(" ")
var camelCaseString = ""
parts.forEach {
camelCaseString += it.toProperCase()
}
camelCaseString = "${camelCaseString.first().toLowerCase()}${camelCaseString.substring(1)}"
return camelCaseString
}

fun String.toProperCase(): String = "${substring(0, 1).toUpperCase()}${substring(1).toLowerCase()}"
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,17 @@ class FieldData(
var className: String,

/**
* Class field name that annotated with [Analytics] annotation
* Class field reference that annotated with [Analytics] annotation
*/
var name: String,
var reference: String,

/**
* The [Analytics] annotation for read [Event] annotations.
*/
var type: Annotation
var type: Annotation,

/**
* For custom field name.
*/
var eventName: String = ""
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import javax.annotation.processing.ProcessingEnvironment
* or use filer to create kotlin or java files etc...
* @param pkg The package that contains class which has class fields annotated with [Analytics]
* annotation for generate class in the same package.
* @param className The class name for generate analytics class with same name + "_Analytics" suffix
* @param className The class reference for generate analytics class with same reference + "_Analytics" suffix
* @param configuration This configuration for generate methods based on configurations.
* @param configurationType This type for generate class with name "Analytics" contains every
* @param configurationType This type for generate class with reference "Analytics" contains every
* object of analytics classes.
*/
class AnalyticsType(
Expand All @@ -31,7 +31,7 @@ class AnalyticsType(
/**
* Methods for generate every value method with multiple client support.
*/
private val methods = mutableMapOf<String, MutableList<String>>()
private val methods = mutableMapOf<String, Pair<String, MutableList<Pair<String, String>>>>()

/**
* For add current analytics class object to Analytics class to use from everywhere
Expand Down Expand Up @@ -70,11 +70,11 @@ class AnalyticsType(
* @see Configuration.any
*/
override fun addStatement(data: FieldData) {
(data.type as Analytics).value.forEach {
addAnalyticsMethod(configuration.any(), it.value.toLowerCase(), data)
addAnalyticsMethod(configuration.firebaseEnabled, it.value.toLowerCase() + FIREBASE_PREFIX, data)
addAnalyticsMethod(configuration.fabricEnabled, it.value.toLowerCase() + FABRIC_PREFIX, data)
addAnalyticsMethod(configuration.mixPanelEnabled, it.value.toLowerCase() + MIXPANEL_PREFIX, data)
(data.type as Analytics).events.forEach {
addAnalyticsMethod(configuration.any(), it.value, it.value.toCamelCase(), data)
addAnalyticsMethod(configuration.firebaseEnabled, it.value, it.value.toCamelCase() + FIREBASE_PREFIX, data)
addAnalyticsMethod(configuration.fabricEnabled, it.value, it.value.toCamelCase() + FABRIC_PREFIX, data)
addAnalyticsMethod(configuration.mixPanelEnabled, it.value, it.value.toCamelCase() + MIXPANEL_PREFIX, data)
}
}

Expand Down Expand Up @@ -102,15 +102,15 @@ class AnalyticsType(
* object.
*
* @param enabled for check if clients enabled or not.
* @param name the method name
* @param name the method reference
* @param data the class field for add to log value.
*/
private fun addAnalyticsMethod(enabled: Boolean, name: String, data: FieldData) {
private fun addAnalyticsMethod(enabled: Boolean, event: String, name: String, data: FieldData) {
if (enabled) {
if (methods[name] == null) {
methods[name] = mutableListOf(data.name)
methods[name] = Pair(event, mutableListOf(Pair(data.eventName, data.reference)))
} else {
methods[name]?.add(data.name)
methods[name]?.second?.add(Pair(data.eventName, data.reference))
}
}
}
Expand All @@ -119,7 +119,7 @@ class AnalyticsType(
* For add parameter to function based on [add] param
*
* @param add for check if can add parameter on not based on [configuration].
* @param name the parameter name.
* @param name the parameter reference.
* @param className the parameter value.
*
* @see FunSpec.Builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import java.io.File
import javax.annotation.processing.ProcessingEnvironment

/**
* This type for generate class with name "Analytics" contains every object of analytics classes.
* This type for generate class with reference "Analytics" contains every object of analytics classes.
*
* @param processingEnv for use processing utils like messager for log errors or warnings
* or use filer to create kotlin or java files etc...
Expand Down
15 changes: 0 additions & 15 deletions sample/src/main/java/com/winfooz/winanalytics/sample/Address.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,34 +1,18 @@
package com.winfooz.winanalytics.sample

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import com.winfooz.winanalytics.annotations.AnalyticsEmbedded

class MainActivity : AppCompatActivity() {

var user: User? = null

@AnalyticsEmbedded
var userEmbedded: User? = null

@AnalyticsEmbedded
var addressEmbedded: Address? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val address = Address("Address", "11.25454654", "10,45454654664")
addressEmbedded = Address("Address", "11.25454654", "10,45454654664")

user = User("Name", "email@email.com", "123456789", 0, address)
userEmbedded = User("Name", "email@email.com", "123456789", 0, address)

findViewById<View>(R.id.hello_world).setOnClickListener(this::onHelloWorldClicked)
}

private fun onHelloWorldClicked(view: View) {
Example.getInstance(applicationContext).mainActivityAnalytics.loginEvent(this)
}
}
Loading

0 comments on commit 7fa4e99

Please sign in to comment.