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
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;
52
53 final protected int _kind;
54 final protected Class<?> _keyClass;
55
56
59 protected final FromStringDeserializer<?> _deser;
60
61 protected StdKeyDeserializer(int kind, Class<?> cls) {
62 this(kind, cls, null);
63 }
64
65 protected StdKeyDeserializer(int kind, Class<?> cls, FromStringDeserializer<?> deser) {
66 _kind = kind;
67 _keyClass = cls;
68 _deser = deser;
69 }
70
71 public static StdKeyDeserializer forType(Class<?> raw)
72 {
73 int kind;
74
75
76 if (raw == String.class || raw == Object.class
77 || raw == CharSequence.class
78
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
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
126 public Object deserializeKey(String key, DeserializationContext ctxt)
127 throws IOException
128 {
129 if (key == null) {
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
149 public Class<?> getKeyClass() { return _keyClass; }
150
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
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
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
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
252
253 protected int _parseInt(String key) throws IllegalArgumentException {
254 return Integer.parseInt(key);
255 }
256
257 protected long _parseLong(String key) throws IllegalArgumentException {
258 return Long.parseLong(key);
259 }
260
261 protected double _parseDouble(String key) throws IllegalArgumentException {
262 return NumberInput.parseDouble(key);
263 }
264
265
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
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
284 private StringKD(Class<?> nominalType) { super(-1, nominalType); }
285
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
298 public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {
299 return key;
300 }
301 }
302
303
308
309
314 final static class DelegatingKD
315 extends KeyDeserializer
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
324 protected DelegatingKD(Class<?> cls, JsonDeserializer<?> deser) {
325 _keyClass = cls;
326 _delegate = deser;
327 }
328
329 @SuppressWarnings("resource")
330 @Override
331 public final Object deserializeKey(String key, DeserializationContext ctxt)
332 throws IOException
333 {
334 if (key == null) {
335 return null;
336 }
337 TokenBuffer tb = new TokenBuffer(ctxt.getParser(), ctxt);
338 tb.writeString(key);
339 try {
340
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
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
371 protected EnumResolver _byToStringResolver;
372
373 protected final Enum<?> _enumDefaultValue;
374
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
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
404 }
405 return e;
406 }
407
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
426 final static class StringCtorKeyDeserializer extends StdKeyDeserializer
427 {
428 private static final long serialVersionUID = 1L;
429
430 protected final Constructor<?> _ctor;
431
432 public StringCtorKeyDeserializer(Constructor<?> ctor) {
433 super(-1, ctor.getDeclaringClass());
434 _ctor = ctor;
435 }
436
437 @Override
438 public Object _parse(String key, DeserializationContext ctxt) throws Exception
439 {
440 return _ctor.newInstance(key);
441 }
442 }
443
444
448 final static class StringFactoryKeyDeserializer extends StdKeyDeserializer
449 {
450 private static final long serialVersionUID = 1L;
451
452 final Method _factoryMethod;
453
454 public StringFactoryKeyDeserializer(Method fm) {
455 super(-1, fm.getDeclaringClass());
456 _factoryMethod = fm;
457 }
458
459 @Override
460 public Object _parse(String key, DeserializationContext ctxt) throws Exception
461 {
462 return _factoryMethod.invoke(null, key);
463 }
464 }
465 }
466
467