1 package com.fasterxml.jackson.databind.ser.impl;
2 
3 import java.util.Arrays;
4 
5 import com.fasterxml.jackson.databind.BeanProperty;
6 import com.fasterxml.jackson.databind.JavaType;
7 import com.fasterxml.jackson.databind.JsonMappingException;
8 import com.fasterxml.jackson.databind.JsonSerializer;
9 import com.fasterxml.jackson.databind.SerializerProvider;
10 
11 /**
12  * Helper container used for resolving serializers for dynamic (possibly but not
13  * necessarily polymorphic) properties: properties whose type is not forced
14  * to use dynamic (declared) type and that are not final.
15  * If so, serializer to use can only be established once actual value type is known.
16  * Since this happens a lot unless static typing is forced (or types are final)
17  * this implementation is optimized for efficiency.
18  * Instances are immutable; new instances are created with factory methods: this
19  * is important to ensure correct multi-threaded access.
20  */
21 public abstract class PropertySerializerMap
22 {
23     /**
24      * Configuration setting that determines what happens when maximum
25      * size (currently 8) is reached: if true, will "start from beginning";
26      * if false, will simply stop adding new entries.
27      *
28      * @since 2.5
29      */
30     protected final boolean _resetWhenFull;
31 
32     /**
33      * @since 2.5
34      */
PropertySerializerMap(boolean resetWhenFull)35     protected PropertySerializerMap(boolean resetWhenFull) {
36         _resetWhenFull = resetWhenFull;
37     }
38 
PropertySerializerMap(PropertySerializerMap base)39     protected PropertySerializerMap(PropertySerializerMap base) {
40         _resetWhenFull = base._resetWhenFull;
41     }
42 
43     /**
44      * Main lookup method. Takes a "raw" type since usage is always from
45      * place where parameterization is fixed such that there cannot be
46      * type-parametric variations.
47      */
serializerFor(Class<?> type)48     public abstract JsonSerializer<Object> serializerFor(Class<?> type);
49 
50     /**
51      * Method called if initial lookup fails, when looking for a primary
52      * serializer (one that is directly attached to a property).
53      * Will both find serializer
54      * and construct new map instance if warranted, and return both.
55      *
56      * @since 2.3
57      *
58      * @throws JsonMappingException
59      */
findAndAddPrimarySerializer(Class<?> type, SerializerProvider provider, BeanProperty property)60     public final SerializerAndMapResult findAndAddPrimarySerializer(Class<?> type,
61             SerializerProvider provider, BeanProperty property)
62         throws JsonMappingException
63     {
64         JsonSerializer<Object> serializer = provider.findPrimaryPropertySerializer(type, property);
65         return new SerializerAndMapResult(serializer, newWith(type, serializer));
66     }
67 
findAndAddPrimarySerializer(JavaType type, SerializerProvider provider, BeanProperty property)68     public final SerializerAndMapResult findAndAddPrimarySerializer(JavaType type,
69             SerializerProvider provider, BeanProperty property)
70         throws JsonMappingException
71     {
72         JsonSerializer<Object> serializer = provider.findPrimaryPropertySerializer(type, property);
73         return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer));
74     }
75 
76     /**
77      * Method called if initial lookup fails, when looking for a non-primary
78      * serializer (one that is not directly attached to a property).
79      * Will both find serializer
80      * and construct new map instance if warranted, and return both.
81      *
82      * @since 2.3
83      *
84      * @throws JsonMappingException
85      */
findAndAddSecondarySerializer(Class<?> type, SerializerProvider provider, BeanProperty property)86     public final SerializerAndMapResult findAndAddSecondarySerializer(Class<?> type,
87             SerializerProvider provider, BeanProperty property)
88         throws JsonMappingException
89     {
90         JsonSerializer<Object> serializer = provider.findContentValueSerializer(type, property);
91         return new SerializerAndMapResult(serializer, newWith(type, serializer));
92     }
93 
findAndAddSecondarySerializer(JavaType type, SerializerProvider provider, BeanProperty property)94     public final SerializerAndMapResult findAndAddSecondarySerializer(JavaType type,
95             SerializerProvider provider, BeanProperty property)
96         throws JsonMappingException
97     {
98         JsonSerializer<Object> serializer = provider.findContentValueSerializer(type, property);
99         return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer));
100     }
101 
102     /**
103      * Method called if initial lookup fails, when looking for a root value
104      * serializer: one that is not directly attached to a property, but needs to
105      * have {@link com.fasterxml.jackson.databind.jsontype.TypeSerializer} wrapped
106      * around it. Will both find the serializer
107      * and construct new map instance if warranted, and return both.
108      *
109      * @since 2.5
110      *
111      * @throws JsonMappingException
112      */
findAndAddRootValueSerializer(Class<?> type, SerializerProvider provider)113     public final SerializerAndMapResult findAndAddRootValueSerializer(Class<?> type,
114             SerializerProvider provider)
115         throws JsonMappingException
116     {
117         JsonSerializer<Object> serializer = provider.findTypedValueSerializer(type, false, null);
118         return new SerializerAndMapResult(serializer, newWith(type, serializer));
119     }
120 
121     /**
122      * @since 2.5
123      */
findAndAddRootValueSerializer(JavaType type, SerializerProvider provider)124     public final SerializerAndMapResult findAndAddRootValueSerializer(JavaType type,
125             SerializerProvider provider)
126         throws JsonMappingException
127     {
128         JsonSerializer<Object> serializer = provider.findTypedValueSerializer(type, false, null);
129         return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer));
130     }
131 
132     /**
133      * Method called if initial lookup fails, when looking for a key
134      * serializer (possible attached indirectly to a property)
135      * Will both find serializer
136      * and construct new map instance if warranted, and return both.
137      *
138      * @since 2.7
139      */
findAndAddKeySerializer(Class<?> type, SerializerProvider provider, BeanProperty property)140     public final SerializerAndMapResult findAndAddKeySerializer(Class<?> type,
141             SerializerProvider provider, BeanProperty property)
142         throws JsonMappingException
143     {
144         JsonSerializer<Object> serializer = provider.findKeySerializer(type, property);
145         return new SerializerAndMapResult(serializer, newWith(type, serializer));
146     }
147 
148     /**
149      * Method that can be used to 'register' a serializer that caller has resolved
150      * without help of this map.
151      *
152      * @since 2.5
153      */
addSerializer(Class<?> type, JsonSerializer<Object> serializer)154     public final SerializerAndMapResult addSerializer(Class<?> type, JsonSerializer<Object> serializer) {
155         return new SerializerAndMapResult(serializer, newWith(type, serializer));
156     }
157 
158     /**
159      * @since 2.5
160      */
addSerializer(JavaType type, JsonSerializer<Object> serializer)161     public final SerializerAndMapResult addSerializer(JavaType type, JsonSerializer<Object> serializer) {
162         return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer));
163     }
164 
newWith(Class<?> type, JsonSerializer<Object> serializer)165     public abstract PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer);
166 
167     /**
168      * @since 2.5
169      */
emptyForProperties()170     public static PropertySerializerMap emptyForProperties() {
171         return Empty.FOR_PROPERTIES;
172     }
173 
174     /**
175      * @since 2.5
176      */
emptyForRootValues()177     public static PropertySerializerMap emptyForRootValues() {
178         return Empty.FOR_ROOT_VALUES;
179     }
180 
181     /*
182     /**********************************************************
183     /* Helper classes
184     /**********************************************************
185      */
186 
187     /**
188      * Value class used for returning tuple that has both serializer
189      * that was retrieved and new map instance
190      */
191     public final static class SerializerAndMapResult
192     {
193         public final JsonSerializer<Object> serializer;
194         public final PropertySerializerMap map;
195 
SerializerAndMapResult(JsonSerializer<Object> serializer, PropertySerializerMap map)196         public SerializerAndMapResult(JsonSerializer<Object> serializer,
197                 PropertySerializerMap map)
198         {
199             this.serializer = serializer;
200             this.map = map;
201         }
202     }
203 
204     /**
205      * Trivial container for bundling type + serializer entries.
206      */
207     private final static class TypeAndSerializer
208     {
209         public final Class<?> type;
210         public final JsonSerializer<Object> serializer;
211 
TypeAndSerializer(Class<?> type, JsonSerializer<Object> serializer)212         public TypeAndSerializer(Class<?> type, JsonSerializer<Object> serializer) {
213             this.type = type;
214             this.serializer = serializer;
215         }
216     }
217 
218     /*
219     /**********************************************************
220     /* Implementations
221     /**********************************************************
222      */
223 
224     /**
225      * Bogus instance that contains no serializers; used as the default
226      * map with new serializers.
227      */
228     private final static class Empty extends PropertySerializerMap
229     {
230         // No root serializers; do not reset when full
231         public final static Empty FOR_PROPERTIES = new Empty(false);
232 
233         // Yes, root serializers; do reset when full
234         public final static Empty FOR_ROOT_VALUES = new Empty(true);
235 
Empty(boolean resetWhenFull)236         protected Empty(boolean resetWhenFull) {
237             super(resetWhenFull);
238         }
239 
240         @Override
serializerFor(Class<?> type)241         public JsonSerializer<Object> serializerFor(Class<?> type) {
242             return null; // empty, nothing to find
243         }
244 
245         @Override
newWith(Class<?> type, JsonSerializer<Object> serializer)246         public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) {
247             return new Single(this, type, serializer);
248         }
249     }
250 
251     /**
252      * Map that contains a single serializer; although seemingly silly
253      * this is probably the most commonly used variant because many
254      * theoretically dynamic or polymorphic types just have single
255      * actual type.
256      */
257     private final static class Single extends PropertySerializerMap
258     {
259         private final Class<?> _type;
260         private final JsonSerializer<Object> _serializer;
261 
Single(PropertySerializerMap base, Class<?> type, JsonSerializer<Object> serializer)262         public Single(PropertySerializerMap base, Class<?> type, JsonSerializer<Object> serializer) {
263             super(base);
264             _type = type;
265             _serializer = serializer;
266         }
267 
268         @Override
serializerFor(Class<?> type)269         public JsonSerializer<Object> serializerFor(Class<?> type)
270         {
271             if (type == _type) {
272                 return _serializer;
273             }
274             return null;
275         }
276 
277         @Override
newWith(Class<?> type, JsonSerializer<Object> serializer)278         public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) {
279             return new Double(this, _type, _serializer, type, serializer);
280         }
281     }
282 
283     private final static class Double extends PropertySerializerMap
284     {
285         private final Class<?> _type1, _type2;
286         private final JsonSerializer<Object> _serializer1, _serializer2;
287 
Double(PropertySerializerMap base, Class<?> type1, JsonSerializer<Object> serializer1, Class<?> type2, JsonSerializer<Object> serializer2)288         public Double(PropertySerializerMap base,
289                 Class<?> type1, JsonSerializer<Object> serializer1,
290                 Class<?> type2, JsonSerializer<Object> serializer2)
291         {
292             super(base);
293             _type1 = type1;
294             _serializer1 = serializer1;
295             _type2 = type2;
296             _serializer2 = serializer2;
297         }
298 
299         @Override
serializerFor(Class<?> type)300         public JsonSerializer<Object> serializerFor(Class<?> type)
301         {
302             if (type == _type1) {
303                 return _serializer1;
304             }
305             if (type == _type2) {
306                 return _serializer2;
307             }
308             return null;
309         }
310 
311         @Override
newWith(Class<?> type, JsonSerializer<Object> serializer)312         public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) {
313             // Ok: let's just create generic one
314             TypeAndSerializer[] ts = new TypeAndSerializer[3];
315             ts[0] = new TypeAndSerializer(_type1, _serializer1);
316             ts[1] = new TypeAndSerializer(_type2, _serializer2);
317             ts[2] = new TypeAndSerializer(type, serializer);
318             return new Multi(this, ts);
319         }
320     }
321 
322     private final static class Multi extends PropertySerializerMap
323     {
324         /**
325          * Let's limit number of serializers we actually cache; linear
326          * lookup won't scale too well beyond smallish number, and if
327          * we really want to support larger collections should use
328          * a hash map. But it seems unlikely this is a common use
329          * case so for now let's just stop building after hard-coded
330          * limit. 8 sounds like a reasonable stab for now.
331          */
332         private final static int MAX_ENTRIES = 8;
333 
334         private final TypeAndSerializer[] _entries;
335 
Multi(PropertySerializerMap base, TypeAndSerializer[] entries)336         public Multi(PropertySerializerMap base, TypeAndSerializer[] entries) {
337             super(base);
338             _entries = entries;
339         }
340 
341         @Override
serializerFor(Class<?> type)342         public JsonSerializer<Object> serializerFor(Class<?> type)
343         {
344             // Always have first 3 populated so
345             TypeAndSerializer entry;
346             entry = _entries[0];
347             if (entry.type == type) return entry.serializer;
348             entry = _entries[1];
349             if (entry.type == type) return entry.serializer;
350             entry = _entries[2];
351             if (entry.type == type) return entry.serializer;
352 
353             switch (_entries.length) {
354             case 8:
355                 entry = _entries[7];
356                 if (entry.type == type) return entry.serializer;
357             case 7:
358                 entry = _entries[6];
359                 if (entry.type == type) return entry.serializer;
360             case 6:
361                 entry = _entries[5];
362                 if (entry.type == type) return entry.serializer;
363             case 5:
364                 entry = _entries[4];
365                 if (entry.type == type) return entry.serializer;
366             case 4:
367                 entry = _entries[3];
368                 if (entry.type == type) return entry.serializer;
369             default:
370             }
371             return null;
372         }
373 
374         @Override
newWith(Class<?> type, JsonSerializer<Object> serializer)375         public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer)
376         {
377             int len = _entries.length;
378             // Will only grow up to N entries. We could consider couple of alternatives after
379             // this if we wanted to... but for now, two main choices make most sense
380             if (len == MAX_ENTRIES) {
381                 if (_resetWhenFull) {
382                     return new Single(this, type, serializer);
383                 }
384                 return this;
385             }
386             TypeAndSerializer[] entries = Arrays.copyOf(_entries, len+1);
387             entries[len] = new TypeAndSerializer(type, serializer);
388             return new Multi(this, entries);
389         }
390     }
391 }
392