001package gudusoft.gsqlparser.ir.semantic.joinanalysis; 002 003import java.util.Objects; 004 005/** 006 * Resolution of a {@code ColumnRef} to its base relation / final table 007 * (GAP 5). Pairs the resolved qualified name with an explicit 008 * {@link ColumnResolutionStatus} so an unresolved column is surfaced 009 * honestly rather than as a silent empty. 010 * 011 * <p>Invariants: 012 * <ul> 013 * <li>{@link ColumnResolutionStatus#RESOLVED} ⇒ 014 * {@code resolvedTableQualifiedName != null}.</li> 015 * <li>{@link ColumnResolutionStatus#UNRESOLVED} / 016 * {@link ColumnResolutionStatus#NOT_APPLICABLE} ⇒ 017 * {@code resolvedTableQualifiedName == null}; a final table is 018 * <em>never</em> fabricated.</li> 019 * </ul> 020 * 021 * <p>Immutable. Introduced by join-analysis slice 162 (S1); populated 022 * from {@code ColumnBinding} in slice 165 (S4). 023 */ 024public final class ColumnResolution { 025 026 /** Shared singleton for the unresolved case. */ 027 public static final ColumnResolution UNRESOLVED = 028 new ColumnResolution(null, ColumnResolutionStatus.UNRESOLVED); 029 030 /** Shared singleton for the not-applicable case. */ 031 public static final ColumnResolution NOT_APPLICABLE = 032 new ColumnResolution(null, ColumnResolutionStatus.NOT_APPLICABLE); 033 034 private final String resolvedTableQualifiedName; 035 private final ColumnResolutionStatus status; 036 037 public ColumnResolution(String resolvedTableQualifiedName, ColumnResolutionStatus status) { 038 if (status == null) { 039 throw new IllegalArgumentException("status must be non-null"); 040 } 041 if (status == ColumnResolutionStatus.RESOLVED 042 && (resolvedTableQualifiedName == null || resolvedTableQualifiedName.isEmpty())) { 043 throw new IllegalArgumentException( 044 "RESOLVED requires a non-empty resolvedTableQualifiedName"); 045 } 046 if (status != ColumnResolutionStatus.RESOLVED && resolvedTableQualifiedName != null) { 047 throw new IllegalArgumentException( 048 "resolvedTableQualifiedName must be null unless RESOLVED"); 049 } 050 this.resolvedTableQualifiedName = resolvedTableQualifiedName; 051 this.status = status; 052 } 053 054 /** Convenience: build a RESOLVED resolution. */ 055 public static ColumnResolution resolved(String resolvedTableQualifiedName) { 056 return new ColumnResolution(resolvedTableQualifiedName, ColumnResolutionStatus.RESOLVED); 057 } 058 059 /** Null when not {@link ColumnResolutionStatus#RESOLVED}. */ 060 public String getResolvedTableQualifiedName() { 061 return resolvedTableQualifiedName; 062 } 063 064 public ColumnResolutionStatus getStatus() { 065 return status; 066 } 067 068 public boolean isResolved() { 069 return status == ColumnResolutionStatus.RESOLVED; 070 } 071 072 @Override 073 public boolean equals(Object o) { 074 if (this == o) return true; 075 if (!(o instanceof ColumnResolution)) return false; 076 ColumnResolution that = (ColumnResolution) o; 077 return status == that.status 078 && Objects.equals(resolvedTableQualifiedName, that.resolvedTableQualifiedName); 079 } 080 081 @Override 082 public int hashCode() { 083 return Objects.hash(resolvedTableQualifiedName, status); 084 } 085 086 @Override 087 public String toString() { 088 return status + (resolvedTableQualifiedName != null 089 ? "(" + resolvedTableQualifiedName + ")" : ""); 090 } 091}