| package com.fasterxml.jackson.databind.jsontype.impl; |
| |
| import java.util.Collection; |
| |
| import com.fasterxml.jackson.annotation.JsonTypeInfo; |
| |
| import com.fasterxml.jackson.databind.*; |
| import com.fasterxml.jackson.databind.annotation.NoClass; |
| import com.fasterxml.jackson.databind.cfg.MapperConfig; |
| import com.fasterxml.jackson.databind.jsontype.*; |
| |
| /** |
| * Default {@link TypeResolverBuilder} implementation. |
| */ |
| public class StdTypeResolverBuilder |
| implements TypeResolverBuilder<StdTypeResolverBuilder> |
| { |
| // Configuration settings: |
| |
| protected JsonTypeInfo.Id _idType; |
| |
| protected JsonTypeInfo.As _includeAs; |
| |
| protected String _typeProperty; |
| |
| /** |
| * Whether type id should be exposed to deserializers or not |
| */ |
| protected boolean _typeIdVisible = false; |
| |
| /** |
| * Default class to use in case type information is not available |
| * or is broken. |
| */ |
| protected Class<?> _defaultImpl; |
| |
| // Objects |
| |
| protected TypeIdResolver _customIdResolver; |
| |
| /* |
| /********************************************************** |
| /* Construction, initialization, actual building |
| /********************************************************** |
| */ |
| |
| public StdTypeResolverBuilder() { } |
| |
| public static StdTypeResolverBuilder noTypeInfoBuilder() { |
| return new StdTypeResolverBuilder().init(JsonTypeInfo.Id.NONE, null); |
| } |
| |
| @Override |
| public StdTypeResolverBuilder init(JsonTypeInfo.Id idType, TypeIdResolver idRes) |
| { |
| // sanity checks |
| if (idType == null) { |
| throw new IllegalArgumentException("idType can not be null"); |
| } |
| _idType = idType; |
| _customIdResolver = idRes; |
| // Let's also initialize property name as per idType default |
| _typeProperty = idType.getDefaultPropertyName(); |
| return this; |
| } |
| |
| @Override |
| public TypeSerializer buildTypeSerializer(SerializationConfig config, |
| JavaType baseType, Collection<NamedType> subtypes) |
| { |
| if (_idType == JsonTypeInfo.Id.NONE) { return null; } |
| // 03-Oct-2016, tatu: As per [databind#1395] better prevent use for primitives, |
| // regardless of setting |
| if (baseType.isPrimitive()) { |
| return null; |
| } |
| TypeIdResolver idRes = idResolver(config, baseType, subtypes, true, false); |
| switch (_includeAs) { |
| case WRAPPER_ARRAY: |
| return new AsArrayTypeSerializer(idRes, null); |
| case PROPERTY: |
| return new AsPropertyTypeSerializer(idRes, null, _typeProperty); |
| case WRAPPER_OBJECT: |
| return new AsWrapperTypeSerializer(idRes, null); |
| case EXTERNAL_PROPERTY: |
| return new AsExternalTypeSerializer(idRes, null, _typeProperty); |
| case EXISTING_PROPERTY: |
| // as per [#528] |
| return new AsExistingPropertyTypeSerializer(idRes, null, _typeProperty); |
| } |
| throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs); |
| } |
| |
| // as per [#368] |
| // removed when fix [#528] |
| //private IllegalArgumentException _noExisting() { |
| // return new IllegalArgumentException("Inclusion type "+_includeAs+" not yet supported"); |
| //} |
| |
| @Override |
| public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, |
| JavaType baseType, Collection<NamedType> subtypes) |
| { |
| if (_idType == JsonTypeInfo.Id.NONE) { return null; } |
| // 03-Oct-2016, tatu: As per [databind#1395] better prevent use for primitives, |
| // regardless of setting |
| if (baseType.isPrimitive()) { |
| return null; |
| } |
| |
| TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true); |
| |
| JavaType defaultImpl; |
| |
| if (_defaultImpl == null) { |
| defaultImpl = null; |
| } else { |
| // 20-Mar-2016, tatu: It is important to do specialization go through |
| // TypeFactory to ensure proper resolution; with 2.7 and before, direct |
| // call to JavaType was used, but that can not work reliably with 2.7 |
| // 20-Mar-2016, tatu: Can finally add a check for type compatibility BUT |
| // if so, need to add explicit checks for marker types. Not ideal, but |
| // seems like a reasonable compromise. |
| if ((_defaultImpl == Void.class) |
| || (_defaultImpl == NoClass.class)) { |
| defaultImpl = config.getTypeFactory().constructType(_defaultImpl); |
| } else { |
| defaultImpl = config.getTypeFactory() |
| .constructSpecializedType(baseType, _defaultImpl); |
| } |
| } |
| |
| // First, method for converting type info to type id: |
| switch (_includeAs) { |
| case WRAPPER_ARRAY: |
| return new AsArrayTypeDeserializer(baseType, idRes, |
| _typeProperty, _typeIdVisible, defaultImpl); |
| case PROPERTY: |
| case EXISTING_PROPERTY: // as per [#528] same class as PROPERTY |
| return new AsPropertyTypeDeserializer(baseType, idRes, |
| _typeProperty, _typeIdVisible, defaultImpl, _includeAs); |
| case WRAPPER_OBJECT: |
| return new AsWrapperTypeDeserializer(baseType, idRes, |
| _typeProperty, _typeIdVisible, defaultImpl); |
| case EXTERNAL_PROPERTY: |
| return new AsExternalTypeDeserializer(baseType, idRes, |
| _typeProperty, _typeIdVisible, defaultImpl); |
| } |
| throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs); |
| } |
| |
| /* |
| /********************************************************** |
| /* Construction, configuration |
| /********************************************************** |
| */ |
| |
| @Override |
| public StdTypeResolverBuilder inclusion(JsonTypeInfo.As includeAs) { |
| if (includeAs == null) { |
| throw new IllegalArgumentException("includeAs can not be null"); |
| } |
| _includeAs = includeAs; |
| return this; |
| } |
| |
| /** |
| * Method for constructing an instance with specified type property name |
| * (property name to use for type id when using "as-property" inclusion). |
| */ |
| @Override |
| public StdTypeResolverBuilder typeProperty(String typeIdPropName) { |
| // ok to have null/empty; will restore to use defaults |
| if (typeIdPropName == null || typeIdPropName.length() == 0) { |
| typeIdPropName = _idType.getDefaultPropertyName(); |
| } |
| _typeProperty = typeIdPropName; |
| return this; |
| } |
| |
| @Override |
| public StdTypeResolverBuilder defaultImpl(Class<?> defaultImpl) { |
| _defaultImpl = defaultImpl; |
| return this; |
| } |
| |
| @Override |
| public StdTypeResolverBuilder typeIdVisibility(boolean isVisible) { |
| _typeIdVisible = isVisible; |
| return this; |
| } |
| |
| /* |
| /********************************************************** |
| /* Accessors |
| /********************************************************** |
| */ |
| |
| @Override public Class<?> getDefaultImpl() { return _defaultImpl; } |
| |
| public String getTypeProperty() { return _typeProperty; } |
| public boolean isTypeIdVisible() { return _typeIdVisible; } |
| |
| /* |
| /********************************************************** |
| /* Internal methods |
| /********************************************************** |
| */ |
| |
| /** |
| * Helper method that will either return configured custom |
| * type id resolver, or construct a standard resolver |
| * given configuration. |
| */ |
| protected TypeIdResolver idResolver(MapperConfig<?> config, |
| JavaType baseType, Collection<NamedType> subtypes, boolean forSer, boolean forDeser) |
| { |
| // Custom id resolver? |
| if (_customIdResolver != null) { return _customIdResolver; } |
| if (_idType == null) throw new IllegalStateException("Can not build, 'init()' not yet called"); |
| switch (_idType) { |
| case CLASS: |
| return new ClassNameIdResolver(baseType, config.getTypeFactory()); |
| case MINIMAL_CLASS: |
| return new MinimalClassNameIdResolver(baseType, config.getTypeFactory()); |
| case NAME: |
| return TypeNameIdResolver.construct(config, baseType, subtypes, forSer, forDeser); |
| case NONE: // hmmh. should never get this far with 'none' |
| return null; |
| case CUSTOM: // need custom resolver... |
| } |
| throw new IllegalStateException("Do not know how to construct standard type id resolver for idType: "+_idType); |
| } |
| } |