com.addthis.codec.jackson.CodecIntrospector.java Source code

Java tutorial

Introduction

Here is the source code for com.addthis.codec.jackson.CodecIntrospector.java

Source

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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.addthis.codec.jackson;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.addthis.codec.annotations.FieldConfig;
import com.addthis.codec.annotations.Pluggable;
import com.addthis.codec.plugins.PluginMap;
import com.addthis.codec.plugins.PluginRegistry;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CodecIntrospector extends NopAnnotationIntrospector {
    private static final Logger log = LoggerFactory.getLogger(CodecIntrospector.class);

    private final PluginRegistry pluginRegistry;

    public CodecIntrospector(PluginRegistry pluginRegistry) {
        this.pluginRegistry = pluginRegistry;
    }

    // these three methods are borrowed from the default jackson annotation introspector

    @Override
    public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config, AnnotatedClass ac, JavaType baseType) {
        TypeResolverBuilder<?> annotationLookup = _findTypeResolver(config, ac, baseType);
        if (annotationLookup != null) {
            return annotationLookup;
        }
        if (pluginRegistry.byClass().containsKey(ac.getRawType())) {
            PluginMap pluginMap = pluginRegistry.byClass().get(ac.getRawType());
            return new CodecTypeResolverBuilder(pluginMap, config.getTypeFactory(), pluginRegistry);
        }
        return null;
    }

    @Override
    public TypeResolverBuilder<?> findPropertyTypeResolver(MapperConfig<?> config, AnnotatedMember am,
            JavaType baseType) {
        /* As per definition of @JsonTypeInfo, should only apply to contents of container
         * (collection, map) types, not container types themselves:
         */
        if (baseType.isContainerType())
            return null;
        // No per-member type overrides (yet)
        return _findTypeResolver(config, am, baseType);
    }

    @Override
    public TypeResolverBuilder<?> findPropertyContentTypeResolver(MapperConfig<?> config, AnnotatedMember am,
            JavaType containerType) {
        /* First: let's ensure property is a container type: caller should have
         * verified but just to be sure
         */
        if (!containerType.isContainerType()) {
            throw new IllegalArgumentException(
                    "Must call method with a container type (got " + containerType + ")");
        }
        return _findTypeResolver(config, am, containerType);
    }

    public TypeResolverBuilder<?> _findTypeResolver(MapperConfig<?> config, Annotated ac, JavaType baseType) {
        Pluggable pluggable = ac.getAnnotation(Pluggable.class);
        if (pluggable != null) {
            PluginMap pluginMap = pluginRegistry.byCategory().get(pluggable.value());
            if (pluginMap != null) {
                return new CodecTypeResolverBuilder(pluginMap, config.getTypeFactory(), pluginRegistry);
            } else {
                log.warn("missing plugin map for {}, reached from {}", pluggable.value(), ac.getRawType());
            }
        }
        return null;
    }

    /** report all non-alias plugin types */
    @Override
    public List<NamedType> findSubtypes(Annotated a) {
        Pluggable pluggable = a.getAnnotation(Pluggable.class);
        PluginMap pluginMap;
        if (pluggable != null) {
            pluginMap = pluginRegistry.byCategory().get(pluggable.value());
        } else if (pluginRegistry.byClass().containsKey(a.getRawType())) {
            pluginMap = pluginRegistry.byClass().get(a.getRawType());
        } else {
            return null;
        }
        List<NamedType> result = new ArrayList<>(pluginMap.asBiMap().size());
        for (Map.Entry<String, Class<?>> type : pluginMap.asBiMap().entrySet()) {
            result.add(new NamedType(type.getValue(), type.getKey()));
        }
        return result;
    }

    @Override
    public boolean hasIgnoreMarker(AnnotatedMember m) {
        FieldConfig fieldConfig = m.getAnnotation(FieldConfig.class);
        if (fieldConfig != null) {
            return !fieldConfig.codable();
        }
        return false;
    }

    @Override
    public Boolean hasRequiredMarker(AnnotatedMember m) {
        FieldConfig fieldConfig = m.getAnnotation(FieldConfig.class);
        if (fieldConfig != null) {
            return fieldConfig.required();
        }
        return null;
    }

    // read and write only settings only work in the absence of another, explicit, JsonProperty.
    // also, note that these methods are what cause FieldConfig to trigger inclusion, so if/when
    // we just drop readonly/writeonly, we can (should) add meta annotations to FieldConfig et al.

    @Override
    public PropertyName findNameForDeserialization(Annotated a) {
        FieldConfig fieldConfig = a.getAnnotation(FieldConfig.class);
        if ((fieldConfig != null) && !fieldConfig.writeonly() && fieldConfig.codable()) {
            return PropertyName.USE_DEFAULT;
        }
        return null;
    }

    @Override
    public PropertyName findNameForSerialization(Annotated a) {
        FieldConfig fieldConfig = a.getAnnotation(FieldConfig.class);
        if ((fieldConfig != null) && !fieldConfig.readonly() && fieldConfig.codable()) {
            return PropertyName.USE_DEFAULT;
        }
        return null;
    }
}