1 package com.fasterxml.jackson.databind.jsontype.impl;
2
3 import java.lang.reflect.Modifier;
4 import java.util.*;
5
6 import com.fasterxml.jackson.databind.AnnotationIntrospector;
7 import com.fasterxml.jackson.databind.JavaType;
8 import com.fasterxml.jackson.databind.cfg.MapperConfig;
9 import com.fasterxml.jackson.databind.introspect.*;
10 import com.fasterxml.jackson.databind.jsontype.NamedType;
11 import com.fasterxml.jackson.databind.jsontype.SubtypeResolver;
12
13 /**
14  * Standard {@link SubtypeResolver} implementation.
15  */

16 public class StdSubtypeResolver
17     extends SubtypeResolver
18     implements java.io.Serializable
19 {
20     private static final long serialVersionUID = 1L;
21
22     protected LinkedHashSet<NamedType> _registeredSubtypes;
23
24     public StdSubtypeResolver() { }
25
26     // @since 2.12
27     protected StdSubtypeResolver(StdSubtypeResolver src) {
28         LinkedHashSet<NamedType> reg = src._registeredSubtypes;
29         _registeredSubtypes = (reg == null) ? null
30                 : new LinkedHashSet<>(reg);
31     }
32
33     // @since 2.12
34     @Override
35     public SubtypeResolver copy() {
36         return new StdSubtypeResolver(this);
37     }
38
39     /*
40     /**********************************************************
41     /* Subtype registration
42     /**********************************************************
43      */

44
45     @Override    
46     public void registerSubtypes(NamedType... types) {
47         if (_registeredSubtypes == null) {
48             _registeredSubtypes = new LinkedHashSet<NamedType>();
49         }
50         for (NamedType type : types) {
51             _registeredSubtypes.add(type);
52         }
53     }
54
55     @Override
56     public void registerSubtypes(Class<?>... classes) {
57         NamedType[] types = new NamedType[classes.length];
58         for (int i = 0, len = classes.length; i < len; ++i) {
59             types[i] = new NamedType(classes[i]);
60         }
61         registerSubtypes(types);
62     }
63
64     @Override // since 2.9
65     public void registerSubtypes(Collection<Class<?>> subtypes) {
66         int len = subtypes.size();
67         NamedType[] types = new NamedType[len];
68         int i = 0;
69         for (Class<?> subtype : subtypes) {
70             types[i++] = new NamedType(subtype);
71         }
72         registerSubtypes(types);
73     }
74
75     /*
76     /**********************************************************
77     /* Resolution by class (serialization)
78     /**********************************************************
79      */

80
81     @Override
82     public Collection<NamedType> collectAndResolveSubtypesByClass(MapperConfig<?> config, 
83             AnnotatedMember property, JavaType baseType)
84     {
85         final AnnotationIntrospector ai = config.getAnnotationIntrospector();
86         // for backwards compatibility, must allow null here:
87         Class<?> rawBase = (baseType == null) ? property.getRawType() : baseType.getRawClass();
88         
89         HashMap<NamedType, NamedType> collected = new HashMap<NamedType, NamedType>();
90         // start with registered subtypes (which have precedence)
91         if (_registeredSubtypes != null) {
92             for (NamedType subtype : _registeredSubtypes) {
93                 // is it a subtype of root type?
94                 if (rawBase.isAssignableFrom(subtype.getType())) { // yes
95                     AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
96                             subtype.getType());
97                     _collectAndResolve(curr, subtype, config, ai, collected);
98                 }
99             }
100         }
101         
102         // then annotated types for property itself
103         if (property != null) {
104             Collection<NamedType> st = ai.findSubtypes(property);
105             if (st != null) {
106                 for (NamedType nt : st) {
107                     AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
108                             nt.getType());
109                     _collectAndResolve(ac, nt, config, ai, collected);
110                 }            
111             }
112         }
113
114         NamedType rootType = new NamedType(rawBase, null);
115         AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config, rawBase);
116             
117         // and finally subtypes via annotations from base type (recursively)
118         _collectAndResolve(ac, rootType, config, ai, collected);
119
120         return new ArrayList<NamedType>(collected.values());
121     }
122
123     @Override
124     public Collection<NamedType> collectAndResolveSubtypesByClass(MapperConfig<?> config,
125             AnnotatedClass type)
126     {
127         final AnnotationIntrospector ai = config.getAnnotationIntrospector();
128         HashMap<NamedType, NamedType> subtypes = new HashMap<>();
129
130         // then consider registered subtypes (which have precedence over annotations)
131         if (_registeredSubtypes != null) {
132             Class<?> rawBase = type.getRawType();
133             for (NamedType subtype : _registeredSubtypes) {
134                 // is it a subtype of root type?
135                 if (rawBase.isAssignableFrom(subtype.getType())) { // yes
136                     AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
137                             subtype.getType());
138                     _collectAndResolve(curr, subtype, config, ai, subtypes);
139                 }
140             }
141         }
142         // and then check subtypes via annotations from base type (recursively)
143         NamedType rootType = new NamedType(type.getRawType(), null);
144         _collectAndResolve(type, rootType, config, ai, subtypes);
145         return new ArrayList<NamedType>(subtypes.values());
146     }
147
148     /*
149     /**********************************************************
150     /* Resolution by class (deserialization)
151     /**********************************************************
152      */

153
154     @Override
155     public Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config, 
156             AnnotatedMember property, JavaType baseType)
157     {
158         final AnnotationIntrospector ai = config.getAnnotationIntrospector();
159         Class<?> rawBase = baseType.getRawClass();
160
161         // Need to keep track of classes that have been handled already 
162         Set<Class<?>> typesHandled = new HashSet<Class<?>>();
163         Map<String,NamedType> byName = new LinkedHashMap<String,NamedType>();
164
165         // start with lowest-precedence, which is from type hierarchy
166         NamedType rootType = new NamedType(rawBase, null);
167         AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
168                 rawBase);
169         _collectAndResolveByTypeId(ac, rootType, config, typesHandled, byName);
170         
171         // then with definitions from property
172         if (property != null) {
173             Collection<NamedType> st = ai.findSubtypes(property);
174             if (st != null) {
175                 for (NamedType nt : st) {
176                     ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config, nt.getType());
177                     _collectAndResolveByTypeId(ac, nt, config, typesHandled, byName);
178                 }            
179             }
180         }
181         // and finally explicit type registrations (highest precedence)
182         if (_registeredSubtypes != null) {
183             for (NamedType subtype : _registeredSubtypes) {
184                 // is it a subtype of root type?
185                 if (rawBase.isAssignableFrom(subtype.getType())) { // yes
186                     AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
187                             subtype.getType());
188                     _collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName);
189                 }
190             }
191         }
192         return _combineNamedAndUnnamed(rawBase, typesHandled, byName);
193     }
194
195     @Override
196     public Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config,
197             AnnotatedClass baseType)
198     {
199         final Class<?> rawBase = baseType.getRawType();
200         Set<Class<?>> typesHandled = new HashSet<Class<?>>();
201         Map<String,NamedType> byName = new LinkedHashMap<String,NamedType>();
202
203         NamedType rootType = new NamedType(rawBase, null);
204         _collectAndResolveByTypeId(baseType, rootType, config, typesHandled, byName);
205         
206         if (_registeredSubtypes != null) {
207             for (NamedType subtype : _registeredSubtypes) {
208                 // is it a subtype of root type?
209                 if (rawBase.isAssignableFrom(subtype.getType())) { // yes
210                     AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
211                             subtype.getType());
212                     _collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName);
213                 }
214             }
215         }
216         return _combineNamedAndUnnamed(rawBase, typesHandled, byName);
217     }
218
219     /*
220     /**********************************************************
221     /* Internal methods
222     /**********************************************************
223      */

224
225     /**
226      * Method called to find subtypes for a specific type (class), using
227      * type (class) as the unique key (in case of conflicts).
228      */

229     protected void _collectAndResolve(AnnotatedClass annotatedType, NamedType namedType,
230             MapperConfig<?> config, AnnotationIntrospector ai,
231             HashMap<NamedType, NamedType> collectedSubtypes)
232     {
233         if (!namedType.hasName()) {
234             String name = ai.findTypeName(annotatedType);
235             if (name != null) {
236                 namedType = new NamedType(namedType.getType(), name);
237             }
238         }
239
240         //For Serialization we only want to return a single NamedType per class so it's
241         //unambiguous what name we use.
242         NamedType typeOnlyNamedType = new NamedType(namedType.getType());
243
244         // First things first: is base type itself included?
245         if (collectedSubtypes.containsKey(typeOnlyNamedType)) {
246             // if so, no recursion; however, may need to update name?
247             if (namedType.hasName()) {
248                 NamedType prev = collectedSubtypes.get(typeOnlyNamedType);
249                 if (!prev.hasName()) {
250                     collectedSubtypes.put(typeOnlyNamedType, namedType);
251                 }
252             }
253             return;
254         }
255         // if it wasn't, add and check subtypes recursively
256         collectedSubtypes.put(typeOnlyNamedType, namedType);
257         Collection<NamedType> st = ai.findSubtypes(annotatedType);
258         if (st != null && !st.isEmpty()) {
259             for (NamedType subtype : st) {
260                 AnnotatedClass subtypeClass = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
261                         subtype.getType());
262                 _collectAndResolve(subtypeClass, subtype, config, ai, collectedSubtypes);
263             }
264         }
265     }
266
267     /**
268      * Method called to find subtypes for a specific type (class), using
269      * type id as the unique key (in case of conflicts).
270      */

271     protected void _collectAndResolveByTypeId(AnnotatedClass annotatedType, NamedType namedType,
272             MapperConfig<?> config,
273             Set<Class<?>> typesHandled, Map<String,NamedType> byName)
274     {
275         final AnnotationIntrospector ai = config.getAnnotationIntrospector();
276         if (!namedType.hasName()) {
277             String name = ai.findTypeName(annotatedType);
278             if (name != null) {
279                 namedType = new NamedType(namedType.getType(), name);
280             }
281         }
282         if (namedType.hasName()) {
283             byName.put(namedType.getName(), namedType);
284         }
285
286         // only check subtypes if this type hadn't yet been handled
287         if (typesHandled.add(namedType.getType())) {
288             Collection<NamedType> st = ai.findSubtypes(annotatedType);
289             if (st != null && !st.isEmpty()) {
290                 for (NamedType subtype : st) {
291                     AnnotatedClass subtypeClass = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
292                             subtype.getType());
293                     _collectAndResolveByTypeId(subtypeClass, subtype, config, typesHandled, byName);
294                 }
295             }
296         }
297     }
298
299     /**
300      * Helper method used for merging explicitly named types and handled classes
301      * without explicit names.
302      */

303     protected Collection<NamedType> _combineNamedAndUnnamed(Class<?> rawBase,
304             Set<Class<?>> typesHandled, Map<String,NamedType> byName)
305     {
306         ArrayList<NamedType> result = new ArrayList<NamedType>(byName.values());
307
308         // Ok, so... we will figure out which classes have no explicitly assigned name,
309         // by removing Classes from Set. And for remaining classes, add an anonymous
310         // marker
311         for (NamedType t : byName.values()) {
312             typesHandled.remove(t.getType());
313         }
314         for (Class<?> cls : typesHandled) {
315             // 27-Apr-2017, tatu: [databind#1616] Do not add base type itself unless
316             //     it is concrete (or has explicit type name)
317             if ((cls == rawBase) && Modifier.isAbstract(cls.getModifiers())) {
318                 continue;
319             }
320             result.add(new NamedType(cls));
321         }
322         return result;
323     }
324 }
325