001package gudusoft.gsqlparser.parser;
002
003import gudusoft.gsqlparser.EDbVendor;
004import gudusoft.gsqlparser.IMetaDatabase;
005import gudusoft.gsqlparser.ISQLStatementHandle;
006import gudusoft.gsqlparser.ITokenHandle;
007import gudusoft.gsqlparser.ITokenListHandle;
008import gudusoft.gsqlparser.compiler.TFrame;
009import gudusoft.gsqlparser.sqlenv.TSQLEnv;
010import gudusoft.gsqlparser.stmt.teradata.utilities.TeradataUtilityType;
011
012import java.io.InputStream;
013import java.util.Stack;
014
015/**
016 * Immutable context carrying all parser inputs and settings.
017 *
018 * <p>This value object encapsulates all inputs required for SQL parsing,
019 * providing clean separation between input configuration and parser implementation.
020 * Once built, a ParserContext instance cannot be modified (immutable).
021 *
022 * <p><b>Design Pattern:</b> Value Object + Builder
023 * <ul>
024 *   <li>Immutable: Thread-safe, no defensive copying needed</li>
025 *   <li>Builder: Flexible construction with optional parameters</li>
026 *   <li>Clean boundaries: Input/Output separation</li>
027 * </ul>
028 *
029 * <p><b>Usage Example:</b>
030 * <pre>
031 * ParserContext context = new ParserContext.Builder(EDbVendor.dbvoracle)
032 *     .sqlText("SELECT * FROM employees WHERE salary > 50000")
033 *     .enablePartialParsing(true)
034 *     .metaDatabase(myMetaDatabase)
035 *     .build();
036 * </pre>
037 *
038 * @see SqlParser
039 * @see SqlParseResult
040 * @since 3.2.0.0
041 */
042public final class ParserContext {
043
044    // ========== Input Source ==========
045    private final EDbVendor vendor;
046    private final String sqlText;
047    private final String sqlFilename;
048    private final InputStream sqlInputStream;
049    private final String sqlCharset;
050
051    // ========== Parser Options ==========
052    private final boolean enablePartialParsing;
053    private final boolean singlePLBlock;
054    private final boolean onlyNeedRawParseTree;
055    private final TeradataUtilityType teradataUtilityType;
056
057    // ========== Callbacks and Hooks ==========
058    private final ISQLStatementHandle sqlStatementHandle;
059    private final ITokenHandle tokenHandle;
060    private final ITokenListHandle tokenListHandle;
061
062    // ========== Metadata and Environment ==========
063    private final IMetaDatabase metaDatabase;
064    private final TSQLEnv sqlEnv;
065
066    // ========== Diagnostic Settings ==========
067    private final boolean dumpResolverLog;
068    private final boolean enableTimeLogging;
069
070    // ========== State (for special cases) ==========
071    private final Stack<TFrame> frameStack;
072
073    // ========== Parser Reference (for backfilling) ==========
074    /**
075     * Reference to TGSqlParser facade for backfilling metadata.
076     * This allows parsed statements to access parser's vendor and other properties.
077     */
078    private final Object gsqlparser;  // Object type to avoid circular dependency
079
080    /**
081     * Private constructor - use Builder to create instances.
082     */
083    private ParserContext(Builder builder) {
084        // Input source
085        this.vendor = builder.vendor;
086        this.sqlText = builder.sqlText;
087        this.sqlFilename = builder.sqlFilename;
088        this.sqlInputStream = builder.sqlInputStream;
089        this.sqlCharset = builder.sqlCharset;
090
091        // Parser options
092        this.enablePartialParsing = builder.enablePartialParsing;
093        this.singlePLBlock = builder.singlePLBlock;
094        this.onlyNeedRawParseTree = builder.onlyNeedRawParseTree;
095        this.teradataUtilityType = builder.teradataUtilityType;
096
097        // Callbacks
098        this.sqlStatementHandle = builder.sqlStatementHandle;
099        this.tokenHandle = builder.tokenHandle;
100        this.tokenListHandle = builder.tokenListHandle;
101
102        // Metadata
103        this.metaDatabase = builder.metaDatabase;
104        this.sqlEnv = builder.sqlEnv;
105
106        // Diagnostics
107        this.dumpResolverLog = builder.dumpResolverLog;
108        this.enableTimeLogging = builder.enableTimeLogging;
109
110        // State
111        this.frameStack = builder.frameStack;
112
113        // Parser reference
114        this.gsqlparser = builder.gsqlparser;
115    }
116
117    // ========== Getters (Read-only access) ==========
118
119    public EDbVendor getVendor() {
120        return vendor;
121    }
122
123    public String getSqlText() {
124        return sqlText;
125    }
126
127    public String getSqlFilename() {
128        return sqlFilename;
129    }
130
131    public InputStream getSqlInputStream() {
132        return sqlInputStream;
133    }
134
135    public String getSqlCharset() {
136        return sqlCharset;
137    }
138
139    public boolean isEnablePartialParsing() {
140        return enablePartialParsing;
141    }
142
143    public boolean isSinglePLBlock() {
144        return singlePLBlock;
145    }
146
147    public boolean isOnlyNeedRawParseTree() {
148        return onlyNeedRawParseTree;
149    }
150
151    public TeradataUtilityType getTeradataUtilityType() {
152        return teradataUtilityType;
153    }
154
155    public ISQLStatementHandle getSqlStatementHandle() {
156        return sqlStatementHandle;
157    }
158
159    public ITokenHandle getTokenHandle() {
160        return tokenHandle;
161    }
162
163    public ITokenListHandle getTokenListHandle() {
164        return tokenListHandle;
165    }
166
167    public IMetaDatabase getMetaDatabase() {
168        return metaDatabase;
169    }
170
171    public TSQLEnv getSqlEnv() {
172        return sqlEnv;
173    }
174
175    public boolean isDumpResolverLog() {
176        return dumpResolverLog;
177    }
178
179    public boolean isEnableTimeLogging() {
180        return enableTimeLogging;
181    }
182
183    public Stack<TFrame> getFrameStack() {
184        return frameStack;
185    }
186
187    /**
188     * Get reference to TGSqlParser facade for backfilling statement metadata.
189     * @return TGSqlParser instance or null if not set
190     */
191    public Object getGsqlparser() {
192        return gsqlparser;
193    }
194
195    /**
196     * Builder for constructing ParserContext instances.
197     *
198     * <p>The Builder pattern provides:
199     * <ul>
200     *   <li>Flexible construction with optional parameters</li>
201     *   <li>Readable, fluent API</li>
202     *   <li>Default values for optional parameters</li>
203     *   <li>Immutability of final ParserContext object</li>
204     * </ul>
205     *
206     * <p><b>Example:</b>
207     * <pre>
208     * ParserContext ctx = new ParserContext.Builder(EDbVendor.dbvoracle)
209     *     .sqlText("SELECT * FROM dual")
210     *     .enablePartialParsing(true)
211     *     .metaDatabase(myMetaDb)
212     *     .build();
213     * </pre>
214     */
215    public static class Builder {
216        // Required parameters
217        private final EDbVendor vendor;
218
219        // Optional parameters with defaults
220        private String sqlText = null;
221        private String sqlFilename = "";
222        private InputStream sqlInputStream = null;
223        private String sqlCharset = null;
224
225        private boolean enablePartialParsing = true;
226        private boolean singlePLBlock = false;
227        private boolean onlyNeedRawParseTree = false;
228        private TeradataUtilityType teradataUtilityType = null;
229
230        private ISQLStatementHandle sqlStatementHandle = null;
231        private ITokenHandle tokenHandle = null;
232        private ITokenListHandle tokenListHandle = null;
233
234        private IMetaDatabase metaDatabase = null;
235        private TSQLEnv sqlEnv = null;
236
237        private boolean dumpResolverLog = false;
238        private boolean enableTimeLogging = false;
239
240        private Stack<TFrame> frameStack = null;
241
242        private Object gsqlparser = null;
243
244        /**
245         * Create a builder with required vendor parameter.
246         *
247         * @param vendor the database vendor (required)
248         */
249        public Builder(EDbVendor vendor) {
250            if (vendor == null) {
251                throw new IllegalArgumentException("vendor cannot be null");
252            }
253            this.vendor = vendor;
254        }
255
256        /**
257         * Set SQL text to parse.
258         *
259         * @param sqlText the SQL text
260         * @return this builder for method chaining
261         */
262        public Builder sqlText(String sqlText) {
263            this.sqlText = sqlText != null ? sqlText : "";
264            return this;
265        }
266
267        /**
268         * Set SQL filename to read from.
269         *
270         * @param sqlFilename the SQL filename
271         * @return this builder for method chaining
272         */
273        public Builder sqlFilename(String sqlFilename) {
274            this.sqlFilename = sqlFilename != null ? sqlFilename : "";
275            return this;
276        }
277
278        /**
279         * Set SQL input stream to read from.
280         *
281         * @param sqlInputStream the SQL input stream
282         * @return this builder for method chaining
283         */
284        public Builder sqlInputStream(InputStream sqlInputStream) {
285            this.sqlInputStream = sqlInputStream;
286            return this;
287        }
288
289        /**
290         * Set SQL character set for file reading.
291         *
292         * @param sqlCharset the character set name
293         * @return this builder for method chaining
294         */
295        public Builder sqlCharset(String sqlCharset) {
296            this.sqlCharset = sqlCharset;
297            return this;
298        }
299
300        /**
301         * Enable/disable partial parsing.
302         *
303         * @param enablePartialParsing true to enable partial parsing
304         * @return this builder for method chaining
305         */
306        public Builder enablePartialParsing(boolean enablePartialParsing) {
307            this.enablePartialParsing = enablePartialParsing;
308            return this;
309        }
310
311        /**
312         * Set single PL block mode.
313         *
314         * @param singlePLBlock true for single PL block
315         * @return this builder for method chaining
316         */
317        public Builder singlePLBlock(boolean singlePLBlock) {
318            this.singlePLBlock = singlePLBlock;
319            return this;
320        }
321
322        /**
323         * Set if only raw parse tree is needed.
324         *
325         * @param onlyNeedRawParseTree true if only raw parse tree needed
326         * @return this builder for method chaining
327         */
328        public Builder onlyNeedRawParseTree(boolean onlyNeedRawParseTree) {
329            this.onlyNeedRawParseTree = onlyNeedRawParseTree;
330            return this;
331        }
332
333        /**
334         * Set Teradata utility type.
335         *
336         * @param teradataUtilityType the Teradata utility type
337         * @return this builder for method chaining
338         */
339        public Builder teradataUtilityType(TeradataUtilityType teradataUtilityType) {
340            this.teradataUtilityType = teradataUtilityType;
341            return this;
342        }
343
344        /**
345         * Set SQL statement handle callback.
346         *
347         * @param sqlStatementHandle the statement handle callback
348         * @return this builder for method chaining
349         */
350        public Builder sqlStatementHandle(ISQLStatementHandle sqlStatementHandle) {
351            this.sqlStatementHandle = sqlStatementHandle;
352            return this;
353        }
354
355        /**
356         * Set token handle callback.
357         *
358         * @param tokenHandle the token handle callback
359         * @return this builder for method chaining
360         */
361        public Builder tokenHandle(ITokenHandle tokenHandle) {
362            this.tokenHandle = tokenHandle;
363            return this;
364        }
365
366        /**
367         * Set token list handle callback.
368         *
369         * @param tokenListHandle the token list handle callback
370         * @return this builder for method chaining
371         */
372        public Builder tokenListHandle(ITokenListHandle tokenListHandle) {
373            this.tokenListHandle = tokenListHandle;
374            return this;
375        }
376
377        /**
378         * Set metadata database for column/table resolution.
379         *
380         * @param metaDatabase the metadata database
381         * @return this builder for method chaining
382         */
383        public Builder metaDatabase(IMetaDatabase metaDatabase) {
384            this.metaDatabase = metaDatabase;
385            return this;
386        }
387
388        /**
389         * Set SQL environment.
390         *
391         * @param sqlEnv the SQL environment
392         * @return this builder for method chaining
393         */
394        public Builder sqlEnv(TSQLEnv sqlEnv) {
395            this.sqlEnv = sqlEnv;
396            return this;
397        }
398
399        /**
400         * Enable/disable resolver log dumping.
401         *
402         * @param dumpResolverLog true to dump resolver log
403         * @return this builder for method chaining
404         */
405        public Builder dumpResolverLog(boolean dumpResolverLog) {
406            this.dumpResolverLog = dumpResolverLog;
407            return this;
408        }
409
410        /**
411         * Enable/disable time logging.
412         *
413         * @param enableTimeLogging true to enable time logging
414         * @return this builder for method chaining
415         */
416        public Builder enableTimeLogging(boolean enableTimeLogging) {
417            this.enableTimeLogging = enableTimeLogging;
418            return this;
419        }
420
421        /**
422         * Set frame stack for compiler context.
423         *
424         * @param frameStack the frame stack
425         * @return this builder for method chaining
426         */
427        public Builder frameStack(Stack<TFrame> frameStack) {
428            this.frameStack = frameStack;
429            return this;
430        }
431
432        /**
433         * Set reference to TGSqlParser facade for backfilling metadata.
434         * @param gsqlparser TGSqlParser instance
435         * @return this builder
436         */
437        public Builder gsqlparser(Object gsqlparser) {
438            this.gsqlparser = gsqlparser;
439            return this;
440        }
441
442        /**
443         * Build the immutable ParserContext.
444         *
445         * @return a new immutable ParserContext instance
446         */
447        public ParserContext build() {
448            return new ParserContext(this);
449        }
450    }
451
452    @Override
453    public String toString() {
454        return "ParserContext{" +
455                "vendor=" + vendor +
456                ", sqlText=" + (sqlText != null && sqlText.length() > 50 ?
457                    sqlText.substring(0, 50) + "..." : sqlText) +
458                ", sqlFilename='" + sqlFilename + '\'' +
459                ", hasInputStream=" + (sqlInputStream != null) +
460                '}';
461    }
462}