Skip to content
This repository was archived by the owner on Mar 16, 2025. It is now read-only.

Commit b1bdd46

Browse files
committed
improve bean validation generation, openapi-processor/openapi-processor-spring#132
1 parent 04daba4 commit b1bdd46

14 files changed

Lines changed: 396 additions & 82 deletions

File tree

src/main/kotlin/io/openapiprocessor/core/model/datatypes/ArrayDataType.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ class ArrayDataType(
2222
return "${item.getTypeName()}[]"
2323
}
2424

25+
override fun getTypeName(annotations: Set<String>, itemAnnotations: Set<String>): String {
26+
val sb = StringBuilder()
27+
28+
if (annotations.isNotEmpty()) {
29+
sb.append(annotations.joinToString(" ", "", " "))
30+
}
31+
sb.append(getTypeName())
32+
33+
return sb.toString()
34+
}
35+
2536
override fun getPackageName(): String {
2637
return item.getPackageName()
2738
}

src/main/kotlin/io/openapiprocessor/core/model/datatypes/CollectionDataType.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ package io.openapiprocessor.core.model.datatypes
77

88
interface CollectionDataType {
99
val item: DataType
10+
11+
fun getTypeName(annotations: Set<String>, itemAnnotations: Set<String>): String
1012
}

src/main/kotlin/io/openapiprocessor/core/model/datatypes/MappedCollectionDataType.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,20 @@ open class MappedCollectionDataType(
2525
return "${name}<${item.getTypeName()}>"
2626
}
2727

28-
fun getTypeNameWithAnnotatedItem(annotation: String): String {
29-
return "${name}<$annotation ${item.getTypeName()}>"
28+
override fun getTypeName(annotations: Set<String>, itemAnnotations: Set<String>): String {
29+
val sb = StringBuilder()
30+
31+
if (annotations.isNotEmpty()) {
32+
sb.append(annotations.joinToString(" ", "", " "))
33+
}
34+
sb.append("$name<")
35+
36+
if (itemAnnotations.isNotEmpty()) {
37+
sb.append(itemAnnotations.joinToString(" ", "", " "))
38+
}
39+
sb.append("${item.getTypeName()}>")
40+
41+
return sb.toString()
3042
}
3143

3244
override fun getPackageName(): String {

src/main/kotlin/io/openapiprocessor/core/writer/java/BeanValidationFactory.kt

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,24 @@ open class BeanValidationFactory {
1717
* override to add annotations to the model object class.
1818
*/
1919
open fun validate(dataType: ModelDataType): BeanValidationInfo {
20-
return BeanValidationInfoObject(dataType, emptySet(), emptyList())
20+
return BeanValidationInfoProperty(dataType, emptySet(), emptyList())
2121
}
2222

2323
fun validate(dataType: DataType, required: Boolean = false): BeanValidationInfo {
24-
return BeanValidationInfoProperty(
25-
dataType,
26-
collectImports(dataType, required),
27-
collectAnnotations(dataType, required)
28-
)
24+
return if (dataType is CollectionDataType) {
25+
BeanValidationInfoCollection(
26+
dataType,
27+
collectImports(dataType, required),
28+
collectAnnotations(dataType, required),
29+
validate(dataType.item, false)
30+
)
31+
} else {
32+
BeanValidationInfoProperty(
33+
dataType,
34+
collectImports(dataType, required),
35+
collectAnnotations(dataType, required)
36+
)
37+
}
2938
}
3039

3140
private fun collectImports(dataType: DataType, required: Boolean = false): Set<String> {
@@ -58,6 +67,7 @@ open class BeanValidationFactory {
5867
return imports
5968
}
6069

70+
// uses list to keep order
6171
private fun collectAnnotations(dataType: DataType, required: Boolean = false): List<String> {
6272
val annotations = mutableListOf<String>()
6373

src/main/kotlin/io/openapiprocessor/core/writer/java/BeanValidationInfo.kt

Lines changed: 114 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,140 @@
55

66
package io.openapiprocessor.core.writer.java
77

8-
import io.openapiprocessor.core.model.datatypes.DataType
9-
import io.openapiprocessor.core.model.datatypes.MappedCollectionDataType
10-
import io.openapiprocessor.core.model.datatypes.ModelDataType
8+
import io.openapiprocessor.core.model.datatypes.*
9+
10+
data class BeanValidationValue(
11+
val dataTypeValue: String,
12+
val imports: Set<String>,
13+
val annotations: List<String>)
1114

1215
interface BeanValidationInfo {
1316
val dataType: DataType
1417
val imports: Set<String>
1518
val annotations: List<String>
1619

17-
val typeName: String
18-
get() {
19-
return dataType.getTypeName()
20-
}
21-
22-
val hasAnnotations
23-
get() = annotations.isNotEmpty()
24-
20+
val prop: BeanValidationValue
21+
val inout: BeanValidationValue
2522
}
2623

27-
class BeanValidationInfoObject(
24+
// BeanValidationInfoSimple
25+
class BeanValidationInfoProperty(
2826
override val dataType: DataType,
2927
override val imports: Set<String>,
3028
override val annotations: List<String>
31-
): BeanValidationInfo
29+
): BeanValidationInfo {
3230

31+
override val prop: BeanValidationValue
32+
get() {
33+
return BeanValidationValue(dataType.getTypeName(), imports, annotations)
34+
}
3335

34-
class BeanValidationInfoProperty(
36+
override val inout: BeanValidationValue
37+
get() {
38+
val dt = mutableListOf<String>()
39+
dt.addAll(annotations)
40+
dt.add(dataType.getTypeName())
41+
return BeanValidationValue(dt.joinToString(" "), imports, emptyList())
42+
}
43+
}
44+
45+
class BeanValidationInfoCollection(
3546
override val dataType: DataType,
3647
override val imports: Set<String>,
37-
override val annotations: List<String>
48+
override val annotations: List<String>,
49+
val item: BeanValidationInfo
3850
): BeanValidationInfo {
3951

40-
override val typeName: String
52+
override val prop: BeanValidationValue
4153
get() {
42-
// no collection or array
43-
if (dataType !is MappedCollectionDataType)
44-
return dataType.getTypeName()
54+
dataType as CollectionDataType
55+
56+
return if (item.dataType is ModelDataType) {
57+
val allImports = mutableSetOf<String>()
58+
allImports.addAll(imports)
59+
if (dataType !is ArrayDataType) {
60+
allImports.add(BeanValidation.VALID.import)
61+
}
62+
63+
val colAnnotations = mutableListOf<String>()
64+
colAnnotations.addAll(annotations)
65+
66+
val itemAnnotations = mutableSetOf<String>()
67+
if (dataType !is ArrayDataType) {
68+
itemAnnotations.add(BeanValidation.VALID.annotation)
69+
}
70+
71+
BeanValidationValue(
72+
dataType.getTypeName(emptySet(), itemAnnotations),
73+
allImports,
74+
colAnnotations
75+
)
76+
} else {
77+
val allImports = mutableSetOf<String>()
78+
allImports.addAll(imports)
79+
if (dataType !is ArrayDataType) {
80+
allImports.addAll(item.imports)
81+
}
82+
83+
val colAnnotations = mutableListOf<String>()
84+
colAnnotations.addAll(annotations)
85+
86+
val itemAnnotations = mutableSetOf<String>()
87+
if (dataType !is ArrayDataType) {
88+
itemAnnotations.addAll(item.annotations)
89+
}
90+
91+
BeanValidationValue(
92+
dataType.getTypeName(emptySet(), itemAnnotations),
93+
allImports,
94+
colAnnotations
95+
)
96+
}
97+
}
98+
99+
override val inout: BeanValidationValue
100+
get() {
101+
val cdt = dataType as CollectionDataType
102+
103+
return if (item.dataType is ModelDataType) {
104+
val allImports = mutableSetOf<String>()
105+
allImports.addAll(imports)
106+
if (dataType !is ArrayDataType) {
107+
allImports.add(BeanValidation.VALID.import)
108+
}
109+
110+
val colAnnotations = mutableSetOf<String>()
111+
colAnnotations.addAll(annotations)
112+
113+
val itemAnnotations = mutableSetOf<String>()
114+
if (dataType !is ArrayDataType) {
115+
itemAnnotations.add(BeanValidation.VALID.annotation)
116+
}
117+
118+
BeanValidationValue(
119+
cdt.getTypeName(colAnnotations, itemAnnotations),
120+
allImports,
121+
emptyList())
122+
} else {
123+
val allImports = mutableSetOf<String>()
124+
allImports.addAll(imports)
125+
if (dataType !is ArrayDataType) {
126+
allImports.addAll(item.imports)
127+
}
128+
129+
val colAnnotations = mutableSetOf<String>()
130+
colAnnotations.addAll(annotations)
45131

46-
if (dataType.item !is ModelDataType)
47-
return dataType.getTypeName()
132+
val itemAnnotations = mutableSetOf<String>()
133+
if (dataType !is ArrayDataType) {
134+
itemAnnotations.addAll(item.annotations)
135+
}
48136

49-
return dataType.getTypeNameWithAnnotatedItem(BeanValidation.VALID.annotation)
137+
BeanValidationValue(
138+
cdt.getTypeName(colAnnotations, itemAnnotations),
139+
allImports,
140+
emptyList())
141+
}
50142
}
51143

52144
}

src/main/kotlin/io/openapiprocessor/core/writer/java/DataTypeWriter.kt

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@ class DataTypeWriter(
4747

4848
if (apiOptions.beanValidation) {
4949
val objectInfo = validationAnnotations.validate(dataType)
50-
if(objectInfo.hasAnnotations) {
51-
objectInfo.annotations.forEach {
52-
target.write("$it\n")
53-
}
50+
objectInfo.annotations.forEach {
51+
target.write("$it\n")
5452
}
5553
}
5654

@@ -86,10 +84,11 @@ class DataTypeWriter(
8684
var propTypeName = propDataType.getTypeName()
8785
if(apiOptions.beanValidation) {
8886
val info = validationAnnotations.validate(propDataType, required)
89-
if (info.hasAnnotations) {
90-
result += " ${info.annotations.joinToString(" ")}\n"
87+
val prop = info.prop
88+
prop.annotations.forEach {
89+
result += " ${it}\n"
9190
}
92-
propTypeName = info.typeName
91+
propTypeName = prop.dataTypeValue
9392
}
9493

9594
result += " @JsonProperty(\"$propertyName\")\n"
@@ -152,16 +151,16 @@ class DataTypeWriter(
152151
imports.addAll(dataType.referencedImports)
153152

154153
if (apiOptions.beanValidation) {
155-
val objectInfo = validationAnnotations.validate(dataType)
156-
if (objectInfo.hasAnnotations) {
157-
imports.addAll(objectInfo.imports)
158-
}
154+
val info = validationAnnotations.validate(dataType)
155+
val prop = info.prop
156+
imports.addAll(prop.imports)
159157

160158
dataType.forEach { propName, propDataType ->
161159
val propInfo = validationAnnotations.validate(
162160
propDataType, dataType.isRequired(propName))
163161

164-
imports.addAll(propInfo.imports)
162+
val propProp = propInfo.prop
163+
imports.addAll(propProp.imports)
165164
}
166165
}
167166

src/main/kotlin/io/openapiprocessor/core/writer/java/MethodWriter.kt

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -89,30 +89,28 @@ open class MethodWriter(
8989

9090
private fun createParameters(endpoint: Endpoint): String {
9191
val ps = endpoint.parameters.map {
92-
var methodDefinition = ""
9392

94-
if (apiOptions.beanValidation) {
93+
val dataTypeValue = if (apiOptions.beanValidation) {
9594
val info = beanValidationFactory.validate(it.dataType, it.required)
96-
methodDefinition += " " + info.annotations.joinToString(" ")
95+
info.inout.dataTypeValue
96+
} else {
97+
it.dataType.getTypeName()
9798
}
9899

99-
val annotation = createParameterAnnotation (it)
100-
if (annotation.isNotEmpty()) {
101-
methodDefinition += " $annotation"
102-
}
103-
104-
methodDefinition += " ${it.dataType.getTypeName()} ${toCamelCase (it.name)}"
105-
methodDefinition.trim()
100+
"${createParameterAnnotation(it)} $dataTypeValue ${toCamelCase (it.name)}".trim()
106101
}.toMutableList()
107102

108103
if (endpoint.requestBodies.isNotEmpty()) {
109104
val body = endpoint.getRequestBody()
110-
var beanValidationAnnotations = ""
111-
if (apiOptions.beanValidation) {
105+
106+
val dataTypeValue = if (apiOptions.beanValidation) {
112107
val info = beanValidationFactory.validate(body.dataType, false)
113-
beanValidationAnnotations += " ${info.annotations.joinToString(" ")}"
108+
info.inout.dataTypeValue
109+
} else {
110+
body.dataType.getTypeName()
114111
}
115-
val param = "$beanValidationAnnotations ${createParameterAnnotation(body)} ${body.dataType.getTypeName()} ${body.name}"
112+
113+
val param = "${createParameterAnnotation(body)} $dataTypeValue ${body.name}"
116114
ps.add (param.trim())
117115
}
118116

src/test/groovy/com/github/hauner/openapi/core/writer/java/DataTypeWriterSpec.groovy

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package com.github.hauner.openapi.core.writer.java
77

88
import io.openapiprocessor.core.converter.ApiOptions
9+
import io.openapiprocessor.core.model.datatypes.DataTypeConstraints
910
import io.openapiprocessor.core.model.datatypes.DataTypeName
1011
import io.openapiprocessor.core.model.datatypes.ObjectDataType as ObjectDataTypeP
1112
import io.openapiprocessor.core.model.datatypes.StringDataType
@@ -76,7 +77,7 @@ package $pkg;
7677

7778
void "writes import of generic list type" () {
7879
def dataType = new ObjectDataType ('Book', 'mine', [
79-
'authors': new ListDataType (new StringDataType())
80+
'authors': new ListDataType (new StringDataType(), new DataTypeConstraints())
8081
], null, false, null)
8182

8283
when:
@@ -89,8 +90,9 @@ package $pkg;
8990

9091
void "writes import of generic object list type" () {
9192
def dataType = new ObjectDataType ('Foo', 'mine', [
92-
'bars': new ListDataType (new ObjectDataTypeP (new DataTypeName(id, type), 'other', [:],
93-
null, false, null))
93+
'bars': new ListDataType (
94+
new ObjectDataTypeP (new DataTypeName(id, type), 'other', [:],
95+
null, false, null), new DataTypeConstraints())
9496
], null, false, null)
9597

9698
when:

0 commit comments

Comments
 (0)