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}