| package com.fasterxml.jackson.databind.type; |
| |
| import java.lang.reflect.TypeVariable; |
| import java.util.*; |
| |
| import com.fasterxml.jackson.databind.JavaType; |
| |
| /** |
| * Type that represents Map-like types; things that consist of key/value pairs |
| * but that do not necessarily implement {@link java.util.Map}, but that do not |
| * have enough introspection functionality to allow for some level of generic |
| * handling. This specifically allows framework to check for configuration and |
| * annotation settings used for Map types, and pass these to custom handlers |
| * that may be more familiar with actual type. |
| */ |
| public class MapLikeType extends TypeBase { |
| private static final long serialVersionUID = 1L; |
| |
| /** |
| * Type of keys of Map. |
| */ |
| protected final JavaType _keyType; |
| |
| /** |
| * Type of values of Map. |
| */ |
| protected final JavaType _valueType; |
| |
| /* |
| /********************************************************** |
| * Life-cycle |
| /********************************************************** |
| */ |
| |
| protected MapLikeType(Class<?> mapType, TypeBindings bindings, |
| JavaType superClass, JavaType[] superInts, JavaType keyT, |
| JavaType valueT, Object valueHandler, Object typeHandler, |
| boolean asStatic) { |
| super(mapType, bindings, superClass, superInts, keyT.hashCode() |
| ^ valueT.hashCode(), valueHandler, typeHandler, asStatic); |
| _keyType = keyT; |
| _valueType = valueT; |
| } |
| |
| /** |
| * @since 2.7 |
| */ |
| protected MapLikeType(TypeBase base, JavaType keyT, JavaType valueT) { |
| super(base); |
| _keyType = keyT; |
| _valueType = valueT; |
| } |
| |
| /** |
| * Factory method that can be used to "upgrade" a basic type into |
| * collection-like one; usually done via {@link TypeModifier} |
| * |
| * @since 2.7 |
| */ |
| public static MapLikeType upgradeFrom(JavaType baseType, JavaType keyT, |
| JavaType valueT) { |
| // 19-Oct-2015, tatu: Not sure if and how other types could be used as |
| // base; |
| // will cross that bridge if and when need be |
| if (baseType instanceof TypeBase) { |
| return new MapLikeType((TypeBase) baseType, keyT, valueT); |
| } |
| throw new IllegalArgumentException( |
| "Can not upgrade from an instance of " + baseType.getClass()); |
| } |
| |
| @Deprecated |
| // since 2.7; remove from 2.8 |
| public static MapLikeType construct(Class<?> rawType, JavaType keyT, |
| JavaType valueT) { |
| // First: may need to fabricate TypeBindings (needed for refining into |
| // concrete collection types, as per [databind#1102]) |
| TypeVariable<?>[] vars = rawType.getTypeParameters(); |
| TypeBindings bindings; |
| if ((vars == null) || (vars.length != 2)) { |
| bindings = TypeBindings.emptyBindings(); |
| } else { |
| bindings = TypeBindings.create(rawType, keyT, valueT); |
| } |
| return new MapLikeType(rawType, bindings, _bogusSuperClass(rawType), |
| null, keyT, valueT, null, null, false); |
| } |
| |
| @Deprecated |
| // since 2.7 |
| @Override |
| protected JavaType _narrow(Class<?> subclass) { |
| return new MapLikeType(subclass, _bindings, _superClass, |
| _superInterfaces, _keyType, _valueType, _valueHandler, |
| _typeHandler, _asStatic); |
| } |
| |
| /** |
| * @since 2.7 |
| */ |
| public MapLikeType withKeyType(JavaType keyType) { |
| if (keyType == _keyType) { |
| return this; |
| } |
| return new MapLikeType(_class, _bindings, _superClass, |
| _superInterfaces, keyType, _valueType, _valueHandler, |
| _typeHandler, _asStatic); |
| } |
| |
| @Override |
| public JavaType withContentType(JavaType contentType) { |
| if (_valueType == contentType) { |
| return this; |
| } |
| return new MapLikeType(_class, _bindings, _superClass, |
| _superInterfaces, _keyType, contentType, _valueHandler, |
| _typeHandler, _asStatic); |
| } |
| |
| @Override |
| public MapLikeType withTypeHandler(Object h) { |
| return new MapLikeType(_class, _bindings, _superClass, |
| _superInterfaces, _keyType, _valueType, _valueHandler, h, |
| _asStatic); |
| } |
| |
| @Override |
| public MapLikeType withContentTypeHandler(Object h) { |
| return new MapLikeType(_class, _bindings, _superClass, |
| _superInterfaces, _keyType, _valueType.withTypeHandler(h), |
| _valueHandler, _typeHandler, _asStatic); |
| } |
| |
| @Override |
| public MapLikeType withValueHandler(Object h) { |
| return new MapLikeType(_class, _bindings, _superClass, |
| _superInterfaces, _keyType, _valueType, h, _typeHandler, |
| _asStatic); |
| } |
| |
| @Override |
| public MapLikeType withContentValueHandler(Object h) { |
| return new MapLikeType(_class, _bindings, _superClass, |
| _superInterfaces, _keyType, _valueType.withValueHandler(h), |
| _valueHandler, _typeHandler, _asStatic); |
| } |
| |
| @Override |
| public JavaType withHandlersFrom(JavaType src) { |
| JavaType type = super.withHandlersFrom(src); |
| JavaType srcKeyType = src.getKeyType(); |
| // "withKeyType()" not part of JavaType, hence must verify: |
| if (type instanceof MapLikeType) { |
| if (srcKeyType != null) { |
| JavaType ct = _keyType.withHandlersFrom(srcKeyType); |
| if (ct != _keyType) { |
| type = ((MapLikeType) type).withKeyType(ct); |
| } |
| } |
| } |
| JavaType srcCt = src.getContentType(); |
| if (srcCt != null) { |
| JavaType ct = _valueType.withHandlersFrom(srcCt); |
| if (ct != _valueType) { |
| type = type.withContentType(ct); |
| } |
| } |
| return type; |
| } |
| |
| @Override |
| public MapLikeType withStaticTyping() { |
| if (_asStatic) { |
| return this; |
| } |
| return new MapLikeType(_class, _bindings, _superClass, |
| _superInterfaces, _keyType, _valueType.withStaticTyping(), |
| _valueHandler, _typeHandler, true); |
| } |
| |
| @Override |
| public JavaType refine(Class<?> rawType, TypeBindings bindings, |
| JavaType superClass, JavaType[] superInterfaces) { |
| return new MapLikeType(rawType, bindings, superClass, superInterfaces, |
| _keyType, _valueType, _valueHandler, _typeHandler, _asStatic); |
| } |
| |
| @Override |
| protected String buildCanonicalName() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(_class.getName()); |
| if (_keyType != null) { |
| sb.append('<'); |
| sb.append(_keyType.toCanonical()); |
| sb.append(','); |
| sb.append(_valueType.toCanonical()); |
| sb.append('>'); |
| } |
| return sb.toString(); |
| } |
| |
| /* |
| /********************************************************** |
| /* Public API |
| /********************************************************** |
| */ |
| |
| @Override |
| public boolean isContainerType() { |
| return true; |
| } |
| |
| @Override |
| public boolean isMapLikeType() { |
| return true; |
| } |
| |
| @Override |
| public JavaType getKeyType() { |
| return _keyType; |
| } |
| |
| @Override |
| public JavaType getContentType() { |
| return _valueType; |
| } |
| |
| @Override |
| public Object getContentValueHandler() { |
| return _valueType.getValueHandler(); |
| } |
| |
| @Override |
| public Object getContentTypeHandler() { |
| return _valueType.getTypeHandler(); |
| } |
| |
| @Override |
| public boolean hasHandlers() { |
| return super.hasHandlers() || _valueType.hasHandlers() |
| || _keyType.hasHandlers(); |
| } |
| |
| @Override |
| public StringBuilder getErasedSignature(StringBuilder sb) { |
| return _classSignature(_class, sb, true); |
| } |
| |
| @Override |
| public StringBuilder getGenericSignature(StringBuilder sb) { |
| _classSignature(_class, sb, false); |
| sb.append('<'); |
| _keyType.getGenericSignature(sb); |
| _valueType.getGenericSignature(sb); |
| sb.append(">;"); |
| return sb; |
| } |
| |
| /* |
| /********************************************************** |
| /* Extended API |
| /********************************************************** |
| */ |
| |
| public MapLikeType withKeyTypeHandler(Object h) { |
| return new MapLikeType(_class, _bindings, _superClass, |
| _superInterfaces, _keyType.withTypeHandler(h), _valueType, |
| _valueHandler, _typeHandler, _asStatic); |
| } |
| |
| public MapLikeType withKeyValueHandler(Object h) { |
| return new MapLikeType(_class, _bindings, _superClass, |
| _superInterfaces, _keyType.withValueHandler(h), _valueType, |
| _valueHandler, _typeHandler, _asStatic); |
| } |
| |
| /** |
| * Method that can be used for checking whether this type is a "real" |
| * Collection type; meaning whether it represents a parameterized subtype of |
| * {@link java.util.Collection} or just something that acts like one. |
| */ |
| public boolean isTrueMapType() { |
| return Map.class.isAssignableFrom(_class); |
| } |
| |
| /* |
| /********************************************************** |
| /* Standard methods |
| /********************************************************** |
| */ |
| |
| @Override |
| public String toString() { |
| return String.format("[map-like type; class %s, %s -> %s]", |
| _class.getName(), _keyType, _valueType); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == this) return true; |
| if (o == null) return false; |
| if (o.getClass() != getClass()) return false; |
| |
| MapLikeType other = (MapLikeType) o; |
| return (_class == other._class) && _keyType.equals(other._keyType) |
| && _valueType.equals(other._valueType); |
| } |
| } |