1 package com.fasterxml.jackson.databind.deser.impl;
2 
3 import java.util.*;
4 
5 import com.fasterxml.jackson.databind.DeserializationContext;
6 import com.fasterxml.jackson.databind.JavaType;
7 import com.fasterxml.jackson.databind.JsonDeserializer;
8 import com.fasterxml.jackson.databind.JsonMappingException;
9 import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
10 import com.fasterxml.jackson.databind.type.TypeFactory;
11 import com.fasterxml.jackson.databind.util.Converter;
12 
13 /**
14  * Helper class used to contain logic for deserializing "special" containers
15  * from {@code java.util.Collections} and {@code java.util.Arrays}. This is needed
16  * because they do not have usable no-arguments constructor: however, are easy enough
17  * to deserialize using delegating deserializer.
18  *
19  * @since 2.9.4
20  */
21 public abstract class JavaUtilCollectionsDeserializers
22 {
23     private final static int TYPE_SINGLETON_SET = 1;
24     private final static int TYPE_SINGLETON_LIST = 2;
25     private final static int TYPE_SINGLETON_MAP = 3;
26 
27     private final static int TYPE_UNMODIFIABLE_SET = 4;
28     private final static int TYPE_UNMODIFIABLE_LIST = 5;
29     private final static int TYPE_UNMODIFIABLE_MAP = 6;
30 
31     public final static int TYPE_AS_LIST = 7;
32 
33     // 10-Jan-2018, tatu: There are a few "well-known" special containers in JDK too:
34 
35     private final static Class<?> CLASS_AS_ARRAYS_LIST = Arrays.asList(null, null).getClass();
36 
37     private final static Class<?> CLASS_SINGLETON_SET;
38     private final static Class<?> CLASS_SINGLETON_LIST;
39     private final static Class<?> CLASS_SINGLETON_MAP;
40 
41     private final static Class<?> CLASS_UNMODIFIABLE_SET;
42     private final static Class<?> CLASS_UNMODIFIABLE_LIST;
43 
44     /* 02-Mar-2019, tatu: for [databind#2265], need to consider possible alternate type...
45      *    which we essentially coerce into the other one
46      */
47     private final static Class<?> CLASS_UNMODIFIABLE_LIST_ALIAS;
48     private final static Class<?> CLASS_UNMODIFIABLE_MAP;
49 
50     static {
51         Set<?> set = Collections.singleton(Boolean.TRUE);
52         CLASS_SINGLETON_SET = set.getClass();
53         CLASS_UNMODIFIABLE_SET = Collections.unmodifiableSet(set).getClass();
54 
55         List<?> list = Collections.singletonList(Boolean.TRUE);
56         CLASS_SINGLETON_LIST = list.getClass();
57         CLASS_UNMODIFIABLE_LIST = Collections.unmodifiableList(list).getClass();
58         // for [databind#2265]
59         CLASS_UNMODIFIABLE_LIST_ALIAS = Collections.unmodifiableList(new LinkedList<Object>()).getClass();
60 
61         Map<?,?> map = Collections.singletonMap("a", "b");
62         CLASS_SINGLETON_MAP = map.getClass();
63         CLASS_UNMODIFIABLE_MAP = Collections.unmodifiableMap(map).getClass();
64     }
65 
findForCollection(DeserializationContext ctxt, JavaType type)66     public static JsonDeserializer<?> findForCollection(DeserializationContext ctxt,
67             JavaType type)
68         throws JsonMappingException
69     {
70         JavaUtilCollectionsConverter conv;
71 
72         // 10-Jan-2017, tatu: Some types from `java.util.Collections`/`java.util.Arrays` need bit of help...
73         if (type.hasRawClass(CLASS_AS_ARRAYS_LIST)) {
74             conv = converter(TYPE_AS_LIST, type, List.class);
75         } else if (type.hasRawClass(CLASS_SINGLETON_LIST)) {
76             conv = converter(TYPE_SINGLETON_LIST, type, List.class);
77         } else if (type.hasRawClass(CLASS_SINGLETON_SET)) {
78             conv = converter(TYPE_SINGLETON_SET, type, Set.class);
79         // [databind#2265]: we may have another impl type for unmodifiable Lists, check both
80         } else if (type.hasRawClass(CLASS_UNMODIFIABLE_LIST) || type.hasRawClass(CLASS_UNMODIFIABLE_LIST_ALIAS)) {
81             conv = converter(TYPE_UNMODIFIABLE_LIST, type, List.class);
82         } else if (type.hasRawClass(CLASS_UNMODIFIABLE_SET)) {
83             conv = converter(TYPE_UNMODIFIABLE_SET, type, Set.class);
84         } else {
85             return null;
86         }
87         return new StdDelegatingDeserializer<Object>(conv);
88     }
89 
findForMap(DeserializationContext ctxt, JavaType type)90     public static JsonDeserializer<?> findForMap(DeserializationContext ctxt,
91             JavaType type)
92         throws JsonMappingException
93     {
94         JavaUtilCollectionsConverter conv;
95 
96         // 10-Jan-2017, tatu: Some types from `java.util.Collections`/`java.util.Arrays` need bit of help...
97         if (type.hasRawClass(CLASS_SINGLETON_MAP)) {
98             conv = converter(TYPE_SINGLETON_MAP, type, Map.class);
99         } else if (type.hasRawClass(CLASS_UNMODIFIABLE_MAP)) {
100             conv = converter(TYPE_UNMODIFIABLE_MAP, type, Map.class);
101         } else {
102             return null;
103         }
104         return new StdDelegatingDeserializer<Object>(conv);
105     }
106 
converter(int kind, JavaType concreteType, Class<?> rawSuper)107     static JavaUtilCollectionsConverter converter(int kind,
108             JavaType concreteType, Class<?> rawSuper)
109     {
110         return new JavaUtilCollectionsConverter(kind, concreteType.findSuperType(rawSuper));
111     }
112 
113     /**
114      * Implementation used for converting from various generic container
115      * types ({@link java.util.Set}, {@link java.util.List}, {@link java.util.Map})
116      * into more specific implementations accessible via {@code java.util.Collections}.
117      */
118     private static class JavaUtilCollectionsConverter implements Converter<Object,Object>
119     {
120         private final JavaType _inputType;
121 
122         private final int _kind;
123 
JavaUtilCollectionsConverter(int kind, JavaType inputType)124         private JavaUtilCollectionsConverter(int kind, JavaType inputType) {
125             _inputType = inputType;
126             _kind = kind;
127         }
128 
129         @Override
convert(Object value)130         public Object convert(Object value) {
131             if (value == null) { // is this legal to get?
132                 return null;
133             }
134 
135             switch (_kind) {
136             case TYPE_SINGLETON_SET:
137                 {
138                     Set<?> set = (Set<?>) value;
139                     _checkSingleton(set.size());
140                     return Collections.singleton(set.iterator().next());
141                 }
142             case TYPE_SINGLETON_LIST:
143                 {
144                     List<?> list = (List<?>) value;
145                     _checkSingleton(list.size());
146                     return Collections.singletonList(list.get(0));
147                 }
148             case TYPE_SINGLETON_MAP:
149                 {
150                     Map<?,?> map = (Map<?,?>) value;
151                     _checkSingleton(map.size());
152                     Map.Entry<?,?> entry = map.entrySet().iterator().next();
153                     return Collections.singletonMap(entry.getKey(), entry.getValue());
154                 }
155 
156             case TYPE_UNMODIFIABLE_SET:
157                 return Collections.unmodifiableSet((Set<?>) value);
158             case TYPE_UNMODIFIABLE_LIST:
159                 return Collections.unmodifiableList((List<?>) value);
160             case TYPE_UNMODIFIABLE_MAP:
161                 return Collections.unmodifiableMap((Map<?,?>) value);
162 
163             case TYPE_AS_LIST:
164             default:
165                 // Here we do not actually care about impl type, just return List as-is:
166                 return value;
167             }
168         }
169 
170         @Override
getInputType(TypeFactory typeFactory)171         public JavaType getInputType(TypeFactory typeFactory) {
172             return _inputType;
173         }
174 
175         @Override
getOutputType(TypeFactory typeFactory)176         public JavaType getOutputType(TypeFactory typeFactory) {
177             // we don't actually care, so:
178             return _inputType;
179         }
180 
_checkSingleton(int size)181         private void _checkSingleton(int size) {
182             if (size != 1) {
183                 // not the best error ever but... has to do
184                 throw new IllegalArgumentException("Can not deserialize Singleton container from "+size+" entries");
185             }
186         }
187     }
188 
189 }
190