Skip to content

Commit d349b7b

Browse files
committed
properly parse type parameters from mapping (#224)
1 parent 9882218 commit d349b7b

File tree

6 files changed

+127
-11
lines changed

6 files changed

+127
-11
lines changed

openapi-processor-core/src/main/antlr/Mapping.g4

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ annotationParameters
5959
;
6060

6161
annotationParameterUnnamed
62-
: (Identifier | Boolean | String | Number | QualifiedTypeClass)
62+
: (Identifier | Boolean | String | Number | QualifiedType | QualifiedTypeClass)
6363
;
6464

6565
annotationParameterNamed
66-
: Identifier '=' (Identifier | Boolean | String | Number | QualifiedTypeClass)
66+
: Identifier '=' (Identifier | Boolean | String | Number | QualifiedType | QualifiedTypeClass)
6767
;
6868

6969
qualifiedTargetType

openapi-processor-core/src/main/kotlin/io/openapiprocessor/core/converter/mapping/Annotation.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,24 @@ interface ParameterValue {
4848
val import: String?
4949
}
5050

51-
class SimpleParameterValue(override val value: String, override val import: String? = null)
52-
: ParameterValue {
51+
class SimpleParameterValue(override val value: String, override val import: String? = null): ParameterValue {
5352

5453
override fun toString(): String {
5554
return value
5655
}
5756
}
5857

59-
class ClassParameterValue(private val clazz: String)
60-
: ParameterValue {
58+
class TypeParameterValue(val type: String): ParameterValue {
59+
private val qualifiedType: QualifiedType = QualifiedType(type)
60+
61+
override val value: String = qualifiedType.type
62+
63+
override val import: String? = qualifiedType.import
64+
65+
override fun toString(): String = qualifiedType.toString()
66+
}
67+
68+
class ClassParameterValue(private val clazz: String): ParameterValue {
6169

6270
override val value: String
6371
get() = clazz.substring(import.substringBeforeLast('.').length + 1)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2025 https://github.com/openapi-processor/openapi-processor-base
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.openapiprocessor.core.converter.mapping
7+
8+
class QualifiedType(private val qualifiedType: String) {
9+
val import: String?
10+
val type: String
11+
12+
init {
13+
import = extractImport()
14+
type = extractType()
15+
}
16+
17+
private fun extractImport(): String? {
18+
val parts = qualifiedType.split(".")
19+
val index = parts.indexOfFirst { it.isNotEmpty() && it[0].isUpperCase() }
20+
if (index <= 0) {
21+
return null
22+
}
23+
24+
return parts.subList(0, index + 1).joinToString(".")
25+
}
26+
27+
private fun extractType(): String {
28+
val imp = import ?: return qualifiedType
29+
val last = imp.lastIndexOf(".")
30+
return qualifiedType.substring(last + 1)
31+
}
32+
33+
override fun toString(): String {
34+
return qualifiedType
35+
}
36+
}

openapi-processor-core/src/main/kotlin/io/openapiprocessor/core/processor/mapping/v2/parser/antlr/MappingExtractor.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package io.openapiprocessor.core.processor.mapping.v2.parser.antlr
88
import io.openapiprocessor.core.converter.mapping.ClassParameterValue
99
import io.openapiprocessor.core.converter.mapping.ParameterValue
1010
import io.openapiprocessor.core.converter.mapping.SimpleParameterValue
11+
import io.openapiprocessor.core.converter.mapping.TypeParameterValue
1112
import io.openapiprocessor.core.processor.mapping.v2.parser.Mapping
1213
import io.openapiprocessor.core.processor.mapping.v2.parser.MappingType
1314

@@ -137,11 +138,16 @@ class MappingExtractor: MappingBaseListener(), Mapping {
137138
val parameterName = ctx.getChild(0).text
138139
val parameterValue = ctx.getChild(2).text
139140

140-
val clazz = ctx.stop.type == MappingLexer.QualifiedTypeClass
141-
if (clazz) {
142-
annotationParameters[parameterName] = ClassParameterValue(parameterValue)
143-
} else {
144-
annotationParameters[parameterName] = SimpleParameterValue(parameterValue)
141+
when(ctx.stop.type) {
142+
MappingLexer.QualifiedType -> {
143+
annotationParameters[parameterName] = TypeParameterValue(parameterValue)
144+
}
145+
MappingLexer.QualifiedTypeClass -> {
146+
annotationParameters[parameterName] = ClassParameterValue(parameterValue)
147+
}
148+
else -> {
149+
annotationParameters[parameterName] = SimpleParameterValue(parameterValue)
150+
}
145151
}
146152
}
147153
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2025 https://github.com/openapi-processor/openapi-processor-base
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.openapiprocessor.core.converter.mapping
7+
8+
import io.kotest.core.spec.style.StringSpec
9+
import io.kotest.matchers.shouldBe
10+
11+
class QualifiedTypeSpec : StringSpec({
12+
13+
"extracts import & type from qualified class" {
14+
val type = QualifiedType("io.openapiprocessor.Something")
15+
16+
type.import shouldBe "io.openapiprocessor.Something"
17+
type.type shouldBe "Something"
18+
}
19+
20+
"extracts import & type from inner class" {
21+
val type = QualifiedType("io.openapiprocessor.Something.Inner")
22+
23+
type.import shouldBe "io.openapiprocessor.Something"
24+
type.type shouldBe "Something.Inner"
25+
}
26+
27+
"extracts import & type from inner enum value" {
28+
val type = QualifiedType("io.openapiprocessor.Something.Enum.VALUE")
29+
30+
type.import shouldBe "io.openapiprocessor.Something"
31+
type.type shouldBe "Something.Enum.VALUE"
32+
}
33+
})

openapi-processor-core/src/test/kotlin/io/openapiprocessor/core/processor/mapping/v2/AntlrParserSpec.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,4 +319,37 @@ class AntlrParserSpec: StringSpec({
319319
mapping.targetTypePrimitive.shouldBeTrue()
320320
mapping.targetTypePrimitiveArray.shouldBeFalse()
321321
}
322+
323+
"annotate source type with fully qualified java annotation and nested type parameter" {
324+
val source = """integer:year @ com.fasterxml.jackson.annotation.JsonFormat(shape = JsonFormat.Shape.NUMBER, pattern = "yyyy")"""
325+
326+
val mapping = parseMapping(source)
327+
mapping.kind shouldBe Mapping.Kind.ANNOTATE
328+
mapping.sourceType shouldBe "integer"
329+
mapping.sourceFormat shouldBe "year"
330+
mapping.targetType.shouldBeNull()
331+
mapping.targetGenericTypes.shouldBeEmpty()
332+
mapping.annotationType shouldBe "com.fasterxml.jackson.annotation.JsonFormat"
333+
val shape = mapping.annotationParameters["shape"]!!
334+
shape.value shouldBe "JsonFormat.Shape.NUMBER"
335+
shape.import.shouldBeNull()
336+
mapping.annotationParameters["pattern"]!!.value shouldBe """"yyyy""""
337+
}
338+
339+
"annotate source type with fully qualified java annotation and qualified type parameter" {
340+
val source = """integer:year @ com.fasterxml.jackson.annotation.JsonFormat(shape = com.fasterxml.jackson.annotation.JsonFormat.Shape.NUMBER, pattern = "yyyy")"""
341+
342+
val mapping = parseMapping(source)
343+
mapping.kind shouldBe Mapping.Kind.ANNOTATE
344+
mapping.sourceType shouldBe "integer"
345+
mapping.sourceFormat shouldBe "year"
346+
mapping.targetType.shouldBeNull()
347+
mapping.targetGenericTypes.shouldBeEmpty()
348+
mapping.annotationType shouldBe "com.fasterxml.jackson.annotation.JsonFormat"
349+
val shape = mapping.annotationParameters["shape"]!!
350+
shape.value shouldBe "JsonFormat.Shape.NUMBER"
351+
shape.import shouldBe "com.fasterxml.jackson.annotation.JsonFormat"
352+
mapping.annotationParameters["pattern"]!!.value shouldBe """"yyyy""""
353+
}
354+
322355
})

0 commit comments

Comments
 (0)