1 package com.fasterxml.jackson.databind.type; 2 3 import java.lang.reflect.TypeVariable; 4 import java.util.*; 5 6 import com.fasterxml.jackson.databind.JavaType; 7 8 /** 9 * Type that represents Map-like types; things that consist of key/value pairs 10 * but that do not necessarily implement {@link java.util.Map}, but that do not 11 * have enough introspection functionality to allow for some level of generic 12 * handling. This specifically allows framework to check for configuration and 13 * annotation settings used for Map types, and pass these to custom handlers 14 * that may be more familiar with actual type. 15 */ 16 public class MapLikeType extends TypeBase { 17 private static final long serialVersionUID = 1L; 18 19 /** 20 * Type of keys of Map. 21 */ 22 protected final JavaType _keyType; 23 24 /** 25 * Type of values of Map. 26 */ 27 protected final JavaType _valueType; 28 29 /* 30 /********************************************************** 31 * Life-cycle 32 /********************************************************** 33 */ 34 MapLikeType(Class<?> mapType, TypeBindings bindings, JavaType superClass, JavaType[] superInts, JavaType keyT, JavaType valueT, Object valueHandler, Object typeHandler, boolean asStatic)35 protected MapLikeType(Class<?> mapType, TypeBindings bindings, 36 JavaType superClass, JavaType[] superInts, JavaType keyT, 37 JavaType valueT, Object valueHandler, Object typeHandler, 38 boolean asStatic) { 39 super(mapType, bindings, superClass, superInts, keyT.hashCode() 40 ^ valueT.hashCode(), valueHandler, typeHandler, asStatic); 41 _keyType = keyT; 42 _valueType = valueT; 43 } 44 45 /** 46 * @since 2.7 47 */ MapLikeType(TypeBase base, JavaType keyT, JavaType valueT)48 protected MapLikeType(TypeBase base, JavaType keyT, JavaType valueT) { 49 super(base); 50 _keyType = keyT; 51 _valueType = valueT; 52 } 53 54 /** 55 * Factory method that can be used to "upgrade" a basic type into 56 * collection-like one; usually done via {@link TypeModifier} 57 * 58 * @since 2.7 59 */ upgradeFrom(JavaType baseType, JavaType keyT, JavaType valueT)60 public static MapLikeType upgradeFrom(JavaType baseType, JavaType keyT, 61 JavaType valueT) { 62 // 19-Oct-2015, tatu: Not sure if and how other types could be used as 63 // base; 64 // will cross that bridge if and when need be 65 if (baseType instanceof TypeBase) { 66 return new MapLikeType((TypeBase) baseType, keyT, valueT); 67 } 68 throw new IllegalArgumentException( 69 "Cannot upgrade from an instance of " + baseType.getClass()); 70 } 71 72 @Deprecated 73 // since 2.7; remove from 2.8 construct(Class<?> rawType, JavaType keyT, JavaType valueT)74 public static MapLikeType construct(Class<?> rawType, JavaType keyT, 75 JavaType valueT) { 76 // First: may need to fabricate TypeBindings (needed for refining into 77 // concrete collection types, as per [databind#1102]) 78 TypeVariable<?>[] vars = rawType.getTypeParameters(); 79 TypeBindings bindings; 80 if ((vars == null) || (vars.length != 2)) { 81 bindings = TypeBindings.emptyBindings(); 82 } else { 83 bindings = TypeBindings.create(rawType, keyT, valueT); 84 } 85 return new MapLikeType(rawType, bindings, _bogusSuperClass(rawType), 86 null, keyT, valueT, null, null, false); 87 } 88 89 @Deprecated 90 // since 2.7 91 @Override _narrow(Class<?> subclass)92 protected JavaType _narrow(Class<?> subclass) { 93 return new MapLikeType(subclass, _bindings, _superClass, 94 _superInterfaces, _keyType, _valueType, _valueHandler, 95 _typeHandler, _asStatic); 96 } 97 98 /** 99 * @since 2.7 100 */ withKeyType(JavaType keyType)101 public MapLikeType withKeyType(JavaType keyType) { 102 if (keyType == _keyType) { 103 return this; 104 } 105 return new MapLikeType(_class, _bindings, _superClass, 106 _superInterfaces, keyType, _valueType, _valueHandler, 107 _typeHandler, _asStatic); 108 } 109 110 @Override withContentType(JavaType contentType)111 public JavaType withContentType(JavaType contentType) { 112 if (_valueType == contentType) { 113 return this; 114 } 115 return new MapLikeType(_class, _bindings, _superClass, 116 _superInterfaces, _keyType, contentType, _valueHandler, 117 _typeHandler, _asStatic); 118 } 119 120 @Override withTypeHandler(Object h)121 public MapLikeType withTypeHandler(Object h) { 122 return new MapLikeType(_class, _bindings, _superClass, 123 _superInterfaces, _keyType, _valueType, _valueHandler, h, 124 _asStatic); 125 } 126 127 @Override withContentTypeHandler(Object h)128 public MapLikeType withContentTypeHandler(Object h) { 129 return new MapLikeType(_class, _bindings, _superClass, 130 _superInterfaces, _keyType, _valueType.withTypeHandler(h), 131 _valueHandler, _typeHandler, _asStatic); 132 } 133 134 @Override withValueHandler(Object h)135 public MapLikeType withValueHandler(Object h) { 136 return new MapLikeType(_class, _bindings, _superClass, 137 _superInterfaces, _keyType, _valueType, h, _typeHandler, 138 _asStatic); 139 } 140 141 @Override withContentValueHandler(Object h)142 public MapLikeType withContentValueHandler(Object h) { 143 return new MapLikeType(_class, _bindings, _superClass, 144 _superInterfaces, _keyType, _valueType.withValueHandler(h), 145 _valueHandler, _typeHandler, _asStatic); 146 } 147 148 @Override withHandlersFrom(JavaType src)149 public JavaType withHandlersFrom(JavaType src) { 150 JavaType type = super.withHandlersFrom(src); 151 JavaType srcKeyType = src.getKeyType(); 152 // "withKeyType()" not part of JavaType, hence must verify: 153 if (type instanceof MapLikeType) { 154 if (srcKeyType != null) { 155 JavaType ct = _keyType.withHandlersFrom(srcKeyType); 156 if (ct != _keyType) { 157 type = ((MapLikeType) type).withKeyType(ct); 158 } 159 } 160 } 161 JavaType srcCt = src.getContentType(); 162 if (srcCt != null) { 163 JavaType ct = _valueType.withHandlersFrom(srcCt); 164 if (ct != _valueType) { 165 type = type.withContentType(ct); 166 } 167 } 168 return type; 169 } 170 171 @Override withStaticTyping()172 public MapLikeType withStaticTyping() { 173 if (_asStatic) { 174 return this; 175 } 176 return new MapLikeType(_class, _bindings, _superClass, 177 _superInterfaces, _keyType, _valueType.withStaticTyping(), 178 _valueHandler, _typeHandler, true); 179 } 180 181 @Override refine(Class<?> rawType, TypeBindings bindings, JavaType superClass, JavaType[] superInterfaces)182 public JavaType refine(Class<?> rawType, TypeBindings bindings, 183 JavaType superClass, JavaType[] superInterfaces) { 184 return new MapLikeType(rawType, bindings, superClass, superInterfaces, 185 _keyType, _valueType, _valueHandler, _typeHandler, _asStatic); 186 } 187 188 @Override buildCanonicalName()189 protected String buildCanonicalName() { 190 StringBuilder sb = new StringBuilder(); 191 sb.append(_class.getName()); 192 if (_keyType != null) { 193 sb.append('<'); 194 sb.append(_keyType.toCanonical()); 195 sb.append(','); 196 sb.append(_valueType.toCanonical()); 197 sb.append('>'); 198 } 199 return sb.toString(); 200 } 201 202 /* 203 /********************************************************** 204 /* Public API 205 /********************************************************** 206 */ 207 208 @Override isContainerType()209 public boolean isContainerType() { 210 return true; 211 } 212 213 @Override isMapLikeType()214 public boolean isMapLikeType() { 215 return true; 216 } 217 218 @Override getKeyType()219 public JavaType getKeyType() { 220 return _keyType; 221 } 222 223 @Override getContentType()224 public JavaType getContentType() { 225 return _valueType; 226 } 227 228 @Override getContentValueHandler()229 public Object getContentValueHandler() { 230 return _valueType.getValueHandler(); 231 } 232 233 @Override getContentTypeHandler()234 public Object getContentTypeHandler() { 235 return _valueType.getTypeHandler(); 236 } 237 238 @Override hasHandlers()239 public boolean hasHandlers() { 240 return super.hasHandlers() || _valueType.hasHandlers() 241 || _keyType.hasHandlers(); 242 } 243 244 @Override getErasedSignature(StringBuilder sb)245 public StringBuilder getErasedSignature(StringBuilder sb) { 246 return _classSignature(_class, sb, true); 247 } 248 249 @Override getGenericSignature(StringBuilder sb)250 public StringBuilder getGenericSignature(StringBuilder sb) { 251 _classSignature(_class, sb, false); 252 sb.append('<'); 253 _keyType.getGenericSignature(sb); 254 _valueType.getGenericSignature(sb); 255 sb.append(">;"); 256 return sb; 257 } 258 259 /* 260 /********************************************************** 261 /* Extended API 262 /********************************************************** 263 */ 264 withKeyTypeHandler(Object h)265 public MapLikeType withKeyTypeHandler(Object h) { 266 return new MapLikeType(_class, _bindings, _superClass, 267 _superInterfaces, _keyType.withTypeHandler(h), _valueType, 268 _valueHandler, _typeHandler, _asStatic); 269 } 270 withKeyValueHandler(Object h)271 public MapLikeType withKeyValueHandler(Object h) { 272 return new MapLikeType(_class, _bindings, _superClass, 273 _superInterfaces, _keyType.withValueHandler(h), _valueType, 274 _valueHandler, _typeHandler, _asStatic); 275 } 276 277 /** 278 * Method that can be used for checking whether this type is a "real" 279 * Collection type; meaning whether it represents a parameterized subtype of 280 * {@link java.util.Collection} or just something that acts like one. 281 */ isTrueMapType()282 public boolean isTrueMapType() { 283 return Map.class.isAssignableFrom(_class); 284 } 285 286 /* 287 /********************************************************** 288 /* Standard methods 289 /********************************************************** 290 */ 291 292 @Override toString()293 public String toString() { 294 return String.format("[map-like type; class %s, %s -> %s]", 295 _class.getName(), _keyType, _valueType); 296 } 297 298 @Override equals(Object o)299 public boolean equals(Object o) { 300 if (o == this) return true; 301 if (o == null) return false; 302 if (o.getClass() != getClass()) return false; 303 304 MapLikeType other = (MapLikeType) o; 305 return (_class == other._class) && _keyType.equals(other._keyType) 306 && _valueType.equals(other._valueType); 307 } 308 } 309