Skip to content

Commit

Permalink
Fix #312
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Feb 26, 2015
1 parent f7270ed commit 37879ab
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 20 deletions.
1 change: 1 addition & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Project: jackson-databind

2.6.0 (not yet released)

#312: Support Type Id mappings where two ids map to same Class
#649: Make `BeanDeserializer` use new `parser.nextFieldName()` and `.hasTokenId()` methods
#696: Copy constructor does not preserve `_injectableValues`
(reported by Charles A)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ public TypeDeserializer findTypeDeserializer(JavaType baseType)
return null;
}
} else {
subtypes = getSubtypeResolver().collectAndResolveSubtypesByName(this, ac);
subtypes = getSubtypeResolver().collectAndResolveSubtypesByTypeId(this, ac);
}
/* 04-May-2014, tatu: When called from DeserializerFactory, additional code like
* this is invoked. But here we do not actually have access to mappings, so not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1247,7 +1247,7 @@ public TypeDeserializer findTypeDeserializer(DeserializationConfig config,
return null;
}
} else {
subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByName(config, ac);
subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, ac);
}
// [JACKSON-505]: May need to figure out default implementation, if none found yet
// (note: check for abstract type is not 100% mandatory, more of an optimization)
Expand Down Expand Up @@ -1387,7 +1387,7 @@ public TypeDeserializer findPropertyTypeDeserializer(DeserializationConfig confi
return findTypeDeserializer(config, baseType);
}
// but if annotations found, may need to resolve subtypes:
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByName(
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(
config, annotated, baseType);
return b.buildTypeDeserializer(config, baseType, subtypes);
}
Expand Down Expand Up @@ -1415,7 +1415,7 @@ public TypeDeserializer findPropertyContentTypeDeserializer(DeserializationConfi
return findTypeDeserializer(config, contentType);
}
// but if annotations found, may need to resolve subtypes:
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByName(
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(
config, propertyEntity, contentType);
return b.buildTypeDeserializer(config, contentType, subtypes);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public Collection<NamedType> collectAndResolveSubtypesByClass(MapperConfig<?> co
*
* @since 2.6
*/
public Collection<NamedType> collectAndResolveSubtypesByName(MapperConfig<?> config,
public Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config,
AnnotatedMember property, JavaType baseType) {
// for backwards compatibility...
return collectAndResolveSubtypes(property, config,
Expand All @@ -101,7 +101,7 @@ public Collection<NamedType> collectAndResolveSubtypesByName(MapperConfig<?> con
*
* @since 2.6
*/
public Collection<NamedType> collectAndResolveSubtypesByName(MapperConfig<?> config,
public Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config,
AnnotatedClass baseType) {
// for backwards compatibility...
return collectAndResolveSubtypes(baseType, config, config.getAnnotationIntrospector());
Expand All @@ -116,7 +116,7 @@ public Collection<NamedType> collectAndResolveSubtypesByName(MapperConfig<?> con
/**
* @deprecated Since 2.6 Use either
* {@link #collectAndResolveSubtypesByClass(MapperConfig, AnnotatedMember, JavaType)}
* or {@link #collectAndResolveSubtypesByName(MapperConfig, AnnotatedMember, JavaType)}
* or {@link #collectAndResolveSubtypesByTypeId(MapperConfig, AnnotatedMember, JavaType)}
* instead.
*/
@Deprecated
Expand All @@ -126,7 +126,7 @@ public abstract Collection<NamedType> collectAndResolveSubtypes(AnnotatedMember
/**
* @deprecated Since 2.6 Use either
* {@link #collectAndResolveSubtypesByClass(MapperConfig, AnnotatedClass)}
* or {@link #collectAndResolveSubtypesByName(MapperConfig, AnnotatedClass)}
* or {@link #collectAndResolveSubtypesByTypeId(MapperConfig, AnnotatedClass)}
* instead.
*/
@Deprecated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,19 +121,65 @@ public Collection<NamedType> collectAndResolveSubtypesByClass(MapperConfig<?> co
*/

@Override
public Collection<NamedType> collectAndResolveSubtypesByName(MapperConfig<?> config,
public Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config,
AnnotatedMember property, JavaType baseType)
{
// !!! TODO: implement properly
return collectAndResolveSubtypesByClass(config, property, 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, ai, 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(), ai, 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(), ai, config);
_collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName);
}
}
}
return _combineNamedAndUnnamed(typesHandled, byName);
}

@Override
public Collection<NamedType> collectAndResolveSubtypesByName(MapperConfig<?> config,
public Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config,
AnnotatedClass type)
{
// !!! TODO: implement properly
return collectAndResolveSubtypesByClass(config, 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
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
AnnotatedClass curr = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), ai, config);
_collectAndResolveByTypeId(curr, subtype, config, typesHandled, byName);
}
}
}
return _combineNamedAndUnnamed(typesHandled, byName);
}

/*
Expand All @@ -143,13 +189,15 @@ public Collection<NamedType> collectAndResolveSubtypesByName(MapperConfig<?> con
*/

@Override
@Deprecated
public Collection<NamedType> collectAndResolveSubtypes(AnnotatedMember property,
MapperConfig<?> config, AnnotationIntrospector ai, JavaType baseType)
{
return collectAndResolveSubtypesByClass(config, property, baseType);
}

@Override
@Deprecated
public Collection<NamedType> collectAndResolveSubtypes(AnnotatedClass type,
MapperConfig<?> config, AnnotationIntrospector ai)
{
Expand All @@ -163,7 +211,8 @@ public Collection<NamedType> collectAndResolveSubtypes(AnnotatedClass type,
*/

/**
* Method called to find subtypes for a specific type (class)
* 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,
Expand Down Expand Up @@ -193,12 +242,60 @@ protected void _collectAndResolve(AnnotatedClass annotatedType, NamedType namedT
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);
}
}
}

/**
* 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(), ai, 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;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.fasterxml.jackson.failing;
package com.fasterxml.jackson.databind.jsontype;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.*;
Expand Down

0 comments on commit 37879ab

Please sign in to comment.