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