001package gudusoft.gsqlparser.dlineage.dataflow.model; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.HashMap; 006import java.util.List; 007import java.util.Map; 008 009import gudusoft.gsqlparser.sqlenv.CanonKey; 010 011/** 012 * The columns of a result set or table, found by the star link that names them: for each name, the first column in 013 * list order that carries it. A column is found by its folded name as the link's display, as the analyzer's lookup 014 * map did; while star links are grouped by identity, a link that has an identifier finds the column of that 015 * identifier, and falls back to the display only among columns that have none. 016 * 017 * <p>Internal contract of the lineage analyzer. 018 */ 019public final class StarLinkColumnIndex<C extends Column> { 020 021 private final Map<String, C> byFold; 022 023 private final Map<CanonKey, C> byIdentifier; 024 025 /** While links are grouped by identity: the first column of each folded name, then of each further identity. */ 026 private final List<C> distinct; 027 028 public StarLinkColumnIndex(List<C> columns) { 029 byFold = new HashMap<String, C>(columns.size() * 2); 030 byIdentifier = StarLinkStore.IDENTITY ? new HashMap<CanonKey, C>(columns.size() * 2) : null; 031 List<C> further = byIdentifier == null ? null : new ArrayList<C>(); 032 for (C column : columns) { 033 String key = StarLinkStore.legacyFold(column.getName()); 034 boolean first = !byFold.containsKey(key); 035 if (first) { 036 byFold.put(key, column); 037 } 038 if (byIdentifier != null) { 039 CanonKey identifier = StarLinkStore.modelKey(column).canonKey(); 040 if (identifier != null && !byIdentifier.containsKey(identifier)) { 041 byIdentifier.put(identifier, column); 042 if (!first) { 043 further.add(column); 044 } 045 } 046 } 047 } 048 if (byIdentifier == null) { 049 distinct = null; 050 } else { 051 distinct = new ArrayList<C>(byFold.values()); 052 distinct.addAll(further); 053 } 054 } 055 056 /** The first column the star link at {@code ordinal} of {@code link} names, or null. */ 057 public C get(Column link, int ordinal) { 058 C byName = byFold.get(link.starLinkDisplay(ordinal)); 059 if (byIdentifier == null) { 060 return byName; 061 } 062 Object key = link.starLinkKey(ordinal); 063 CanonKey identifier = key instanceof StarLinkKey ? ((StarLinkKey) key).canonKey() : null; 064 C found; 065 if (identifier == null) { 066 found = byName; 067 } else { 068 found = byIdentifier.get(identifier); 069 if (found == null && byName != null && StarLinkStore.modelKey(byName).canonKey() == null) { 070 found = byName; 071 } 072 } 073 if (found != byName) { 074 StarLinkStore.census("LOOKUP_DIFF column", link.starLinkDisplay(ordinal) + " " 075 + (byName == null ? null : byName.getName()) + " -> " + (found == null ? null : found.getName())); 076 } 077 return found; 078 } 079 080 /** 081 * The first column of each folded name, in the order the analyzer's lookup map iterated them; while links are 082 * grouped by identity, followed by the first column of each identity that shares its folded name with another. 083 */ 084 public Collection<C> values() { 085 return distinct == null ? byFold.values() : distinct; 086 } 087}