001    /*
002     * Copyright 2010-2014 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.lang.resolve.lazy;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.psi.PsiElement;
021    import kotlin.Function0;
022    import kotlin.Function1;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor;
025    import org.jetbrains.jet.lang.psi.*;
026    import org.jetbrains.jet.lang.resolve.ImportPath;
027    import org.jetbrains.jet.lang.resolve.JetModuleUtil;
028    import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace;
029    import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyClassDescriptor;
030    import org.jetbrains.jet.lang.resolve.name.FqName;
031    import org.jetbrains.jet.lang.resolve.scopes.ChainedScope;
032    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
033    import org.jetbrains.jet.storage.MemoizedFunctionToNotNull;
034    import org.jetbrains.jet.storage.NotNullLazyValue;
035    
036    import java.util.Collection;
037    import java.util.List;
038    
039    public class ScopeProvider {
040        private final ResolveSession resolveSession;
041    
042        private final MemoizedFunctionToNotNull<JetFile, LazyImportScope> explicitImportScopes;
043    
044        private final NotNullLazyValue<JetScope> defaultImportsScope;
045    
046        public ScopeProvider(@NotNull ResolveSession resolveSession) {
047            this.resolveSession = resolveSession;
048    
049            this.explicitImportScopes = resolveSession.getStorageManager().createMemoizedFunction(new Function1<JetFile, LazyImportScope>() {
050                @Override
051                public LazyImportScope invoke(@NotNull JetFile file) {
052                    return createExplicitImportScope(file);
053                }
054            });
055    
056            this.defaultImportsScope = resolveSession.getStorageManager().createLazyValue(new Function0<JetScope>() {
057                @Override
058                public JetScope invoke() {
059                    return createScopeWithDefaultImports();
060                }
061            });
062        }
063    
064        private LazyImportScope createExplicitImportScope(@NotNull JetFile file) {
065            return LazyImportScope.createImportScopeForFile(
066                    resolveSession,
067                    getFilePackageDescriptor(file),
068                    file,
069                    resolveSession.getTrace(),
070                    "Lazy Imports Scope for file " + file.getName());
071        }
072    
073        @NotNull
074        public JetScope getFileScope(@NotNull JetFile file) {
075            return new ChainedScope(resolveSession.getPackageFragment(file.getPackageFqName()),
076                                    "File scope: " + file.getName(),
077                                    getFilePackageDescriptor(file).getMemberScope(),
078                                    JetModuleUtil.getSubpackagesOfRootScope(resolveSession.getModuleDescriptor()),
079                                    explicitImportScopes.invoke(file),
080                                    defaultImportsScope.invoke());
081        }
082    
083        @NotNull
084        public LazyImportScope getExplicitImportsScopeForFile(@NotNull JetFile file) {
085            return explicitImportScopes.invoke(file);
086        }
087    
088        private JetScope createScopeWithDefaultImports() {
089            PackageViewDescriptor rootPackage = resolveSession.getModuleDescriptor().getPackage(FqName.ROOT);
090            if (rootPackage == null) {
091                throw new IllegalStateException("Root package not found");
092            }
093    
094            JetImportsFactory importsFactory = resolveSession.getJetImportsFactory();
095            List<ImportPath> defaultImports = resolveSession.getModuleDescriptor().getDefaultImports();
096    
097            Collection<JetImportDirective> defaultImportDirectives = importsFactory.createImportDirectives(defaultImports);
098    
099            return new LazyImportScope(
100                    resolveSession,
101                    rootPackage,
102                    Lists.reverse(Lists.newArrayList(defaultImportDirectives)),
103                    TemporaryBindingTrace.create(resolveSession.getTrace(), "Transient trace for default imports lazy resolve"),
104                    "Lazy default imports scope",
105                    false);
106        }
107    
108        @NotNull
109        private PackageViewDescriptor getFilePackageDescriptor(JetFile file) {
110            FqName fqName = file.getPackageFqName();
111            PackageViewDescriptor packageDescriptor = resolveSession.getModuleDescriptor().getPackage(fqName);
112    
113            if (packageDescriptor == null) {
114                throw new IllegalStateException("Package not found: " + fqName + " maybe the file is not in scope of this resolve session: " + file.getName());
115            }
116    
117            return packageDescriptor;
118        }
119    
120        @NotNull
121        public JetScope getResolutionScopeForDeclaration(@NotNull PsiElement elementOfDeclaration) {
122            JetDeclaration jetDeclaration = JetStubbedPsiUtil.getPsiOrStubParent(elementOfDeclaration, JetDeclaration.class, false);
123    
124            assert !(elementOfDeclaration instanceof JetDeclaration) || jetDeclaration == elementOfDeclaration :
125                    "For JetDeclaration element getParentOfType() should return itself.";
126            assert jetDeclaration != null : "Should be contained inside declaration.";
127    
128            JetDeclaration parentDeclaration = JetStubbedPsiUtil.getContainingDeclaration(jetDeclaration);
129    
130            if (jetDeclaration instanceof JetPropertyAccessor) {
131                parentDeclaration = JetStubbedPsiUtil.getContainingDeclaration(parentDeclaration, JetDeclaration.class);
132            }
133    
134            if (parentDeclaration == null) {
135                return getFileScope((JetFile) elementOfDeclaration.getContainingFile());
136            }
137    
138            if (parentDeclaration instanceof JetClassOrObject) {
139                JetClassOrObject classOrObject = (JetClassOrObject) parentDeclaration;
140                LazyClassDescriptor classDescriptor = (LazyClassDescriptor) resolveSession.getClassDescriptor(classOrObject);
141                if (jetDeclaration instanceof JetClassInitializer || jetDeclaration instanceof JetProperty) {
142                    return classDescriptor.getScopeForInitializerResolution();
143                }
144                return classDescriptor.getScopeForMemberDeclarationResolution();
145            }
146    
147            if (parentDeclaration instanceof JetClassObject) {
148                assert jetDeclaration instanceof JetObjectDeclaration : "Should be situation for getting scope for object in class [object {...}]";
149    
150                JetClassObject classObject = (JetClassObject) parentDeclaration;
151                LazyClassDescriptor classObjectDescriptor =
152                        (LazyClassDescriptor) resolveSession.getClassObjectDescriptor(classObject).getContainingDeclaration();
153    
154                return classObjectDescriptor.getScopeForMemberDeclarationResolution();
155            }
156    
157            throw new IllegalStateException("Don't call this method for local declarations: " + jetDeclaration + " " + jetDeclaration.getText());
158        }
159    }