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
030    AnalysisResult(String schemaVersion,
031                   SemanticProgram program,
032                   String json,
033                   List<Diagnostic> diagnostics) {
034        this.schemaVersion = Objects.requireNonNull(schemaVersion, "schemaVersion");
035        this.program = program;
036        this.json = json;
037        this.diagnostics = Collections.unmodifiableList(
038                new ArrayList<>(Objects.requireNonNull(diagnostics, "diagnostics")));
039    }
040
041    /**
042     * @return {@code true} iff a non-null program was built AND no
043     *         {@link Severity#ERROR ERROR}-severity diagnostics are
044     *         present.
045     */
046    public boolean isSuccessful() {
047        if (program == null) {
048            return false;
049        }
050        for (Diagnostic d : diagnostics) {
051            if (d.getSeverity() == Severity.ERROR) {
052                return false;
053            }
054        }
055        return true;
056    }
057
058    /** JSON schema version emitted by the analyzer (currently {@code "1"}). */
059    public String getSchemaVersion() {
060        return schemaVersion;
061    }
062
063    /**
064     * @return the built program, or {@code null} when analysis
065     *         failed. Use {@link #isSuccessful()} as the discriminator.
066     */
067    public SemanticProgram getProgram() {
068        return program;
069    }
070
071    /**
072     * @return the JSON encoding of {@link #getProgram()}, or
073     *         {@code null} when analysis failed.
074     */
075    public String getJson() {
076        return json;
077    }
078
079    /** Diagnostics emitted during analysis. Never {@code null}; may be empty. */
080    public List<Diagnostic> getDiagnostics() {
081        return diagnostics;
082    }
083
084    /**
085     * @return the first {@link Severity#ERROR ERROR}-severity
086     *         diagnostic, or {@code null} when none were emitted.
087     */
088    public Diagnostic getFirstError() {
089        for (Diagnostic d : diagnostics) {
090            if (d.getSeverity() == Severity.ERROR) {
091                return d;
092            }
093        }
094        return null;
095    }
096}