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}