blob: c9fd65e1281a2c163ba969013051faf32e45313f [file] [log] [blame]
package org.codehaus.jackson.map.jsontype.impl;
import java.util.*;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.map.util.ClassUtil;
/**
* {@link org.codehaus.jackson.map.jsontype.TypeIdResolver} implementation
* that converts between fully-qualified
* Java class names and (JSON) Strings.
*/
public class ClassNameIdResolver
extends TypeIdResolverBase
{
public ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory) {
super(baseType, typeFactory);
}
@Override
public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.CLASS; }
public void registerSubtype(Class<?> type, String name) {
// not used with class name - based resolvers
}
@Override
public String idFromValue(Object value)
{
return _idFrom(value, value.getClass());
}
@Override
public String idFromValueAndType(Object value, Class<?> type)
{
return _idFrom(value, type);
}
@Override
public JavaType typeFromId(String id)
{
/* 30-Jan-2010, tatu: Most ids are basic class names; so let's first
* check if any generics info is added; and only then ask factory
* to do translation when necessary
*/
if (id.indexOf('<') > 0) {
JavaType t = TypeFactory.fromCanonical(id);
// note: may want to try combining with specialization (esp for EnumMap)
return t;
}
try {
Class<?> cls = ClassUtil.findClass(id);
return _typeFactory.constructSpecializedType(_baseType, cls);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Invalid type id '"+id+"' (for id type 'Id.class'): no such class found");
} catch (Exception e) {
throw new IllegalArgumentException("Invalid type id '"+id+"' (for id type 'Id.class'): "+e.getMessage(), e);
}
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
protected final String _idFrom(Object value, Class<?> cls)
{
// [JACKSON-380] Need to ensure that "enum subtypes" work too
if (Enum.class.isAssignableFrom(cls)) {
if (!cls.isEnum()) { // means that it's sub-class of base enum, so:
cls = cls.getSuperclass();
}
}
String str = cls.getName();
if (str.startsWith("java.util")) {
/* 25-Jan-2009, tatu: There are some internal classes that
* we can not access as is. We need better mechanism; for
* now this has to do...
*/
/* Enum sets and maps are problematic since we MUST know
* type of contained enums, to be able to deserialize.
* In addition, EnumSet is not a concrete type either
*/
if (value instanceof EnumSet<?>) { // Regular- and JumboEnumSet...
Class<?> enumClass = ClassUtil.findEnumType((EnumSet<?>) value);
// not optimal: but EnumSet is not a customizable type so this is sort of ok
str = TypeFactory.defaultInstance().constructCollectionType(EnumSet.class, enumClass).toCanonical();
} else if (value instanceof EnumMap<?,?>) {
Class<?> enumClass = ClassUtil.findEnumType((EnumMap<?,?>) value);
Class<?> valueClass = Object.class;
// not optimal: but EnumMap is not a customizable type so this is sort of ok
str = TypeFactory.defaultInstance().constructMapType(EnumMap.class, enumClass, valueClass).toCanonical();
} else {
String end = str.substring(9);
if ((end.startsWith(".Arrays$") || end.startsWith(".Collections$"))
&& str.indexOf("List") >= 0) {
/* 17-Feb-2010, tatus: Another such case: result of
* Arrays.asList() is named like so in Sun JDK...
* Let's just plain old ArrayList in its place
* NOTE: chances are there are plenty of similar cases
* for other wrappers... (immutable, singleton, synced etc)
*/
str = "java.util.ArrayList";
}
}
} else if (str.indexOf('$') >= 0) {
/* Other special handling may be needed for inner classes, [JACKSON-584].
* The best way to handle would be to find 'hidden' constructor; pass parent
* value etc (which is actually done for non-anonymous static classes!),
* but that is just not possible due to various things. So, we will instead
* try to generalize type into something we will be more likely to be able
* construct.
*/
Class<?> outer = ClassUtil.getOuterClass(cls);
if (outer != null) {
/* one more check: let's actually not worry if the declared
* static type is non-static as well; if so, deserializer does
* have a chance at figuring it all out.
*/
Class<?> staticType = _baseType.getRawClass();
if (ClassUtil.getOuterClass(staticType) == null) {
// Is this always correct? Seems like it should be...
cls = _baseType.getRawClass();
str = cls.getName();
}
}
}
return str;
}
}