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