Java2JdbcResolver.java

/*
 * SPDX-FileCopyrightText: 2025 kaumei.io
 * SPDX-License-Identifier: Apache-2.0
 */
package io.kaumei.jdbc.anno.model;

import io.kaumei.jdbc.anno.ctx.Context;
import io.kaumei.jdbc.anno.msg.JdbcMsg;
import io.kaumei.jdbc.anno.msg.Msg;
import io.kaumei.jdbc.anno.store.SearchKey;
import org.jspecify.annotations.Nullable;

import javax.lang.model.element.ElementKind;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;

import static java.util.Objects.requireNonNull;

record Java2JdbcResolver(Context ctx) {

    enum Kind {ARRAY, LIST, RECORD}

    record Single(OptionalFlag optional, SearchKey searchRequest) {
    }

    Msg.Result<Single> single(VariableElement element) {
        var optional = ctx.optionalFlag(element, element.asType());
        if (optional.isOptionalType()) {
            return Msg.result(JdbcMsg.OPTIONAL_TYPE_NOT_SUPPORTED);
        }
        var converterName = ctx.JDBC_CONVERTER_NAME.getAnnoOpt(element);
        if (converterName != null && converterName.isBlankName()) {
            return Msg.result(JdbcMsg.annotationValueMustNotBeBlank("@JdbcConverterName"));
        }
        return Msg.result(new Java2JdbcResolver.Single(optional, SearchKey.of(ctx, element)));
    }

    // ------------------------------------------------------------------------

    static final class Values {
        private final Kind kind;
        private final OptionalFlag optional;
        private final @Nullable SearchKey searchRequest;
        private final @Nullable OptionalFlag cmpOptional;

        Values(Kind kind, OptionalFlag optional,
               @Nullable SearchKey searchRequest,
               @Nullable OptionalFlag cmpOptional) {
            this.kind = kind;
            this.optional = optional;
            this.searchRequest = searchRequest;
            this.cmpOptional = cmpOptional;
        }

        public Kind kind() {
            return kind;
        }

        public OptionalFlag optional() {
            return optional;
        }

        public SearchKey searchRequest() {
            return requireNonNull(searchRequest);
        }

        public OptionalFlag cmpOptional() {
            return requireNonNull(cmpOptional);
        }
    }

    Msg.Result<Values> values(VariableElement element) {
        var mirror = element.asType();
        var optional = ctx.optionalFlag(element, mirror);
        if (optional.isOptionalType()) {
            return Msg.result(JdbcMsg.OPTIONAL_TYPE_NOT_SUPPORTED);
        } else if (optional.isNullable()) {
            return Msg.result(JdbcMsg.NULLABLE_NOT_SUPPORTED);
        }

        var converterName = ctx.JDBC_CONVERTER_NAME.getAnnoOpt(element);
        if (converterName != null) {
            if (converterName.isBlankName()) {
                return Msg.result(JdbcMsg.annotationValueMustNotBeBlank("@JdbcConverterName"));
            }
            return Msg.result(JdbcMsg.JDBC_CONVERTER_NAME_NOT_SUPPORTED);
        }

        Kind kind;
        TypeMirror cmp;
        if (mirror instanceof ArrayType arrayType) {
            kind = Kind.ARRAY;
            cmp = arrayType.getComponentType();
        } else if (ctx.JAVA_List.isSameType(mirror)) {
            var declared = (DeclaredType) mirror;
            if (declared.getTypeArguments().size() != 1) {
                return Msg.result(JdbcMsg.JAVA_TO_JDBC_RESOLVER_LIST_REQUIRES_COMPONENT_TYPE);
            }
            kind = Kind.LIST;
            cmp = declared.getTypeArguments().get(0);
        } else if (mirror instanceof DeclaredType declared
                && declared.asElement().getKind() == ElementKind.RECORD) {
            return Msg.result(new Java2JdbcResolver.Values(Kind.RECORD, optional, null, null));
        } else {
            return Msg.result(JdbcMsg.JAVA_TO_JDBC_RESOLVER_VALUES_UNSUPPORTED_TYPES);
        }

        var cmpOptional = ctx.optionalFlag(element, cmp);
        if (cmpOptional.isOptionalType()) {
            return Msg.result(JdbcMsg.OPTIONAL_TYPE_NOT_SUPPORTED);
        }

        return Msg.result(new Java2JdbcResolver.Values(kind, optional, SearchKey.of(cmp, ConverterNameDV.unnamed()),
                cmpOptional));
    }
}