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     
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         // 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
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
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                 // 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
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     // @since 2.9
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         
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     /*
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         
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) { // 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
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         
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                 // fall-through if problems are collected, not immediately thrown
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     /**
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
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     /**
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
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