quarkus icon indicating copy to clipboard operation
quarkus copied to clipboard

Support for Kotlin collections in type-safe Qute templates

Open mthmulders opened this issue 1 year ago • 8 comments

Description

For the time being, I'm annotating my Kotlin data classes with @JvmRecord (as suggested in #38194). My template takes a list of items, so I've declared the data class with val items: Collection<DataClass> and added {@kotlin.collections.Collection<my.custom.DataClass> items} to the template file.

Further down, my template looks like this

    {#for i in items}
        {i.whatever
    {/}

I would expect to see the whatever property for all items printed out, but instead, Quarkus shows an error message

io.quarkus.qute.TemplateException: Class [kotlin.collections.Collection] used in the parameter declaration in template [email/detail.html] on line 7 was not found in the application index. Make sure it is spelled correctly.

Implementation ideas

No response

mthmulders avatar Jan 28 '24 16:01 mthmulders

/cc @geoand (kotlin), @mkouba (qute)

quarkus-bot[bot] avatar Jan 28 '24 16:01 quarkus-bot[bot]

Forgot to mention, but I feel it might be relevant: if my template takes in a data class that holds a Kotlin Collection (i.e., something like data class MyItems(val items: Collection<Item>), I'm not seeing the error page, and iterating over the collection works just as you'd expect.

mthmulders avatar Jan 29 '24 07:01 mthmulders

Could you pls provide a small reproducer app?

mkouba avatar Jan 29 '24 08:01 mkouba

Could you pls provide a small reproducer app?

Yep, but not this week, maybe next week

mthmulders avatar Jan 30 '24 21:01 mthmulders

Thanks for your patience, @mkouba. I tried to reproduce in a separate project, and it seems the issue may have been caused by the fact that my TemplateInstance subclass (a.k.a. the Kotlin Data Class with @JvmRecord on it) and the template file itself had type parameters.

In other words, I had this

@CheckedTemplate
@JvmRecord
data class DemoPage(
    val items: Collection<DataClass>
): TemplateInstance

and this

{@kotlin.collections.Collection<my.custom.DataClass> items}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Quarkus issue #38440</title>
</head>
<body>
<ul>
    {#for item in items}
        <li>{item.whatever}</li>
    {/}
</ul>
</body>
</html>

I guess you normally don't need to have both, right? Anyway, when I stripped the {@kotlin.collections.Collection<my.custom.DataClass> items} from the HTML file, it worked just fine.

mthmulders avatar Feb 12 '24 21:02 mthmulders

I guess you normally don't need to have both, right?

Exactly. The template param declarations are extracted from the record params. However, items: Collection<DataClass> should be transfomed to {@kotlin.collections.Collection<my.custom.DataClass> items} unless there's some kotlin magic happening under the hood. Maybe if you look at the compiled DemoPage you'll see a different type for the items.

Anyway, when I stripped the {@kotlin.collections.Collection<my.custom.DataClass> items} from the HTML file, it worked just fine.

:+1:

mkouba avatar Feb 13 '24 08:02 mkouba

Maybe if you look at the compiled DemoPage you'll see a different type for the items.

I've inspected that class using the IntelliJ Kotlin bytecode viewer:

  // access flags 0x12
  // signature Ljava/util/Collection<Lit/mulders/quarkus/issue38440/DataClass;>;
  // declaration: items extends java.util.Collection<it.mulders.quarkus.issue38440.DataClass>
  private final Ljava/util/Collection; items

Interesting detail here is that although the Kotlin code does not import java.util.Collection. Thus, by definition, using Collection refers to kotlin.collections.Collection, right? But the bytecode seems to be different nevertheless...

mthmulders avatar Feb 13 '24 09:02 mthmulders

Maybe if you look at the compiled DemoPage you'll see a different type for the items.

I've inspected that class using the IntelliJ Kotlin bytecode viewer:

  // access flags 0x12
  // signature Ljava/util/Collection<Lit/mulders/quarkus/issue38440/DataClass;>;
  // declaration: items extends java.util.Collection<it.mulders.quarkus.issue38440.DataClass>
  private final Ljava/util/Collection; items

Interesting detail here is that although the Kotlin code does not import java.util.Collection. Thus, by definition, using Collection refers to kotlin.collections.Collection, right? But the bytecode seems to be different nevertheless...

Yeah, it seems that the kotlin Collection is transformed to a JDK Collection during compilation...

mkouba avatar Feb 15 '24 13:02 mkouba