Skip to content

Commit 23e5fa0

Browse files
committed
prefix invalid enum identifier instead of removing the invalid characters to avoid duplicate identifiers (openapi-processor/openapi-processor-spring#350)
1 parent 721b4f3 commit 23e5fa0

File tree

9 files changed

+69
-15
lines changed

9 files changed

+69
-15
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,9 @@ class ApiOptions: MappingSettings {
170170
* break identifier names from digits to letters.
171171
*/
172172
var identifierWordBreakFromDigitToLetter = true
173+
174+
/**
175+
* prefix enum identifier if it starts with an invalid character.
176+
*/
177+
var identifierPrefixInvalidEnumStart = true
173178
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class OptionsConverter(private val checkObsoleteProcessorOptions: Boolean = fals
113113
with(mapping.compatibility) {
114114
options.beanValidationValidOnReactive = beanValidationValidOnReactive
115115
options.identifierWordBreakFromDigitToLetter = identifierWordBreakFromDigitToLetter
116+
options.identifierPrefixInvalidEnumStart = identifierPrefixInvalidEnumStart
116117
}
117118

118119
val mappings = MappingConverter().convert(mapping)

openapi-processor-core/src/main/kotlin/io/openapiprocessor/core/processor/mapping/v2/Compatibility.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,10 @@ data class Compatibility(
1717
/**
1818
* split identifier when switching from digits to letters.
1919
*/
20-
val identifierWordBreakFromDigitToLetter: Boolean = true
20+
val identifierWordBreakFromDigitToLetter: Boolean = true,
21+
22+
/**
23+
* prefix enum identifier if it starts with an invalid character.
24+
*/
25+
val identifierPrefixInvalidEnumStart: Boolean = true
2126
)

openapi-processor-core/src/main/kotlin/io/openapiprocessor/core/writer/java/IdentifierOptions.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,10 @@ data class IdentifierOptions(
99
/**
1010
* recognize switch from digits to letters as word break.
1111
*/
12-
val wordBreakFromDigitToLetter: Boolean = true
12+
val wordBreakFromDigitToLetter: Boolean = true,
13+
14+
/**
15+
* prefix OpenAPI enum value if it starts with an invalid start character.
16+
*/
17+
val prefixInvalidEnumStart: Boolean = true
1318
)

openapi-processor-core/src/main/kotlin/io/openapiprocessor/core/writer/java/JavaIdentifier.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import javax.lang.model.SourceVersion
1414
import kotlin.collections.ArrayList
1515

1616

17-
private val INVALID_WORD_PREFIX = "v"
17+
private const val INVALID_WORD_PREFIX = "v" // v for "value"
1818
private val INVALID_WORD_BREAKS = listOf(' ', '-')
1919
private val VALID_WORD_BREAKS = listOf('_')
2020

@@ -51,7 +51,13 @@ class JavaIdentifier(val options: IdentifierOptions = IdentifierOptions()): Iden
5151
}
5252

5353
override fun toEnum(src: String): String {
54-
return joinEnum(splitAtWordBreaks(src))
54+
val item = if (options.prefixInvalidEnumStart) {
55+
src.prefixInvalidStart()
56+
} else {
57+
src
58+
}
59+
60+
return joinEnum(splitAtWordBreaks(item))
5561
}
5662

5763
override fun toMethodTail(src: String): String {
@@ -196,6 +202,13 @@ class JavaIdentifier(val options: IdentifierOptions = IdentifierOptions()): Iden
196202
return false
197203
}
198204

205+
private fun String.prefixInvalidStart(): String {
206+
if(!isValidStart(this[0])) {
207+
return INVALID_WORD_PREFIX.plus(this)
208+
}
209+
return this
210+
}
211+
199212
private fun String.trimInvalidStart(): String {
200213
return trimStart {
201214
!isValidStart(it)

openapi-processor-core/src/main/resources/mapping/v13/mapping.yaml.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@
194194
"description": "identifier word break when switching from digits to letters.",
195195
"type": "boolean",
196196
"default": true
197+
},
198+
"identifier-prefix-invalid-enum-start": {
199+
"description": "identifier, prefix enum if it starts with an invalid character.",
200+
"type": "boolean",
201+
"default": true
197202
}
198203
}
199204
},

openapi-processor-core/src/test/groovy/io/openapiprocessor/core/writer/java/IdentifierSpec.groovy

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,25 @@ import spock.lang.Unroll
1010

1111
class IdentifierSpec extends Specification {
1212

13+
void "prefixes invalid start character of enum values"() {
14+
def convert = new JavaIdentifier(new IdentifierOptions(wbfdtl, pies))
15+
16+
convert.toEnum(src) == enumn
17+
18+
where:
19+
src | enumn | wbfdtl | pies
20+
"1A" | "V1_A" | true | true
21+
"1A" | "A" | true | false
22+
"1A" | "V1A" | false | true
23+
"1A" | "A" | false | false
24+
"_1A" | "V_1_A" | true | true
25+
"_1A" | "A" | true | false
26+
"_1A" | "V_1A" | false | true
27+
"_1A" | "A" | false | false
28+
}
29+
1330
void "add prefix to single invalid identifier"() {
14-
def convert = new JavaIdentifier(new IdentifierOptions(wbfdtl))
31+
def convert = new JavaIdentifier(new IdentifierOptions(wbfdtl, true))
1532

1633
expect:
1734
convert.toCamelCase(src) == camelCase
@@ -28,7 +45,7 @@ class IdentifierSpec extends Specification {
2845
}
2946

3047
void "recognize word break if a digit is followed by a letter" () {
31-
def convert = new JavaIdentifier(new IdentifierOptions(true))
48+
def convert = new JavaIdentifier(new IdentifierOptions(true, true))
3249

3350
expect:
3451
convert.toCamelCase(src) == camelCase
@@ -42,7 +59,7 @@ class IdentifierSpec extends Specification {
4259
}
4360

4461
void "ignore word break if a digit is followed by a letter" () {
45-
def convert = new JavaIdentifier(new IdentifierOptions(false))
62+
def convert = new JavaIdentifier(new IdentifierOptions(false, true))
4663

4764
expect:
4865
convert.toCamelCase(src) == camelCase
@@ -57,7 +74,7 @@ class IdentifierSpec extends Specification {
5774

5875
@Unroll
5976
void "convert source string '#src' to valid identifiers: #identifier/#clazz/#enumn" () {
60-
def convert = new JavaIdentifier(new IdentifierOptions(true))
77+
def convert = new JavaIdentifier(new IdentifierOptions(true, true))
6178

6279
expect:
6380
convert.toCamelCase (src) == camelCase
@@ -74,8 +91,8 @@ class IdentifierSpec extends Specification {
7491
"AA" | "aa" | "aa" | "Aa" | "AA"
7592

7693
// invalid chars are stripped
77-
"1a" | "a" | "a" | "A" | "A"
78-
"2345a" | "a" | "a" | "A" | "A"
94+
"1a" | "a" | "a" | "A" | "V1_A"
95+
"2345a" | "a" | "a" | "A" | "V2345_A"
7996

8097
// word break at invalid character
8198
"a foo" | "aFoo" | "aFoo" | "AFoo" | "A_FOO"
@@ -85,10 +102,10 @@ class IdentifierSpec extends Specification {
85102
"a-foo-bar" | "aFooBar" | "aFooBar" | "AFooBar" | "A_FOO_BAR"
86103
"a foo-bar" | "aFooBar" | "aFooBar" | "AFooBar" | "A_FOO_BAR"
87104
'api/some/thing' | 'apiSomeThing' | 'apiSomeThing' | "ApiSomeThing" | "API_SOME_THING"
88-
"_fo-o" | 'foO' | 'foO' | 'FoO' | "FO_O"
105+
"_fo-o" | 'foO' | 'foO' | 'FoO' | "V_FO_O"
89106

90107
// word break at underscore, it is valid but unwanted except for enums
91-
"_ab" | "ab" | "ab" | "Ab" | "AB"
108+
"_ab" | "ab" | "ab" | "Ab" | "V_AB"
92109
"a_b" | "aB" | "aB" | "AB" | "A_B"
93110
"a_foo" | "aFoo" | "aFoo" | "AFoo" | "A_FOO"
94111
"A_A" | "aA" | "aA" | "AA" | "A_A"
@@ -116,14 +133,14 @@ class IdentifierSpec extends Specification {
116133
}
117134

118135
void "adds suffix to class name" () {
119-
def convert = new JavaIdentifier(new IdentifierOptions(true))
136+
def convert = new JavaIdentifier(new IdentifierOptions(true, true))
120137

121138
expect:
122139
convert.toClass("foo", "X") == "FooX"
123140
}
124141

125142
void "ignores suffix if class name already ends with the suffix"() {
126-
def convert = new JavaIdentifier(new IdentifierOptions(true))
143+
def convert = new JavaIdentifier(new IdentifierOptions(true, true))
127144

128145
expect:
129146
convert.toClass("FooX", "X") == "FooX"

openapi-processor-core/src/test/groovy/io/openapiprocessor/core/writer/java/StringEnumWriterSpec.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public enum $type implements Supplier<String> {
139139
target.toString ().contains ("""\
140140
public enum Foo {
141141
FOO("foo"),
142-
FOO_2("_foo-2"),
142+
V_FOO_2("_foo-2"),
143143
FOO_FOO("foo-foo");
144144
145145
""")

openapi-processor-core/src/test/kotlin/io/openapiprocessor/core/converter/OptionsConverterSpec.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class OptionsConverterSpec: StringSpec({
4949

5050
options.beanValidationValidOnReactive.shouldBeTrue()
5151
options.identifierWordBreakFromDigitToLetter.shouldBeTrue()
52+
options.identifierPrefixInvalidEnumStart.shouldBeTrue()
5253
}
5354

5455
"should set target dir" {
@@ -130,6 +131,7 @@ class OptionsConverterSpec: StringSpec({
130131
compatibility:
131132
bean-validation-valid-on-reactive: false
132133
identifier-word-break-from-digit-to-letter: false
134+
identifier-prefix-invalid-enum-start: false
133135
""".trimIndent()
134136
))
135137

@@ -153,6 +155,7 @@ class OptionsConverterSpec: StringSpec({
153155

154156
options.beanValidationValidOnReactive.shouldBeFalse()
155157
options.identifierWordBreakFromDigitToLetter.shouldBeFalse()
158+
options.identifierPrefixInvalidEnumStart.shouldBeFalse()
156159
}
157160

158161
"overrides old target-dir mapping options" {

0 commit comments

Comments
 (0)