001package gudusoft.gsqlparser.ir.semantic.catalog;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006
007/**
008 * Slice 76 — minimal catalog DTO. Lets external callers supply catalog
009 * metadata to
010 * {@link gudusoft.gsqlparser.ir.semantic.SqlSemanticAnalyzer#analyze(String,
011 *  gudusoft.gsqlparser.EDbVendor,
012 *  gudusoft.gsqlparser.ir.semantic.catalog.Catalog)
013 * SqlSemanticAnalyzer.analyze(sql, vendor, Catalog)} without depending on
014 * the internal {@code gudusoft.gsqlparser.sqlenv.TSQLEnv} type.
015 *
016 * <p>Construct via {@link #builder()}; the returned {@code Catalog} is
017 * immutable and safe to share across threads.
018 *
019 * <p>Identifier semantics (case folding, quoted vs unquoted, vendor
020 * rules) are delegated to the analyzer's internal {@code TSQLEnv} bridge
021 * — the DTO stores names exactly as written by the caller. A later slice
022 * may introduce explicit identifier handling.
023 *
024 * <p><b>Failure-mode note</b> (slice 75 contract preservation):
025 * passing a {@code null} {@code Catalog} as the third argument of
026 * {@link gudusoft.gsqlparser.ir.semantic.SqlSemanticAnalyzer#analyze(String,
027 *  gudusoft.gsqlparser.EDbVendor,
028 *  gudusoft.gsqlparser.ir.semantic.catalog.Catalog)} is equivalent to
029 * calling the 2-arg {@code analyze(sql, vendor)} overload. Passing a
030 * literal {@code null} third argument WITHOUT a cast is ambiguous at
031 * compile time (the 3-arg {@code analyze} overloads accept either
032 * {@code Catalog} or {@code TSQLEnv}, which are unrelated reference
033 * types). Callers should use the 2-arg overload when no catalog is
034 * available.
035 *
036 * <p><b>API status: supported input DTO.</b> Its builder and read-only getters
037 * are part of Join Analysis Consumption Profile v1.
038 */
039public final class Catalog {
040
041    private final List<CatalogTable> tables;
042
043    private Catalog(List<CatalogTable> tables) {
044        this.tables = Collections.unmodifiableList(tables);
045    }
046
047    public static Builder builder() {
048        return new Builder();
049    }
050
051    /**
052     * @return an unmodifiable view of the tables in registration order.
053     */
054    public List<CatalogTable> getTables() {
055        return tables;
056    }
057
058    /**
059     * Package-private: bare-name lookup used by the analyzer bridge.
060     *
061     * <p>Kept non-public for slice 76 because the public matching
062     * semantics (qualifier expansion, case sensitivity, quoting) are
063     * delegated to {@code TSQLEnv} and are not yet part of the
064     * {@code Catalog} contract. Promoting this to public freezes a
065     * matching rule before those concerns are designed.
066     *
067     * <p>Matching: bare-name {@link String#equals(Object)} on
068     * {@link CatalogTable#getName()}. First-match wins.
069     *
070     * @return the matching table, or {@code null} if none.
071     */
072    CatalogTable findTable(String name) {
073        if (name == null) {
074            return null;
075        }
076        for (CatalogTable t : tables) {
077            if (name.equals(t.getName())) {
078                return t;
079            }
080        }
081        return null;
082    }
083
084    @Override
085    public String toString() {
086        return "Catalog{tables=" + tables + "}";
087    }
088
089    /**
090     * Mutable builder for {@link Catalog}. Single-use:
091     * {@link #build()} returns one {@code Catalog} and the builder should
092     * not be reused after that.
093     */
094    public static final class Builder {
095        private final List<CatalogTable> tables = new ArrayList<>();
096
097        private Builder() {}
098
099        public Builder addTable(CatalogTable table) {
100            if (table == null) {
101                throw new IllegalArgumentException("table must not be null");
102            }
103            tables.add(table);
104            return this;
105        }
106
107        public Catalog build() {
108            return new Catalog(new ArrayList<>(tables));
109        }
110    }
111}