001package gudusoft.gsqlparser.dlineage.routine;
002
003import java.util.Collection;
004import java.util.LinkedHashMap;
005import java.util.Map;
006
007/**
008 * The lineage model's name-keyed routine index on {@link RoutineAliasKey}s, with the legacy index's precedence.
009 *
010 * <p>Entries keep insertion order, and re-putting a key replaces its value in place, as the legacy
011 * {@code LinkedHashMap} did, so the scans below meet candidates in the same order as the legacy ones.</p>
012 *
013 * <p>{@link #lookup} is the legacy routine lookup:</p>
014 * <ol>
015 * <li>Inside a package, a one-level name is looked up as a member of it. A qualified name is looked up as written.
016 * (The legacy prefixed the package unless the name's text happened to begin with the package's.)</li>
017 * <li>The exact key.</li>
018 * <li>A name without an alias suffix: the first entry with a suffix whose name is the same, or whose routine level is
019 * the same when exactly one of the two names is qualified.</li>
020 * <li>A name with a suffix, or with a {@code (} in a level as written ({@code [P(]}; the legacy chose this branch by a
021 * {@code (} anywhere in its text): the first entry whose name is the same and whose suffix begins with the lookup's
022 * (any suffix, when the lookup has none), or, when exactly one of the two names is qualified, whose routine level is
023 * the same and whose suffix is the same stored text (or neither has one).</li>
024 * </ol>
025 *
026 * <p>"Qualified" means more than one level. The legacy looked for a {@code .} anywhere in the text, including the
027 * suffix's argument types and quoted names; this index asks the levels.</p>
028 *
029 * @param <V> what a key names: a routine model, its definition, a package, a set of function results
030 */
031public final class RoutineAliasRegistry<V> {
032
033    private final LinkedHashMap<RoutineAliasKey, V> entries = new LinkedHashMap<RoutineAliasKey, V>();
034
035    public void put(RoutineAliasKey key, V value) {
036        if (key != null) {
037            entries.put(key, value);
038        }
039    }
040
041    /** The value under exactly this key, or null. */
042    public V get(RoutineAliasKey key) {
043        return key == null ? null : entries.get(key);
044    }
045
046    public boolean containsKey(RoutineAliasKey key) {
047        return key != null && entries.containsKey(key);
048    }
049
050    /** The legacy routine lookup, {@code enclosingPackage} being the current package's name, or null. */
051    public V lookup(RoutineAliasKey key, RoutineAliasKey enclosingPackage) {
052        if (key == null) {
053            return null;
054        }
055        if (enclosingPackage != null && !key.isQualified()) {
056            key = key.qualifiedBy(enclosingPackage);
057        }
058        V exact = entries.get(key);
059        if (exact != null) {
060            return exact;
061        }
062        boolean suffixed = key.looksSuffixed();
063        for (Map.Entry<RoutineAliasKey, V> entry : entries.entrySet()) {
064            RoutineAliasKey candidate = entry.getKey();
065            boolean leafShape = candidate.isQualified() != key.isQualified();
066            if (!suffixed) {
067                if (candidate.hasSignature() && (candidate.sameName(key) || (leafShape && candidate.sameRoutine(key)))) {
068                    return entry.getValue();
069                }
070            } else {
071                // The legacy key text began with the lookup's: the same name, and the suffix begins with the lookup's.
072                if (candidate.sameName(key) && (!key.hasSignature() || candidate.signatureStartsWith(key))) {
073                    return entry.getValue();
074                }
075                // The legacy whole-leaf comparison: the same routine and the same stored suffix, or neither has one.
076                if (leafShape && candidate.sameRoutine(key) && candidate.sameStoredSignature(key)) {
077                    return entry.getValue();
078                }
079            }
080        }
081        return null;
082    }
083
084    /**
085     * The legacy function-result lookup: the exact name, else the first entry whose routine level is the same when
086     * exactly one of the two names is qualified.
087     */
088    public V lookupName(RoutineAliasKey key) {
089        if (key == null) {
090            return null;
091        }
092        V exact = entries.get(key);
093        if (exact != null) {
094            return exact;
095        }
096        for (Map.Entry<RoutineAliasKey, V> entry : entries.entrySet()) {
097            RoutineAliasKey candidate = entry.getKey();
098            if (candidate.isQualified() != key.isQualified() && candidate.sameRoutine(key)) {
099                return entry.getValue();
100            }
101        }
102        return null;
103    }
104
105    public Collection<V> values() {
106        return entries.values();
107    }
108
109    public int size() {
110        return entries.size();
111    }
112
113    public void clear() {
114        entries.clear();
115    }
116}