001package gudusoft.gsqlparser.resolver2.model; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.List; 006 007/** 008 * Enhanced column source that supports multiple reference traceability. 009 * 010 * <p>A normalized column name may correspond to multiple original references in SQL. 011 * For example, in Oracle SQL {@code SELECT "Column1", column1 FROM dual}:</p> 012 * <ul> 013 * <li>Both "Column1" and column1 normalize to COLUMN1</li> 014 * <li>This class stores the canonical ColumnSource plus all original references</li> 015 * <li>Output shows one column (deduplication)</li> 016 * <li>All original positions can be traced back</li> 017 * </ul> 018 * 019 * <p>Design pattern: Aggregator for Many-to-One mapping</p> 020 * <ul> 021 * <li>Multiple original identifiers (different syntax) → One semantic identifier</li> 022 * <li>Maintains link from semantic identifier back to all original identifiers</li> 023 * </ul> 024 * 025 * @since 3.1.0.9 026 */ 027public class ColumnSourceWithReferences { 028 029 /** Normalized column name (unique identifier) */ 030 private final String normalizedName; 031 032 /** Canonical column source (for resolution) */ 033 private final ColumnSource canonicalSource; 034 035 /** All original references (in order of appearance) */ 036 private final List<ColumnReference> references; 037 038 /** 039 * Create a new column source with references. 040 * 041 * @param normalizedName normalized column name 042 * @param canonicalSource the canonical column source 043 */ 044 public ColumnSourceWithReferences(String normalizedName, ColumnSource canonicalSource) { 045 this.normalizedName = normalizedName; 046 this.canonicalSource = canonicalSource; 047 this.references = new ArrayList<>(); 048 } 049 050 /** 051 * Add an original reference. 052 * 053 * @param ref the column reference to add 054 */ 055 public void addReference(ColumnReference ref) { 056 if (ref != null && !references.contains(ref)) { 057 references.add(ref); 058 } 059 } 060 061 /** 062 * Get all original references. 063 * 064 * @return unmodifiable list of all references in order of appearance 065 */ 066 public List<ColumnReference> getAllReferences() { 067 return Collections.unmodifiableList(references); 068 } 069 070 /** 071 * Get the number of references. 072 * 073 * @return reference count 074 */ 075 public int getReferenceCount() { 076 return references.size(); 077 } 078 079 /** 080 * Check if there are multiple references (indicating deduplication occurred). 081 * 082 * @return true if more than one reference exists 083 */ 084 public boolean hasMultipleReferences() { 085 return references.size() > 1; 086 } 087 088 /** 089 * Get the first (canonical) reference. 090 * 091 * @return the first reference, or null if none 092 */ 093 public ColumnReference getCanonicalReference() { 094 return references.isEmpty() ? null : references.get(0); 095 } 096 097 /** 098 * Get the normalized name. 099 * 100 * @return normalized column name 101 */ 102 public String getNormalizedName() { 103 return normalizedName; 104 } 105 106 /** 107 * Get the canonical column source. 108 * 109 * @return the column source for resolution 110 */ 111 public ColumnSource getCanonicalSource() { 112 return canonicalSource; 113 } 114 115 /** 116 * Get all original texts from all references. 117 * 118 * @return list of original text strings 119 */ 120 public List<String> getAllOriginalTexts() { 121 List<String> texts = new ArrayList<>(references.size()); 122 for (ColumnReference ref : references) { 123 if (ref.getOriginalText() != null) { 124 texts.add(ref.getOriginalText()); 125 } 126 } 127 return texts; 128 } 129 130 @Override 131 public String toString() { 132 StringBuilder sb = new StringBuilder(); 133 sb.append(normalizedName); 134 if (references.size() > 1) { 135 sb.append(" (").append(references.size()).append(" refs: "); 136 for (int i = 0; i < references.size(); i++) { 137 if (i > 0) sb.append(", "); 138 sb.append(references.get(i).getOriginalText()); 139 } 140 sb.append(")"); 141 } else if (references.size() == 1) { 142 ColumnReference ref = references.get(0); 143 if (!normalizedName.equals(ref.getOriginalText())) { 144 sb.append(" (from ").append(ref.getOriginalText()).append(")"); 145 } 146 } 147 return sb.toString(); 148 } 149}