001package gudusoft.gsqlparser.ir.bound;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.LinkedHashMap;
006import java.util.List;
007import java.util.Map;
008
009/**
010 * Aggregation root for the Bound IR phase output.
011 * <p>
012 * Contains all scopes, symbols, and references produced by the bound IR builder.
013 */
014public class BoundProgram {
015
016    /** All scopes in the program (root scope first). */
017    private final List<BoundScope> scopes;
018
019    /** Global routine symbol table indexed by routine ID (deterministic iteration order). */
020    private final Map<String, BoundRoutineSymbol> routineIndex;
021
022    /** Multi-value index: uppercase routine name → list of routines with that name. */
023    private final Map<String, List<BoundRoutineSymbol>> routinesByName;
024
025    /** All object references found during binding. */
026    private final List<BoundObjectRef> allObjectRefs;
027
028    /** All column references found during binding. */
029    private final List<BoundColumnRef> allColumnRefs;
030
031    /** All routine references found during binding. */
032    private final List<BoundRoutineRef> allRoutineRefs;
033
034    public BoundProgram() {
035        this.scopes = new ArrayList<BoundScope>();
036        this.routineIndex = new LinkedHashMap<String, BoundRoutineSymbol>();
037        this.routinesByName = new LinkedHashMap<String, List<BoundRoutineSymbol>>();
038        this.allObjectRefs = new ArrayList<BoundObjectRef>();
039        this.allColumnRefs = new ArrayList<BoundColumnRef>();
040        this.allRoutineRefs = new ArrayList<BoundRoutineRef>();
041    }
042
043    public BoundProgram(List<BoundScope> scopes,
044                        Map<String, BoundRoutineSymbol> routineIndex,
045                        List<BoundObjectRef> allObjectRefs,
046                        List<BoundColumnRef> allColumnRefs,
047                        List<BoundRoutineRef> allRoutineRefs) {
048        this.scopes = scopes != null ? scopes : new ArrayList<BoundScope>();
049        this.routineIndex = routineIndex != null
050                ? new LinkedHashMap<String, BoundRoutineSymbol>(routineIndex)
051                : new LinkedHashMap<String, BoundRoutineSymbol>();
052        this.routinesByName = new LinkedHashMap<String, List<BoundRoutineSymbol>>();
053        // Rebuild the by-name index from routineIndex
054        for (BoundRoutineSymbol sym : this.routineIndex.values()) {
055            addToNameIndex(sym);
056        }
057        this.allObjectRefs = allObjectRefs != null ? allObjectRefs : new ArrayList<BoundObjectRef>();
058        this.allColumnRefs = allColumnRefs != null ? allColumnRefs : new ArrayList<BoundColumnRef>();
059        this.allRoutineRefs = allRoutineRefs != null ? allRoutineRefs : new ArrayList<BoundRoutineRef>();
060    }
061
062    public List<BoundScope> getScopes() { return scopes; }
063    public Map<String, BoundRoutineSymbol> getRoutineIndex() {
064        return Collections.unmodifiableMap(routineIndex);
065    }
066    public Map<String, List<BoundRoutineSymbol>> getRoutinesByName() {
067        return Collections.unmodifiableMap(routinesByName);
068    }
069    public List<BoundObjectRef> getAllObjectRefs() { return allObjectRefs; }
070    public List<BoundColumnRef> getAllColumnRefs() { return allColumnRefs; }
071    public List<BoundRoutineRef> getAllRoutineRefs() { return allRoutineRefs; }
072
073    public void addScope(BoundScope scope) { scopes.add(scope); }
074
075    public void registerRoutine(BoundRoutineSymbol routine) {
076        routineIndex.put(routine.getRoutineId(), routine);
077        addToNameIndex(routine);
078    }
079
080    private void addToNameIndex(BoundRoutineSymbol routine) {
081        String nameKey = routine.getRoutineName().toUpperCase();
082        List<BoundRoutineSymbol> list = routinesByName.get(nameKey);
083        if (list == null) {
084            list = new ArrayList<BoundRoutineSymbol>();
085            routinesByName.put(nameKey, list);
086        }
087        list.add(routine);
088    }
089
090    public void addObjectRef(BoundObjectRef ref) { allObjectRefs.add(ref); }
091    public void addColumnRef(BoundColumnRef ref) { allColumnRefs.add(ref); }
092    public void addRoutineRef(BoundRoutineRef ref) { allRoutineRefs.add(ref); }
093
094    /**
095     * Looks up a routine by its ID.
096     */
097    public BoundRoutineSymbol lookupRoutine(String routineId) {
098        return routineIndex.get(routineId);
099    }
100
101    /**
102     * Replaces the entire routine ref list (used after post-resolution pass).
103     */
104    public void replaceRoutineRefs(List<BoundRoutineRef> resolved) {
105        allRoutineRefs.clear();
106        allRoutineRefs.addAll(resolved);
107    }
108
109    @Override
110    public String toString() {
111        return "BoundProgram{scopes=" + scopes.size()
112                + ", routines=" + routineIndex.size()
113                + ", objectRefs=" + allObjectRefs.size()
114                + ", columnRefs=" + allColumnRefs.size()
115                + ", routineRefs=" + allRoutineRefs.size() + "}";
116    }
117}