/*
 * Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at:
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 */

package com.fasterxml.jackson.dataformat.ion.polymorphism;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.annotation.JsonTypeResolver;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;

A Jackson AnnotationIntrospector (essentially an interceptor for serializer/deserializer construction) that provides type serializer/deserializers that write/read Ion type annotations.

The logic in this class is very similar to JacksonAnnotationIntrospector! We both look at the @JsonTypeResolver, etc annotations and try to make type resolvers.

This class adds a resolveAllTypes override, which allows for universal polymorphism without needing any annotations or mixins, and also permits top-level polymorphism -- deserialize to any object without providing its actual type, as long as type information was serialized. (i.e., ObjectMapper#readValue(serializedData, Object.class))

Note: the provided TypeSerializer will only write type annotations if the configured TypeIdResolver returns non-null.

Note: TypeDeserializer are actually full-on value deserializers -- all deserialization logic goes through them (unlike TypeSerializers, which just write the type metadata).

/** * A Jackson {@link com.fasterxml.jackson.databind.AnnotationIntrospector} (essentially an interceptor for * serializer/deserializer construction) that provides type serializer/deserializers that write/read Ion type * annotations. * <p> * The logic in this class is very similar to {@link com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector}! * We both look at the @JsonTypeResolver, etc annotations and try to make type resolvers. * <p> * This class adds a {@code resolveAllTypes} override, which allows for universal polymorphism without needing * any annotations or mixins, and also permits top-level polymorphism -- deserialize to any object without providing its * actual type, as long as type information was serialized. (i.e., ObjectMapper#readValue(serializedData, Object.class)) * <p> * Note: the provided {@link com.fasterxml.jackson.databind.jsontype.TypeSerializer} will only write type annotations if the configured * {@link TypeIdResolver} returns non-null. * <p> * Note: {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer} are actually full-on value deserializers -- all * deserialization logic goes through them (unlike TypeSerializers, which just write the type metadata). */
public class IonAnnotationIntrospector extends NopAnnotationIntrospector { private static final long serialVersionUID = 1L; private final boolean resolveAllTypes; public IonAnnotationIntrospector(boolean resolveAllTypes) { this.resolveAllTypes = resolveAllTypes; } protected TypeIdResolver defaultIdResolver(MapperConfig<?> config, JavaType baseType) { return null; } protected boolean shouldResolveType(AnnotatedClass ac) { JsonTypeResolver typeResolverAnn = ac.getAnnotation(JsonTypeResolver.class); return null != typeResolverAnn && IonAnnotationTypeResolverBuilder.class.equals(typeResolverAnn.value()); }
Provides a TypeResolverBuilder if the AnnotatedClass is enabled for type resolving, and a TypeIdResolver can be instantiated.

The AnnotatedClass is enabled for type resolving if either resolveAllTypes is set, or shouldResolveType() returns true.

We look for a TypeIdResolver by checking for a JsonTypeIdResolver annotation, and fallback to defaultIdResolver().

Params:
  • config – a serialization or deserialization config object
  • ac – an AnnotatedClass representing a (statically configured) base type for type resolution
  • baseType – a JavaType representing the same class
Returns:a type resolver builder that reads and writes Ion type annotations, or null
/** * Provides a {@link TypeResolverBuilder} if the {@link AnnotatedClass} is enabled for type resolving, and a * {@link TypeIdResolver} can be instantiated. * <p> * The AnnotatedClass is enabled for type resolving if either {@code resolveAllTypes} is set, or shouldResolveType() * returns true. * <p> * We look for a TypeIdResolver by checking for a {@link JsonTypeIdResolver} annotation, and fallback to * {@code defaultIdResolver()}. * * @param config a serialization or deserialization config object * @param ac an AnnotatedClass representing a (statically configured) base type for type resolution * @param baseType a JavaType representing the same class * @return a type resolver builder that reads and writes Ion type annotations, or null */
@Override public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config, AnnotatedClass ac, JavaType baseType) { if (baseType.isArrayType() || baseType.isContainerType() || baseType.isPrimitive()) { // Ion type annotations are not useful for these data types return null; } if (resolveAllTypes || shouldResolveType(ac)) { // First, find a TypeIdResolver. Check for annotation, then defaultIdResolver(). JsonTypeIdResolver typeIdResolverAnn = ac.getAnnotation(JsonTypeIdResolver.class); TypeIdResolver typeIdResolver = null; if (null != typeIdResolverAnn) { typeIdResolver = config.typeIdResolverInstance(ac, typeIdResolverAnn.value()); } if (null == typeIdResolver) { typeIdResolver = defaultIdResolver(config, baseType); } // If we still haven't found one, we can't move forward. if (null != typeIdResolver) { typeIdResolver.init(baseType); TypeResolverBuilder<?> typeResolverBuilder = new IonAnnotationTypeResolverBuilder(); typeResolverBuilder.init(/* ignored */ null, typeIdResolver); typeResolverBuilder.defaultImpl(baseType.getRawClass()); return typeResolverBuilder; } } return super.findTypeResolver(config, ac, baseType); // Nop probably returns null ;D } }