001package gudusoft.gsqlparser.ir.semantic; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.List; 006import java.util.Objects; 007 008/** 009 * Result of a {@link SqlSemanticAnalyzer#analyze} call. Carries the 010 * built {@link SemanticProgram}, the JSON-encoded form, and the list 011 * of {@link Diagnostic}s emitted during analysis. 012 * 013 * <p>A successful analysis produces a non-null program and JSON with 014 * an empty (or warning-only) diagnostics list. A failed analysis 015 * produces a {@code null} program / JSON and at least one 016 * {@link Severity#ERROR ERROR}-severity diagnostic. 017 * 018 * <p>This type is immutable and thread-safe. Consumers should 019 * pattern-match on {@link Diagnostic#getCode()} (the 020 * {@link DiagnosticCode} enum is the stable contract) rather than 021 * parsing message text. 022 */ 023public final class AnalysisResult { 024 025 private final String schemaVersion; 026 private final SemanticProgram program; 027 private final String json; 028 private final List<Diagnostic> diagnostics; 029 private final String statementText; 030 031 AnalysisResult(String schemaVersion, 032 SemanticProgram program, 033 String json, 034 List<Diagnostic> diagnostics) { 035 this(schemaVersion, program, json, diagnostics, null); 036 } 037 038 /** 039 * Full constructor (slice 178, R3). {@code statementText} is the 040 * verbatim source slice of the analyzed top-level statement (null when 041 * not computed, e.g. a rejection before the statement was located). 042 */ 043 AnalysisResult(String schemaVersion, 044 SemanticProgram program, 045 String json, 046 List<Diagnostic> diagnostics, 047 String statementText) { 048 this.schemaVersion = Objects.requireNonNull(schemaVersion, "schemaVersion"); 049 this.program = program; 050 this.json = json; 051 this.diagnostics = Collections.unmodifiableList( 052 new ArrayList<>(Objects.requireNonNull(diagnostics, "diagnostics"))); 053 this.statementText = statementText; 054 } 055 056 /** 057 * @return {@code true} iff a non-null program was built AND no 058 * {@link Severity#ERROR ERROR}-severity diagnostics are 059 * present. 060 */ 061 public boolean isSuccessful() { 062 if (program == null) { 063 return false; 064 } 065 for (Diagnostic d : diagnostics) { 066 if (d.getSeverity() == Severity.ERROR) { 067 return false; 068 } 069 } 070 return true; 071 } 072 073 /** JSON schema version emitted by the analyzer (currently {@code "1"}). */ 074 public String getSchemaVersion() { 075 return schemaVersion; 076 } 077 078 /** 079 * @return the built program, or {@code null} when analysis 080 * failed. Use {@link #isSuccessful()} as the discriminator. 081 */ 082 public SemanticProgram getProgram() { 083 return program; 084 } 085 086 /** 087 * @return the JSON encoding of {@link #getProgram()}, or 088 * {@code null} when analysis failed. 089 */ 090 public String getJson() { 091 return json; 092 } 093 094 /** Diagnostics emitted during analysis. Never {@code null}; may be empty. */ 095 public List<Diagnostic> getDiagnostics() { 096 return diagnostics; 097 } 098 099 /** 100 * The verbatim source slice of the analyzed top-level statement 101 * (slice 178, R3). Non-null for results produced by 102 * {@link SqlSemanticAnalyzer#analyze} and 103 * {@link SqlSemanticAnalyzer#analyzeAll} once the statement is located; 104 * {@code null} for parse-level rejections before a statement exists. 105 * Lets multi-statement consumers reproduce the exact text of one 106 * statement without re-parsing or slicing the parse tree themselves. 107 */ 108 public String getStatementText() { 109 return statementText; 110 } 111 112 /** 113 * @return the first {@link Severity#ERROR ERROR}-severity 114 * diagnostic, or {@code null} when none were emitted. 115 */ 116 public Diagnostic getFirstError() { 117 for (Diagnostic d : diagnostics) { 118 if (d.getSeverity() == Severity.ERROR) { 119 return d; 120 } 121 } 122 return null; 123 } 124}