AnnoMap.java

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

import io.kaumei.jdbc.anno.ProcessorException;
import io.kaumei.jdbc.anno.ctx.ConfigService;
import io.kaumei.jdbc.anno.ctx.Context;
import org.jspecify.annotations.Nullable;

import javax.lang.model.AnnotatedConstruct;
import javax.lang.model.element.TypeElement;
import java.util.*;

public class AnnoMap {

    public static AnnoMap of(Context ctx, AnnotatedConstruct element) {
        var anno2value = new HashMap<JavaAnnoType<?>, Object>();
        for (var annoMirror : element.getAnnotationMirrors()) {
            if (annoMirror.getAnnotationType().asElement() instanceof TypeElement te) { // sanity-check
                var anno = ctx.KAUMEI_ANNO.get(te.getQualifiedName());
                if (anno != null) {
                    if (anno2value.put(anno, anno.getAnno(annoMirror)) != null) { // sanity-check
                        throw new ProcessorException("Duplicate annotation: " + anno); // sanity-check
                    }
                }
            }
        }
        return new AnnoMap(anno2value);
    }

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

    private final Map<JavaAnnoType<?>, Object> anno2value;
    private final Set<JavaAnnoType<?>> unused;

    private AnnoMap(Map<JavaAnnoType<?>, Object> anno2value) {
        this.anno2value = anno2value;
        this.unused = new HashSet<>(this.anno2value.keySet());
    }

    @Override
    public String toString() {
        return anno2value.toString();
    }

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

    public void consume(JavaAnnoType<?> anno) {
        unused.remove(anno);
    }

    @Nullable
    public <T> T getOpt(JavaAnnoType<T> anno) {
        var value = this.anno2value.get(anno);
        if (value != null) {
            unused.remove(anno);
            return anno.cast(value);
        }
        return null;
    }

    public <T> T get(JavaAnnoType<T> anno, T defaultValue) {
        var value = this.anno2value.get(anno);
        if (value != null) {
            unused.remove(anno);
            return anno.cast(value);
        }
        return defaultValue;
    }


    @Nullable
    public <T> T getOpt(ConfigService.Option<T> option) {
        var value = this.anno2value.get(option.anno());
        if (value != null) {
            unused.remove(option.anno());
            return option.cast(value);
        }
        return null;
    }

    @Nullable
    public <T> T getIfNotDefault(ConfigService.Option<T> option) {
        var value = this.anno2value.get(option.anno());
        if (option.isNotDefaultValue(value)) {
            unused.remove(option.anno());
            return option.cast(value);
        }
        return null;
    }

    @Nullable
    public <T> T getIfDefault(ConfigService.Option<T> option) {
        var value = this.anno2value.get(option.anno());
        if (option.isDefaultValue(value)) {
            unused.remove(option.anno());
            return option.cast(value);
        }
        return null;
    }

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

    public boolean hasUnused() {
        return !unused.isEmpty();
    }

    public Set<JavaAnnoType<?>> unused() {
        return Collections.unmodifiableSet(unused);
    }

}