Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 1 | package com.fasterxml.jackson.databind.deser.std; |
| 2 | |
| 3 | import java.io.IOException; |
| 4 | import java.util.*; |
| 5 | |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 6 | import com.fasterxml.jackson.annotation.JsonFormat; |
Tatu Saloranta | 8ac635b | 2011-12-23 09:05:39 -0800 | [diff] [blame] | 7 | import com.fasterxml.jackson.core.*; |
Tatu | 333a7f2 | 2012-01-17 14:54:44 -0800 | [diff] [blame] | 8 | import com.fasterxml.jackson.databind.*; |
Tatu Saloranta | 082511b | 2012-01-30 09:05:05 -0800 | [diff] [blame] | 9 | import com.fasterxml.jackson.databind.deser.ContextualDeserializer; |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 10 | import com.fasterxml.jackson.databind.deser.NullValueProvider; |
| 11 | import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider; |
Tatu Saloranta | df6302f | 2011-12-23 20:05:35 -0800 | [diff] [blame] | 12 | import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; |
Tatu Saloranta | 9432f40 | 2019-10-28 21:04:02 -0700 | [diff] [blame] | 13 | import com.fasterxml.jackson.databind.util.AccessPattern; |
Tatu Saloranta | 54aa38d | 2019-09-29 12:12:38 -0700 | [diff] [blame] | 14 | import com.fasterxml.jackson.databind.util.ClassUtil; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 15 | |
| 16 | /** |
Tatu Saloranta | 082511b | 2012-01-30 09:05:05 -0800 | [diff] [blame] | 17 | * Standard deserializer for {@link EnumSet}s. |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 18 | * <p> |
| 19 | * Note: casting within this class is all messed up -- just could not figure out a way |
Tatu Saloranta | c4add92 | 2015-07-10 20:38:25 -0700 | [diff] [blame] | 20 | * to properly deal with recursive definition of "EnumSet<K extends Enum<K>, V> |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 21 | */ |
| 22 | @SuppressWarnings("rawtypes") |
| 23 | public class EnumSetDeserializer |
| 24 | extends StdDeserializer<EnumSet<?>> |
Tatu Saloranta | 082511b | 2012-01-30 09:05:05 -0800 | [diff] [blame] | 25 | implements ContextualDeserializer |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 26 | { |
Cowtowncoder | 078251b | 2014-11-25 14:38:31 -0800 | [diff] [blame] | 27 | private static final long serialVersionUID = 1L; // since 2.5 |
Tatu Saloranta | 4c8ee2e | 2012-11-18 19:56:49 -0800 | [diff] [blame] | 28 | |
Tatu | bf355ca | 2012-01-24 14:46:25 -0800 | [diff] [blame] | 29 | protected final JavaType _enumType; |
| 30 | |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 31 | protected final Class<Enum> _enumClass; |
| 32 | |
Tatu | bf355ca | 2012-01-24 14:46:25 -0800 | [diff] [blame] | 33 | protected JsonDeserializer<Enum<?>> _enumDeserializer; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 34 | |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 35 | /** |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 36 | * 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 Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 51 | * 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 Saloranta | 082511b | 2012-01-30 09:05:05 -0800 | [diff] [blame] | 59 | /* |
| 60 | /********************************************************** |
| 61 | /* Life-cycle |
| 62 | /********************************************************** |
| 63 | */ |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 64 | |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 65 | @SuppressWarnings("unchecked" ) |
Tatu Saloranta | 082511b | 2012-01-30 09:05:05 -0800 | [diff] [blame] | 66 | public EnumSetDeserializer(JavaType enumType, JsonDeserializer<?> deser) |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 67 | { |
| 68 | super(EnumSet.class); |
Tatu | bf355ca | 2012-01-24 14:46:25 -0800 | [diff] [blame] | 69 | _enumType = enumType; |
Tatu | bf355ca | 2012-01-24 14:46:25 -0800 | [diff] [blame] | 70 | _enumClass = (Class<Enum>) enumType.getRawClass(); |
Tatu Saloranta | 9917c94 | 2015-08-09 14:19:48 -0700 | [diff] [blame] | 71 | // sanity check |
Tatu Saloranta | 54aa38d | 2019-09-29 12:12:38 -0700 | [diff] [blame] | 72 | if (!ClassUtil.isEnumType(_enumClass)) { |
Tatu Saloranta | 9917c94 | 2015-08-09 14:19:48 -0700 | [diff] [blame] | 73 | throw new IllegalArgumentException("Type "+enumType+" not Java Enum type"); |
| 74 | } |
Tatu | 333a7f2 | 2012-01-17 14:54:44 -0800 | [diff] [blame] | 75 | _enumDeserializer = (JsonDeserializer<Enum<?>>) deser; |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 76 | _unwrapSingle = null; |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 77 | _nullProvider = null; |
| 78 | _skipNullValues = false; |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | /** |
| 82 | * @since 2.7 |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 83 | * @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 Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 93 | */ |
| 94 | @SuppressWarnings("unchecked" ) |
| 95 | protected EnumSetDeserializer(EnumSetDeserializer base, |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 96 | JsonDeserializer<?> deser, NullValueProvider nuller, Boolean unwrapSingle) { |
Tatu Saloranta | 5285e4a | 2017-02-23 11:34:04 -0800 | [diff] [blame] | 97 | super(base); |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 98 | _enumType = base._enumType; |
| 99 | _enumClass = base._enumClass; |
| 100 | _enumDeserializer = (JsonDeserializer<Enum<?>>) deser; |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 101 | _nullProvider = nuller; |
| 102 | _skipNullValues = NullsConstantProvider.isSkipper(nuller); |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 103 | _unwrapSingle = unwrapSingle; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 104 | } |
| 105 | |
Tatu Saloranta | 082511b | 2012-01-30 09:05:05 -0800 | [diff] [blame] | 106 | public EnumSetDeserializer withDeserializer(JsonDeserializer<?> deser) { |
Tatu | 6997ce3 | 2012-01-31 15:50:33 -0800 | [diff] [blame] | 107 | if (_enumDeserializer == deser) { |
| 108 | return this; |
| 109 | } |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 110 | return new EnumSetDeserializer(this, deser, _nullProvider, _unwrapSingle); |
Tatu Saloranta | 082511b | 2012-01-30 09:05:05 -0800 | [diff] [blame] | 111 | } |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 112 | |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 113 | @Deprecated // since 2.10.1 |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 114 | public EnumSetDeserializer withResolved(JsonDeserializer<?> deser, Boolean unwrapSingle) { |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 115 | 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 Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 124 | return this; |
| 125 | } |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 126 | return new EnumSetDeserializer(this, deser, nuller, unwrapSingle); |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 127 | } |
| 128 | |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 129 | /* |
| 130 | /********************************************************** |
| 131 | /* Basic metadata |
| 132 | /********************************************************** |
| 133 | */ |
| 134 | |
Tatu Saloranta | 530067b | 2011-12-28 10:35:22 -0800 | [diff] [blame] | 135 | /** |
| 136 | * Because of costs associated with constructing Enum resolvers, |
| 137 | * let's cache instances by default. |
| 138 | */ |
| 139 | @Override |
Cowtowncoder | 05db853 | 2015-03-26 13:19:59 -0700 | [diff] [blame] | 140 | 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 Saloranta | 55f3086 | 2016-10-23 20:35:26 -0700 | [diff] [blame] | 147 | |
| 148 | @Override // since 2.9 |
| 149 | public Boolean supportsUpdate(DeserializationConfig config) { |
| 150 | return Boolean.TRUE; |
| 151 | } |
| 152 | |
Tatu Saloranta | 9432f40 | 2019-10-28 21:04:02 -0700 | [diff] [blame] | 153 | @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 Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 162 | |
| 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 Saloranta | 082511b | 2012-01-30 09:05:05 -0800 | [diff] [blame] | 184 | /* |
| 185 | /********************************************************** |
| 186 | /* JsonDeserializer API |
| 187 | /********************************************************** |
| 188 | */ |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 189 | |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 190 | @Override |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 191 | public EnumSet<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 192 | { |
Tatu Saloranta | 55f3086 | 2016-10-23 20:35:26 -0700 | [diff] [blame] | 193 | EnumSet result = constructSet(); |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 194 | // Ok: must point to START_ARRAY (or equivalent) |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 195 | if (!p.isExpectedStartArrayToken()) { |
Tatu Saloranta | 55f3086 | 2016-10-23 20:35:26 -0700 | [diff] [blame] | 196 | return handleNonArray(p, ctxt, result); |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 197 | } |
Tatu Saloranta | 55f3086 | 2016-10-23 20:35:26 -0700 | [diff] [blame] | 198 | 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 Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 216 | JsonToken t; |
| 217 | |
Tatu Saloranta | 9e080e2 | 2014-07-24 22:33:25 -0700 | [diff] [blame] | 218 | try { |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 219 | while ((t = p.nextToken()) != JsonToken.END_ARRAY) { |
Tatu Saloranta | 9432f40 | 2019-10-28 21:04:02 -0700 | [diff] [blame] | 220 | // 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 Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 223 | Enum<?> value; |
Tatu Saloranta | 9e080e2 | 2014-07-24 22:33:25 -0700 | [diff] [blame] | 224 | if (t == JsonToken.VALUE_NULL) { |
Tatu Saloranta | 9d1cd50 | 2019-10-28 21:21:17 -0700 | [diff] [blame] | 225 | if (_skipNullValues) { |
| 226 | continue; |
| 227 | } |
| 228 | value = (Enum<?>) _nullProvider.getNullValue(ctxt); |
| 229 | } else { |
| 230 | value = _enumDeserializer.deserialize(p, ctxt); |
Tatu Saloranta | 9e080e2 | 2014-07-24 22:33:25 -0700 | [diff] [blame] | 231 | } |
Tatu Saloranta | 9e080e2 | 2014-07-24 22:33:25 -0700 | [diff] [blame] | 232 | if (value != null) { |
| 233 | result.add(value); |
| 234 | } |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 235 | } |
Tatu Saloranta | 9e080e2 | 2014-07-24 22:33:25 -0700 | [diff] [blame] | 236 | } catch (Exception e) { |
Tatu Saloranta | 5c9e4ce | 2014-07-26 21:19:58 -0700 | [diff] [blame] | 237 | throw JsonMappingException.wrapWithPath(e, result, result.size()); |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 238 | } |
| 239 | return result; |
| 240 | } |
| 241 | |
| 242 | @Override |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 243 | public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 244 | TypeDeserializer typeDeserializer) |
| 245 | throws IOException, JsonProcessingException |
| 246 | { |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 247 | return typeDeserializer.deserializeTypedFromArray(p, ctxt); |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 248 | } |
| 249 | |
| 250 | @SuppressWarnings("unchecked") |
| 251 | private EnumSet constructSet() |
| 252 | { |
Tatu Saloranta | 9917c94 | 2015-08-09 14:19:48 -0700 | [diff] [blame] | 253 | return EnumSet.noneOf(_enumClass); |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 254 | } |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 255 | |
| 256 | @SuppressWarnings("unchecked") |
Tatu Saloranta | 55f3086 | 2016-10-23 20:35:26 -0700 | [diff] [blame] | 257 | protected EnumSet<?> handleNonArray(JsonParser p, DeserializationContext ctxt, |
| 258 | EnumSet result) |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 259 | 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 Saloranta | 851092c | 2016-05-21 20:08:40 -0700 | [diff] [blame] | 266 | return (EnumSet<?>) ctxt.handleUnexpectedToken(EnumSet.class, p); |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 267 | } |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 268 | // First: since `null`s not allowed, slightly simpler... |
| 269 | if (p.hasToken(JsonToken.VALUE_NULL)) { |
Tatu Saloranta | 851092c | 2016-05-21 20:08:40 -0700 | [diff] [blame] | 270 | return (EnumSet<?>) ctxt.handleUnexpectedToken(_enumClass, p); |
Tatu Saloranta | 1addb1c | 2015-12-11 11:09:35 -0800 | [diff] [blame] | 271 | } |
| 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 Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 282 | } |