From 5871d7a463c1c1ce4b3f1eb8f589e6c57dbfe5b3 Mon Sep 17 00:00:00 2001 From: shaishavgandhi05 Date: Sun, 2 Sep 2018 15:02:38 -0700 Subject: [PATCH 1/4] Reflect the annotations declared in constructor params Signed-off-by: shaishavgandhi05 --- .../java/com/airbnb/epoxy/PoetExtensions.kt | 5 +++++ .../epoxy/kotlinsample/AnnotationModel.kt | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 kotlinsample/src/test/java/com/airbnb/epoxy/kotlinsample/AnnotationModel.kt diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt b/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt index 8de7c4be16..c281a57042 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt @@ -13,6 +13,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 @@ -197,6 +198,10 @@ fun JavaParameterSpec.toKPoet(): KotlinParameterSpec { if (isLambda(type)) { addModifiers(KModifier.NOINLINE) } + // Add annotations associated with the parameter. + annotations.map { annotation -> + addAnnotation(KotlinClassName.bestGuess(annotation.type.toString())) + } }.build() } diff --git a/kotlinsample/src/test/java/com/airbnb/epoxy/kotlinsample/AnnotationModel.kt b/kotlinsample/src/test/java/com/airbnb/epoxy/kotlinsample/AnnotationModel.kt new file mode 100644 index 0000000000..e525c84069 --- /dev/null +++ b/kotlinsample/src/test/java/com/airbnb/epoxy/kotlinsample/AnnotationModel.kt @@ -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() { + + @EpoxyAttribute @DrawableRes var drawable: Int? = null +} + +class AnnotationHolder: EpoxyHolder() { + override fun bindView(itemView: View) { + + } + +} From b92c1a14bcf0841ad1e27938a9196c03d51d6f43 Mon Sep 17 00:00:00 2001 From: shaishavgandhi05 Date: Mon, 3 Sep 2018 10:20:09 -0700 Subject: [PATCH 2/4] Add converter as KPoet Extension Signed-off-by: shaishavgandhi05 --- .../src/main/java/com/airbnb/epoxy/PoetExtensions.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt b/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt index c281a57042..24cc7edc2f 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt @@ -103,6 +103,11 @@ private fun JavaClassName.getSimpleNamesInKotlin(): List { return originalNames } +fun JavaAnnotationSpec.toKPoet(): KotlinAnnotationSpec { + val annotationClass = KotlinClassName.bestGuess(type.toString()) + return KotlinAnnotationSpec.builder(annotationClass).build() +} + fun JavaClassName.setPackage(packageName: String) = JavaClassName.get(packageName, simpleName(), *simpleNames().drop(1).toTypedArray())!! @@ -190,6 +195,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), @@ -198,10 +205,7 @@ fun JavaParameterSpec.toKPoet(): KotlinParameterSpec { if (isLambda(type)) { addModifiers(KModifier.NOINLINE) } - // Add annotations associated with the parameter. - annotations.map { annotation -> - addAnnotation(KotlinClassName.bestGuess(annotation.type.toString())) - } + addAnnotations(kotlinAnnotations) }.build() } From 72d16388f819a4c81816a11de21446dc6b82ab80 Mon Sep 17 00:00:00 2001 From: shaishavgandhi05 Date: Wed, 5 Sep 2018 11:41:00 -0700 Subject: [PATCH 3/4] Add tests for PoetExtensions Signed-off-by: shaishavgandhi05 --- epoxy-processor/build.gradle | 10 ++- .../java/com/airbnb/epoxy/PoetExtensions.kt | 3 + .../com/airbnb/epoxy/PoetExtensionsTest.kt | 79 +++++++++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 epoxy-processor/src/test/java/com/airbnb/epoxy/PoetExtensionsTest.kt diff --git a/epoxy-processor/build.gradle b/epoxy-processor/build.gradle index d96420ce62..b2d60a29aa 100755 --- a/epoxy-processor/build.gradle +++ b/epoxy-processor/build.gradle @@ -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 { diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt b/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt index 24cc7edc2f..82b576c074 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt @@ -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 @@ -103,6 +104,8 @@ private fun JavaClassName.getSimpleNamesInKotlin(): List { return originalNames } +// Does not support transferring complex annotations which +// have parameters and values associated with them. fun JavaAnnotationSpec.toKPoet(): KotlinAnnotationSpec { val annotationClass = KotlinClassName.bestGuess(type.toString()) return KotlinAnnotationSpec.builder(annotationClass).build() diff --git a/epoxy-processor/src/test/java/com/airbnb/epoxy/PoetExtensionsTest.kt b/epoxy-processor/src/test/java/com/airbnb/epoxy/PoetExtensionsTest.kt new file mode 100644 index 0000000000..87c255d597 --- /dev/null +++ b/epoxy-processor/src/test/java/com/airbnb/epoxy/PoetExtensionsTest.kt @@ -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 { + + @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", 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()) + } +} From 1c94a47f25858c3324a7e1f3dda5e2a0e66ae119 Mon Sep 17 00:00:00 2001 From: shaishavgandhi05 Date: Wed, 5 Sep 2018 13:10:47 -0700 Subject: [PATCH 4/4] Make KotlinAnnotationSpec nullable * Add test asserting null behavior Signed-off-by: shaishavgandhi05 --- .../java/com/airbnb/epoxy/PoetExtensions.kt | 10 ++++++++-- .../java/com/airbnb/epoxy/PoetExtensionsTest.kt | 17 ++++++++++++++++- .../epoxy/kotlinsample/AnnotationModel.kt | 6 +++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt b/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt index 82b576c074..5c99bec111 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/PoetExtensions.kt @@ -106,7 +106,13 @@ private fun JavaClassName.getSimpleNamesInKotlin(): List { // Does not support transferring complex annotations which // have parameters and values associated with them. -fun JavaAnnotationSpec.toKPoet(): KotlinAnnotationSpec { +fun JavaAnnotationSpec.toKPoet(): KotlinAnnotationSpec? { + // If the annotation has any members (params), then we + // return null since we don't yet support translating + // params from Java annotation to Kotlin annotation. + if (members.isNotEmpty()) { + return null + } val annotationClass = KotlinClassName.bestGuess(type.toString()) return KotlinAnnotationSpec.builder(annotationClass).build() } @@ -198,7 +204,7 @@ fun JavaParameterSpec.toKPoet(): KotlinParameterSpec { val nullable = annotations.any { (it.type as? JavaClassName)?.simpleName() == "Nullable" } - val kotlinAnnotations = annotations.map { it.toKPoet() } + val kotlinAnnotations: List = annotations.mapNotNull { it.toKPoet() } return KotlinParameterSpec.builder( paramName, diff --git a/epoxy-processor/src/test/java/com/airbnb/epoxy/PoetExtensionsTest.kt b/epoxy-processor/src/test/java/com/airbnb/epoxy/PoetExtensionsTest.kt index 87c255d597..b2ad6cfb19 100644 --- a/epoxy-processor/src/test/java/com/airbnb/epoxy/PoetExtensionsTest.kt +++ b/epoxy-processor/src/test/java/com/airbnb/epoxy/PoetExtensionsTest.kt @@ -1,9 +1,12 @@ package com.airbnb.epoxy +import android.support.annotation.FloatRange import android.support.annotation.NonNull import com.squareup.kotlinpoet.asTypeName import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Test import javax.lang.model.element.Modifier @@ -16,7 +19,19 @@ class PoetExtensionsTest { val javaAnnotation = JavaAnnotationSpec.builder(annotation).build() val kotlinAnnotation = javaAnnotation.toKPoet() - assertEquals(type, kotlinAnnotation.type) + assertNotNull(kotlinAnnotation) + assertEquals(type, kotlinAnnotation?.type) + } + + @Test fun testAnnotationSpecToKPoetWithParams() { + val annotation = FloatRange::class.java + val javaAnnotation = JavaAnnotationSpec.builder(annotation) + .addMember("from", "0.0") + .addMember("to", "1.0") + .build() + + val kotlinAnnotation = javaAnnotation.toKPoet() + assertNull(kotlinAnnotation) } @Test fun testIsLambdaWithString() { diff --git a/kotlinsample/src/test/java/com/airbnb/epoxy/kotlinsample/AnnotationModel.kt b/kotlinsample/src/test/java/com/airbnb/epoxy/kotlinsample/AnnotationModel.kt index e525c84069..cd6745b371 100644 --- a/kotlinsample/src/test/java/com/airbnb/epoxy/kotlinsample/AnnotationModel.kt +++ b/kotlinsample/src/test/java/com/airbnb/epoxy/kotlinsample/AnnotationModel.kt @@ -1,6 +1,7 @@ package com.airbnb.epoxy.kotlinsample import android.support.annotation.DrawableRes +import android.support.annotation.FloatRange import android.support.annotation.StringRes import android.view.View import com.airbnb.epoxy.EpoxyAttribute @@ -9,7 +10,10 @@ 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() { +abstract class AnnotationModel( + @StringRes val resId: Int, + @FloatRange(from = 0.0, to = 1.0) val range: Float +): EpoxyModelWithHolder() { @EpoxyAttribute @DrawableRes var drawable: Int? = null }