com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
package com.fasterxml.jackson.databind.jsontype.impl;
import java.lang.reflect.Modifier;
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 _registeredSubtypes;
public StdSubtypeResolver() { }
/*
/**********************************************************
/* Subtype registration
/**********************************************************
*/
@Override
public void registerSubtypes(NamedType... types) {
if (_registeredSubtypes == null) {
_registeredSubtypes = new LinkedHashSet();
}
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);
}
@Override // since 2.9
public void registerSubtypes(Collection> subtypes) {
int len = subtypes.size();
NamedType[] types = new NamedType[len];
int i = 0;
for (Class subtype : subtypes) {
types[i++] = new NamedType(subtype);
}
registerSubtypes(types);
}
/*
/**********************************************************
/* Resolution by class (serialization)
/**********************************************************
*/
@Override
public Collection 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 collected = new HashMap();
// 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 = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
subtype.getType());
_collectAndResolve(curr, subtype, config, ai, collected);
}
}
}
// then annotated types for property itself
if (property != null) {
Collection st = ai.findSubtypes(property);
if (st != null) {
for (NamedType nt : st) {
AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
nt.getType());
_collectAndResolve(ac, nt, config, ai, collected);
}
}
}
NamedType rootType = new NamedType(rawBase, null);
AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config, rawBase);
// and finally subtypes via annotations from base type (recursively)
_collectAndResolve(ac, rootType, config, ai, collected);
return new ArrayList(collected.values());
}
@Override
public Collection collectAndResolveSubtypesByClass(MapperConfig config,
AnnotatedClass type)
{
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
HashMap subtypes = new HashMap();
// 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 = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
subtype.getType());
_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(subtypes.values());
}
/*
/**********************************************************
/* Resolution by class (deserialization)
/**********************************************************
*/
@Override
public Collection collectAndResolveSubtypesByTypeId(MapperConfig config,
AnnotatedMember property, JavaType baseType)
{
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
Class rawBase = baseType.getRawClass();
// Need to keep track of classes that have been handled already
Set> typesHandled = new HashSet>();
Map byName = new LinkedHashMap();
// start with lowest-precedence, which is from type hierarchy
NamedType rootType = new NamedType(rawBase, null);
AnnotatedClass ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
rawBase);
_collectAndResolveByTypeId(ac, rootType, config, typesHandled, byName);
// then with definitions from property
if (property != null) {
Collection st = ai.findSubtypes(property);
if (st != null) {
for (NamedType nt : st) {
ac = AnnotatedClassResolver.resolveWithoutSuperTypes(config, nt.getType());
_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 = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
subtype.getType());
_collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName);
}
}
}
return _combineNamedAndUnnamed(rawBase, typesHandled, byName);
}
@Override
public Collection collectAndResolveSubtypesByTypeId(MapperConfig config,
AnnotatedClass baseType)
{
final Class rawBase = baseType.getRawType();
Set> typesHandled = new HashSet>();
Map byName = new LinkedHashMap();
NamedType rootType = new NamedType(rawBase, null);
_collectAndResolveByTypeId(baseType, rootType, config, typesHandled, byName);
if (_registeredSubtypes != null) {
for (NamedType subtype : _registeredSubtypes) {
// is it a subtype of root type?
if (rawBase.isAssignableFrom(subtype.getType())) { // yes
AnnotatedClass curr = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
subtype.getType());
_collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName);
}
}
}
return _combineNamedAndUnnamed(rawBase, 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 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 st = ai.findSubtypes(annotatedType);
if (st != null && !st.isEmpty()) {
for (NamedType subtype : st) {
AnnotatedClass subtypeClass = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
subtype.getType());
_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> typesHandled, Map 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 st = ai.findSubtypes(annotatedType);
if (st != null && !st.isEmpty()) {
for (NamedType subtype : st) {
AnnotatedClass subtypeClass = AnnotatedClassResolver.resolveWithoutSuperTypes(config,
subtype.getType());
_collectAndResolveByTypeId(subtypeClass, subtype, config, typesHandled, byName);
}
}
}
}
/**
* Helper method used for merging explicitly named types and handled classes
* without explicit names.
*/
protected Collection _combineNamedAndUnnamed(Class rawBase,
Set> typesHandled, Map byName)
{
ArrayList result = new ArrayList(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) {
// 27-Apr-2017, tatu: [databind#1616] Do not add base type itself unless
// it is concrete (or has explicit type name)
if ((cls == rawBase) && Modifier.isAbstract(cls.getModifiers())) {
continue;
}
result.add(new NamedType(cls));
}
return result;
}
}