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

Handle generic classes when generating member injectors #30

Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.STAR
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.ksp.addOriginatingKSFile
Expand Down Expand Up @@ -60,6 +62,7 @@ internal class MemberInjectorGenerator(
}
}

private val parameterizedSourceClass: TypeName = sourceClass.maybeParameterizedClassName()
val sourceClassName: ClassName = sourceClass.toClassName()
val generatedClassName: ClassName = sourceClassName.memberInjectorClassName

Expand All @@ -70,7 +73,7 @@ internal class MemberInjectorGenerator(
.addOriginatingKSFile(sourceClass.containingFile!!)
.addModifiers(sourceClass.getVisibility().toKModifier() ?: KModifier.PUBLIC)
.addSuperinterface(
MemberInjector::class.asClassName().parameterizedBy(sourceClassName)
MemberInjector::class.asClassName().parameterizedBy(parameterizedSourceClass)
)
.addAnnotation(
AnnotationSpec.builder(Suppress::class)
Expand All @@ -85,6 +88,14 @@ internal class MemberInjectorGenerator(
)
}

private fun KSClassDeclaration.maybeParameterizedClassName(): TypeName {
return if (this.typeParameters.isEmpty()) {
toClassName()
} else {
toClassName().parameterizedBy(this.typeParameters.map { STAR })
}
}

private fun TypeSpec.Builder.emitSuperMemberInjectorFieldIfNeeded(): TypeSpec.Builder = apply {
if (superClassThatNeedsInjection == null) {
return this
Expand All @@ -111,7 +122,7 @@ internal class MemberInjectorGenerator(
addFunction(
FunSpec.builder("inject")
.addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
.addParameter("target", sourceClassName)
.addParameter("target", parameterizedSourceClass)
.addParameter("scope", Scope::class)
.apply {
if (superClassThatNeedsInjection != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1605,4 +1605,136 @@ class FieldMemberInjectorTest {
"Class test.TestFieldInjection has invalid property: invalid"
)
}

@Test
fun testSimpleFieldInjectionInParameterizedClass_java() {
val source = javaSource(
"TestFieldInjection",
"""
package test;
import javax.inject.Inject;
public class TestFieldInjection<T> {
@Inject Foo foo;
public TestFieldInjection() {}
}
class Foo {}
"""
)

compilationAssert()
.that(source)
.processedWith(MemberInjectorProcessorProvider())
.compilesWithoutError()
.generatesSources(testSimpleFieldInjectionParameterizedClass_expected)
}

@Test
fun testSimpleFieldInjectionInParameterizedClass_kotlin() {
val source = ktSource(
"TestFieldInjection",
"""
package test
import javax.inject.Inject
class TestFieldInjection<T> {
@Inject lateinit var foo: Foo
}
class Foo
"""
)

compilationAssert()
.that(source)
.processedWith(MemberInjectorProcessorProvider())
.compilesWithoutError()
.generatesSources(testSimpleFieldInjectionParameterizedClass_expected)
}

private val testSimpleFieldInjectionParameterizedClass_expected = expectedKtSource(
"test/TestFieldInjection__MemberInjector",
"""
package test

import kotlin.Suppress
import toothpick.MemberInjector
import toothpick.Scope

@Suppress(
"ClassName",
"RedundantVisibilityModifier",
"UNCHECKED_CAST",
)
public class TestFieldInjection__MemberInjector : MemberInjector<TestFieldInjection<*>> {
public override fun inject(target: TestFieldInjection<*>, scope: Scope) {
target.foo = scope.getInstance(Foo::class.java) as Foo
}
}
"""
)

@Test
fun testSimpleFieldInjectionInMultipleParameterizedClass_java() {
val source = javaSource(
"TestFieldInjection",
"""
package test;
import java.io.Reader;
import javax.inject.Inject;
public class TestFieldInjection<T, U extends StringBuilder, V extends Reader> {
@Inject Foo foo;
public TestFieldInjection() {}
}
class Foo {}
"""
)

compilationAssert()
.that(source)
.processedWith(MemberInjectorProcessorProvider())
.compilesWithoutError()
.generatesSources(testSimpleFieldInjectionMultipleParameterizedClass_expected)
}

@Test
fun testSimpleFieldInjectionInMultipleParameterizedClass_kotlin() {
val source = ktSource(
"TestFieldInjection",
"""
package test
import java.io.Reader
import javax.inject.Inject
class TestFieldInjection<T, U: StringBuilder, V: java.io.Reader> {
@Inject lateinit var foo: Foo
}
class Foo
"""
)

compilationAssert()
.that(source)
.processedWith(MemberInjectorProcessorProvider())
.compilesWithoutError()
.generatesSources(testSimpleFieldInjectionMultipleParameterizedClass_expected)
}

private val testSimpleFieldInjectionMultipleParameterizedClass_expected = expectedKtSource(
"test/TestFieldInjection__MemberInjector",
"""
package test

import kotlin.Suppress
import toothpick.MemberInjector
import toothpick.Scope

@Suppress(
"ClassName",
"RedundantVisibilityModifier",
"UNCHECKED_CAST",
)
public class TestFieldInjection__MemberInjector : MemberInjector<TestFieldInjection<*, *, *>> {
public override fun inject(target: TestFieldInjection<*, *, *>, scope: Scope) {
target.foo = scope.getInstance(Foo::class.java) as Foo
}
}
"""
)
}
Loading