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}