001package gudusoft.gsqlparser.pp2; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.List; 006 007/** 008 * Immutable result of a {@code Pp2Formatter.format(...)} call. 009 * 010 * <p>Carries the formatted SQL text, the overall outcome 011 * ({@link FormatStatus}), the per-region rendering record 012 * ({@link Region}), and the diagnostic events the engine emitted along the 013 * way ({@link FormatDiagnostic}). 014 * 015 * <p>Built only by pp2 internals. Callers consume the immutable accessors: 016 * {@link #getText()}, {@link #getStatus()}, {@link #getRegions()}, 017 * {@link #getDiagnostics()}. The two list accessors return unmodifiable 018 * views. 019 */ 020public final class Pp2FormatResult { 021 022 /** 023 * Per-region rendering record. Spans are byte offsets into the 024 * <b>original</b> input string (not the formatted output), so multiple 025 * regions can be reconstructed against the source. 026 * 027 * <p>Carries the {@link RendererId} that the engine actually used and a 028 * region-local {@link FormatStatus} that may be {@link FormatStatus#OK} 029 * even when the result's overall status is 030 * {@link FormatStatus#OK_WITH_RECOVERY} (i.e., some other region needed 031 * recovery, but this one did not). 032 */ 033 public static final class Region { 034 private final int startOffset; 035 private final int endOffset; 036 private final RendererId rendererId; 037 private final FormatStatus status; 038 039 public Region(int startOffset, int endOffset, RendererId rendererId, 040 FormatStatus status) { 041 if (rendererId == null) throw new NullPointerException("rendererId"); 042 if (status == null) throw new NullPointerException("status"); 043 if (startOffset < 0) { 044 throw new IllegalArgumentException("startOffset < 0: " + startOffset); 045 } 046 if (endOffset < startOffset) { 047 throw new IllegalArgumentException( 048 "endOffset < startOffset: " + endOffset + " < " + startOffset); 049 } 050 this.startOffset = startOffset; 051 this.endOffset = endOffset; 052 this.rendererId = rendererId; 053 this.status = status; 054 } 055 056 public int getStartOffset() { return startOffset; } 057 public int getEndOffset() { return endOffset; } 058 public RendererId getRendererId() { return rendererId; } 059 public FormatStatus getStatus() { return status; } 060 061 @Override 062 public String toString() { 063 return "Region{[" + startOffset + ".." + endOffset + "] " 064 + rendererId + " " + status + "}"; 065 } 066 } 067 068 private final String text; 069 private final FormatStatus status; 070 private final List<Region> regions; 071 private final List<FormatDiagnostic> diagnostics; 072 073 public Pp2FormatResult(String text, FormatStatus status, 074 List<Region> regions, 075 List<FormatDiagnostic> diagnostics) { 076 if (text == null) throw new NullPointerException("text"); 077 if (status == null) throw new NullPointerException("status"); 078 this.text = text; 079 this.status = status; 080 this.regions = regions == null || regions.isEmpty() 081 ? Collections.<Region>emptyList() 082 : Collections.unmodifiableList(new ArrayList<Region>(regions)); 083 this.diagnostics = diagnostics == null || diagnostics.isEmpty() 084 ? Collections.<FormatDiagnostic>emptyList() 085 : Collections.unmodifiableList(new ArrayList<FormatDiagnostic>(diagnostics)); 086 } 087 088 public String getText() { return text; } 089 public FormatStatus getStatus() { return status; } 090 091 /** Unmodifiable view; never {@code null}, may be empty. */ 092 public List<Region> getRegions() { return regions; } 093 094 /** Unmodifiable view; never {@code null}, may be empty. */ 095 public List<FormatDiagnostic> getDiagnostics() { return diagnostics; } 096 097 @Override 098 public String toString() { 099 return "Pp2FormatResult{status=" + status 100 + ", regions=" + regions.size() 101 + ", diagnostics=" + diagnostics.size() 102 + ", textLen=" + text.length() + "}"; 103 } 104}