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 com.intellij.psi.util.PsiTreeUtil;
022    import kotlin.Function0;
023    import kotlin.Function1;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor;
026    import org.jetbrains.jet.lang.psi.*;
027    import org.jetbrains.jet.lang.resolve.ImportPath;
028    import org.jetbrains.jet.lang.resolve.JetModuleUtil;
029    import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace;
030    import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyClassDescriptor;
031    import org.jetbrains.jet.lang.resolve.name.FqName;
032    import org.jetbrains.jet.lang.resolve.scopes.ChainedScope;
033    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
034    import org.jetbrains.jet.storage.MemoizedFunctionToNotNull;
035    import org.jetbrains.jet.storage.NotNullLazyValue;
036    
037    import java.util.Collection;
038    import java.util.List;
039    
040    public class ScopeProvider {
041        private final ResolveSession resolveSession;
042    
043        private final MemoizedFunctionToNotNull<JetFile, LazyImportScope> explicitImportScopes;
044    
045        private final NotNullLazyValue<JetScope> defaultImportsScope;
046    
047        public ScopeProvider(@NotNull ResolveSession resolveSession) {
048            this.resolveSession = resolveSession;
049    
050            this.explicitImportScopes = resolveSession.getStorageManager().createMemoizedFunction(new Function1<JetFile, LazyImportScope>() {
051                @Override
052                public LazyImportScope invoke(@NotNull JetFile file) {
053                    return createExplicitImportScope(file);
054                }
055            });
056    
057            this.defaultImportsScope = resolveSession.getStorageManager().createLazyValue(new Function0<JetScope>() {
058                @Override
059                public JetScope invoke() {
060                    return createScopeWithDefaultImports();
061                }
062            });
063        }
064    
065        private LazyImportScope createExplicitImportScope(@NotNull JetFile file) {
066            return LazyImportScope.createImportScopeForFile(
067                    resolveSession,
068                    getFilePackageDescriptor(file),
069                    file,
070                    resolveSession.getTrace(),
071                    "Lazy Imports Scope for file " + file.getName());
072        }
073    
074        @NotNull
075        public JetScope getFileScope(@NotNull JetFile file) {
076            return new ChainedScope(resolveSession.getPackageFragment(JetPsiUtil.getFQName(file)),
077                                    "File scope: " + file.getName(),
078                                    getFilePackageDescriptor(file).getMemberScope(),
079                                    JetModuleUtil.getSubpackagesOfRootScope(resolveSession.getModuleDescriptor()),
080                                    explicitImportScopes.invoke(file),
081                                    defaultImportsScope.invoke());
082        }
083    
084        @NotNull
085        public LazyImportScope getExplicitImportsScopeForFile(@NotNull JetFile file) {
086            return explicitImportScopes.invoke(file);
087        }
088    
089        private JetScope createScopeWithDefaultImports() {
090            PackageViewDescriptor rootPackage = resolveSession.getModuleDescriptor().getPackage(FqName.ROOT);
091            if (rootPackage == null) {
092                throw new IllegalStateException("Root package not found");
093            }
094    
095            JetImportsFactory importsFactory = resolveSession.getJetImportsFactory();
096            List<ImportPath> defaultImports = resolveSession.getModuleDescriptor().getDefaultImports();
097    
098            Collection<JetImportDirective> defaultImportDirectives = importsFactory.createImportDirectives(defaultImports);
099    
100            return new LazyImportScope(
101                    resolveSession,
102                    rootPackage,
103                    Lists.reverse(Lists.newArrayList(defaultImportDirectives)),
104                    TemporaryBindingTrace.create(resolveSession.getTrace(), "Transient trace for default imports lazy resolve"),
105                    "Lazy default imports scope",
106                    false);
107        }
108    
109        @NotNull
110        private PackageViewDescriptor getFilePackageDescriptor(JetFile file) {
111            JetPackageDirective directive = file.getPackageDirective();
112            if (directive == null) {
113                throw new IllegalArgumentException("Scripts are not supported: " + file.getName());
114            }
115    
116            FqName fqName = new FqName(directive.getQualifiedName());
117            PackageViewDescriptor packageDescriptor = resolveSession.getModuleDescriptor().getPackage(fqName);
118    
119            if (packageDescriptor == null) {
120                throw new IllegalStateException("Package not found: " + fqName + " maybe the file is not in scope of this resolve session: " + file.getName());
121            }
122    
123            return packageDescriptor;
124        }
125    
126        @NotNull
127        public JetScope getResolutionScopeForDeclaration(@NotNull PsiElement elementOfDeclaration) {
128            JetDeclaration jetDeclaration = PsiTreeUtil.getParentOfType(elementOfDeclaration, JetDeclaration.class, false);
129    
130            assert !(elementOfDeclaration instanceof JetDeclaration) || jetDeclaration == elementOfDeclaration :
131                    "For JetDeclaration element getParentOfType() should return itself.";
132    
133            JetDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(jetDeclaration, JetDeclaration.class);
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    }