Skip to content

Commit 9f97baa

Browse files
committed
Start work on #2715
1 parent b735280 commit 9f97baa

File tree

2 files changed

+356
-47
lines changed

2 files changed

+356
-47
lines changed
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
package com.fasterxml.jackson.databind;
2+
3+
import com.fasterxml.jackson.databind.cfg.MapperConfig;
4+
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
5+
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
6+
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
7+
8+
/**
9+
* Container for standard {@link PropertyNamingStrategy} implementations
10+
* and singleton instances.
11+
*<p>
12+
* Added in Jackson 2.12 to resolve issue
13+
* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>.
14+
*
15+
* @since 2.12
16+
*/
17+
public abstract class PropertyNamingStrategies
18+
implements java.io.Serializable
19+
{
20+
private static final long serialVersionUID = 2L;
21+
22+
/*
23+
/**********************************************************************
24+
/* Static instances that may be referenced
25+
/**********************************************************************
26+
*/
27+
28+
/**
29+
* Naming convention used in Java, where words other than first are capitalized
30+
* and no separator is used between words. Since this is the native Java naming convention,
31+
* naming strategy will not do any transformation between names in data (JSON) and
32+
* POJOS.
33+
*/
34+
public static final PropertyNamingStrategy LOWER_CAMEL_CASE = new PropertyNamingStrategy();
35+
36+
/**
37+
* Naming convention used in languages like Pascal, where words are capitalized
38+
* and no separator is used between words.
39+
* See {@link UpperCamelCaseStrategy} for details.
40+
*/
41+
public static final PropertyNamingStrategy UPPER_CAMEL_CASE = new UpperCamelCaseStrategy();
42+
43+
/**
44+
* Naming convention used in languages like C, where words are in lower-case
45+
* letters, separated by underscores.
46+
* See {@link SnakeCaseStrategy} for details.
47+
*/
48+
public static final PropertyNamingStrategy SNAKE_CASE = new SnakeCaseStrategy();
49+
50+
/**
51+
* Naming convention in which all words of the logical name are in lower case, and
52+
* no separator is used between words.
53+
* See {@link LowerCaseStrategy} for details.
54+
*/
55+
public static final PropertyNamingStrategy LOWER_CASE = new LowerCaseStrategy();
56+
57+
/**
58+
* Naming convention used in languages like Lisp, where words are in lower-case
59+
* letters, separated by hyphens.
60+
* See {@link KebabCaseStrategy} for details.
61+
*/
62+
public static final PropertyNamingStrategy KEBAB_CASE = new KebabCaseStrategy();
63+
64+
/**
65+
* Naming convention widely used as configuration properties name, where words are in
66+
* lower-case letters, separated by dots.
67+
* See {@link LowerDotCaseStrategy} for details.
68+
*/
69+
public static final PropertyNamingStrategy LOWER_DOT_CASE = new LowerDotCaseStrategy();
70+
71+
/*
72+
/**********************************************************************
73+
/* Public base class for simple implementations
74+
/**********************************************************************
75+
*/
76+
77+
/**
78+
* Intermediate base class for simple implementations
79+
*/
80+
static abstract class NamingBase
81+
extends PropertyNamingStrategy
82+
{
83+
private static final long serialVersionUID = 2L;
84+
85+
@Override
86+
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
87+
return translate(defaultName);
88+
}
89+
90+
@Override
91+
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
92+
return translate(defaultName);
93+
}
94+
95+
@Override
96+
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
97+
return translate(defaultName);
98+
}
99+
100+
@Override
101+
public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam,
102+
String defaultName) {
103+
return translate(defaultName);
104+
}
105+
106+
public abstract String translate(String propertyName);
107+
108+
/**
109+
* Helper method to share implementation between snake and dotted case.
110+
*/
111+
protected String translateLowerCaseWithSeparator(final String input, final char separator)
112+
{
113+
if (input == null) {
114+
return input; // garbage in, garbage out
115+
}
116+
final int length = input.length();
117+
if (length == 0) {
118+
return input;
119+
}
120+
121+
final StringBuilder result = new StringBuilder(length + (length >> 1));
122+
int upperCount = 0;
123+
for (int i = 0; i < length; ++i) {
124+
char ch = input.charAt(i);
125+
char lc = Character.toLowerCase(ch);
126+
127+
if (lc == ch) { // lower-case letter means we can get new word
128+
// but need to check for multi-letter upper-case (acronym), where assumption
129+
// is that the last upper-case char is start of a new word
130+
if (upperCount > 1) {
131+
// so insert hyphen before the last character now
132+
result.insert(result.length() - 1, separator);
133+
}
134+
upperCount = 0;
135+
} else {
136+
// Otherwise starts new word, unless beginning of string
137+
if ((upperCount == 0) && (i > 0)) {
138+
result.append(separator);
139+
}
140+
++upperCount;
141+
}
142+
result.append(lc);
143+
}
144+
return result.toString();
145+
}
146+
}
147+
148+
/*
149+
/**********************************************************************
150+
/* Standard implementations
151+
/**********************************************************************
152+
*/
153+
154+
/**
155+
* A {@link PropertyNamingStrategy} that translates typical camel case Java
156+
* property names to lower case JSON element names, separated by
157+
* underscores. This implementation is somewhat lenient, in that it
158+
* provides some additional translations beyond strictly translating from
159+
* camel case only. In particular, the following translations are applied
160+
* by this PropertyNamingStrategy.
161+
*
162+
* <ul><li>Every upper case letter in the Java property name is translated
163+
* into two characters, an underscore and the lower case equivalent of the
164+
* target character, with three exceptions.
165+
* <ol><li>For contiguous sequences of upper case letters, characters after
166+
* the first character are replaced only by their lower case equivalent,
167+
* and are not preceded by an underscore.
168+
* <ul><li>This provides for reasonable translations of upper case acronyms,
169+
* e.g., &quot;theWWW&quot; is translated to &quot;the_www&quot;.</li></ul></li>
170+
* <li>An upper case character in the first position of the Java property
171+
* name is not preceded by an underscore character, and is translated only
172+
* to its lower case equivalent.
173+
* <ul><li>For example, &quot;Results&quot; is translated to &quot;results&quot;,
174+
* and not to &quot;_results&quot;.</li></ul></li>
175+
* <li>An upper case character in the Java property name that is already
176+
* preceded by an underscore character is translated only to its lower case
177+
* equivalent, and is not preceded by an additional underscore.
178+
* <ul><li>For example, &quot;user_Name&quot; is translated to
179+
* &quot;user_name&quot;, and not to &quot;user__name&quot; (with two
180+
* underscore characters).</li></ul></li></ol></li>
181+
* <li>If the Java property name starts with an underscore, then that
182+
* underscore is not included in the translated name, unless the Java
183+
* property name is just one character in length, i.e., it is the
184+
* underscore character. This applies only to the first character of the
185+
* Java property name.</li></ul>
186+
*
187+
* These rules result in the following additional example translations from
188+
* Java property names to JSON element names.
189+
* <ul><li>&quot;userName&quot; is translated to &quot;user_name&quot;</li>
190+
* <li>&quot;UserName&quot; is translated to &quot;user_name&quot;</li>
191+
* <li>&quot;USER_NAME&quot; is translated to &quot;user_name&quot;</li>
192+
* <li>&quot;user_name&quot; is translated to &quot;user_name&quot; (unchanged)</li>
193+
* <li>&quot;user&quot; is translated to &quot;user&quot; (unchanged)</li>
194+
* <li>&quot;User&quot; is translated to &quot;user&quot;</li>
195+
* <li>&quot;USER&quot; is translated to &quot;user&quot;</li>
196+
* <li>&quot;_user&quot; is translated to &quot;user&quot;</li>
197+
* <li>&quot;_User&quot; is translated to &quot;user&quot;</li>
198+
* <li>&quot;__user&quot; is translated to &quot;_user&quot;
199+
* (the first of two underscores was removed)</li>
200+
* <li>&quot;user__name&quot; is translated to &quot;user__name&quot;
201+
* (unchanged, with two underscores)</li></ul>
202+
*/
203+
public static class SnakeCaseStrategy extends NamingBase
204+
{
205+
private static final long serialVersionUID = 2L;
206+
207+
@Override
208+
public String translate(String input)
209+
{
210+
if (input == null) return input; // garbage in, garbage out
211+
int length = input.length();
212+
StringBuilder result = new StringBuilder(length * 2);
213+
int resultLength = 0;
214+
boolean wasPrevTranslated = false;
215+
for (int i = 0; i < length; i++)
216+
{
217+
char c = input.charAt(i);
218+
if (i > 0 || c != '_') // skip first starting underscore
219+
{
220+
if (Character.isUpperCase(c))
221+
{
222+
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
223+
{
224+
result.append('_');
225+
resultLength++;
226+
}
227+
c = Character.toLowerCase(c);
228+
wasPrevTranslated = true;
229+
}
230+
else
231+
{
232+
wasPrevTranslated = false;
233+
}
234+
result.append(c);
235+
resultLength++;
236+
}
237+
}
238+
return resultLength > 0 ? result.toString() : input;
239+
}
240+
}
241+
242+
/**
243+
* A {@link PropertyNamingStrategy} that translates typical camelCase Java
244+
* property names to PascalCase JSON element names (i.e., with a capital
245+
* first letter). In particular, the following translations are applied by
246+
* this PropertyNamingStrategy.
247+
*
248+
* <ul><li>The first lower-case letter in the Java property name is translated
249+
* into its equivalent upper-case representation.</li></ul>
250+
*
251+
* This rules result in the following example translation from
252+
* Java property names to JSON element names.
253+
* <ul><li>&quot;userName&quot; is translated to &quot;UserName&quot;</li></ul>
254+
*/
255+
public static class UpperCamelCaseStrategy extends NamingBase
256+
{
257+
private static final long serialVersionUID = 2L;
258+
259+
/**
260+
* Converts camelCase to PascalCase
261+
*
262+
* For example, "userName" would be converted to
263+
* "UserName".
264+
*
265+
* @param input formatted as camelCase string
266+
* @return input converted to PascalCase format
267+
*/
268+
@Override
269+
public String translate(String input) {
270+
if (input == null || input.length() == 0){
271+
return input; // garbage in, garbage out
272+
}
273+
// Replace first lower-case letter with upper-case equivalent
274+
char c = input.charAt(0);
275+
char uc = Character.toUpperCase(c);
276+
if (c == uc) {
277+
return input;
278+
}
279+
StringBuilder sb = new StringBuilder(input);
280+
sb.setCharAt(0, uc);
281+
return sb.toString();
282+
}
283+
}
284+
285+
/**
286+
* Simple strategy where external name simply only uses lower-case characters,
287+
* and no separators.
288+
* Conversion from internal name like "someOtherValue" would be into external name
289+
* if "someothervalue".
290+
*/
291+
public static class LowerCaseStrategy extends NamingBase
292+
{
293+
private static final long serialVersionUID = 2L;
294+
295+
@Override
296+
public String translate(String input) {
297+
return input.toLowerCase();
298+
}
299+
}
300+
301+
/**
302+
* Naming strategy similar to {@link PropertyNamingStrategies.SnakeCaseStrategy},
303+
* but instead of underscores
304+
* as separators, uses hyphens. Naming convention traditionally used for languages
305+
* like Lisp.
306+
*/
307+
public static class KebabCaseStrategy extends NamingBase
308+
{
309+
private static final long serialVersionUID = 2L;
310+
311+
@Override
312+
public String translate(String input) {
313+
return translateLowerCaseWithSeparator(input, '-');
314+
}
315+
}
316+
317+
/**
318+
* Naming strategy similar to {@link PropertyNamingStrategies.KebabCaseStrategy},
319+
* but instead of hyphens
320+
* as separators, uses dots. Naming convention widely used as configuration properties name.
321+
*/
322+
public static class LowerDotCaseStrategy extends NamingBase {
323+
private static final long serialVersionUID = 2L;
324+
325+
@Override
326+
public String translate(String input){
327+
return translateLowerCaseWithSeparator(input, '.');
328+
}
329+
}
330+
}

0 commit comments

Comments
 (0)