001package gudusoft.gsqlparser.dlineage.dataflow.model;
002
003import gudusoft.gsqlparser.dlineage.util.DlineageUtil;
004
005/**
006 * Matches the star links and model columns of a relation's sources against one star link of its target, the link a
007 * star expansion is publishing. Obtained from {@link Column#starLinkMatcher(int)} once per expansion, it decides the
008 * matches the analyzer used to decide on the target link's folded display, with the same comparisons: a target link
009 * matches a source link or model column under {@link DlineageUtil#sameColumnNormalName}, and is named by a model
010 * column whose folded name equals its own folded display.
011 *
012 * <p>Internal contract of the lineage analyzer.
013 */
014public final class StarLinkMatcher {
015
016        private final String foldedDisplay;
017
018        private final StarLinkKey key;
019
020        StarLinkMatcher(String foldedDisplay, StarLinkKey key) {
021                this.foldedDisplay = foldedDisplay;
022                this.key = key;
023        }
024
025        /** Whether the star link at {@code ordinal} of {@code source} is this link's column. */
026        public boolean matches(Column source, int ordinal) {
027                if (StarLinkStore.IDENTITY) {
028                        Object other = source.starLinkKey(ordinal);
029                        if (other instanceof StarLinkKey && StarLinkStore.identifiers(key, (StarLinkKey) other)) {
030                                return decided("matches", key.equals(other), source.starLinkDisplay(ordinal));
031                        }
032                }
033                return DlineageUtil.sameColumnNormalName(foldedDisplay, source.starLinkDisplay(ordinal));
034        }
035
036        /** Whether {@code model} is this link's column, compared as the analyzer compared a model name. */
037        public boolean matchesColumn(Column model) {
038                if (StarLinkStore.IDENTITY) {
039                        StarLinkKey other = StarLinkStore.modelKey(model);
040                        if (StarLinkStore.identifiers(key, other)) {
041                                return decided("matchesColumn", key.equals(other), model.getName());
042                        }
043                }
044                return DlineageUtil.sameColumnNormalName(foldedDisplay, StarLinkStore.legacyFold(model.getName()));
045        }
046
047        /** Whether {@code model} carries exactly this link's name. */
048        public boolean isNamedBy(Column model) {
049                if (StarLinkStore.IDENTITY) {
050                        StarLinkKey other = StarLinkStore.modelKey(model);
051                        if (StarLinkStore.identifiers(key, other)) {
052                                return decided("isNamedBy", key.equals(other), model.getName());
053                        }
054                }
055                return StarLinkStore.legacyFold(model.getName()).equals(foldedDisplay);
056        }
057
058        /**
059         * Whether the star links of {@code source} hold another identity's column under this link's folded display: a
060         * column the grouping by display would take for this link's, and which, grouped by identity, is a different
061         * column (PostgreSQL {@code "Amount"} where this link is {@code amount}). A star that holds it is not inferred to
062         * hold this link's column as well; false while links are grouped by display, and for a label.
063         */
064        public boolean isDisplacedIn(Column source) {
065                if (!StarLinkStore.IDENTITY || key == null || !key.isIdentifier() || source == null) {
066                        return false;
067                }
068                for (int k = 0; k < source.starLinkCount(); k++) {
069                        Object other = source.starLinkKey(k);
070                        if (other instanceof StarLinkKey && ((StarLinkKey) other).isIdentifier() && !key.equals(other)
071                                        && DlineageUtil.sameColumnNormalName(foldedDisplay, source.starLinkDisplay(k))) {
072                                return true;
073                        }
074                }
075                return false;
076        }
077
078        /**
079         * The key this link is remembered by among the target links that already have an explicit source. A source link
080         * is looked up in that set by {@link Column#starLinkKey(int)}.
081         */
082        public Object key() {
083                return StarLinkStore.IDENTITY && key != null ? key : foldedDisplay;
084        }
085
086        String foldedDisplay() {
087                return foldedDisplay;
088        }
089
090        StarLinkKey identity() {
091                return key;
092        }
093
094        /** Reports an identity answer that the legacy comparison disputes; the legacy comparison runs for the census only. */
095        private boolean decided(String predicate, boolean identity, String other) {
096                if (StarLinkStore.censusOn()) {
097                        boolean answer = "matches".equals(predicate) ? DlineageUtil.sameColumnNormalName(foldedDisplay, other)
098                                        : "isNamedBy".equals(predicate) ? other != null && foldedDisplay.equals(StarLinkStore.legacyFold(other))
099                                        : DlineageUtil.sameColumnNormalName(foldedDisplay, StarLinkStore.legacyFold(other));
100                        if (identity != answer) {
101                                StarLinkStore.census("PREDICATE_DIFF " + predicate, foldedDisplay + " ~ " + other);
102                        }
103                }
104                return identity;
105        }
106}