blob: cebc848a7ff98339e961c736def66bb6b900be2c [file] [log] [blame]
package org.codehaus.jackson.map.jsontype.impl;
import java.util.*;
import org.codehaus.jackson.map.AnnotationIntrospector;
import org.codehaus.jackson.map.MapperConfig;
import org.codehaus.jackson.map.introspect.*;
import org.codehaus.jackson.map.jsontype.NamedType;
import org.codehaus.jackson.map.jsontype.SubtypeResolver;
/**
* @since 1.5
*/
public class StdSubtypeResolver extends SubtypeResolver
{
protected LinkedHashSet<NamedType> _registeredSubtypes;
public StdSubtypeResolver() { }
/*
/**********************************************************
/* Public API
/**********************************************************
*/
@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);
}
/**
*
* @param property Base member to use for type resolution: either annotated type (class),
* or property (field, getter/setter)
*/
@Override
public Collection<NamedType> collectAndResolveSubtypes(AnnotatedMember property,
MapperConfig<?> config, AnnotationIntrospector ai)
{
HashMap<NamedType, NamedType> collected = new HashMap<NamedType, NamedType>();
// start with registered subtypes (which have precedence)
if (_registeredSubtypes != null) {
Class<?> rawBase = property.getRawType();
for (NamedType subtype : _registeredSubtypes) {
// is it a subtype of root type?
if (rawBase.isAssignableFrom(subtype.getType())) { // yes
AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), ai, 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(), ai, config);
_collectAndResolve(ac, nt, config, ai, collected);
}
}
NamedType rootType = new NamedType(property.getRawType(), null);
AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(property.getRawType(), ai, 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> collectAndResolveSubtypes(AnnotatedClass type,
MapperConfig<?> config, AnnotationIntrospector ai)
{
HashMap<NamedType, NamedType> subtypes = new HashMap<NamedType, NamedType>();
// [JACKSON-257] 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(), ai, 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());
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
/**
* Method called to find subtypes for a specific type (class)
*/
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(), ai, config);
// One more thing: name may be either in reference, or in subtype:
if (!subtype.hasName()) {
subtype = new NamedType(subtype.getType(), ai.findTypeName(subtypeClass));
}
_collectAndResolve(subtypeClass, subtype, config, ai, collectedSubtypes);
}
}
}
}