001    /*
002     * Copyright 2010-2013 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.openapi.util.Computable;
021    import com.intellij.psi.PsiElement;
022    import com.intellij.psi.util.PsiTreeUtil;
023    import com.intellij.util.Function;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
026    import org.jetbrains.jet.lang.psi.*;
027    import org.jetbrains.jet.lang.resolve.ImportPath;
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.lazy.storage.MemoizedFunctionToNotNull;
031    import org.jetbrains.jet.lang.resolve.lazy.storage.NotNullLazyValue;
032    import org.jetbrains.jet.lang.resolve.name.FqName;
033    import org.jetbrains.jet.lang.resolve.scopes.ChainedScope;
034    import org.jetbrains.jet.lang.resolve.scopes.InnerClassesScopeWrapper;
035    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
036    
037    import java.util.Collection;
038    import java.util.List;
039    
040    import static org.jetbrains.jet.lang.resolve.lazy.storage.StorageManager.ReferenceKind.WEAK;
041    
042    public class ScopeProvider {
043        private final ResolveSession resolveSession;
044    
045        private final MemoizedFunctionToNotNull<JetFile, JetScope> fileScopes;
046    
047        private final NotNullLazyValue<JetScope> defaultImportsScope;
048    
049        public ScopeProvider(@NotNull ResolveSession resolveSession) {
050            this.resolveSession = resolveSession;
051    
052            this.fileScopes = resolveSession.getStorageManager().createMemoizedFunction(new Function<JetFile, JetScope>() {
053                @Override
054                public JetScope fun(@NotNull JetFile file) {
055                    return createFileScope(file);
056                }
057            }, WEAK);
058    
059            this.defaultImportsScope = resolveSession.getStorageManager().createLazyValue(new Computable<JetScope>() {
060                @Override
061                public JetScope compute() {
062                    return createScopeWithDefaultImports();
063                }
064            });
065        }
066    
067        @NotNull
068        public JetScope getFileScope(JetFile file) {
069            return fileScopes.fun(file);
070        }
071    
072        private JetScope createFileScope(JetFile file) {
073            NamespaceDescriptor rootPackageDescriptor = resolveSession.getPackageDescriptorByFqName(FqName.ROOT);
074            if (rootPackageDescriptor == null) {
075                throw new IllegalStateException("Root package not found");
076            }
077    
078            NamespaceDescriptor packageDescriptor = getFilePackageDescriptor(file);
079    
080            JetScope importsScope = LazyImportScope.createImportScopeForFile(
081                    resolveSession,
082                    packageDescriptor,
083                    file,
084                    resolveSession.getTrace(),
085                    "Lazy Imports Scope for file " + file.getName());
086    
087            return new ChainedScope(packageDescriptor,
088                                    "File scope: " + file.getName(),
089                                    packageDescriptor.getMemberScope(),
090                                    rootPackageDescriptor.getMemberScope(),
091                                    importsScope,
092                                    defaultImportsScope.compute());
093        }
094    
095        private JetScope createScopeWithDefaultImports() {
096            NamespaceDescriptor rootPackageDescriptor = resolveSession.getPackageDescriptorByFqName(FqName.ROOT);
097            if (rootPackageDescriptor == null) {
098                throw new IllegalStateException("Root package not found");
099            }
100    
101            JetImportsFactory importsFactory = resolveSession.getInjector().getJetImportsFactory();
102            List<ImportPath> defaultImports = resolveSession.getRootModuleDescriptor().getDefaultImports();
103    
104            Collection<JetImportDirective> defaultImportDirectives = importsFactory.createImportDirectives(defaultImports);
105    
106            return new LazyImportScope(
107                    resolveSession,
108                    rootPackageDescriptor,
109                    Lists.reverse(Lists.newArrayList(defaultImportDirectives)),
110                    TemporaryBindingTrace.create(resolveSession.getTrace(), "Transient trace for default imports lazy resolve"),
111                    "Lazy default imports scope");
112        }
113    
114        @NotNull
115        private NamespaceDescriptor getFilePackageDescriptor(JetFile file) {
116            JetNamespaceHeader header = file.getNamespaceHeader();
117            if (header == null) {
118                throw new IllegalArgumentException("Scripts are not supported: " + file.getName());
119            }
120    
121            FqName fqName = new FqName(header.getQualifiedName());
122            NamespaceDescriptor packageDescriptor = resolveSession.getPackageDescriptorByFqName(fqName);
123    
124            if (packageDescriptor == null) {
125                throw new IllegalStateException("Package not found: " + fqName + " maybe the file is not in scope of this resolve session: " + file.getName());
126            }
127    
128            return packageDescriptor;
129        }
130    
131        @NotNull
132        public JetScope getResolutionScopeForDeclaration(@NotNull PsiElement elementOfDeclaration) {
133            JetDeclaration jetDeclaration = PsiTreeUtil.getParentOfType(elementOfDeclaration, JetDeclaration.class, false);
134    
135            assert !(elementOfDeclaration instanceof JetDeclaration) || jetDeclaration == elementOfDeclaration :
136                    "For JetDeclaration element getParentOfType() should return itself.";
137    
138            JetDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(jetDeclaration, JetDeclaration.class);
139            if (parentDeclaration == null) {
140                return getFileScope((JetFile) elementOfDeclaration.getContainingFile());
141            }
142    
143            assert jetDeclaration != null : "Can't happen because of getParentOfType(null, ?) == null";
144    
145            if (parentDeclaration instanceof JetClassOrObject) {
146                JetClassOrObject classOrObject = (JetClassOrObject) parentDeclaration;
147                LazyClassDescriptor classDescriptor = (LazyClassDescriptor) resolveSession.getClassDescriptor(classOrObject);
148                if (jetDeclaration instanceof JetClassInitializer || jetDeclaration instanceof JetProperty) {
149                    return classDescriptor.getScopeForPropertyInitializerResolution();
150                }
151                if (jetDeclaration instanceof JetEnumEntry) {
152                    LazyClassDescriptor descriptor = (LazyClassDescriptor) classDescriptor.getClassObjectDescriptor();
153                    assert descriptor != null : "There should be class object descriptor for enum class " + parentDeclaration.getText() +
154                                                " on entry " + jetDeclaration.getText();
155    
156                    return descriptor.getScopeForMemberDeclarationResolution();
157                }
158                return classDescriptor.getScopeForMemberDeclarationResolution();
159            }
160    
161            if (parentDeclaration instanceof JetClassObject) {
162                assert jetDeclaration instanceof JetObjectDeclaration : "Should be situation for getting scope for object in class [object {...}]";
163    
164                JetClassObject classObject = (JetClassObject) parentDeclaration;
165                LazyClassDescriptor classObjectDescriptor =
166                        (LazyClassDescriptor) resolveSession.getClassObjectDescriptor(classObject).getContainingDeclaration();
167    
168                return classObjectDescriptor.getScopeForMemberDeclarationResolution();
169            }
170    
171            throw new IllegalStateException("Don't call this method for local declarations: " + jetDeclaration + " " + jetDeclaration.getText());
172        }
173    }