1 package com.fasterxml.jackson.databind.deser.std;
2
3 import java.io.IOException;
4
5 import com.fasterxml.jackson.annotation.JsonFormat;
6
7 import com.fasterxml.jackson.core.*;
8
9 import com.fasterxml.jackson.databind.*;
10 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
11 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
12 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
13 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
14 import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
15 import com.fasterxml.jackson.databind.util.ClassUtil;
16 import com.fasterxml.jackson.databind.util.CompactStringObjectMap;
17 import com.fasterxml.jackson.databind.util.EnumResolver;
18
19
23 @JacksonStdImpl
24 public class EnumDeserializer
25 extends StdScalarDeserializer<Object>
26 implements ContextualDeserializer
27 {
28 private static final long serialVersionUID = 1L;
29
30 protected Object[] _enumsByIndex;
31
32
35 private final Enum<?> _enumDefaultValue;
36
37
40 protected final CompactStringObjectMap _lookupByName;
41
42
48 protected CompactStringObjectMap _lookupByToString;
49
50 protected final Boolean _caseInsensitive;
51
52
55 public EnumDeserializer(EnumResolver byNameResolver, Boolean caseInsensitive)
56 {
57 super(byNameResolver.getEnumClass());
58 _lookupByName = byNameResolver.constructLookup();
59 _enumsByIndex = byNameResolver.getRawEnums();
60 _enumDefaultValue = byNameResolver.getDefaultValue();
61 _caseInsensitive = caseInsensitive;
62 }
63
64
67 protected EnumDeserializer(EnumDeserializer base, Boolean caseInsensitive)
68 {
69 super(base);
70 _lookupByName = base._lookupByName;
71 _enumsByIndex = base._enumsByIndex;
72 _enumDefaultValue = base._enumDefaultValue;
73 _caseInsensitive = caseInsensitive;
74 }
75
76
79 @Deprecated
80 public EnumDeserializer(EnumResolver byNameResolver) {
81 this(byNameResolver, null);
82 }
83
84
87 @Deprecated
88 public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
89 Class<?> enumClass, AnnotatedMethod factory) {
90 return deserializerForCreator(config, enumClass, factory, null, null);
91 }
92
93
101 public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
102 Class<?> enumClass, AnnotatedMethod factory,
103 ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps)
104 {
105 if (config.canOverrideAccessModifiers()) {
106 ClassUtil.checkAndFixAccess(factory.getMember(),
107 config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
108 }
109 return new FactoryBasedEnumDeserializer(enumClass, factory,
110 factory.getParameterType(0),
111 valueInstantiator, creatorProps);
112 }
113
114
122 public static JsonDeserializer<?> deserializerForNoArgsCreator(DeserializationConfig config,
123 Class<?> enumClass, AnnotatedMethod factory)
124 {
125 if (config.canOverrideAccessModifiers()) {
126 ClassUtil.checkAndFixAccess(factory.getMember(),
127 config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
128 }
129 return new FactoryBasedEnumDeserializer(enumClass, factory);
130 }
131
132
135 public EnumDeserializer withResolved(Boolean caseInsensitive) {
136 if (_caseInsensitive == caseInsensitive) {
137 return this;
138 }
139 return new EnumDeserializer(this, caseInsensitive);
140 }
141
142 @Override
143 public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
144 BeanProperty property) throws JsonMappingException
145 {
146 Boolean caseInsensitive = findFormatFeature(ctxt, property, handledType(),
147 JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
148 if (caseInsensitive == null) {
149 caseInsensitive = _caseInsensitive;
150 }
151 return withResolved(caseInsensitive);
152 }
153
154
159
160
164 @Override
165 public boolean isCachable() { return true; }
166
167 @Override
168 public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
169 {
170 JsonToken curr = p.currentToken();
171
172
173
174
175 if (curr == JsonToken.VALUE_STRING) {
176 CompactStringObjectMap lookup = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
177 ? _getToStringLookup(ctxt) : _lookupByName;
178 final String name = p.getText();
179 Object result = lookup.find(name);
180 if (result == null) {
181 return _deserializeAltString(p, ctxt, lookup, name);
182 }
183 return result;
184 }
185
186 if (curr == JsonToken.VALUE_NUMBER_INT) {
187
188 int index = p.getIntValue();
189 if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
190 return ctxt.handleWeirdNumberValue(_enumClass(), index,
191 "not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
192 );
193 }
194 if (index >= 0 && index < _enumsByIndex.length) {
195 return _enumsByIndex[index];
196 }
197 if ((_enumDefaultValue != null)
198 && ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
199 return _enumDefaultValue;
200 }
201 if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
202 return ctxt.handleWeirdNumberValue(_enumClass(), index,
203 "index value outside legal index range [0..%s]",
204 _enumsByIndex.length-1);
205 }
206 return null;
207 }
208 return _deserializeOther(p, ctxt);
209 }
210
211
216
217 private final Object _deserializeAltString(JsonParser p, DeserializationContext ctxt,
218 CompactStringObjectMap lookup, String name) throws IOException
219 {
220 name = name.trim();
221 if (name.length() == 0) {
222 if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
223 return getEmptyValue(ctxt);
224 }
225 } else {
226
227 if (Boolean.TRUE.equals(_caseInsensitive)) {
228 Object match = lookup.findCaseInsensitive(name);
229 if (match != null) {
230 return match;
231 }
232 } else if (!ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
233
234 char c = name.charAt(0);
235 if (c >= '0' && c <= '9') {
236 try {
237 int index = Integer.parseInt(name);
238 if (!ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS)) {
239 return ctxt.handleWeirdStringValue(_enumClass(), name,
240 "value looks like quoted Enum index, but `MapperFeature.ALLOW_COERCION_OF_SCALARS` prevents use"
241 );
242 }
243 if (index >= 0 && index < _enumsByIndex.length) {
244 return _enumsByIndex[index];
245 }
246 } catch (NumberFormatException e) {
247
248 }
249 }
250 }
251 }
252 if ((_enumDefaultValue != null)
253 && ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
254 return _enumDefaultValue;
255 }
256 if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
257 return ctxt.handleWeirdStringValue(_enumClass(), name,
258 "not one of the values accepted for Enum class: %s", lookup.keys());
259 }
260 return null;
261 }
262
263 protected Object _deserializeOther(JsonParser p, DeserializationContext ctxt) throws IOException
264 {
265
266 if (p.hasToken(JsonToken.START_ARRAY)) {
267 return _deserializeFromArray(p, ctxt);
268 }
269 return ctxt.handleUnexpectedToken(_enumClass(), p);
270 }
271
272 protected Class<?> _enumClass() {
273 return handledType();
274 }
275
276 protected CompactStringObjectMap _getToStringLookup(DeserializationContext ctxt)
277 {
278 CompactStringObjectMap lookup = _lookupByToString;
279
280
281 if (lookup == null) {
282 synchronized (this) {
283 lookup = EnumResolver.constructUnsafeUsingToString(_enumClass(),
284 ctxt.getAnnotationIntrospector())
285 .constructLookup();
286 }
287 _lookupByToString = lookup;
288 }
289 return lookup;
290 }
291 }
292