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      */

35     protected PropertySerializerMap(boolean resetWhenFull) {
36         _resetWhenFull = resetWhenFull;
37     }
38
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      */

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      */

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
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      */

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
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      */

113     public final SerializerAndMapResult findAndAddRootValueSerializer(Class<?> type,
114             SerializerProvider provider)
115         throws JsonMappingException
116     {
117         JsonSerializer<Object> serializer = provider.findTypedValueSerializer(type, falsenull);
118         return new SerializerAndMapResult(serializer, newWith(type, serializer));
119     }
120
121     /**
122      * @since 2.5
123      */

124     public final SerializerAndMapResult findAndAddRootValueSerializer(JavaType type,
125             SerializerProvider provider)
126         throws JsonMappingException
127     {
128         JsonSerializer<Object> serializer = provider.findTypedValueSerializer(type, falsenull);
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      */

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      */

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      */

161     public final SerializerAndMapResult addSerializer(JavaType type, JsonSerializer<Object> serializer) {
162         return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer));
163     }
164
165     public abstract PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer);
166
167     /**
168      * @deprecated Since 2.5 Use {@link #emptyForProperties} instead
169      */

170     @Deprecated
171     public static PropertySerializerMap emptyMap() {
172         return emptyForProperties();
173     }
174
175     /**
176      * @since 2.5
177      */

178     public static PropertySerializerMap emptyForProperties() {
179         return Empty.FOR_PROPERTIES;
180     }
181
182     /**
183      * @since 2.5
184      */

185     public static PropertySerializerMap emptyForRootValues() {
186         return Empty.FOR_ROOT_VALUES;
187     }
188
189     /*
190     /**********************************************************
191     /* Helper classes
192     /**********************************************************
193      */

194
195     /**
196      * Value class used for returning tuple that has both serializer
197      * that was retrieved and new map instance
198      */

199     public final static class SerializerAndMapResult
200     {
201         public final JsonSerializer<Object> serializer;
202         public final PropertySerializerMap map;
203         
204         public SerializerAndMapResult(JsonSerializer<Object> serializer,
205                 PropertySerializerMap map)
206         {
207             this.serializer = serializer;
208             this.map = map;
209         }
210     }
211
212     /**
213      * Trivial container for bundling type + serializer entries.
214      */

215     private final static class TypeAndSerializer
216     {
217         public final Class<?> type;
218         public final JsonSerializer<Object> serializer;
219
220         public TypeAndSerializer(Class<?> type, JsonSerializer<Object> serializer) {
221             this.type = type;
222             this.serializer = serializer;
223         }
224     }
225
226     /*
227     /**********************************************************
228     /* Implementations
229     /**********************************************************
230      */

231
232     /**
233      * Bogus instance that contains no serializers; used as the default
234      * map with new serializers.
235      */

236     private final static class Empty extends PropertySerializerMap
237     {
238         // No root serializers; do not reset when full
239         public final static Empty FOR_PROPERTIES = new Empty(false);
240
241         // Yes, root serializers; do reset when full
242         public final static Empty FOR_ROOT_VALUES = new Empty(true);
243
244         protected Empty(boolean resetWhenFull) {
245             super(resetWhenFull);
246         }
247         
248         @Override
249         public JsonSerializer<Object> serializerFor(Class<?> type) {
250             return null// empty, nothing to find
251         }        
252
253         @Override
254         public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) {
255             return new Single(this, type, serializer);
256         }
257     }
258
259     /**
260      * Map that contains a single serializer; although seemingly silly
261      * this is probably the most commonly used variant because many
262      * theoretically dynamic or polymorphic types just have single
263      * actual type.
264      */

265     private final static class Single extends PropertySerializerMap
266     {
267         private final Class<?> _type;
268         private final JsonSerializer<Object> _serializer;
269
270         public Single(PropertySerializerMap base, Class<?> type, JsonSerializer<Object> serializer) {
271             super(base);
272             _type = type;
273             _serializer = serializer;
274         }
275
276         @Override
277         public JsonSerializer<Object> serializerFor(Class<?> type)
278         {
279             if (type == _type) {
280                 return _serializer;
281             }
282             return null;
283         }
284
285         @Override
286         public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) {
287             return new Double(this, _type, _serializer, type, serializer);
288         }
289     }
290
291     private final static class Double extends PropertySerializerMap
292     {
293         private final Class<?> _type1, _type2;
294         private final JsonSerializer<Object> _serializer1, _serializer2;
295
296         public Double(PropertySerializerMap base,
297                 Class<?> type1, JsonSerializer<Object> serializer1,
298                 Class<?> type2, JsonSerializer<Object> serializer2)
299         {
300             super(base);
301             _type1 = type1;
302             _serializer1 = serializer1;
303             _type2 = type2;
304             _serializer2 = serializer2;
305         }
306
307         @Override
308         public JsonSerializer<Object> serializerFor(Class<?> type)
309         {
310             if (type == _type1) {
311                 return _serializer1;
312             }
313             if (type == _type2) {
314                 return _serializer2;
315             }
316             return null;
317         }        
318
319         @Override
320         public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) {
321             // Ok: let's just create generic one
322             TypeAndSerializer[] ts = new TypeAndSerializer[3];
323             ts[0] = new TypeAndSerializer(_type1, _serializer1);
324             ts[1] = new TypeAndSerializer(_type2, _serializer2);
325             ts[2] = new TypeAndSerializer(type, serializer);
326             return new Multi(this, ts);
327         }
328     }
329     
330     private final static class Multi extends PropertySerializerMap
331     {
332         /**
333          * Let's limit number of serializers we actually cache; linear
334          * lookup won't scale too well beyond smallish number, and if
335          * we really want to support larger collections should use
336          * a hash map. But it seems unlikely this is a common use
337          * case so for now let's just stop building after hard-coded
338          * limit. 8 sounds like a reasonable stab for now.
339          */

340         private final static int MAX_ENTRIES = 8;
341         
342         private final TypeAndSerializer[] _entries;
343
344         public Multi(PropertySerializerMap base, TypeAndSerializer[] entries) {
345             super(base);
346             _entries = entries;
347         }
348
349         @Override
350         public JsonSerializer<Object> serializerFor(Class<?> type)
351         {
352             // Always have first 3 populated so
353             TypeAndSerializer entry;
354             entry = _entries[0];
355             if (entry.type == type) return entry.serializer;
356             entry = _entries[1];
357             if (entry.type == type) return entry.serializer;
358             entry = _entries[2];
359             if (entry.type == type) return entry.serializer;
360
361             switch (_entries.length) {
362             case 8:
363                 entry = _entries[7];
364                 if (entry.type == type) return entry.serializer;
365             case 7:
366                 entry = _entries[6];
367                 if (entry.type == type) return entry.serializer;
368             case 6:
369                 entry = _entries[5];
370                 if (entry.type == type) return entry.serializer;
371             case 5:
372                 entry = _entries[4];
373                 if (entry.type == type) return entry.serializer;
374             case 4:
375                 entry = _entries[3];
376                 if (entry.type == type) return entry.serializer;
377             default:
378             }
379             return null;
380         }
381
382         @Override
383         public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer)
384         {
385             int len = _entries.length;
386             // Will only grow up to N entries. We could consider couple of alternatives after
387             // this if we wanted to... but for now, two main choices make most sense
388             if (len == MAX_ENTRIES) {
389                 if (_resetWhenFull) {
390                     return new Single(this, type, serializer);
391                 }
392                 return this;
393             }
394             TypeAndSerializer[] entries = Arrays.copyOf(_entries, len+1);
395             entries[len] = new TypeAndSerializer(type, serializer);
396             return new Multi(this, entries);
397         }
398     }
399 }
400