All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micronaut.data.mongodb.serde.DataEncoderContext Maven / Gradle / Ivy

There is a newer version: 4.10.5
Show newest version
/*
 * Copyright 2017-2022 original authors
 *
 * 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
 *
 * https://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 io.micronaut.data.mongodb.serde;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.type.Argument;
import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.MappedProperty;
import io.micronaut.data.document.serde.CustomConverterSerializer;
import io.micronaut.data.document.serde.IdPropertyNamingStrategy;
import io.micronaut.data.document.serde.IdSerializer;
import io.micronaut.data.model.runtime.AttributeConverterRegistry;
import io.micronaut.data.model.runtime.convert.AttributeConverter;
import io.micronaut.data.mongodb.conf.MongoDataConfiguration;
import io.micronaut.serde.Encoder;
import io.micronaut.serde.Serializer;
import io.micronaut.serde.bson.custom.CodecBsonDecoder;
import io.micronaut.serde.config.naming.PropertyNamingStrategy;
import io.micronaut.serde.exceptions.SerdeException;
import io.micronaut.serde.reference.PropertyReference;
import io.micronaut.serde.reference.SerializationReference;
import org.bson.codecs.Codec;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.types.ObjectId;

import java.io.IOException;

/**
 * The Micronaut Data's Serde's {@link Serializer.EncoderContext}.
 *
 * @author Denis Stepanov
 * @since 3.3
 */
@Internal
final class DataEncoderContext implements Serializer.EncoderContext {

    private final Argument OBJECT_ID = Argument.of(ObjectId.class);

    private final MongoDataConfiguration mongoDataConfiguration;
    private final AttributeConverterRegistry attributeConverterRegistry;
    private final Serializer.EncoderContext parent;
    private final CodecRegistry codecRegistry;

    /**
     * Default constructor.
     *
     * @param mongoDataConfiguration     The Mongo Data configuration
     * @param attributeConverterRegistry The AttributeConverterRegistry
     * @param parent                     The parent context
     * @param codecRegistry              The codec registry
     */
    DataEncoderContext(MongoDataConfiguration mongoDataConfiguration,
                       AttributeConverterRegistry attributeConverterRegistry,
                       Serializer.EncoderContext parent,
                       CodecRegistry codecRegistry) {
        this.mongoDataConfiguration = mongoDataConfiguration;
        this.attributeConverterRegistry = attributeConverterRegistry;
        this.parent = parent;
        this.codecRegistry = codecRegistry;
    }

    @Override
    public ConversionService getConversionService() {
        return parent.getConversionService();
    }

    @Override
    public boolean hasView(Class... views) {
        return this.mongoDataConfiguration.isIgnoreJsonViews() || parent.hasView(views);
    }

    @Override
    public  SerializationReference resolveReference(SerializationReference reference) {
        return parent.resolveReference(reference);
    }

    @Override
    public > D findCustomSerializer(Class serializerClass) throws SerdeException {
        if (serializerClass == IdSerializer.class) {
            IdSerializer idSerializer = new IdSerializer() {

                @Override
                public Serializer createSpecific(EncoderContext encoderContext, Argument type) throws SerdeException {
                    boolean isGeneratedObjectIdAsString = type.isAssignableFrom(String.class)
                            && type.isAnnotationPresent(GeneratedValue.class);
                    if (isGeneratedObjectIdAsString) {
                        Serializer objectIdSerializer = findSerializer(OBJECT_ID);
                        return (encoder, encoderContext2, stringType, value) -> {
                            String stringId = (String) value;
                            objectIdSerializer.serialize(encoder, encoderContext2, OBJECT_ID, new ObjectId(stringId));
                        };
                    }
                    Serializer serializer = findSerializer(type);
                    return serializer.createSpecific(encoderContext, type);
                }

                @Override
                public void serialize(Encoder encoder, EncoderContext context, Argument type, Object value) {
                    throw new IllegalStateException("Create specific call is required!");
                }
            };
            return (D) idSerializer;
        }
        if (serializerClass == CustomConverterSerializer.class) {
            CustomConverterSerializer customConverterSerializer = new CustomConverterSerializer() {
                @Override
                public Serializer createSpecific(EncoderContext encoderContext, Argument type) throws SerdeException {
                    Class converterClass = type.getAnnotationMetadata().classValue(MappedProperty.class, "converter")
                            .orElseThrow(IllegalStateException::new);
                    Class converterPersistedType = type.getAnnotationMetadata().classValue(MappedProperty.class, "converterPersistedType")
                            .orElseThrow(IllegalStateException::new);
                    Argument convertedType = Argument.of(converterPersistedType);
                    Serializer serializer = findSerializer(convertedType);
                    AttributeConverter converter = attributeConverterRegistry.getConverter(converterClass);
                    return new Serializer<>() {

                        @Override
                        public void serialize(Encoder encoder, EncoderContext context, Argument type, Object value) throws IOException {
                            if (value == null) {
                                encoder.encodeNull();
                                return;
                            }
                            Object converted = converter.convertToPersistedValue(value, ConversionContext.of(type));
                            if (converted == null) {
                                encoder.encodeNull();
                                return;
                            }
                            serializer.serialize(encoder, context, convertedType, converted);
                        }

                    };
                }

                @Override
                public void serialize(Encoder encoder, EncoderContext context, Argument type, Object value) {
                    throw new IllegalStateException("Create specific call is required!");
                }
            };
            return (D) customConverterSerializer;
        }
        return parent.findCustomSerializer(serializerClass);
    }

    @Override
    public  Serializer findSerializer(Argument type) throws SerdeException {
        Codec codec = codecRegistry.get(type.getType(), codecRegistry);
        if (codec instanceof MappedCodec mappedCodec) {
            return mappedCodec.serializer;
        }
        if (codec != null && CodecUtils.shouldUseCodec(codec)) {
            return new CodecBsonDecoder<>((Codec) codec);
        }
        return parent.findSerializer(type);
    }

    @Override
    public  D findNamingStrategy(Class namingStrategyClass) throws SerdeException {
        if (namingStrategyClass == IdPropertyNamingStrategy.class) {
            return (D) DataSerdeRegistry.ID_PROPERTY_NAMING_STRATEGY;
        }
        return parent.findNamingStrategy(namingStrategyClass);
    }

    @Override
    public  void pushManagedRef(PropertyReference reference) {
        parent.pushManagedRef(reference);
    }

    @Override
    public void popManagedRef() {
        parent.popManagedRef();
    }
}