001package gudusoft.gsqlparser.catalog.input;
002
003import gudusoft.gsqlparser.catalog.input.model.UnifiedCatalogModel;
004
005import java.io.Reader;
006import java.net.URL;
007import java.nio.file.Path;
008import java.util.Collections;
009import java.util.LinkedHashMap;
010import java.util.Map;
011
012/**
013 * Immutable handle to a catalog input: file path, URL, reader supplier, byte buffer, or
014 * pre-built {@link UnifiedCatalogModel}.
015 *
016 * <p>Plan §7.1. Exactly one of {@link #path}, {@link #url}, {@link #reader}, {@link #bytes},
017 * or {@link #inMemoryModel} is non-null; the {@code declaredKind} hints at the format
018 * (used by {@code CatalogInputReaders.forSource} when multiple readers register the same
019 * extension or when the URL has no recognizable extension).</p>
020 */
021public final class CatalogInputSource {
022
023    private final Path path;
024    private final URL url;
025    private final Reader reader;
026    private final byte[] bytes;
027    private final UnifiedCatalogModel inMemoryModel;
028    private final CatalogInputKind declaredKind;
029    private final String name;
030    private final Map<String, Object> properties;
031
032    private CatalogInputSource(Path path, URL url, Reader reader, byte[] bytes,
033                               UnifiedCatalogModel inMemoryModel,
034                               CatalogInputKind declaredKind,
035                               String name,
036                               Map<String, Object> properties) {
037        this.path = path;
038        this.url = url;
039        this.reader = reader;
040        this.bytes = bytes;
041        this.inMemoryModel = inMemoryModel;
042        this.declaredKind = declaredKind;
043        this.name = name;
044        this.properties = properties == null
045            ? Collections.<String, Object>emptyMap()
046            : Collections.unmodifiableMap(new LinkedHashMap<String, Object>(properties));
047    }
048
049    public static CatalogInputSource fromPath(Path path, CatalogInputKind kind) {
050        if (path == null) {
051            throw new IllegalArgumentException("CatalogInputSource.fromPath: path is required");
052        }
053        return new CatalogInputSource(path, null, null, null, null, kind, path.toString(), null);
054    }
055
056    public static CatalogInputSource fromReader(Reader reader, CatalogInputKind kind) {
057        if (reader == null) {
058            throw new IllegalArgumentException("CatalogInputSource.fromReader: reader is required");
059        }
060        return new CatalogInputSource(null, null, reader, null, null, kind, "<reader>", null);
061    }
062
063    public static CatalogInputSource fromUrl(URL url, CatalogInputKind kind) {
064        if (url == null) {
065            throw new IllegalArgumentException("CatalogInputSource.fromUrl: url is required");
066        }
067        return new CatalogInputSource(null, url, null, null, null, kind, url.toString(), null);
068    }
069
070    public static CatalogInputSource fromBytes(byte[] bytes, CatalogInputKind kind) {
071        if (bytes == null) {
072            throw new IllegalArgumentException("CatalogInputSource.fromBytes: bytes is required");
073        }
074        // Defensive copy: this type is documented as an immutable handle, so the source
075        // contents must not change if the caller reuses or clears their buffer afterward.
076        byte[] copy = new byte[bytes.length];
077        System.arraycopy(bytes, 0, copy, 0, bytes.length);
078        return new CatalogInputSource(null, null, null, copy, null, kind, "<bytes>", null);
079    }
080
081    public static CatalogInputSource fromMemory(UnifiedCatalogModel model) {
082        if (model == null) {
083            throw new IllegalArgumentException("CatalogInputSource.fromMemory: model is required");
084        }
085        return new CatalogInputSource(null, null, null, null, model,
086            CatalogInputKind.IN_MEMORY, "<memory>", null);
087    }
088
089    public Path path() {
090        return path;
091    }
092
093    public URL url() {
094        return url;
095    }
096
097    public Reader reader() {
098        return reader;
099    }
100
101    /**
102     * Returns a defensive copy of the byte payload. Callers that touch the array — for
103     * instance to feed it to a parser — never see a mutation from this instance, and a
104     * subsequent {@link #fromBytes} on the same buffer is unaffected.
105     */
106    public byte[] bytes() {
107        if (bytes == null) return null;
108        byte[] copy = new byte[bytes.length];
109        System.arraycopy(bytes, 0, copy, 0, bytes.length);
110        return copy;
111    }
112
113    public UnifiedCatalogModel inMemoryModel() {
114        return inMemoryModel;
115    }
116
117    public CatalogInputKind declaredKind() {
118        return declaredKind;
119    }
120
121    public String name() {
122        return name;
123    }
124
125    public Map<String, Object> properties() {
126        return properties;
127    }
128
129    @Override
130    public String toString() {
131        return "CatalogInputSource{name=" + name + ", declaredKind=" + declaredKind + '}';
132    }
133}