| package com.fasterxml.jackson.databind.jsontype.impl; |
| |
| import java.util.*; |
| |
| import com.fasterxml.jackson.annotation.JsonTypeInfo; |
| |
| import com.fasterxml.jackson.databind.BeanDescription; |
| import com.fasterxml.jackson.databind.JavaType; |
| import com.fasterxml.jackson.databind.cfg.MapperConfig; |
| import com.fasterxml.jackson.databind.jsontype.NamedType; |
| |
| public class TypeNameIdResolver |
| extends TypeIdResolverBase |
| { |
| protected final MapperConfig<?> _config; |
| |
| /** |
| * Mappings from class name to type id, used for serialization |
| */ |
| protected final HashMap<String, String> _typeToId; |
| |
| /** |
| * Mappings from type id to JavaType, used for deserialization |
| */ |
| protected final HashMap<String, JavaType> _idToType; |
| |
| protected TypeNameIdResolver(MapperConfig<?> config, JavaType baseType, |
| HashMap<String, String> typeToId, HashMap<String, JavaType> idToType) |
| { |
| super(baseType, config.getTypeFactory()); |
| _config = config; |
| _typeToId = typeToId; |
| _idToType = idToType; |
| } |
| |
| public static TypeNameIdResolver construct(MapperConfig<?> config, |
| JavaType baseType, |
| Collection<NamedType> subtypes, boolean forSer, boolean forDeser) |
| { |
| // sanity check |
| if (forSer == forDeser) throw new IllegalArgumentException(); |
| HashMap<String, String> typeToId = null; |
| HashMap<String, JavaType> idToType = null; |
| |
| if (forSer) { |
| typeToId = new HashMap<String, String>(); |
| } |
| if (forDeser) { |
| idToType = new HashMap<String, JavaType>(); |
| } |
| if (subtypes != null) { |
| for (NamedType t : subtypes) { |
| /* no name? Need to figure out default; for now, let's just |
| * use non-qualified class name |
| */ |
| Class<?> cls = t.getType(); |
| String id = t.hasName() ? t.getName() : _defaultTypeId(cls); |
| if (forSer) { |
| typeToId.put(cls.getName(), id); |
| } |
| if (forDeser) { |
| /* 24-Feb-2011, tatu: [JACKSON-498] One more problem; sometimes |
| * we have same name for multiple types; if so, use most specific |
| * one. |
| */ |
| JavaType prev = idToType.get(id); |
| if (prev != null) { // Can only override if more specific |
| if (cls.isAssignableFrom(prev.getRawClass())) { // nope, more generic (or same) |
| continue; |
| } |
| } |
| idToType.put(id, config.constructType(cls)); |
| } |
| } |
| } |
| return new TypeNameIdResolver(config, baseType, typeToId, idToType); |
| } |
| |
| // @Override |
| public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.NAME; } |
| |
| // @Override |
| public String idFromValue(Object value) |
| { |
| Class<?> cls = value.getClass(); |
| final String key = cls.getName(); |
| String name; |
| synchronized (_typeToId) { |
| name = _typeToId.get(key); |
| if (name == null) { |
| // 24-Feb-2011, tatu: As per [JACKSON-498], may need to dynamically look up name |
| // can either throw an exception, or use default name... |
| if (_config.isAnnotationProcessingEnabled()) { |
| BeanDescription beanDesc = _config.introspectClassAnnotations(cls); |
| name = _config.getAnnotationIntrospector().findTypeName(beanDesc.getClassInfo()); |
| } |
| if (name == null) { |
| // And if still not found, let's choose default? |
| name = _defaultTypeId(cls); |
| } |
| _typeToId.put(key, name); |
| } |
| } |
| return name; |
| } |
| |
| // @Override |
| public String idFromValueAndType(Object value, Class<?> type) |
| { |
| return idFromValue(value); |
| } |
| |
| // @Override |
| public JavaType typeFromId(String id) |
| throws IllegalArgumentException |
| { |
| JavaType t = _idToType.get(id); |
| /* Now: if no type is found, should we try to locate it by |
| * some other means? (specifically, if in same package as base type, |
| * could just try Class.forName) |
| * For now let's not add any such workarounds; can add if need be |
| */ |
| return t; |
| } |
| |
| @Override |
| public String toString() |
| { |
| StringBuilder sb = new StringBuilder(); |
| sb.append('[').append(getClass().getName()); |
| sb.append("; id-to-type=").append(_idToType); |
| sb.append(']'); |
| return sb.toString(); |
| } |
| |
| /* |
| /********************************************************* |
| /* Helper methods |
| /********************************************************* |
| */ |
| |
| /** |
| * If no name was explicitly given for a class, we will just |
| * use non-qualified class name |
| */ |
| protected static String _defaultTypeId(Class<?> cls) |
| { |
| String n = cls.getName(); |
| int ix = n.lastIndexOf('.'); |
| return (ix < 0) ? n : n.substring(ix+1); |
| } |
| } |