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}