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}