Skip to content

Commit 9ee27c2

Browse files
committed
Merge branch '3.x' into JacksonJang/3.x
2 parents 39af812 + 0f410f1 commit 9ee27c2

File tree

13 files changed

+409
-206
lines changed

13 files changed

+409
-206
lines changed

src/main/java/tools/jackson/databind/AnnotationIntrospector.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -939,17 +939,17 @@ public Boolean hasAnyGetter(MapperConfig<?> config, Annotated ann) {
939939
}
940940

941941
/**
942-
* Finds the explicitly defined name of the given set of {@code Enum} values, if any.
942+
* Finds the explicitly defined names, if any, of the given set of {@code Enum} values.
943943
* The method overwrites entries in the incoming {@code names} array with the explicit
944944
* names found, if any, leaving other entries unmodified.
945945
*
946946
* @param config the mapper configuration to use
947947
* @param annotatedClass the annotated class for which to find the explicit names
948948
* @param enumValues the set of {@code Enum} values to find the explicit names for
949949
* @param names the matching declared names of enumeration values (with indexes matching
950-
* {@code enumValues} entries)
950+
* {@code enumValues} entries); modified as necessary
951951
*
952-
* @return an array of names to use (possibly {@code names} passed as argument)
952+
* @return an array of names to use (usually {@code names} passed as argument)
953953
*
954954
* @since 2.16
955955
*/

src/main/java/tools/jackson/databind/EnumNamingStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ public interface EnumNamingStrategy {
1313
* Translates the given <code>enumName</code> into an external property name according to
1414
* the implementation of this {@link EnumNamingStrategy}.
1515
*
16-
* @param enumName the name of the enum value to translate
1716
* @param config the mapper configuration
1817
* @param cls the Enum class
18+
* @param enumName the name of the enum value to translate
1919
*
2020
* @return the external property name that corresponds to the given <code>enumName</code>
2121
* according to the implementation of this {@link EnumNamingStrategy}.

src/main/java/tools/jackson/databind/cfg/EnumFeature.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ public enum EnumFeature implements DatatypeFeature
117117
*/
118118
WRITE_ENUMS_TO_LOWERCASE(false),
119119

120-
121120
/**
122121
* Feature that determines standard serialization mechanism used for
123122
* Enum values: if enabled, return value of <code>Enum.toString()</code>

src/main/java/tools/jackson/databind/cfg/MapperConfig.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,15 @@ public final boolean shouldSortPropertiesAlphabetically() {
156156
*
157157
* @param src Text to represent
158158
*
159-
* @return Optimized text object constructed
159+
* @return Optimized text object constructed, if {@code src} not {@code null};
160+
* {@code null} otherwise
160161
*/
161162
public SerializableString compileString(String src) {
162163
// 20-Jan-2014, tatu: For now we will just construct it directly but in distant
163164
// future might want to allow overriding somehow?
165+
if (src == null) {
166+
return null;
167+
}
164168
return new SerializedString(src);
165169
}
166170

src/main/java/tools/jackson/databind/ser/jdk/JDKKeySerializers.java

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import tools.jackson.databind.ser.impl.PropertySerializerMap;
1212
import tools.jackson.databind.ser.std.StdSerializer;
1313
import tools.jackson.databind.util.ClassUtil;
14-
import tools.jackson.databind.util.EnumValues;
14+
import tools.jackson.databind.util.EnumDefinition;
15+
import tools.jackson.databind.util.EnumValuesToWrite;
1516

1617
public abstract class JDKKeySerializers
1718
{
@@ -78,9 +79,8 @@ public static ValueSerializer<Object> getStdKeySerializer(SerializationConfig co
7879

7980
/**
8081
* Method called if no specified key serializer was located; will return a
81-
* "default" key serializer initialized by {@link EnumKeySerializer#construct(Class, EnumValues, EnumValues)}
82+
* "default" key serializer except with special handling for {@code Enum}s
8283
*/
83-
@SuppressWarnings("unchecked")
8484
public static ValueSerializer<Object> getFallbackKeySerializer(SerializationConfig config,
8585
Class<?> rawKeyType, AnnotatedClass annotatedClass)
8686
{
@@ -98,8 +98,7 @@ public static ValueSerializer<Object> getFallbackKeySerializer(SerializationConf
9898
// for subtypes.
9999
if (ClassUtil.isEnumType(rawKeyType)) {
100100
return EnumKeySerializer.construct(rawKeyType,
101-
EnumValues.constructFromName(config, annotatedClass),
102-
EnumSerializer.constructEnumNamingStrategyValues(config, (Class<Enum<?>>) rawKeyType, annotatedClass));
101+
EnumDefinition.construct(config, annotatedClass).valuesToWrite(config));
103102
}
104103
}
105104
// 19-Oct-2016, tatu: Used to just return DEFAULT_KEY_SERIALIZER but why not:
@@ -265,56 +264,36 @@ public void serialize(Object value, JsonGenerator g, SerializationContext provid
265264
*/
266265
public static class EnumKeySerializer extends StdSerializer<Object>
267266
{
268-
protected final EnumValues _values;
267+
protected final EnumValuesToWrite _valuesToWrite;
269268

270-
/**
271-
* Map with key as converted property class defined implementation of {@link EnumNamingStrategy}
272-
* and with value as Enum names collected using <code>Enum.name()</code>.
273-
*/
274-
protected final EnumValues _valuesByEnumNaming;
275-
276-
@Deprecated
277-
protected EnumKeySerializer(Class<?> enumType, EnumValues values) {
278-
this(enumType, values, null);
279-
}
280-
281-
protected EnumKeySerializer(Class<?> enumType, EnumValues values, EnumValues valuesByEnumNaming) {
282-
super(enumType);
283-
_values = values;
284-
_valuesByEnumNaming = valuesByEnumNaming;
285-
}
286-
287-
public static EnumKeySerializer construct(Class<?> enumType,
288-
EnumValues enumValues)
269+
protected EnumKeySerializer(Class<?> enumType, EnumValuesToWrite valuesToWrite)
289270
{
290-
return new EnumKeySerializer(enumType, enumValues);
271+
super(enumType);
272+
_valuesToWrite = valuesToWrite;
291273
}
292274

293275
public static EnumKeySerializer construct(Class<?> enumType,
294-
EnumValues enumValues, EnumValues valuesByEnumNaming)
276+
EnumValuesToWrite valuesToWrite)
295277
{
296-
return new EnumKeySerializer(enumType, enumValues, valuesByEnumNaming);
278+
return new EnumKeySerializer(enumType, valuesToWrite);
297279
}
298280

299281
@Override
300-
public void serialize(Object value, JsonGenerator g, SerializationContext serializers)
282+
public void serialize(Object value, JsonGenerator g, SerializationContext ctxt)
301283
throws JacksonException
302284
{
303-
if (serializers.isEnabled(EnumFeature.WRITE_ENUMS_USING_TO_STRING)) {
304-
g.writeName(value.toString());
305-
return;
306-
}
307-
Enum<?> en = (Enum<?>) value;
308-
if (_valuesByEnumNaming != null) {
309-
g.writeName(_valuesByEnumNaming.serializedValueFor(en));
310-
return;
311-
}
312-
// 14-Sep-2019, tatu: [databind#2129] Use this specific feature
313-
if (serializers.isEnabled(EnumFeature.WRITE_ENUM_KEYS_USING_INDEX)) {
285+
final Enum<?> en = (Enum<?>) value;
286+
287+
// 26-Nov-2025, tatu: In 3.0 order was opposite (TO_STRING first,
288+
// then INDEX); changed in 3.1
289+
if (ctxt.isEnabled(EnumFeature.WRITE_ENUM_KEYS_USING_INDEX)) {
290+
// 14-Sep-2019, tatu: [databind#2129] Use this specific feature
314291
g.writeName(String.valueOf(en.ordinal()));
315-
return;
292+
} else if (ctxt.isEnabled(EnumFeature.WRITE_ENUMS_USING_TO_STRING)) {
293+
g.writeName(_valuesToWrite.fromToString(ctxt.getConfig(), en));
294+
} else {
295+
g.writeName(_valuesToWrite.fromName(ctxt.getConfig(), en));
316296
}
317-
g.writeName(_values.serializedValueFor(en));
318297
}
319298
}
320299
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package tools.jackson.databind.util;
2+
3+
import java.util.Arrays;
4+
import java.util.List;
5+
6+
import tools.jackson.databind.AnnotationIntrospector;
7+
import tools.jackson.databind.EnumNamingStrategy;
8+
import tools.jackson.databind.cfg.MapperConfig;
9+
import tools.jackson.databind.introspect.AnnotatedClass;
10+
import tools.jackson.databind.introspect.EnumNamingStrategyFactory;
11+
12+
/**
13+
* Encapsulation of a {@link java.lang.Enum} type definition with its elements
14+
* and explicitly annotated names for elements.
15+
*
16+
* @since 3.0.3
17+
*/
18+
public class EnumDefinition
19+
{
20+
private final AnnotatedClass _annotatedClass;
21+
private final EnumNamingStrategy _enumNamingStrategy;
22+
private final Enum<?>[] _enumConstants;
23+
private final String[] _explicitNames;
24+
25+
private EnumDefinition(AnnotatedClass annotatedClass,
26+
EnumNamingStrategy enumNamingStrategy,
27+
Enum<?>[] enumConstants,
28+
String[] explicitNames)
29+
{
30+
_annotatedClass = annotatedClass;
31+
_enumNamingStrategy = enumNamingStrategy;
32+
_enumConstants = enumConstants;
33+
_explicitNames = explicitNames;
34+
}
35+
36+
public static EnumDefinition construct(MapperConfig<?> config,
37+
AnnotatedClass annotatedClass)
38+
{
39+
final Class<?> enumCls0 = annotatedClass.getRawType();
40+
final Enum<?>[] enumConstants = _enumConstants(enumCls0);
41+
String[] explicitNames = new String[enumConstants.length];
42+
43+
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
44+
if (ai != null) {
45+
explicitNames = ai.findEnumValues(config, annotatedClass,
46+
enumConstants, explicitNames);
47+
}
48+
Object namingDef = config.getAnnotationIntrospector().findEnumNamingStrategy(config, annotatedClass);
49+
EnumNamingStrategy enumNamingStrategy = EnumNamingStrategyFactory.createEnumNamingStrategyInstance(
50+
namingDef, config.canOverrideAccessModifiers(), config.getEnumNamingStrategy());
51+
52+
return new EnumDefinition(annotatedClass, enumNamingStrategy,
53+
enumConstants, explicitNames);
54+
}
55+
56+
public EnumValuesToWrite valuesToWrite(MapperConfig<?> config) {
57+
return EnumValuesToWrite.construct(config, _annotatedClass,
58+
_enumNamingStrategy,
59+
_enumConstants, _explicitNames);
60+
}
61+
62+
public int size() {
63+
return _enumConstants.length;
64+
}
65+
66+
@SuppressWarnings("unchecked")
67+
public Class<Enum<?>> enumClass() {
68+
Class<?> cls = _annotatedClass.getRawType();
69+
return (Class<Enum<?>>) cls;
70+
}
71+
72+
public Enum<?>[] enumConstants() {
73+
return _enumConstants;
74+
}
75+
76+
public List<String> explicitNames() {
77+
return Arrays.asList(_explicitNames);
78+
}
79+
80+
private static Enum<?>[] _enumConstants(Class<?> enumCls) {
81+
final Enum<?>[] enumValues = ClassUtil.findEnumType(enumCls).getEnumConstants();
82+
if (enumValues == null) {
83+
throw new IllegalArgumentException("Internal error: no Enum constants for Class "+enumCls.getName());
84+
}
85+
return enumValues;
86+
}
87+
}

src/main/java/tools/jackson/databind/util/EnumValues.java

Lines changed: 27 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -34,66 +34,52 @@ private EnumValues(Class<Enum<?>> enumClass, SerializableString[] textual)
3434
/**
3535
* NOTE: do NOT call this if configuration may change, and choice between toString()
3636
* and name() might change dynamically.
37+
*
38+
* @deprecated Since 3.1 call {@link #constructFromName} or {@link #constructFromToString}
39+
* instead.
3740
*/
41+
@Deprecated // since 3.1
3842
public static EnumValues construct(SerializationConfig config, AnnotatedClass enumClass) {
3943
if (config.isEnabled(EnumFeature.WRITE_ENUMS_USING_TO_STRING)) {
4044
return constructFromToString(config, enumClass);
4145
}
4246
return constructFromName(config, enumClass);
4347
}
4448

45-
/**
46-
* @since 2.16
47-
*/
48-
public static EnumValues constructFromName(MapperConfig<?> config, AnnotatedClass annotatedClass)
49+
public static EnumValues constructFromName(MapperConfig<?> config,
50+
AnnotatedClass annotatedClass)
4951
{
50-
// prepare data
51-
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
52-
final boolean useLowerCase = config.isEnabled(EnumFeature.WRITE_ENUMS_TO_LOWERCASE);
53-
final Class<?> enumCls0 = annotatedClass.getRawType();
54-
final Class<Enum<?>> enumCls = _enumClass(enumCls0);
55-
final Enum<?>[] enumConstants = _enumConstants(enumCls0);
52+
final EnumDefinition def = EnumDefinition.construct(config, annotatedClass);
53+
final Class<Enum<?>> enumCls = def.enumClass();
54+
final Enum<?>[] enumConstants = def.enumConstants();
5655

57-
// introspect
58-
String[] names = ai.findEnumValues(config, annotatedClass,
59-
enumConstants, new String[enumConstants.length]);
60-
61-
// build
56+
List<String> explicitNames = def.explicitNames();
6257
SerializableString[] textual = new SerializableString[enumConstants.length];
58+
final boolean useLowerCase = config.isEnabled(EnumFeature.WRITE_ENUMS_TO_LOWERCASE);
6359
for (int i = 0, len = enumConstants.length; i < len; ++i) {
6460
Enum<?> enumValue = enumConstants[i];
65-
String name = _findNameToUse(names[i], enumValue.name(), useLowerCase);
61+
String name = _findNameToUse(explicitNames.get(i), enumValue.name(), useLowerCase);
6662
textual[enumValue.ordinal()] = config.compileString(name);
6763
}
6864
return construct(enumCls, textual);
6965
}
7066

71-
/**
72-
* @since 2.16
73-
*/
74-
public static EnumValues constructFromToString(MapperConfig<?> config, AnnotatedClass annotatedClass)
67+
public static EnumValues constructFromToString(MapperConfig<?> config,
68+
AnnotatedClass annotatedClass)
7569
{
76-
// prepare data
77-
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
78-
final boolean useLowerCase = config.isEnabled(EnumFeature.WRITE_ENUMS_TO_LOWERCASE);
79-
final Class<?> enumCls0 = annotatedClass.getRawType();
80-
final Class<Enum<?>> enumCls = _enumClass(enumCls0);
81-
final Enum<?>[] enumConstants = _enumConstants(enumCls0);
82-
83-
// introspect
84-
String[] names = new String[enumConstants.length];
85-
if (ai != null) {
86-
ai.findEnumValues(config, annotatedClass, enumConstants, names);
87-
}
70+
final EnumDefinition def = EnumDefinition.construct(config, annotatedClass);
71+
final Class<Enum<?>> enumCls = def.enumClass();
72+
final Enum<?>[] enumConstants = def.enumConstants();
8873

89-
// build
74+
List<String> explicitNames = def.explicitNames();
9075
SerializableString[] textual = new SerializableString[enumConstants.length];
76+
final boolean useLowerCase = config.isEnabled(EnumFeature.WRITE_ENUMS_TO_LOWERCASE);
9177
for (int i = 0; i < enumConstants.length; i++) {
9278
String enumToString = enumConstants[i].toString();
9379
// 01-Feb-2024, tatu: [databind#4355] Nulls not great but... let's
9480
// coerce into "" for backwards compatibility
9581
enumToString = (enumToString == null) ? "" : enumToString;
96-
String name = _findNameToUse(names[i], enumToString, useLowerCase);
82+
String name = _findNameToUse(explicitNames.get(i), enumToString, useLowerCase);
9783
textual[i] = config.compileString(name);
9884
}
9985
return construct(enumCls, textual);
@@ -104,31 +90,21 @@ public static EnumValues constructFromToString(MapperConfig<?> config, Annotated
10490
* <p>
10591
* The output {@link EnumValues} should contain values that are symmetric to
10692
* {@link EnumResolver#constructUsingEnumNamingStrategy(DeserializationConfig, AnnotatedClass, EnumNamingStrategy)}.
107-
*
108-
* @since 2.16
10993
*/
11094
public static EnumValues constructUsingEnumNamingStrategy(MapperConfig<?> config,
11195
AnnotatedClass annotatedClass,
11296
EnumNamingStrategy namingStrategy)
11397
{
114-
// prepare data
115-
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
116-
final boolean useLowerCase = config.isEnabled(EnumFeature.WRITE_ENUMS_TO_LOWERCASE);
117-
final Class<?> enumCls0 = annotatedClass.getRawType();
118-
final Class<Enum<?>> enumCls = _enumClass(enumCls0);
119-
final Enum<?>[] enumConstants = _enumConstants(enumCls0);
120-
121-
// introspect
122-
String[] names = new String[enumConstants.length];
123-
if (ai != null) {
124-
ai.findEnumValues(config, annotatedClass, enumConstants, names);
125-
}
98+
final EnumDefinition def = EnumDefinition.construct(config, annotatedClass);
99+
final Class<Enum<?>> enumCls = def.enumClass();
100+
final Enum<?>[] enumConstants = def.enumConstants();
126101

127-
// build
102+
List<String> explicitNames = def.explicitNames();
128103
SerializableString[] textual = new SerializableString[enumConstants.length];
104+
final boolean useLowerCase = config.isEnabled(EnumFeature.WRITE_ENUMS_TO_LOWERCASE);
129105
for (int i = 0, len = enumConstants.length; i < len; i++) {
130106
Enum<?> enumValue = enumConstants[i];
131-
String name = _findNameToUse(names[i], namingStrategy.convertEnumToExternalName(config,
107+
String name = _findNameToUse(explicitNames.get(i), namingStrategy.convertEnumToExternalName(config,
132108
annotatedClass, enumValue.name()), useLowerCase);
133109
textual[i] = config.compileString(name);
134110
}
@@ -215,7 +191,7 @@ public EnumMap<?,SerializableString> internalMap() {
215191
EnumMap<?,SerializableString> result = _asMap;
216192
if (result == null) {
217193
// Alas, need to create it in a round-about way, due to typing constraints...
218-
Map<Enum<?>,SerializableString> map = new LinkedHashMap<Enum<?>,SerializableString>();
194+
Map<Enum<?>,SerializableString> map = new LinkedHashMap<>();
219195
for (Enum<?> en : _values) {
220196
map.put(en, _textual[en.ordinal()]);
221197
}

0 commit comments

Comments
 (0)