blob: 93eb07eab39b0e1b1432e1961f706e3d1ea938df [file] [log] [blame]
Tatu Salorantae4f23bb2011-12-23 00:31:35 -08001package com.fasterxml.jackson.databind.deser.std;
2
3import java.io.IOException;
4import java.util.*;
5
Tatu Saloranta1addb1c2015-12-11 11:09:35 -08006import com.fasterxml.jackson.annotation.JsonFormat;
Tatu Saloranta8ac635b2011-12-23 09:05:39 -08007import com.fasterxml.jackson.core.*;
Tatu333a7f22012-01-17 14:54:44 -08008import com.fasterxml.jackson.databind.*;
Tatu Saloranta082511b2012-01-30 09:05:05 -08009import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
Tatu Saloranta9d1cd502019-10-28 21:21:17 -070010import com.fasterxml.jackson.databind.deser.NullValueProvider;
11import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider;
Tatu Salorantadf6302f2011-12-23 20:05:35 -080012import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
Tatu Saloranta9432f402019-10-28 21:04:02 -070013import com.fasterxml.jackson.databind.util.AccessPattern;
Tatu Saloranta54aa38d2019-09-29 12:12:38 -070014import com.fasterxml.jackson.databind.util.ClassUtil;
Tatu Salorantae4f23bb2011-12-23 00:31:35 -080015
16/**
Tatu Saloranta082511b2012-01-30 09:05:05 -080017 * Standard deserializer for {@link EnumSet}s.
Tatu Salorantae4f23bb2011-12-23 00:31:35 -080018 * <p>
19 * Note: casting within this class is all messed up -- just could not figure out a way
Tatu Salorantac4add922015-07-10 20:38:25 -070020 * to properly deal with recursive definition of "EnumSet&lt;K extends Enum&lt;K&gt;, V&gt;
Tatu Salorantae4f23bb2011-12-23 00:31:35 -080021 */
22@SuppressWarnings("rawtypes")
23public class EnumSetDeserializer
24 extends StdDeserializer<EnumSet<?>>
Tatu Saloranta082511b2012-01-30 09:05:05 -080025 implements ContextualDeserializer
Tatu Salorantae4f23bb2011-12-23 00:31:35 -080026{
Cowtowncoder078251b2014-11-25 14:38:31 -080027 private static final long serialVersionUID = 1L; // since 2.5
Tatu Saloranta4c8ee2e2012-11-18 19:56:49 -080028
Tatubf355ca2012-01-24 14:46:25 -080029 protected final JavaType _enumType;
30
Tatu Salorantae4f23bb2011-12-23 00:31:35 -080031 protected final Class<Enum> _enumClass;
32
Tatubf355ca2012-01-24 14:46:25 -080033 protected JsonDeserializer<Enum<?>> _enumDeserializer;
Tatu Salorantae4f23bb2011-12-23 00:31:35 -080034
Tatu Saloranta1addb1c2015-12-11 11:09:35 -080035 /**
Tatu Saloranta9d1cd502019-10-28 21:21:17 -070036 * Handler we need for dealing with nulls.
37 *
38 * @since 2.10.1
39 */
40 protected final NullValueProvider _nullProvider;
41
42 /**
43 * Marker flag set if the <code>_nullProvider</code> indicates that all null
44 * content values should be skipped (instead of being possibly converted).
45 *
46 * @since 2.10.1
47 */
48 protected final boolean _skipNullValues;
49
50 /**
Tatu Saloranta1addb1c2015-12-11 11:09:35 -080051 * Specific override for this instance (from proper, or global per-type overrides)
52 * to indicate whether single value may be taken to mean an unwrapped one-element array
53 * or not. If null, left to global defaults.
54 *
55 * @since 2.7
56 */
57 protected final Boolean _unwrapSingle;
58
Tatu Saloranta082511b2012-01-30 09:05:05 -080059 /*
60 /**********************************************************
61 /* Life-cycle
62 /**********************************************************
63 */
Tatu Saloranta1addb1c2015-12-11 11:09:35 -080064
Tatu Salorantae4f23bb2011-12-23 00:31:35 -080065 @SuppressWarnings("unchecked" )
Tatu Saloranta082511b2012-01-30 09:05:05 -080066 public EnumSetDeserializer(JavaType enumType, JsonDeserializer<?> deser)
Tatu Salorantae4f23bb2011-12-23 00:31:35 -080067 {
68 super(EnumSet.class);
Tatubf355ca2012-01-24 14:46:25 -080069 _enumType = enumType;
Tatubf355ca2012-01-24 14:46:25 -080070 _enumClass = (Class<Enum>) enumType.getRawClass();
Tatu Saloranta9917c942015-08-09 14:19:48 -070071 // sanity check
Tatu Saloranta54aa38d2019-09-29 12:12:38 -070072 if (!ClassUtil.isEnumType(_enumClass)) {
Tatu Saloranta9917c942015-08-09 14:19:48 -070073 throw new IllegalArgumentException("Type "+enumType+" not Java Enum type");
74 }
Tatu333a7f22012-01-17 14:54:44 -080075 _enumDeserializer = (JsonDeserializer<Enum<?>>) deser;
Tatu Saloranta1addb1c2015-12-11 11:09:35 -080076 _unwrapSingle = null;
Tatu Saloranta9d1cd502019-10-28 21:21:17 -070077 _nullProvider = null;
78 _skipNullValues = false;
Tatu Saloranta1addb1c2015-12-11 11:09:35 -080079 }
80
81 /**
82 * @since 2.7
Tatu Saloranta9d1cd502019-10-28 21:21:17 -070083 * @deprecated Since 2.10.1
84 */
85 @Deprecated
86 protected EnumSetDeserializer(EnumSetDeserializer base,
87 JsonDeserializer<?> deser, Boolean unwrapSingle) {
88 this(base, deser, base._nullProvider, unwrapSingle);
89 }
90
91 /**
92 * @since 2.10.1
Tatu Saloranta1addb1c2015-12-11 11:09:35 -080093 */
94 @SuppressWarnings("unchecked" )
95 protected EnumSetDeserializer(EnumSetDeserializer base,
Tatu Saloranta9d1cd502019-10-28 21:21:17 -070096 JsonDeserializer<?> deser, NullValueProvider nuller, Boolean unwrapSingle) {
Tatu Saloranta5285e4a2017-02-23 11:34:04 -080097 super(base);
Tatu Saloranta1addb1c2015-12-11 11:09:35 -080098 _enumType = base._enumType;
99 _enumClass = base._enumClass;
100 _enumDeserializer = (JsonDeserializer<Enum<?>>) deser;
Tatu Saloranta9d1cd502019-10-28 21:21:17 -0700101 _nullProvider = nuller;
102 _skipNullValues = NullsConstantProvider.isSkipper(nuller);
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800103 _unwrapSingle = unwrapSingle;
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800104 }
105
Tatu Saloranta082511b2012-01-30 09:05:05 -0800106 public EnumSetDeserializer withDeserializer(JsonDeserializer<?> deser) {
Tatu6997ce32012-01-31 15:50:33 -0800107 if (_enumDeserializer == deser) {
108 return this;
109 }
Tatu Saloranta9d1cd502019-10-28 21:21:17 -0700110 return new EnumSetDeserializer(this, deser, _nullProvider, _unwrapSingle);
Tatu Saloranta082511b2012-01-30 09:05:05 -0800111 }
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800112
Tatu Saloranta9d1cd502019-10-28 21:21:17 -0700113 @Deprecated // since 2.10.1
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800114 public EnumSetDeserializer withResolved(JsonDeserializer<?> deser, Boolean unwrapSingle) {
Tatu Saloranta9d1cd502019-10-28 21:21:17 -0700115 return withResolved(deser, _nullProvider, unwrapSingle);
116 }
117
118 /**
119 * @since 2.10.1
120 */
121 public EnumSetDeserializer withResolved(JsonDeserializer<?> deser, NullValueProvider nuller,
122 Boolean unwrapSingle) {
123 if ((_unwrapSingle == unwrapSingle) && (_enumDeserializer == deser) && (_nullProvider == deser)) {
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800124 return this;
125 }
Tatu Saloranta9d1cd502019-10-28 21:21:17 -0700126 return new EnumSetDeserializer(this, deser, nuller, unwrapSingle);
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800127 }
128
Tatu Saloranta9d1cd502019-10-28 21:21:17 -0700129 /*
130 /**********************************************************
131 /* Basic metadata
132 /**********************************************************
133 */
134
Tatu Saloranta530067b2011-12-28 10:35:22 -0800135 /**
136 * Because of costs associated with constructing Enum resolvers,
137 * let's cache instances by default.
138 */
139 @Override
Cowtowncoder05db8532015-03-26 13:19:59 -0700140 public boolean isCachable() {
141 // One caveat: content deserializer should prevent caching
142 if (_enumType.getValueHandler() != null) {
143 return false;
144 }
145 return true;
146 }
Tatu Saloranta55f30862016-10-23 20:35:26 -0700147
148 @Override // since 2.9
149 public Boolean supportsUpdate(DeserializationConfig config) {
150 return Boolean.TRUE;
151 }
152
Tatu Saloranta9432f402019-10-28 21:04:02 -0700153 @Override // since 2.10.1
154 public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
155 return constructSet();
156 }
157
158 @Override // since 2.10.1
159 public AccessPattern getEmptyAccessPattern() {
160 return AccessPattern.DYNAMIC;
161 }
Tatu Saloranta9d1cd502019-10-28 21:21:17 -0700162
163 /*
164 /**********************************************************
165 /* Contextualization
166 /**********************************************************
167 */
168
169 @Override
170 public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
171 BeanProperty property) throws JsonMappingException
172 {
173 final Boolean unwrapSingle = findFormatFeature(ctxt, property, EnumSet.class,
174 JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
175 JsonDeserializer<?> deser = _enumDeserializer;
176 if (deser == null) {
177 deser = ctxt.findContextualValueDeserializer(_enumType, property);
178 } else { // if directly assigned, probably not yet contextual, so:
179 deser = ctxt.handleSecondaryContextualization(deser, property, _enumType);
180 }
181 return withResolved(deser, findContentNullProvider(ctxt, property, deser), unwrapSingle);
182 }
183
Tatu Saloranta082511b2012-01-30 09:05:05 -0800184 /*
185 /**********************************************************
186 /* JsonDeserializer API
187 /**********************************************************
188 */
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800189
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800190 @Override
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800191 public EnumSet<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800192 {
Tatu Saloranta55f30862016-10-23 20:35:26 -0700193 EnumSet result = constructSet();
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800194 // Ok: must point to START_ARRAY (or equivalent)
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800195 if (!p.isExpectedStartArrayToken()) {
Tatu Saloranta55f30862016-10-23 20:35:26 -0700196 return handleNonArray(p, ctxt, result);
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800197 }
Tatu Saloranta55f30862016-10-23 20:35:26 -0700198 return _deserialize(p, ctxt, result);
199 }
200
201 @Override
202 public EnumSet<?> deserialize(JsonParser p, DeserializationContext ctxt,
203 EnumSet<?> result) throws IOException
204 {
205 // Ok: must point to START_ARRAY (or equivalent)
206 if (!p.isExpectedStartArrayToken()) {
207 return handleNonArray(p, ctxt, result);
208 }
209 return _deserialize(p, ctxt, result);
210 }
211
212 @SuppressWarnings("unchecked")
213 protected final EnumSet<?> _deserialize(JsonParser p, DeserializationContext ctxt,
214 EnumSet result) throws IOException
215 {
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800216 JsonToken t;
217
Tatu Saloranta9e080e22014-07-24 22:33:25 -0700218 try {
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800219 while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
Tatu Saloranta9432f402019-10-28 21:04:02 -0700220 // What to do with nulls? Fail or ignore? Fail, for now (note: would fail if we
221 // passed it to EnumDeserializer, too, but in general nulls should never be passed
222 // to non-container deserializers)
Tatu Saloranta9d1cd502019-10-28 21:21:17 -0700223 Enum<?> value;
Tatu Saloranta9e080e22014-07-24 22:33:25 -0700224 if (t == JsonToken.VALUE_NULL) {
Tatu Saloranta9d1cd502019-10-28 21:21:17 -0700225 if (_skipNullValues) {
226 continue;
227 }
228 value = (Enum<?>) _nullProvider.getNullValue(ctxt);
229 } else {
230 value = _enumDeserializer.deserialize(p, ctxt);
Tatu Saloranta9e080e22014-07-24 22:33:25 -0700231 }
Tatu Saloranta9e080e22014-07-24 22:33:25 -0700232 if (value != null) {
233 result.add(value);
234 }
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800235 }
Tatu Saloranta9e080e22014-07-24 22:33:25 -0700236 } catch (Exception e) {
Tatu Saloranta5c9e4ce2014-07-26 21:19:58 -0700237 throw JsonMappingException.wrapWithPath(e, result, result.size());
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800238 }
239 return result;
240 }
241
242 @Override
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800243 public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800244 TypeDeserializer typeDeserializer)
245 throws IOException, JsonProcessingException
246 {
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800247 return typeDeserializer.deserializeTypedFromArray(p, ctxt);
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800248 }
249
250 @SuppressWarnings("unchecked")
251 private EnumSet constructSet()
252 {
Tatu Saloranta9917c942015-08-09 14:19:48 -0700253 return EnumSet.noneOf(_enumClass);
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800254 }
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800255
256 @SuppressWarnings("unchecked")
Tatu Saloranta55f30862016-10-23 20:35:26 -0700257 protected EnumSet<?> handleNonArray(JsonParser p, DeserializationContext ctxt,
258 EnumSet result)
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800259 throws IOException
260 {
261 boolean canWrap = (_unwrapSingle == Boolean.TRUE) ||
262 ((_unwrapSingle == null) &&
263 ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
264
265 if (!canWrap) {
Tatu Saloranta851092c2016-05-21 20:08:40 -0700266 return (EnumSet<?>) ctxt.handleUnexpectedToken(EnumSet.class, p);
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800267 }
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800268 // First: since `null`s not allowed, slightly simpler...
269 if (p.hasToken(JsonToken.VALUE_NULL)) {
Tatu Saloranta851092c2016-05-21 20:08:40 -0700270 return (EnumSet<?>) ctxt.handleUnexpectedToken(_enumClass, p);
Tatu Saloranta1addb1c2015-12-11 11:09:35 -0800271 }
272 try {
273 Enum<?> value = _enumDeserializer.deserialize(p, ctxt);
274 if (value != null) {
275 result.add(value);
276 }
277 } catch (Exception e) {
278 throw JsonMappingException.wrapWithPath(e, result, result.size());
279 }
280 return result;
281 }
Tatu Salorantae4f23bb2011-12-23 00:31:35 -0800282}