SourceMethodService.java

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

import io.kaumei.jdbc.anno.ProcessorEnvironment;
import io.kaumei.jdbc.anno.ProcessorSteps;
import io.kaumei.jdbc.anno.ctx.Context;
import org.jspecify.annotations.Nullable;

import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

import static java.util.Objects.requireNonNull;

public class SourceMethodService implements ProcessorSteps {

    // ----- services
    private final Context ctx;
    // ----- state
    private final Map<ExecutableElement, SourceMethod> methods = new LinkedHashMap<>();
    private final Map<ExecutableElement, RuntimeException> failures = new LinkedHashMap<>();

    public SourceMethodService(Context ctx) {
        this.ctx = requireNonNull(ctx);
    }

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

    @Override
    public void process(ProcessorEnvironment roundEnv) {
        for (var entry : roundEnv.jdbcInterfaces()) {
            this.ctx.logger.acceptWithDebugFlag(entry, this::resolveMethods);
        }
    }

    private void resolveMethods(TypeElement iface) {
        this.ctx.logger.info("Resolve methods", iface);
        for (var child : iface.getEnclosedElements()) {
            var modifiers = child.getModifiers();
            if (child.getKind() == ElementKind.METHOD
                    && modifiers.contains(Modifier.ABSTRACT)
                    && !modifiers.contains(Modifier.PRIVATE)
                    && !modifiers.contains(Modifier.STATIC)
                    && child instanceof ExecutableElement method) {
                resolveMethod(method);
            }
        }
    }

    private void resolveMethod(ExecutableElement method) {
        try {
            var source = SourceMethod.of(this.ctx, method);
            var old = this.methods.put(method, source);
            if (old != null) {
                throw new IllegalStateException("duplicate: " + method);
            }
        } catch (RuntimeException e) {
            this.methods.remove(method);
            this.failures.put(method, e);
        }
    }

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

    public @Nullable SourceMethod getOpt(ExecutableElement method) {
        var exp = this.failures.get(method);
        if (exp != null) {
            throw exp; // throw the exception saved in the init stage
        }
        return this.methods.get(method);
    }

    public Collection<SourceMethod> values() {
        return this.methods.values();
    }

}