001package gudusoft.gsqlparser.pp2.region; 002 003/** 004 * Immutable record of a single statement region produced by 005 * {@link StatementBoundaryDetector}. 006 * 007 * <p>A range covers a half-open token index interval 008 * {@code [startTokenIndex, endTokenIndex)} on the {@code Pp2TokenStream} 009 * that produced it. The terminator token (if any) is included in the 010 * range. The matching source-offset interval 011 * {@code [startOffset, endOffset)} on the original input lets the 012 * region assembler (S15) reconstruct verbatim bytes from the 013 * {@code SourceSpanLedger}. 014 * 015 * <p>Plan reference: §6 layout, §7.3/S11, §7.4/S11. 016 */ 017public final class StatementRange { 018 019 /** What ends a statement range. */ 020 public enum Terminator { 021 /** Standard SQL semicolon. */ 022 SEMICOLON, 023 /** SQL Server / Sybase {@code GO} batch separator (case-insensitive keyword). */ 024 GO, 025 /** The range is the final segment of the script with no trailing terminator. */ 026 NONE 027 } 028 029 private final int startTokenIndex; 030 private final int endTokenIndex; 031 private final int startOffset; 032 private final int endOffset; 033 private final Terminator terminator; 034 035 public StatementRange(int startTokenIndex, int endTokenIndex, 036 int startOffset, int endOffset, 037 Terminator terminator) { 038 if (terminator == null) throw new NullPointerException("terminator"); 039 if (startTokenIndex < 0) { 040 throw new IllegalArgumentException( 041 "startTokenIndex < 0: " + startTokenIndex); 042 } 043 if (endTokenIndex < startTokenIndex) { 044 throw new IllegalArgumentException( 045 "endTokenIndex < startTokenIndex: " 046 + endTokenIndex + " < " + startTokenIndex); 047 } 048 if (startOffset < 0) { 049 throw new IllegalArgumentException( 050 "startOffset < 0: " + startOffset); 051 } 052 if (endOffset < startOffset) { 053 throw new IllegalArgumentException( 054 "endOffset < startOffset: " 055 + endOffset + " < " + startOffset); 056 } 057 this.startTokenIndex = startTokenIndex; 058 this.endTokenIndex = endTokenIndex; 059 this.startOffset = startOffset; 060 this.endOffset = endOffset; 061 this.terminator = terminator; 062 } 063 064 public int getStartTokenIndex() { return startTokenIndex; } 065 public int getEndTokenIndex() { return endTokenIndex; } 066 public int getStartOffset() { return startOffset; } 067 public int getEndOffset() { return endOffset; } 068 public Terminator getTerminator() { return terminator; } 069 070 /** Number of token slots in this range (including the terminator). */ 071 public int tokenCount() { return endTokenIndex - startTokenIndex; } 072 073 /** True if the range carries no token slots. */ 074 public boolean isEmpty() { return endTokenIndex == startTokenIndex; } 075 076 @Override 077 public String toString() { 078 return "StatementRange{tokens=[" + startTokenIndex + ".." + endTokenIndex 079 + ") chars=[" + startOffset + ".." + endOffset + ") " + terminator + "}"; 080 } 081}