001package gudusoft.gsqlparser.pp2.layout;
002
003/**
004 * The layout decision for the whitespace <i>before</i> one token: how many
005 * linebreaks and blanks should precede it, and at what indent level it sits.
006 *
007 * <p>Each property defaults to {@link #UNSET} ({@code -1}), meaning "no rule
008 * decided this; the output writer should fall back to the token's original
009 * {@code precedingLinebreaks} / {@code precedingBlanks}". This is what makes the
010 * zero-rule pipeline an identity pass.
011 *
012 * <p>Properties are written only through {@link LayoutContext}'s request methods,
013 * which apply the {@link LayoutConflictResolver} precedence. The fields are
014 * package-visible setters so the context (same package) can apply resolved
015 * writes; rules never set them directly.
016 *
017 * <p>Consumers read decisions through the {@link LayoutDecisionView} interface
018 * (what {@link LayoutContext#decisionAt(int)} returns); the mutating setters are
019 * package-visible so only {@code LayoutContext} (same package) applies resolved
020 * writes. The layout rules live in the sub-package {@code pp2.layout.rules}, so
021 * they cannot reach these setters and must use the context's {@code request*}
022 * methods — keeping all writes under {@link LayoutConflictResolver} precedence.
023 *
024 * <p>Plan reference: §7.3/S23, §7.4/S23.
025 */
026public final class LayoutDecision implements LayoutDecisionView {
027
028    /** Sentinel: this property has not been decided by any rule. */
029    public static final int UNSET = -1;
030
031    private int linebreaksBefore = UNSET;
032    private int blanksBefore = UNSET;
033    private int indentLevel = UNSET;
034    private String textOverride = null;
035
036    /** Number of linebreaks to emit before the token, or {@link #UNSET}. */
037    public int getLinebreaksBefore() { return linebreaksBefore; }
038
039    /** Number of blank columns to emit before the token, or {@link #UNSET}. */
040    public int getBlanksBefore() { return blanksBefore; }
041
042    /** Indent level for the token's line, or {@link #UNSET}. */
043    public int getIndentLevel() { return indentLevel; }
044
045    public boolean isLinebreaksDecided() { return linebreaksBefore != UNSET; }
046    public boolean isBlanksDecided() { return blanksBefore != UNSET; }
047    public boolean isIndentDecided() { return indentLevel != UNSET; }
048
049    /** The text to emit for the token instead of its source text, or {@code null}. */
050    public String getTextOverride() { return textOverride; }
051    public boolean isTextDecided() { return textOverride != null; }
052
053    // Package-visible writers — only LayoutContext (post-resolution) calls these.
054    void setLinebreaksBefore(int v) { this.linebreaksBefore = v; }
055    void setBlanksBefore(int v) { this.blanksBefore = v; }
056    void setIndentLevel(int v) { this.indentLevel = v; }
057    void setTextOverride(String v) { this.textOverride = v; }
058
059    @Override
060    public String toString() {
061        return "LayoutDecision[lb=" + linebreaksBefore
062            + " bl=" + blanksBefore + " ind=" + indentLevel
063            + (textOverride != null ? " text='" + textOverride + "'" : "") + "]";
064    }
065}