| package com.fasterxml.jackson.databind.jsontype.impl; |
| |
| import java.util.*; |
| |
| import com.fasterxml.jackson.databind.AnnotationIntrospector; |
| import com.fasterxml.jackson.databind.JavaType; |
| import com.fasterxml.jackson.databind.cfg.MapperConfig; |
| import com.fasterxml.jackson.databind.introspect.*; |
| import com.fasterxml.jackson.databind.jsontype.NamedType; |
| import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; |
| |
| /** |
| * Standard {@link SubtypeResolver} implementation. |
| */ |
| public class StdSubtypeResolver |
| extends SubtypeResolver |
| implements java.io.Serializable |
| { |
| private static final long serialVersionUID = 1L; |
| |
| protected LinkedHashSet<NamedType> _registeredSubtypes; |
| |
| public StdSubtypeResolver() { } |
| |
| /* |
| /********************************************************** |
| /* Subtype registration |
| /********************************************************** |
| */ |
| |
| @Override |
| public void registerSubtypes(NamedType... types) { |
| if (_registeredSubtypes == null) { |
| _registeredSubtypes = new LinkedHashSet<NamedType>(); |
| } |
| for (NamedType type : types) { |
| _registeredSubtypes.add(type); |
| } |
| } |
| |
| @Override |
| public void registerSubtypes(Class<?>... classes) { |
| NamedType[] types = new NamedType[classes.length]; |
| for (int i = 0, len = classes.length; i < len; ++i) { |
| types[i] = new NamedType(classes[i]); |
| } |
| registerSubtypes(types); |
| } |
| |
| /* |
| /********************************************************** |
| /* Resolution by class (serialization) |
| /********************************************************** |
| */ |
| |
| @Override |
| public Collection<NamedType> collectAndResolveSubtypesByClass(MapperConfig<?> config, |
| AnnotatedMember property, JavaType baseType) |
| { |
| final AnnotationIntrospector ai = config.getAnnotationIntrospector(); |
| // for backwards compatibility, must allow null here: |
| Class<?> rawBase = (baseType == null) ? property.getRawType() : baseType.getRawClass(); |
| |
| HashMap<NamedType, NamedType> collected = new HashMap<NamedType, NamedType>(); |
| // start with registered subtypes (which have precedence) |
| if (_registeredSubtypes != null) { |
| for (NamedType subtype : _registeredSubtypes) { |
| // is it a subtype of root type? |
| if (rawBase.isAssignableFrom(subtype.getType())) { // yes |
| AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); |
| _collectAndResolve(curr, subtype, config, ai, collected); |
| } |
| } |
| } |
| |
| // then annotated types for property itself |
| Collection<NamedType> st = ai.findSubtypes(property); |
| if (st != null) { |
| for (NamedType nt : st) { |
| AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(nt.getType(), config); |
| _collectAndResolve(ac, nt, config, ai, collected); |
| } |
| } |
| |
| NamedType rootType = new NamedType(rawBase, null); |
| AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(rawBase, config); |
| |
| // and finally subtypes via annotations from base type (recursively) |
| _collectAndResolve(ac, rootType, config, ai, collected); |
| |
| return new ArrayList<NamedType>(collected.values()); |
| } |
| |
| @Override |
| public Collection<NamedType> collectAndResolveSubtypesByClass(MapperConfig<?> config, |
| AnnotatedClass type) |
| { |
| final AnnotationIntrospector ai = config.getAnnotationIntrospector(); |
| HashMap<NamedType, NamedType> subtypes = new HashMap<NamedType, NamedType>(); |
| // then consider registered subtypes (which have precedence over annotations) |
| if (_registeredSubtypes != null) { |
| Class<?> rawBase = type.getRawType(); |
| for (NamedType subtype : _registeredSubtypes) { |
| // is it a subtype of root type? |
| if (rawBase.isAssignableFrom(subtype.getType())) { // yes |
| AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); |
| _collectAndResolve(curr, subtype, config, ai, subtypes); |
| } |
| } |
| } |
| // and then check subtypes via annotations from base type (recursively) |
| NamedType rootType = new NamedType(type.getRawType(), null); |
| _collectAndResolve(type, rootType, config, ai, subtypes); |
| return new ArrayList<NamedType>(subtypes.values()); |
| } |
| |
| /* |
| /********************************************************** |
| /* Resolution by class (deserialization) |
| /********************************************************** |
| */ |
| |
| @Override |
| public Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config, |
| AnnotatedMember property, JavaType baseType) |
| { |
| final AnnotationIntrospector ai = config.getAnnotationIntrospector(); |
| Class<?> rawBase = (baseType == null) ? property.getRawType() : baseType.getRawClass(); |
| |
| // Need to keep track of classes that have been handled already |
| Set<Class<?>> typesHandled = new HashSet<Class<?>>(); |
| Map<String,NamedType> byName = new LinkedHashMap<String,NamedType>(); |
| |
| // start with lowest-precedence, which is from type hierarchy |
| NamedType rootType = new NamedType(rawBase, null); |
| AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(rawBase, config); |
| _collectAndResolveByTypeId(ac, rootType, config, typesHandled, byName); |
| |
| // then with definitions from property |
| Collection<NamedType> st = ai.findSubtypes(property); |
| if (st != null) { |
| for (NamedType nt : st) { |
| ac = AnnotatedClass.constructWithoutSuperTypes(nt.getType(), config); |
| _collectAndResolveByTypeId(ac, nt, config, typesHandled, byName); |
| } |
| } |
| |
| // and finally explicit type registrations (highest precedence) |
| if (_registeredSubtypes != null) { |
| for (NamedType subtype : _registeredSubtypes) { |
| // is it a subtype of root type? |
| if (rawBase.isAssignableFrom(subtype.getType())) { // yes |
| AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); |
| _collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName); |
| } |
| } |
| } |
| return _combineNamedAndUnnamed(typesHandled, byName); |
| } |
| |
| @Override |
| public Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config, |
| AnnotatedClass type) |
| { |
| Set<Class<?>> typesHandled = new HashSet<Class<?>>(); |
| Map<String,NamedType> byName = new LinkedHashMap<String,NamedType>(); |
| |
| NamedType rootType = new NamedType(type.getRawType(), null); |
| _collectAndResolveByTypeId(type, rootType, config, typesHandled, byName); |
| |
| if (_registeredSubtypes != null) { |
| Class<?> rawBase = type.getRawType(); |
| for (NamedType subtype : _registeredSubtypes) { |
| // is it a subtype of root type? |
| if (rawBase.isAssignableFrom(subtype.getType())) { // yes |
| AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); |
| _collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName); |
| } |
| } |
| } |
| return _combineNamedAndUnnamed(typesHandled, byName); |
| } |
| |
| /* |
| /********************************************************** |
| /* Internal methods |
| /********************************************************** |
| */ |
| |
| /** |
| * Method called to find subtypes for a specific type (class), using |
| * type (class) as the unique key (in case of conflicts). |
| */ |
| protected void _collectAndResolve(AnnotatedClass annotatedType, NamedType namedType, |
| MapperConfig<?> config, AnnotationIntrospector ai, |
| HashMap<NamedType, NamedType> collectedSubtypes) |
| { |
| if (!namedType.hasName()) { |
| String name = ai.findTypeName(annotatedType); |
| if (name != null) { |
| namedType = new NamedType(namedType.getType(), name); |
| } |
| } |
| |
| // First things first: is base type itself included? |
| if (collectedSubtypes.containsKey(namedType)) { |
| // if so, no recursion; however, may need to update name? |
| if (namedType.hasName()) { |
| NamedType prev = collectedSubtypes.get(namedType); |
| if (!prev.hasName()) { |
| collectedSubtypes.put(namedType, namedType); |
| } |
| } |
| return; |
| } |
| // if it wasn't, add and check subtypes recursively |
| collectedSubtypes.put(namedType, namedType); |
| Collection<NamedType> st = ai.findSubtypes(annotatedType); |
| if (st != null && !st.isEmpty()) { |
| for (NamedType subtype : st) { |
| AnnotatedClass subtypeClass = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); |
| _collectAndResolve(subtypeClass, subtype, config, ai, collectedSubtypes); |
| } |
| } |
| } |
| |
| /** |
| * Method called to find subtypes for a specific type (class), using |
| * type id as the unique key (in case of conflicts). |
| */ |
| protected void _collectAndResolveByTypeId(AnnotatedClass annotatedType, NamedType namedType, |
| MapperConfig<?> config, |
| Set<Class<?>> typesHandled, Map<String,NamedType> byName) |
| { |
| final AnnotationIntrospector ai = config.getAnnotationIntrospector(); |
| if (!namedType.hasName()) { |
| String name = ai.findTypeName(annotatedType); |
| if (name != null) { |
| namedType = new NamedType(namedType.getType(), name); |
| } |
| } |
| if (namedType.hasName()) { |
| byName.put(namedType.getName(), namedType); |
| } |
| |
| // only check subtypes if this type hadn't yet been handled |
| if (typesHandled.add(namedType.getType())) { |
| Collection<NamedType> st = ai.findSubtypes(annotatedType); |
| if (st != null && !st.isEmpty()) { |
| for (NamedType subtype : st) { |
| AnnotatedClass subtypeClass = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), config); |
| _collectAndResolveByTypeId(subtypeClass, subtype, config, typesHandled, byName); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Helper method used for merging explicitly named types and handled classes |
| * without explicit names. |
| */ |
| protected Collection<NamedType> _combineNamedAndUnnamed(Set<Class<?>> typesHandled, |
| Map<String,NamedType> byName) |
| { |
| ArrayList<NamedType> result = new ArrayList<NamedType>(byName.values()); |
| |
| // Ok, so... we will figure out which classes have no explicitly assigned name, |
| // by removing Classes from Set. And for remaining classes, add an anonymous |
| // marker |
| for (NamedType t : byName.values()) { |
| typesHandled.remove(t.getType()); |
| } |
| for (Class<?> cls : typesHandled) { |
| result.add(new NamedType(cls)); |
| } |
| return result; |
| } |
| } |