001package gudusoft.gsqlparser.catalog.runtime;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.LinkedHashMap;
006import java.util.List;
007import java.util.Map;
008import java.util.Objects;
009
010/**
011 * Factory + immutable record for {@link CatalogEntry}.
012 *
013 * <p>Plan ยง7.2. Held separately from the interface so consumers (resolver, bridge mapper)
014 * can construct entries without touching the snapshot internals.</p>
015 *
016 * <p>{@link #builder()} returns a builder; {@link #of} is the convenience static for the
017 * common case (no children, no extra properties).</p>
018 */
019public final class CatalogEntries {
020
021    private CatalogEntries() {
022        // Static factory.
023    }
024
025    public static Builder builder() {
026        return new Builder();
027    }
028
029    public static CatalogEntry of(CatalogObjectId id,
030                                  CatalogQualifiedName name,
031                                  CatalogObjectKind kind) {
032        return builder().id(id).name(name).kind(kind).build();
033    }
034
035    public static CatalogEntry of(CatalogObjectId id,
036                                  CatalogQualifiedName name,
037                                  CatalogObjectKind kind,
038                                  Map<String, Object> properties) {
039        Builder b = builder().id(id).name(name).kind(kind);
040        if (properties != null) {
041            for (Map.Entry<String, Object> e : properties.entrySet()) {
042                b.property(e.getKey(), e.getValue());
043            }
044        }
045        return b.build();
046    }
047
048    /** Compute a stable {@link CatalogObjectId} from a qualified name. */
049    public static CatalogObjectId derivedIdFor(CatalogQualifiedName name) {
050        if (name == null) {
051            throw new IllegalArgumentException("CatalogEntries.derivedIdFor: name is required");
052        }
053        return CatalogObjectId.of(CatalogIdentifierPolicy.keyForMap(name));
054    }
055
056    public static final class Builder {
057
058        private CatalogObjectId id;
059        private CatalogQualifiedName name;
060        private CatalogObjectKind kind;
061        private final Map<String, Object> properties = new LinkedHashMap<String, Object>();
062        private final List<CatalogObjectId> childIds = new ArrayList<CatalogObjectId>();
063
064        private Builder() {
065        }
066
067        public Builder id(CatalogObjectId v) {
068            this.id = v;
069            return this;
070        }
071
072        public Builder name(CatalogQualifiedName v) {
073            this.name = v;
074            return this;
075        }
076
077        public Builder kind(CatalogObjectKind v) {
078            this.kind = v;
079            return this;
080        }
081
082        public Builder property(String key, Object value) {
083            if (key == null) {
084                throw new IllegalArgumentException("CatalogEntry property key may not be null");
085            }
086            this.properties.put(key, value);
087            return this;
088        }
089
090        /**
091         * Pre-record the {@link CatalogObjectId} of a direct child. Used so that the
092         * snapshot's {@code children(parent, kind)} call can return a structured list
093         * without walking every entry.
094         */
095        public Builder addChild(CatalogObjectId childId) {
096            if (childId == null) {
097                throw new IllegalArgumentException("CatalogEntry child id may not be null");
098            }
099            this.childIds.add(childId);
100            return this;
101        }
102
103        public CatalogEntry build() {
104            if (id == null) {
105                throw new IllegalArgumentException("CatalogEntry.id is required");
106            }
107            if (name == null) {
108                throw new IllegalArgumentException("CatalogEntry.name is required");
109            }
110            if (kind == null) {
111                throw new IllegalArgumentException("CatalogEntry.kind is required");
112            }
113            return new ImmutableCatalogEntry(id, name, kind, properties, childIds);
114        }
115    }
116
117    static final class ImmutableCatalogEntry implements CatalogEntry {
118
119        private final CatalogObjectId id;
120        private final CatalogQualifiedName name;
121        private final CatalogObjectKind kind;
122        private final Map<String, Object> properties;
123        private final List<CatalogObjectId> childIds;
124
125        ImmutableCatalogEntry(CatalogObjectId id,
126                              CatalogQualifiedName name,
127                              CatalogObjectKind kind,
128                              Map<String, Object> properties,
129                              List<CatalogObjectId> childIds) {
130            this.id = id;
131            this.name = name;
132            this.kind = kind;
133            this.properties = Collections.unmodifiableMap(new LinkedHashMap<String, Object>(properties));
134            this.childIds = Collections.unmodifiableList(new ArrayList<CatalogObjectId>(childIds));
135        }
136
137        @Override
138        public CatalogObjectId id() {
139            return id;
140        }
141
142        @Override
143        public CatalogQualifiedName name() {
144            return name;
145        }
146
147        @Override
148        public CatalogObjectKind kind() {
149            return kind;
150        }
151
152        @Override
153        public Map<String, Object> properties() {
154            return properties;
155        }
156
157        /** Internal accessor used by {@link InMemoryCatalogSnapshot#children}. */
158        List<CatalogObjectId> childIds() {
159            return childIds;
160        }
161
162        @Override
163        public boolean equals(Object o) {
164            if (this == o) return true;
165            if (!(o instanceof ImmutableCatalogEntry)) return false;
166            ImmutableCatalogEntry that = (ImmutableCatalogEntry) o;
167            return id.equals(that.id)
168                && name.equals(that.name)
169                && kind == that.kind
170                && properties.equals(that.properties)
171                && childIds.equals(that.childIds);
172        }
173
174        @Override
175        public int hashCode() {
176            return Objects.hash(id, name, kind, properties, childIds);
177        }
178
179        @Override
180        public String toString() {
181            return "CatalogEntry{id=" + id + ", name=" + name + ", kind=" + kind + '}';
182        }
183    }
184}