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

Commit 51118a0

Browse files
committed
improved identifier conversion to preserve camel case names and _ in enum value names
1 parent 2353ee0 commit 51118a0

File tree

2 files changed

+35
-57
lines changed

2 files changed

+35
-57
lines changed

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

Lines changed: 26 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import java.lang.Character.isJavaIdentifierStart
3939
* @author Martin Hauner
4040
*/
4141
fun toCamelCase(src: String): String {
42-
return joinCamelCase(joinSingleCharWords(splitAtWordBreaks(src)))
42+
return joinCamelCase(splitAtWordBreaks(src))
4343
}
4444

4545
/**
@@ -81,10 +81,9 @@ fun toClass(src: String): String {
8181
* @author Martin Hauner
8282
*/
8383
fun toEnum(src: String): String {
84-
return joinEnum(joinSingleCharWords(splitAtWordBreaks(src)))
84+
return joinEnum(splitAtWordBreaks(src))
8585
}
8686

87-
8887
/**
8988
* joins the given words to a single camel case string.
9089
*
@@ -95,7 +94,7 @@ fun toEnum(src: String): String {
9594
*
9695
* @author Martin Hauner
9796
*/
98-
private fun joinCamelCase(words: ArrayList<String>): String {
97+
private fun joinCamelCase(words: List<String>): String {
9998
val sb = StringBuilder()
10099

101100
words.forEachIndexed { idx, p ->
@@ -121,7 +120,7 @@ private fun joinCamelCase(words: ArrayList<String>): String {
121120
*
122121
* @author Martin Hauner
123122
*/
124-
private fun joinEnum(words: ArrayList<String>): String {
123+
private fun joinEnum(words: List<String>): String {
125124
val result = words.joinToString("_") { it.toUpperCase() }
126125

127126
if (result.isEmpty()) {
@@ -131,42 +130,6 @@ private fun joinEnum(words: ArrayList<String>): String {
131130
return result
132131
}
133132

134-
/**
135-
* joins two words if at least one has only a single character.
136-
*
137-
* this tries to avoid identifiers with multiple uppercase characters in a row.
138-
*
139-
* @param words a list of words
140-
* @return a list of words
141-
*
142-
* @author Martin Hauner
143-
*/
144-
private fun joinSingleCharWords(words: List<String>): ArrayList<String> {
145-
val merged = ArrayList<String>()
146-
val current = StringBuilder()
147-
148-
words.forEachIndexed { idx, p ->
149-
if (idx == 0) {
150-
current.append(p)
151-
} else {
152-
if (current.last().isUpperCase() && (current.length == 1 || p.length == 1)) {
153-
current.append(p)
154-
} else {
155-
merged.add(current.toString())
156-
current.clear()
157-
current.append(p)
158-
}
159-
}
160-
}
161-
162-
163-
if (current.isNotEmpty()) {
164-
merged.add(current.toString())
165-
}
166-
167-
return merged
168-
}
169-
170133
/**
171134
* splits the given string at the word breaks.
172135
*
@@ -182,7 +145,7 @@ private fun splitAtWordBreaks(src: String): List<String> {
182145
val trimmed = src.trimInvalidStart()
183146
trimmed.forEachIndexed { idx, c ->
184147

185-
if (idx == 0 || c.isNoWordBreak()) {
148+
if (idx == 0 || !src.isWordBreak(idx)) {
186149
current.append(c)
187150
return@forEachIndexed
188151
}
@@ -219,13 +182,33 @@ private fun StringBuilder.appendValid(c: Char): StringBuilder {
219182
return this
220183
}
221184

185+
private fun String.isWordBreak(idx: Int): Boolean {
186+
return this.isForcedBreak(idx)
187+
|| this.isCaseBreak(idx)
188+
}
189+
190+
private fun String.isForcedBreak(idx: Int): Boolean {
191+
return this[idx].isWordBreak()
192+
}
193+
194+
// detect existing camel case word breaks
195+
private fun String.isCaseBreak(idx: Int): Boolean {
196+
if (idx == 0)
197+
return false
198+
199+
val prev = this[idx - 1]
200+
val curr = this[idx]
201+
202+
return prev.isLowerCase()
203+
&& curr.isUpperCase()
204+
}
205+
222206
private fun Char.isNoWordBreak(): Boolean {
223207
return !isWordBreak()
224208
}
225209

226210
private fun Char.isWordBreak(): Boolean {
227211
return isWordBreakChar(this)
228-
|| this.isUpperCase()
229212
|| !isJavaIdentifierPart(this)
230213
}
231214

src/test/groovy/io/openapiprocessor/core/writer/java/IdentifierSpec.groovy

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ class IdentifierSpec extends Specification {
3434
// first char should be lowercase
3535
"a" | "a" | "A" | "A"
3636
"A" | "a" | "A" | "A"
37-
38-
//
3937
"AA" | "aa" | "Aa" | "AA"
40-
"AAFoo" | "aaFoo" | "AaFoo" | "AA_FOO"
4138

4239
// invalid chars are stripped
4340
"1a" | "a" | "A" | "A"
@@ -46,30 +43,28 @@ class IdentifierSpec extends Specification {
4643
// word break at invalid characters
4744
"a foo" | "aFoo" | "AFoo" | "A_FOO"
4845
"a-foo" | "aFoo" | "AFoo" | "A_FOO"
46+
"FOO-bar" | "fooBar" | "FooBar" | "FOO_BAR"
4947
"a foo bar" | "aFooBar" | "AFooBar" | "A_FOO_BAR"
5048
"a-foo-bar" | "aFooBar" | "AFooBar" | "A_FOO_BAR"
5149
"a foo-bar" | "aFooBar" | "AFooBar" | "A_FOO_BAR"
5250
'api/some/thing' | 'apiSomeThing' | "ApiSomeThing" | "API_SOME_THING"
5351

54-
// word break at underscore, it is valid but unwanted
52+
// word break at underscore, it is valid but unwanted except for enums
5553
"_ab" | "ab" | "Ab" | "AB"
5654
"a_b" | "aB" | "AB" | "A_B"
5755
"a_foo" | "aFoo" | "AFoo" | "A_FOO"
56+
"A_A" | "aA" | "AA" | "A_A"
57+
"FOO_FOO" | "fooFoo" | "FooFoo" | "FOO_FOO"
58+
59+
// word break at case change: lower to upper, preserve camel case
60+
"fooBar" | "fooBar" | "FooBar" | "FOO_BAR"
61+
"fooBAr" | "fooBar" | "FooBar" | "FOO_BAR"
62+
"fooBAR" | "fooBar" | "FooBar" | "FOO_BAR"
5863

5964
// final result is empty
6065
" " | "invalid" | "Invalid" | "INVALID"
6166
"_" | "invalid" | "Invalid" | "INVALID"
6267
"-" | "invalid" | "Invalid" | "INVALID"
63-
64-
// word break at uppercase
65-
"fooBar" | "fooBar" | "FooBar" | "FOO_BAR"
66-
67-
// upper case only at word break
68-
"fooBAr" | "fooBar" | "FooBar" | "FOO_BAR"
69-
"fooBAR" | "fooBar" | "FooBar" | "FOO_BAR"
70-
"FOO-bar" | "fooBar" | "FooBar" | "FOO_BAR"
71-
"FOOBar" | "fooBar" | "FooBar" | "FOO_BAR"
72-
"FOObar" | "foObar" | "FoObar" | "FO_OBAR"
7368
}
7469

7570
}

0 commit comments

Comments
 (0)