ProcessorEnvironment.java

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

import io.kaumei.jdbc.anno.ctx.ConfigService;
import io.kaumei.jdbc.anno.ctx.Context;
import io.kaumei.jdbc.anno.ctx.JavaMessenger;
import io.kaumei.jdbc.anno.msg.JdbcMsg;
import io.kaumei.jdbc.annotation.JdbcBatchUpdate;
import io.kaumei.jdbc.annotation.JdbcCall;
import io.kaumei.jdbc.annotation.JdbcNative;
import io.kaumei.jdbc.annotation.JdbcSelect;
import io.kaumei.jdbc.annotation.JdbcUpdate;
import io.kaumei.jdbc.annotation.config.JdbcConfig;

import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import java.util.HashSet;
import java.util.Set;

import static java.util.Objects.requireNonNull;

public final class ProcessorEnvironment {

    // ----- services
    private final JavaMessenger logger;

    private final Set<? extends Element> jdbcConfig;
    private final ConfigService jdbcConfigService;
    private final Set<TypeElement> jdbcInterfaces = new HashSet<>();
    private final Set<Element> invalidElements = new HashSet<>();

    public ProcessorEnvironment(Context ctx, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        this.logger = requireNonNull(ctx.logger);
        this.jdbcConfigService = requireNonNull(ctx.kaumeiConfig);
        this.jdbcConfig = roundEnv.getElementsAnnotatedWith(JdbcConfig.class);
        var jdbcNative = updateJdbcInterfaces(roundEnv.getElementsAnnotatedWith(JdbcNative.class));
        var jdbcCall = updateJdbcInterfaces(roundEnv.getElementsAnnotatedWith(JdbcCall.class));
        var jdbcSelect = updateJdbcInterfaces(roundEnv.getElementsAnnotatedWith(JdbcSelect.class));
        var jdbcUpdate = updateJdbcInterfaces(roundEnv.getElementsAnnotatedWith(JdbcUpdate.class));
        var jdbcBatchUpdate = updateJdbcInterfaces(roundEnv.getElementsAnnotatedWith(JdbcBatchUpdate.class));
        var rootElements = roundEnv.getRootElements();

        if (logger.isDebugEnabled()) {
            logger.debug("annotations....", annotations);
            logger.debug("rootElements...", rootElements);
            logger.debug("jdbcNative.....", jdbcNative);
            logger.debug("jdbcCall.......", jdbcCall);
            logger.debug("jdbcSelect.....", jdbcSelect);
            logger.debug("jdbcUpdate.....", jdbcUpdate);
            logger.debug("jdbcBatchUpdate", jdbcBatchUpdate);
            logger.debug("jdbcInterfaces.", jdbcInterfaces);
        } else {
            logger.always("Kaumei JDBC input annotation types.", annotations);
            logger.always("Kaumei JDBC found annotations.", "rootElements", rootElements.size(),
                    "jdbcNative", jdbcNative.size(),
                    "jdbcCall", jdbcCall.size(),
                    "jdbcSelect", jdbcSelect.size(),
                    "jdbcUpdate", jdbcUpdate.size(),
                    "jdbcBatchUpdate", jdbcBatchUpdate.size(),
                    "jdbcInterfaces", jdbcInterfaces.size());
        }
    }

    private Set<ExecutableElement> updateJdbcInterfaces(Set<? extends Element> elements) {
        var methods = new HashSet<ExecutableElement>();
        for (Element elem : elements) {
            if (elem.getKind() != ElementKind.METHOD || !(elem instanceof ExecutableElement method)) {
                this.markError(elem, "Skip element, because it is not a method.");
                continue;
            }
            var enclosing = method.getEnclosingElement();
            if (enclosing.getKind() != ElementKind.INTERFACE
                    || !(enclosing instanceof TypeElement iface)) {
                this.markError(method, "Skip method, because it is not a member of an interface.");
                continue;
            }

            if (!iface.getTypeParameters().isEmpty()) {
                this.markError(iface, JdbcMsg.JDBC_INTERFACE_TYPE_PARAMETERS_NOT_SUPPORTED.text());
            } else if (!method.getTypeParameters().isEmpty()) {
                this.markError(method, JdbcMsg.JDBC_METHOD_TYPE_PARAMETERS_NOT_SUPPORTED.text());
            } else {
                methods.add(method);
                jdbcInterfaces.add(iface);
            }
        }
        return methods;
    }

    void markError(Element elem, String message) {
        if (this.invalidElements.add(elem)) { // to avoid duplicate error messages
            this.logger.error(elem, message);
        }
    }

    public Set<? extends Element> jdbcConfig() {
        return this.jdbcConfig;
    }

    public Set<ExecutableElement> jdbcToJava() {
        return jdbcConfigService.jdbcToJava();
    }

    public Set<ExecutableElement> javaToJdbc() {
        return jdbcConfigService.javaToJdbc();
    }

    public Set<TypeElement> jdbcInterfaces() {
        return this.jdbcInterfaces;
    }

}