Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reflect the annotations declared in constructor params #519

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions epoxy-processor/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ for (File file : sdkHandler.sdkLoader.repositories) {
}

dependencies {
compile deps.kotlin
compile deps.squareJavaPoet
compile deps.squareKotlinPoet
implementation deps.kotlin
implementation deps.squareJavaPoet
implementation deps.squareKotlinPoet

/** Provides the sun javac tools for looking up the R class trees. */
compileOnly files(Jvm.current().getToolsJar())

compile project(':epoxy-annotations')
implementation project(':epoxy-annotations')

testImplementation rootProject.deps.junit
}

checkstyle {
Expand Down
12 changes: 12 additions & 0 deletions epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.airbnb.epoxy

import com.squareup.javapoet.CodeBlock
import com.squareup.javapoet.TypeName
import com.squareup.kotlinpoet.ANY
import com.squareup.kotlinpoet.BOOLEAN
Expand All @@ -13,6 +14,7 @@ import com.squareup.kotlinpoet.LONG
import com.squareup.kotlinpoet.SHORT
import com.squareup.kotlinpoet.UNIT
import javax.lang.model.element.Modifier
import kotlin.reflect.jvm.internal.impl.types.KotlinType

typealias JavaClassName = com.squareup.javapoet.ClassName
typealias JavaTypeName = com.squareup.javapoet.TypeName
Expand Down Expand Up @@ -102,6 +104,13 @@ private fun JavaClassName.getSimpleNamesInKotlin(): List<String> {
return originalNames
}

// Does not support transferring complex annotations which
// have parameters and values associated with them.
fun JavaAnnotationSpec.toKPoet(): KotlinAnnotationSpec {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this can be nullable, and we would return null if the JavaAnnotationSpec has a code block?

val annotationClass = KotlinClassName.bestGuess(type.toString())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't quite right because it won't capture parameter values in the annotation - so it will only work for basic annotations.

If you're interested it would be great to add that full functionality, otherwise can you add a note warning of this partial implementation?

Copy link
Contributor Author

@ShaishavGandhi ShaishavGandhi Sep 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Will update the extension to include the parameter values also.

I'm interested in also adding tests for the PoetExtensions file. Would it be best for adding tests in the epoxy-processor module itself?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shaishavgandhi05 great! yes, I think adding tests to epoxy-processor makes sense

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some investigation, this seems like something really hard to do. Parameters are added as CodeBlock's to the AnnotationSpec. There's no way to convert a JavaCodeBlock to KotlinCodeBlock since CodeBlock's have no getters to know what values they're going to emit.

return KotlinAnnotationSpec.builder(annotationClass).build()
}

fun JavaClassName.setPackage(packageName: String) =
JavaClassName.get(packageName, simpleName(), *simpleNames().drop(1).toTypedArray())!!

Expand Down Expand Up @@ -189,6 +198,8 @@ fun JavaParameterSpec.toKPoet(): KotlinParameterSpec {

val nullable = annotations.any { (it.type as? JavaClassName)?.simpleName() == "Nullable" }

val kotlinAnnotations = annotations.map { it.toKPoet() }

return KotlinParameterSpec.builder(
paramName,
type.toKPoet(nullable),
Expand All @@ -197,6 +208,7 @@ fun JavaParameterSpec.toKPoet(): KotlinParameterSpec {
if (isLambda(type)) {
addModifiers(KModifier.NOINLINE)
}
addAnnotations(kotlinAnnotations)
}.build()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.airbnb.epoxy

import android.support.annotation.NonNull
import com.squareup.kotlinpoet.asTypeName
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import javax.lang.model.element.Modifier

class PoetExtensionsTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for the tests!


@Test fun testAnnotationSpecToKPoet() {
val annotation = EpoxyModelClass::class.java
val type = annotation.asTypeName()
val javaAnnotation = JavaAnnotationSpec.builder(annotation).build()

val kotlinAnnotation = javaAnnotation.toKPoet()
assertEquals(type, kotlinAnnotation.type)
}

@Test fun testIsLambdaWithString() {
val stringType = JavaClassName.bestGuess("java.lang.String")
assertFalse(isLambda(stringType))
}

@Test fun testIsLambdaWithLambda() {
val lambdaType = JavaClassName.bestGuess("kotlin.Function2")
assertTrue(isLambda(lambdaType))
}

@Test fun testJavaParameterSpecToKPoet() {
val name = "android"
val javaParameter = JavaParameterSpec.builder(JavaClassName.bestGuess("java.lang.String"), name, Modifier.PRIVATE)
.addAnnotation(NonNull::class.java)
.build()
val kotlinString = KotlinClassName("kotlin", "String")

val kotlinParameter = javaParameter.toKPoet()
assertEquals(name, kotlinParameter.name)
assertEquals(kotlinString, kotlinParameter.type)
assertEquals(javaParameter.annotations.size, kotlinParameter.annotations.size)
assertEquals(NonNull::class.java.asTypeName(), kotlinParameter.annotations[0].type)
}

@Test fun testJavaTypeNameToKPoet() {
val javaType = JavaParametrizedTypeName.get(JavaClassName.bestGuess("java.util.List"),
JavaClassName.bestGuess("java.lang.String"))

val kotlinType = javaType.toKPoet()
val kotlinList = KotlinClassName("kotlin.collections", "List")
val kotlinString = KotlinClassName("kotlin", "String")
assertEquals(kotlinList, kotlinType.rawType)
assertEquals(kotlinString, kotlinType.typeArguments[0])
}

@Test fun testJavaArrayTypeNameToKPoet() {
val javaIntArray = JavaArrayTypeName.of(JavaClassName.INT)
val kotlinIntArray = javaIntArray.toKPoet()

assertEquals("kotlin.IntArray", kotlinIntArray.toString())

val javaFloatArray = JavaArrayTypeName.of(JavaClassName.bestGuess("java.lang.Float"))
val kotlinFloatArray = javaFloatArray.toKPoet()

assertEquals("kotlin.Array<kotlin.Float>", kotlinFloatArray.toString())
}

@Test fun testJavaClassNameToKPoet() {
val javaClassName = JavaClassName.bestGuess("java.lang.Integer")
val kotlinClassName = javaClassName.toKPoet()

val javaByteName = JavaClassName.BYTE
val kotlinByteName = javaByteName.toKPoet()

assertEquals("kotlin.Int", kotlinClassName.toString())
assertEquals("kotlin.Byte", kotlinByteName.toString())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.airbnb.epoxy.kotlinsample

import android.support.annotation.DrawableRes
import android.support.annotation.StringRes
import android.view.View
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyHolder
import com.airbnb.epoxy.EpoxyModelClass
import com.airbnb.epoxy.EpoxyModelWithHolder

@EpoxyModelClass(layout = R.layout.activity_kotlin_sample)
abstract class AnnotationModel(@StringRes val resId: Int): EpoxyModelWithHolder<AnnotationHolder>() {

@EpoxyAttribute @DrawableRes var drawable: Int? = null
}

class AnnotationHolder: EpoxyHolder() {
override fun bindView(itemView: View) {

}

}