001package gudusoft.gsqlparser.resolver2.scope;
002
003import gudusoft.gsqlparser.EDbVendor;
004import gudusoft.gsqlparser.resolver2.ScopeType;
005import gudusoft.gsqlparser.resolver2.matcher.INameMatcher;
006import gudusoft.gsqlparser.resolver2.model.ScopeChild;
007import gudusoft.gsqlparser.resolver2.namespace.INamespace;
008import gudusoft.gsqlparser.resolver2.namespace.OraclePackageNamespace;
009import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
010import gudusoft.gsqlparser.stmt.oracle.TPlsqlCreatePackage;
011import gudusoft.gsqlparser.util.SQLUtil;
012
013import java.util.*;
014
015/**
016 * Scope for Oracle PL/SQL package bodies.
017 *
018 * <p>Provides access to package-level declarations for all nested
019 * procedures, functions, and blocks within the package body.
020 *
021 * <p>Name resolution priority within package body:
022 * <ol>
023 *   <li>Local variables (PlsqlBlockScope)</li>
024 *   <li>Package members (OraclePackageNamespace)</li>
025 *   <li>SQL table columns (SelectScope/FromScope)</li>
026 *   <li>External packages (via GlobalScope)</li>
027 * </ol>
028 */
029public class OraclePackageScope extends AbstractScope {
030
031    /** Package namespace containing package-level declarations */
032    private final OraclePackageNamespace packageNamespace;
033
034    /** Package name for qualified reference resolution */
035    private final String packageName;
036
037    /** Vendor from the package AST node (dameng builds PL/SQL scopes too) */
038    private final EDbVendor vendor;
039
040    /** Child scopes */
041    private final List<ScopeChild> children = new ArrayList<>();
042
043    /** Child ordinal counter */
044    private int childOrdinal = 0;
045
046    /**
047     * Create a new Oracle package scope.
048     *
049     * @param parent The parent scope
050     * @param packageStmt The package AST node
051     * @param packageNamespace The namespace containing package members
052     */
053    public OraclePackageScope(IScope parent, TPlsqlCreatePackage packageStmt,
054                               OraclePackageNamespace packageNamespace) {
055        super(parent, packageStmt, ScopeType.PLSQL_BLOCK);  // Reuse PLSQL_BLOCK type
056        this.packageNamespace = packageNamespace;
057        this.packageName = packageNamespace != null ? packageNamespace.getPackageName() : null;
058        this.vendor = (packageStmt != null && packageStmt.dbvendor != null)
059                ? packageStmt.dbvendor : EDbVendor.dbvoracle;
060    }
061
062    /**
063     * Get the package namespace.
064     *
065     * @return The OraclePackageNamespace containing package-level declarations
066     */
067    public OraclePackageNamespace getPackageNamespace() {
068        return packageNamespace;
069    }
070
071    /**
072     * Get the package name.
073     *
074     * @return The package name
075     */
076    public String getPackageName() {
077        return packageName;
078    }
079
080    @Override
081    public void addChild(INamespace namespace, String alias, boolean nullable) {
082        children.add(new ScopeChild(childOrdinal++, alias, namespace, nullable));
083    }
084
085    @Override
086    public List<ScopeChild> getChildren() {
087        return children;
088    }
089
090    @Override
091    public INamespace resolveTable(String tableName) {
092        // Check if this is a reference to our package
093        if (packageName != null && SQLUtil.sameName(vendor, ESQLDataObjectType.dotOraclePackage, packageName, tableName)) {
094            return packageNamespace;
095        }
096        // Delegate to parent for other table/package lookups
097        return parent.resolveTable(tableName);
098    }
099
100    @Override
101    public void resolve(List<String> names, INameMatcher matcher,
102                        boolean deep, IResolved resolved) {
103        // Handle unqualified references - check if it's a package member
104        if (names.size() == 1 && packageNamespace != null) {
105            String name = names.get(0);
106            if (packageNamespace.hasMember(name)) {
107                resolved.found(packageNamespace, false, this, null, names);
108                return;
109            }
110        }
111
112        // Handle qualified references like pkg.member
113        if (names.size() >= 2 && packageNamespace != null) {
114            String firstName = names.get(0);
115            if (matcher.matches(firstName, packageName)) {
116                String memberName = names.get(1);
117                if (packageNamespace.hasMember(memberName)) {
118                    resolved.found(packageNamespace, false, this, null,
119                        Collections.singletonList(memberName));
120                    return;
121                }
122            }
123        }
124
125        // Delegate to parent (GlobalScope or outer scope)
126        parent.resolve(names, matcher, deep, resolved);
127    }
128
129    @Override
130    public List<INamespace> getVisibleNamespaces() {
131        List<INamespace> namespaces = new ArrayList<>();
132        if (packageNamespace != null) {
133            namespaces.add(packageNamespace);
134        }
135        namespaces.addAll(parent.getVisibleNamespaces());
136        return namespaces;
137    }
138
139    @Override
140    public String toString() {
141        return "OraclePackageScope(" + packageName +
142               ", members=" + (packageNamespace != null ? packageNamespace.getMembers().size() : 0) + ")";
143    }
144}