1 package com.fasterxml.jackson.databind.deser.std; 2 3 import java.io.IOException; 4 import java.io.Serializable; 5 import java.lang.reflect.Constructor; 6 import java.lang.reflect.Method; 7 import java.net.MalformedURLException; 8 import java.net.URI; 9 import java.net.URL; 10 import java.util.*; 11 12 import com.fasterxml.jackson.core.JsonParser; 13 import com.fasterxml.jackson.core.JsonProcessingException; 14 import com.fasterxml.jackson.core.io.NumberInput; 15 import com.fasterxml.jackson.databind.*; 16 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; 17 import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; 18 import com.fasterxml.jackson.databind.util.ClassUtil; 19 import com.fasterxml.jackson.databind.util.EnumResolver; 20 import com.fasterxml.jackson.databind.util.TokenBuffer; 21 22 /** 23 * Default {@link KeyDeserializer} implementation used for most {@link java.util.Map} 24 * types Jackson supports. 25 * Implemented as "chameleon" (or swiss pocket knife) class; not particularly elegant, 26 * but helps reduce number of classes and jar size (class metadata adds significant 27 * per-class overhead; much more than bytecode). 28 */ 29 @JacksonStdImpl 30 public class StdKeyDeserializer extends KeyDeserializer 31 implements java.io.Serializable 32 { 33 private static final long serialVersionUID = 1L; 34 35 public final static int TYPE_BOOLEAN = 1; 36 public final static int TYPE_BYTE = 2; 37 public final static int TYPE_SHORT = 3; 38 public final static int TYPE_CHAR = 4; 39 public final static int TYPE_INT = 5; 40 public final static int TYPE_LONG = 6; 41 public final static int TYPE_FLOAT = 7; 42 public final static int TYPE_DOUBLE = 8; 43 public final static int TYPE_LOCALE = 9; 44 public final static int TYPE_DATE = 10; 45 public final static int TYPE_CALENDAR = 11; 46 public final static int TYPE_UUID = 12; 47 public final static int TYPE_URI = 13; 48 public final static int TYPE_URL = 14; 49 public final static int TYPE_CLASS = 15; 50 public final static int TYPE_CURRENCY = 16; 51 public final static int TYPE_BYTE_ARRAY = 17; // since 2.9 52 53 final protected int _kind; 54 final protected Class<?> _keyClass; 55 56 /** 57 * Some types that are deserialized using a helper deserializer. 58 */ 59 protected final FromStringDeserializer<?> _deser; 60 StdKeyDeserializer(int kind, Class<?> cls)61 protected StdKeyDeserializer(int kind, Class<?> cls) { 62 this(kind, cls, null); 63 } 64 StdKeyDeserializer(int kind, Class<?> cls, FromStringDeserializer<?> deser)65 protected StdKeyDeserializer(int kind, Class<?> cls, FromStringDeserializer<?> deser) { 66 _kind = kind; 67 _keyClass = cls; 68 _deser = deser; 69 } 70 forType(Class<?> raw)71 public static StdKeyDeserializer forType(Class<?> raw) 72 { 73 int kind; 74 75 // first common types: 76 if (raw == String.class || raw == Object.class 77 || raw == CharSequence.class 78 // see [databind#2115]: 79 || raw == Serializable.class) { 80 return StringKD.forType(raw); 81 } 82 if (raw == UUID.class) { 83 kind = TYPE_UUID; 84 } else if (raw == Integer.class) { 85 kind = TYPE_INT; 86 } else if (raw == Long.class) { 87 kind = TYPE_LONG; 88 } else if (raw == Date.class) { 89 kind = TYPE_DATE; 90 } else if (raw == Calendar.class) { 91 kind = TYPE_CALENDAR; 92 // then less common ones... 93 } else if (raw == Boolean.class) { 94 kind = TYPE_BOOLEAN; 95 } else if (raw == Byte.class) { 96 kind = TYPE_BYTE; 97 } else if (raw == Character.class) { 98 kind = TYPE_CHAR; 99 } else if (raw == Short.class) { 100 kind = TYPE_SHORT; 101 } else if (raw == Float.class) { 102 kind = TYPE_FLOAT; 103 } else if (raw == Double.class) { 104 kind = TYPE_DOUBLE; 105 } else if (raw == URI.class) { 106 kind = TYPE_URI; 107 } else if (raw == URL.class) { 108 kind = TYPE_URL; 109 } else if (raw == Class.class) { 110 kind = TYPE_CLASS; 111 } else if (raw == Locale.class) { 112 FromStringDeserializer<?> deser = FromStringDeserializer.findDeserializer(Locale.class); 113 return new StdKeyDeserializer(TYPE_LOCALE, raw, deser); 114 } else if (raw == Currency.class) { 115 FromStringDeserializer<?> deser = FromStringDeserializer.findDeserializer(Currency.class); 116 return new StdKeyDeserializer(TYPE_CURRENCY, raw, deser); 117 } else if (raw == byte[].class) { 118 kind = TYPE_BYTE_ARRAY; 119 } else { 120 return null; 121 } 122 return new StdKeyDeserializer(kind, raw); 123 } 124 125 @Override deserializeKey(String key, DeserializationContext ctxt)126 public Object deserializeKey(String key, DeserializationContext ctxt) 127 throws IOException 128 { 129 if (key == null) { // is this even legal call? 130 return null; 131 } 132 try { 133 Object result = _parse(key, ctxt); 134 if (result != null) { 135 return result; 136 } 137 } catch (Exception re) { 138 return ctxt.handleWeirdKey(_keyClass, key, "not a valid representation, problem: (%s) %s", 139 re.getClass().getName(), 140 ClassUtil.exceptionMessage(re)); 141 } 142 if (ClassUtil.isEnumType(_keyClass) 143 && ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) { 144 return null; 145 } 146 return ctxt.handleWeirdKey(_keyClass, key, "not a valid representation"); 147 } 148 getKeyClass()149 public Class<?> getKeyClass() { return _keyClass; } 150 _parse(String key, DeserializationContext ctxt)151 protected Object _parse(String key, DeserializationContext ctxt) throws Exception 152 { 153 switch (_kind) { 154 case TYPE_BOOLEAN: 155 if ("true".equals(key)) { 156 return Boolean.TRUE; 157 } 158 if ("false".equals(key)) { 159 return Boolean.FALSE; 160 } 161 return ctxt.handleWeirdKey(_keyClass, key, "value not 'true' or 'false'"); 162 case TYPE_BYTE: 163 { 164 int value = _parseInt(key); 165 // allow range up to 255, inclusive (to support "unsigned" byte) 166 if (value < Byte.MIN_VALUE || value > 255) { 167 return ctxt.handleWeirdKey(_keyClass, key, "overflow, value cannot be represented as 8-bit value"); 168 } 169 return Byte.valueOf((byte) value); 170 } 171 case TYPE_SHORT: 172 { 173 int value = _parseInt(key); 174 if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { 175 return ctxt.handleWeirdKey(_keyClass, key, "overflow, value cannot be represented as 16-bit value"); 176 // fall-through and truncate if need be 177 } 178 return Short.valueOf((short) value); 179 } 180 case TYPE_CHAR: 181 if (key.length() == 1) { 182 return Character.valueOf(key.charAt(0)); 183 } 184 return ctxt.handleWeirdKey(_keyClass, key, "can only convert 1-character Strings"); 185 case TYPE_INT: 186 return _parseInt(key); 187 188 case TYPE_LONG: 189 return _parseLong(key); 190 191 case TYPE_FLOAT: 192 // Bounds/range checks would be tricky here, so let's not bother even trying... 193 return Float.valueOf((float) _parseDouble(key)); 194 case TYPE_DOUBLE: 195 return _parseDouble(key); 196 case TYPE_LOCALE: 197 try { 198 return _deser._deserialize(key, ctxt); 199 } catch (IllegalArgumentException e) { 200 return _weirdKey(ctxt, key, e); 201 } 202 case TYPE_CURRENCY: 203 try { 204 return _deser._deserialize(key, ctxt); 205 } catch (IllegalArgumentException e) { 206 return _weirdKey(ctxt, key, e); 207 } 208 case TYPE_DATE: 209 return ctxt.parseDate(key); 210 case TYPE_CALENDAR: 211 return ctxt.constructCalendar(ctxt.parseDate(key)); 212 case TYPE_UUID: 213 try { 214 return UUID.fromString(key); 215 } catch (Exception e) { 216 return _weirdKey(ctxt, key, e); 217 } 218 case TYPE_URI: 219 try { 220 return URI.create(key); 221 } catch (Exception e) { 222 return _weirdKey(ctxt, key, e); 223 } 224 case TYPE_URL: 225 try { 226 return new URL(key); 227 } catch (MalformedURLException e) { 228 return _weirdKey(ctxt, key, e); 229 } 230 case TYPE_CLASS: 231 try { 232 return ctxt.findClass(key); 233 } catch (Exception e) { 234 return ctxt.handleWeirdKey(_keyClass, key, "unable to parse key as Class"); 235 } 236 case TYPE_BYTE_ARRAY: 237 try { 238 return ctxt.getConfig().getBase64Variant().decode(key); 239 } catch (IllegalArgumentException e) { 240 return _weirdKey(ctxt, key, e); 241 } 242 default: 243 throw new IllegalStateException("Internal error: unknown key type "+_keyClass); 244 } 245 } 246 247 /* 248 /********************************************************** 249 /* Helper methods for sub-classes 250 /********************************************************** 251 */ 252 _parseInt(String key)253 protected int _parseInt(String key) throws IllegalArgumentException { 254 return Integer.parseInt(key); 255 } 256 _parseLong(String key)257 protected long _parseLong(String key) throws IllegalArgumentException { 258 return Long.parseLong(key); 259 } 260 _parseDouble(String key)261 protected double _parseDouble(String key) throws IllegalArgumentException { 262 return NumberInput.parseDouble(key); 263 } 264 265 // @since 2.9 _weirdKey(DeserializationContext ctxt, String key, Exception e)266 protected Object _weirdKey(DeserializationContext ctxt, String key, Exception e) throws IOException { 267 return ctxt.handleWeirdKey(_keyClass, key, "problem: %s", 268 ClassUtil.exceptionMessage(e)); 269 } 270 271 /* 272 /********************************************************** 273 /* First: the standard "String as String" deserializer 274 /********************************************************** 275 */ 276 277 @JacksonStdImpl 278 final static class StringKD extends StdKeyDeserializer 279 { 280 private static final long serialVersionUID = 1L; 281 private final static StringKD sString = new StringKD(String.class); 282 private final static StringKD sObject = new StringKD(Object.class); 283 StringKD(Class<?> nominalType)284 private StringKD(Class<?> nominalType) { super(-1, nominalType); } 285 forType(Class<?> nominalType)286 public static StringKD forType(Class<?> nominalType) 287 { 288 if (nominalType == String.class) { 289 return sString; 290 } 291 if (nominalType == Object.class) { 292 return sObject; 293 } 294 return new StringKD(nominalType); 295 } 296 297 @Override deserializeKey(String key, DeserializationContext ctxt)298 public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException { 299 return key; 300 } 301 } 302 303 /* 304 /********************************************************** 305 /* Key deserializer implementations; other 306 /********************************************************** 307 */ 308 309 /** 310 * Key deserializer that wraps a "regular" deserializer (but one 311 * that must recognize FIELD_NAMEs as text!) to reuse existing 312 * handlers as key handlers. 313 */ 314 final static class DelegatingKD 315 extends KeyDeserializer // note: NOT the std one 316 implements java.io.Serializable 317 { 318 private static final long serialVersionUID = 1L; 319 320 final protected Class<?> _keyClass; 321 322 protected final JsonDeserializer<?> _delegate; 323 DelegatingKD(Class<?> cls, JsonDeserializer<?> deser)324 protected DelegatingKD(Class<?> cls, JsonDeserializer<?> deser) { 325 _keyClass = cls; 326 _delegate = deser; 327 } 328 329 @SuppressWarnings("resource") 330 @Override deserializeKey(String key, DeserializationContext ctxt)331 public final Object deserializeKey(String key, DeserializationContext ctxt) 332 throws IOException 333 { 334 if (key == null) { // is this even legal call? 335 return null; 336 } 337 TokenBuffer tb = new TokenBuffer(ctxt.getParser(), ctxt); 338 tb.writeString(key); 339 try { 340 // Ugh... should not have to give parser which may or may not be correct one... 341 JsonParser p = tb.asParser(); 342 p.nextToken(); 343 Object result = _delegate.deserialize(p, ctxt); 344 if (result != null) { 345 return result; 346 } 347 return ctxt.handleWeirdKey(_keyClass, key, "not a valid representation"); 348 } catch (Exception re) { 349 return ctxt.handleWeirdKey(_keyClass, key, "not a valid representation: %s", re.getMessage()); 350 } 351 } 352 getKeyClass()353 public Class<?> getKeyClass() { return _keyClass; } 354 } 355 356 @JacksonStdImpl 357 final static class EnumKD extends StdKeyDeserializer 358 { 359 private static final long serialVersionUID = 1L; 360 361 protected final EnumResolver _byNameResolver; 362 363 protected final AnnotatedMethod _factory; 364 365 /** 366 * Lazily constructed alternative in case there is need to 367 * use 'toString()' method as the source. 368 * 369 * @since 2.7.3 370 */ 371 protected EnumResolver _byToStringResolver; 372 373 protected final Enum<?> _enumDefaultValue; 374 EnumKD(EnumResolver er, AnnotatedMethod factory)375 protected EnumKD(EnumResolver er, AnnotatedMethod factory) { 376 super(-1, er.getEnumClass()); 377 _byNameResolver = er; 378 _factory = factory; 379 _enumDefaultValue = er.getDefaultValue(); 380 } 381 382 @Override _parse(String key, DeserializationContext ctxt)383 public Object _parse(String key, DeserializationContext ctxt) throws IOException 384 { 385 if (_factory != null) { 386 try { 387 return _factory.call1(key); 388 } catch (Exception e) { 389 ClassUtil.unwrapAndThrowAsIAE(e); 390 } 391 } 392 EnumResolver res = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING) 393 ? _getToStringResolver(ctxt) : _byNameResolver; 394 Enum<?> e = res.findEnum(key); 395 if (e == null) { 396 if ((_enumDefaultValue != null) 397 && ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) { 398 e = _enumDefaultValue; 399 } else if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) { 400 return ctxt.handleWeirdKey(_keyClass, key, "not one of the values accepted for Enum class: %s", 401 res.getEnumIds()); 402 } 403 // fall-through if problems are collected, not immediately thrown 404 } 405 return e; 406 } 407 _getToStringResolver(DeserializationContext ctxt)408 private EnumResolver _getToStringResolver(DeserializationContext ctxt) 409 { 410 EnumResolver res = _byToStringResolver; 411 if (res == null) { 412 synchronized (this) { 413 res = EnumResolver.constructUnsafeUsingToString(_byNameResolver.getEnumClass(), 414 ctxt.getAnnotationIntrospector()); 415 _byToStringResolver = res; 416 } 417 } 418 return res; 419 } 420 } 421 422 /** 423 * Key deserializer that calls a single-string-arg constructor 424 * to instantiate desired key type. 425 */ 426 final static class StringCtorKeyDeserializer extends StdKeyDeserializer 427 { 428 private static final long serialVersionUID = 1L; 429 430 protected final Constructor<?> _ctor; 431 StringCtorKeyDeserializer(Constructor<?> ctor)432 public StringCtorKeyDeserializer(Constructor<?> ctor) { 433 super(-1, ctor.getDeclaringClass()); 434 _ctor = ctor; 435 } 436 437 @Override _parse(String key, DeserializationContext ctxt)438 public Object _parse(String key, DeserializationContext ctxt) throws Exception 439 { 440 return _ctor.newInstance(key); 441 } 442 } 443 444 /** 445 * Key deserializer that calls a static no-args factory method 446 * to instantiate desired key type. 447 */ 448 final static class StringFactoryKeyDeserializer extends StdKeyDeserializer 449 { 450 private static final long serialVersionUID = 1L; 451 452 final Method _factoryMethod; 453 StringFactoryKeyDeserializer(Method fm)454 public StringFactoryKeyDeserializer(Method fm) { 455 super(-1, fm.getDeclaringClass()); 456 _factoryMethod = fm; 457 } 458 459 @Override _parse(String key, DeserializationContext ctxt)460 public Object _parse(String key, DeserializationContext ctxt) throws Exception 461 { 462 return _factoryMethod.invoke(null, key); 463 } 464 } 465 } 466 467