Skip to content

Commit

Permalink
feat: handle generic classes when generating member injectors (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
arctouch-ianribas authored Aug 22, 2024
1 parent 7e3d650 commit 787d138
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 2 deletions.
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
}
}
"""
)
}

0 comments on commit 787d138

Please sign in to comment.