ConverterRowObjects.java

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

import com.palantir.javapoet.CodeBlock;
import io.kaumei.jdbc.anno.gen.TargetMethod;
import io.kaumei.jdbc.anno.model.OptionalFlag;
import io.kaumei.jdbc.anno.model.SQLNameDV;
import io.kaumei.jdbc.anno.store.SearchKey;
import io.kaumei.jdbc.anno.store.SourceDV;
import io.kaumei.jdbc.anno.store.StoreID;
import org.jspecify.annotations.Nullable;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.type.TypeMirror;
import java.util.*;

final class ConverterRowObjects extends Jdbc2JavaConverter {

    record Placeholder(TypeMirror type, SourceDV source, ExecutableElement method,
                       OptionalFlag[] optionalFlags,
                       SQLNameDV[] jdbcNames,
                       @Nullable SearchKey[] others) implements Jdbc2JavaConverter.Placeholder {
        @Override
        public Jdbc2JavaConverter resolve(Map<SearchKey, Jdbc2JavaConverter> converter) {
            var converters = new Jdbc2JavaConverter[others.length];
            for (int i = 0; i < others.length; i++) {
                converters[i] = Objects.requireNonNull(converter.get(others[i]));
            }
            return new ConverterRowObjects(this, converters);
        }

        @Override
        public Set<SearchKey> otherPlaceholders() {
            var result = new HashSet<SearchKey>();
            for (var r : others) {
                if (r != null) {
                    result.add(r);
                }
            }
            return result;
        }

        Set<StoreID> nonColumnConverters(Map<SearchKey, Jdbc2JavaConverter> converter) {
            var result = new HashSet<StoreID>();
            for (var other : others) {
                if (other != null && !Objects.requireNonNull(converter.get(other)).isColumn()) {
                    result.add(other.toStoreId());
                }
            }
            return result;
        }
    }

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

    private final boolean isMethod;
    private final Element typeElement;
    private final Name methodName;
    private final int paramLength;
    private final SQLNameDV[] jdbcNames;
    private final Jdbc2JavaConverter[] converters;
    private final String[] paramNames;
    private final OptionalFlag[] optionalFlags;

    private ConverterRowObjects(Placeholder placeholder,
                                Jdbc2JavaConverter[] converters) {
        super(placeholder.type, placeholder.source);
        this.isMethod = placeholder.method.getKind() == ElementKind.METHOD;
        this.typeElement = placeholder.method.getEnclosingElement();
        this.methodName = placeholder.method.getSimpleName();

        this.jdbcNames = placeholder.jdbcNames;
        this.converters = converters;
        this.optionalFlags = placeholder.optionalFlags;
        this.paramLength = placeholder.method.getParameters().size();
        this.paramNames = new String[paramLength];

        for (int i = 0; i < this.paramLength; i++) {
            this.paramNames[i] = placeholder.method.getParameters().get(i).getSimpleName().toString();
        }
    }

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

    @Override
    public boolean isColumn() {
        return false;
    }

    @Override
    public void addResultSetToRow(TargetMethod builder, String localVarName, OptionalFlag optional) {
        builder.addComment("ConverterRowObjects.addResultSetToRow", "type", type());
        CodeBlock.Builder args = CodeBlock.builder();
        for (int i = 0; i < paramLength; i++) {
            var tempVarName = builder.tempVarName(paramNames[i]);
            converters[i].addColumnByName(builder, tempVarName, this.jdbcNames[i], this.optionalFlags[i]);
            if (i == 0) {
                args.add("$L", tempVarName);
            } else {
                args.add(", $L", tempVarName);
            }
        }
        if (isMethod) {
            if (optional.isOptionalType()) {
                builder.addStatement("var $L = $T.of($T.$N($L))",
                        localVarName, Optional.class, this.typeElement, this.methodName, args.build());
            } else {
                builder.addStatement("var $L = $T.$N($L)",
                        localVarName, this.typeElement, this.methodName, args.build());
            }
        } else {
            if (optional.isOptionalType()) {
                builder.addStatement("var $L = $T.of(new $T($L))",
                        localVarName, Optional.class, this.typeElement, args.build());
            } else {
                builder.addStatement("var $L = new $T($L)",
                        localVarName, this.typeElement, args.build());
            }
        }
    }
}