ProtoDeserializers.java

/*
 * Copyright 2022 Proto Utils Authors
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 net.morimekta.proto.jackson.adapter;

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
import com.fasterxml.jackson.databind.type.MapType;
import com.google.protobuf.Message;
import net.morimekta.proto.ProtoEnum;

import static net.morimekta.proto.ProtoEnum.getEnumDescriptorUnchecked;
import static net.morimekta.proto.ProtoMessage.getMessageDescriptor;

/**
 * Deserializer handler for complex proto types.
 */
public class ProtoDeserializers
        extends SimpleDeserializers {
    /** {@inheritDoc} */
    @Override
    public JsonDeserializer<?> findMapDeserializer(MapType type,
                                                   DeserializationConfig config,
                                                   BeanDescription beanDesc,
                                                   KeyDeserializer keyDeserializer,
                                                   TypeDeserializer elementTypeDeserializer,
                                                   JsonDeserializer<?> elementDeserializer)
            throws JsonMappingException {
        if (ProtoEnum.isProtoEnumClass(type.getKeyType().getRawClass())) {
            return new ProtoMapDeserializer<>(
                    type.getRawClass(),
                    createEnumKeyDeserializer(type.getKeyType().getRawClass()),
                    type.getContentType());
        }
        return super.findMapDeserializer(type,
                                         config,
                                         beanDesc,
                                         keyDeserializer,
                                         elementTypeDeserializer,
                                         elementDeserializer);
    }

    /** {@inheritDoc} */
    @Override
    public JsonDeserializer<?> findEnumDeserializer(Class<?> type,
                                                    DeserializationConfig config,
                                                    BeanDescription beanDesc) throws JsonMappingException {
        if (ProtoEnum.isProtoEnumClass(type)) {
            try {
                return createEnumDeserializer(type);
            } catch (IllegalArgumentException e) {
                throw new JsonMappingException(null, e.getMessage(), e);
            }
        }
        return super.findEnumDeserializer(type, config, beanDesc);
    }

    /** {@inheritDoc} */
    @Override
    public JsonDeserializer<?> findBeanDeserializer(JavaType type,
                                                    DeserializationConfig config,
                                                    BeanDescription beanDesc) throws JsonMappingException {
        if (Message.class.isAssignableFrom(type.getRawClass())) {
            try {
                return this.createMessageDeserializer(type.getRawClass(), config);
            } catch (IllegalArgumentException e) {
                throw new JsonMappingException(null, e.getMessage(), e);
            }
        }
        return super.findBeanDeserializer(type, config, beanDesc);
    }

    private JsonDeserializer<?> createMessageDeserializer(Class<?> type, DeserializationConfig config) {
        return new ProtoMessageDeserializer<>(getMessageDescriptor(type), config);
    }

    @SuppressWarnings("rawtypes")
    private JsonDeserializer<?> createEnumDeserializer(Class<?> type) {
        return new ProtoEnumDeserializer(getEnumDescriptorUnchecked(type));
    }

    private KeyDeserializer createEnumKeyDeserializer(Class<?> type) {
        return new ProtoEnumKeyDeserializer(getEnumDescriptorUnchecked(type));
    }
}