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. A {@code ;} terminator is included in the range; a 010 * {@code GO} batch separator (and the count of {@code GO n}) is not: it 011 * lies in the gap before the next range, where the assembler emits it 012 * verbatim. The matching source-offset interval 013 * {@code [startOffset, endOffset)} on the original input lets the 014 * region assembler (S15) reconstruct verbatim bytes from the 015 * {@code SourceSpanLedger}. 016 * 017 * <p>Plan reference: §6 layout, §7.3/S11, §7.4/S11. 018 */ 019public final class StatementRange { 020 021 /** What ends a statement range. */ 022 public enum Terminator { 023 /** Standard SQL semicolon. */ 024 SEMICOLON, 025 /** 026 * SQL Server / Sybase {@code GO} batch separator (case-insensitive keyword), which 027 * follows the range rather than ending it. 028 */ 029 GO, 030 /** The range is the final segment of the script with no trailing terminator. */ 031 NONE 032 } 033 034 private final int startTokenIndex; 035 private final int endTokenIndex; 036 private final int startOffset; 037 private final int endOffset; 038 private final Terminator terminator; 039 040 public StatementRange(int startTokenIndex, int endTokenIndex, 041 int startOffset, int endOffset, 042 Terminator terminator) { 043 if (terminator == null) throw new NullPointerException("terminator"); 044 if (startTokenIndex < 0) { 045 throw new IllegalArgumentException( 046 "startTokenIndex < 0: " + startTokenIndex); 047 } 048 if (endTokenIndex < startTokenIndex) { 049 throw new IllegalArgumentException( 050 "endTokenIndex < startTokenIndex: " 051 + endTokenIndex + " < " + startTokenIndex); 052 } 053 if (startOffset < 0) { 054 throw new IllegalArgumentException( 055 "startOffset < 0: " + startOffset); 056 } 057 if (endOffset < startOffset) { 058 throw new IllegalArgumentException( 059 "endOffset < startOffset: " 060 + endOffset + " < " + startOffset); 061 } 062 this.startTokenIndex = startTokenIndex; 063 this.endTokenIndex = endTokenIndex; 064 this.startOffset = startOffset; 065 this.endOffset = endOffset; 066 this.terminator = terminator; 067 } 068 069 public int getStartTokenIndex() { return startTokenIndex; } 070 public int getEndTokenIndex() { return endTokenIndex; } 071 public int getStartOffset() { return startOffset; } 072 public int getEndOffset() { return endOffset; } 073 public Terminator getTerminator() { return terminator; } 074 075 /** Number of token slots in this range (a {@code ;} included, a following {@code GO} not). */ 076 public int tokenCount() { return endTokenIndex - startTokenIndex; } 077 078 /** True if the range carries no token slots. */ 079 public boolean isEmpty() { return endTokenIndex == startTokenIndex; } 080 081 @Override 082 public String toString() { 083 return "StatementRange{tokens=[" + startTokenIndex + ".." + endTokenIndex 084 + ") chars=[" + startOffset + ".." + endOffset + ") " + terminator + "}"; 085 } 086}