Skip to content

Commit 26bdfec

Browse files
authored
Add more EnumNamingStrategies (#4728)
1 parent 629af99 commit 26bdfec

File tree

8 files changed

+706
-269
lines changed

8 files changed

+706
-269
lines changed

release-notes/CREDITS-2.x

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,3 +1837,8 @@ Rikkarth (rikkarth@github)
18371837
Maxim Valeev (@MaximValeev)
18381838
* Reported #4508: Deserialized JsonAnySetter field in Kotlin data class is null
18391839
(2.18.1)
1840+
1841+
1842+
Lars Benedetto (@lbenedetto)
1843+
* Contributed #4676: Support other enum naming strategies than camelCase
1844+
(2.19.0)

release-notes/VERSION-2.x

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ Project: jackson-databind
66

77
2.19.0 (not yet released)
88

9-
-
9+
#4676: Support other enum naming strategies than camelCase
10+
(requested by @hajdamak)
11+
(contributed by Lars B)
1012

1113
2.18.1 (WIP-2024)
1214

src/main/java/com/fasterxml/jackson/databind/EnumNamingStrategies.java

Lines changed: 405 additions & 58 deletions
Large diffs are not rendered by default.

src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategies.java

Lines changed: 8 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
55
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
66
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
7+
import com.fasterxml.jackson.databind.util.NamingStrategyImpls;
78

89
/**
910
* Container for standard {@link PropertyNamingStrategy} implementations
@@ -124,42 +125,6 @@ public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParam
124125
}
125126

126127
public abstract String translate(String propertyName);
127-
128-
/**
129-
* Helper method to share implementation between snake and dotted case.
130-
*/
131-
protected String translateLowerCaseWithSeparator(final String input, final char separator)
132-
{
133-
if (input == null || input.isEmpty()) {
134-
return input;
135-
}
136-
137-
final int length = input.length();
138-
final StringBuilder result = new StringBuilder(length + (length >> 1));
139-
int upperCount = 0;
140-
for (int i = 0; i < length; ++i) {
141-
char ch = input.charAt(i);
142-
char lc = Character.toLowerCase(ch);
143-
144-
if (lc == ch) { // lower-case letter means we can get new word
145-
// but need to check for multi-letter upper-case (acronym), where assumption
146-
// is that the last upper-case char is start of a new word
147-
if (upperCount > 1) {
148-
// so insert hyphen before the last character now
149-
result.insert(result.length() - 1, separator);
150-
}
151-
upperCount = 0;
152-
} else {
153-
// Otherwise starts new word, unless beginning of string
154-
if ((upperCount == 0) && (i > 0)) {
155-
result.append(separator);
156-
}
157-
++upperCount;
158-
}
159-
result.append(lc);
160-
}
161-
return result.toString();
162-
}
163128
}
164129

165130
/*
@@ -230,35 +195,7 @@ public static class SnakeCaseStrategy extends NamingBase
230195
@Override
231196
public String translate(String input)
232197
{
233-
if (input == null) return input; // garbage in, garbage out
234-
int length = input.length();
235-
StringBuilder result = new StringBuilder(length * 2);
236-
int resultLength = 0;
237-
boolean wasPrevTranslated = false;
238-
for (int i = 0; i < length; i++)
239-
{
240-
char c = input.charAt(i);
241-
if (i > 0 || c != '_') // skip first starting underscore
242-
{
243-
if (Character.isUpperCase(c))
244-
{
245-
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
246-
{
247-
result.append('_');
248-
resultLength++;
249-
}
250-
c = Character.toLowerCase(c);
251-
wasPrevTranslated = true;
252-
}
253-
else
254-
{
255-
wasPrevTranslated = false;
256-
}
257-
result.append(c);
258-
resultLength++;
259-
}
260-
}
261-
return resultLength > 0 ? result.toString() : input;
198+
return NamingStrategyImpls.SNAKE_CASE.translate(input);
262199
}
263200
}
264201

@@ -281,11 +218,7 @@ public static class UpperSnakeCaseStrategy extends SnakeCaseStrategy
281218

282219
@Override
283220
public String translate(String input) {
284-
String output = super.translate(input);
285-
if (output == null) {
286-
return null;
287-
}
288-
return output.toUpperCase();
221+
return NamingStrategyImpls.UPPER_SNAKE_CASE.translate(input);
289222
}
290223
}
291224

@@ -305,7 +238,7 @@ public static class LowerCamelCaseStrategy extends NamingBase
305238

306239
@Override
307240
public String translate(String input) {
308-
return input;
241+
return NamingStrategyImpls.LOWER_CAMEL_CASE.translate(input);
309242
}
310243
}
311244

@@ -343,18 +276,7 @@ public static class UpperCamelCaseStrategy extends NamingBase
343276
*/
344277
@Override
345278
public String translate(String input) {
346-
if (input == null || input.isEmpty()){
347-
return input; // garbage in, garbage out
348-
}
349-
// Replace first lower-case letter with upper-case equivalent
350-
char c = input.charAt(0);
351-
char uc = Character.toUpperCase(c);
352-
if (c == uc) {
353-
return input;
354-
}
355-
StringBuilder sb = new StringBuilder(input);
356-
sb.setCharAt(0, uc);
357-
return sb.toString();
279+
return NamingStrategyImpls.UPPER_CAMEL_CASE.translate(input);
358280
}
359281
}
360282

@@ -376,10 +298,7 @@ public static class LowerCaseStrategy extends NamingBase
376298

377299
@Override
378300
public String translate(String input) {
379-
if (input == null || input.isEmpty()) {
380-
return input;
381-
}
382-
return input.toLowerCase();
301+
return NamingStrategyImpls.LOWER_CASE.translate(input);
383302
}
384303
}
385304

@@ -401,7 +320,7 @@ public static class KebabCaseStrategy extends NamingBase
401320

402321
@Override
403322
public String translate(String input) {
404-
return translateLowerCaseWithSeparator(input, '-');
323+
return NamingStrategyImpls.KEBAB_CASE.translate(input);
405324
}
406325
}
407326

@@ -422,7 +341,7 @@ public static class LowerDotCaseStrategy extends NamingBase
422341

423342
@Override
424343
public String translate(String input){
425-
return translateLowerCaseWithSeparator(input, '.');
344+
return NamingStrategyImpls.LOWER_DOT_CASE.translate(input);
426345
}
427346
}
428347
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package com.fasterxml.jackson.databind.util;
2+
3+
/**
4+
* Container for standard naming strategy implementations, specifically
5+
* used by property naming strategies (see {@link com.fasterxml.jackson.databind.PropertyNamingStrategies})
6+
* and enum naming strategies (see {@link com.fasterxml.jackson.databind.EnumNamingStrategies}).
7+
*/
8+
public enum NamingStrategyImpls {
9+
/**
10+
* beanName -> beanName
11+
*/
12+
LOWER_CAMEL_CASE {
13+
@Override
14+
public String translate(String beanName) {
15+
return beanName; // beanName is already in lower camel case
16+
}
17+
},
18+
19+
/**
20+
* beanName -> BeanName
21+
*/
22+
UPPER_CAMEL_CASE {
23+
@Override
24+
public String translate(String beanName) {
25+
if (beanName == null || beanName.isEmpty()) {
26+
return beanName; // garbage in, garbage out
27+
}
28+
// Replace first lower-case letter with upper-case equivalent
29+
char c = beanName.charAt(0);
30+
char uc = Character.toUpperCase(c);
31+
if (c == uc) {
32+
return beanName;
33+
}
34+
StringBuilder sb = new StringBuilder(beanName);
35+
sb.setCharAt(0, uc);
36+
return sb.toString();
37+
}
38+
},
39+
40+
/**
41+
* beanName -> bean_name
42+
*/
43+
SNAKE_CASE {
44+
@Override
45+
public String translate(String beanName) {
46+
if (beanName == null) return beanName; // garbage in, garbage out
47+
int length = beanName.length();
48+
StringBuilder result = new StringBuilder(length * 2);
49+
int resultLength = 0;
50+
boolean wasPrevTranslated = false;
51+
for (int i = 0; i < length; i++) {
52+
char c = beanName.charAt(i);
53+
if (i > 0 || c != '_') // skip first starting underscore
54+
{
55+
if (Character.isUpperCase(c)) {
56+
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_') {
57+
result.append('_');
58+
resultLength++;
59+
}
60+
c = Character.toLowerCase(c);
61+
wasPrevTranslated = true;
62+
} else {
63+
wasPrevTranslated = false;
64+
}
65+
result.append(c);
66+
resultLength++;
67+
}
68+
}
69+
return resultLength > 0 ? result.toString() : beanName;
70+
}
71+
},
72+
73+
/**
74+
* beanName -> BEAN_NAME
75+
*/
76+
UPPER_SNAKE_CASE {
77+
@Override
78+
public String translate(String beanName) {
79+
String output = SNAKE_CASE.translate(beanName);
80+
if (output == null) {
81+
return null;
82+
}
83+
return output.toUpperCase();
84+
}
85+
},
86+
87+
/**
88+
* beanName -> beanname
89+
*/
90+
LOWER_CASE {
91+
@Override
92+
public String translate(String beanName) {
93+
if (beanName == null || beanName.isEmpty()) {
94+
return beanName;
95+
}
96+
return beanName.toLowerCase();
97+
}
98+
},
99+
100+
/**
101+
* beanName -> bean-name
102+
*/
103+
KEBAB_CASE {
104+
@Override
105+
public String translate(String beanName) {
106+
return translateLowerCaseWithSeparator(beanName, '-');
107+
}
108+
},
109+
110+
/**
111+
* beanName -> bean.name
112+
*/
113+
LOWER_DOT_CASE {
114+
@Override
115+
public String translate(String beanName) {
116+
return translateLowerCaseWithSeparator(beanName, '.');
117+
}
118+
},
119+
;
120+
121+
public abstract String translate(final String beanName);
122+
123+
/**
124+
* Helper method to share implementation between snake and dotted case.
125+
*/
126+
static String translateLowerCaseWithSeparator(final String beanName, final char separator) {
127+
if (beanName == null || beanName.isEmpty()) {
128+
return beanName;
129+
}
130+
131+
final int length = beanName.length();
132+
final StringBuilder result = new StringBuilder(length + (length >> 1));
133+
int upperCount = 0;
134+
for (int i = 0; i < length; ++i) {
135+
char ch = beanName.charAt(i);
136+
char lc = Character.toLowerCase(ch);
137+
138+
if (lc == ch) { // lower-case letter means we can get new word
139+
// but need to check for multi-letter upper-case (acronym), where assumption
140+
// is that the last upper-case char is start of a new word
141+
if (upperCount > 1) {
142+
// so insert hyphen before the last character now
143+
result.insert(result.length() - 1, separator);
144+
}
145+
upperCount = 0;
146+
} else {
147+
// Otherwise starts new word, unless beginning of string
148+
if ((upperCount == 0) && (i > 0)) {
149+
result.append(separator);
150+
}
151+
++upperCount;
152+
}
153+
result.append(lc);
154+
}
155+
return result.toString();
156+
}
157+
}

0 commit comments

Comments
 (0)