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
017package org.jetbrains.jet.lang.resolve.lazy;
018
019import com.google.common.collect.Lists;
020import com.intellij.openapi.util.Computable;
021import com.intellij.psi.PsiElement;
022import com.intellij.psi.util.PsiTreeUtil;
023import com.intellij.util.Function;
024import org.jetbrains.annotations.NotNull;
025import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
026import org.jetbrains.jet.lang.psi.*;
027import org.jetbrains.jet.lang.resolve.ImportPath;
028import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace;
029import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyClassDescriptor;
030import org.jetbrains.jet.lang.resolve.lazy.storage.MemoizedFunctionToNotNull;
031import org.jetbrains.jet.lang.resolve.lazy.storage.NotNullLazyValue;
032import org.jetbrains.jet.lang.resolve.name.FqName;
033import org.jetbrains.jet.lang.resolve.scopes.ChainedScope;
034import org.jetbrains.jet.lang.resolve.scopes.InnerClassesScopeWrapper;
035import org.jetbrains.jet.lang.resolve.scopes.JetScope;
036
037import java.util.Collection;
038import java.util.List;
039
040import static org.jetbrains.jet.lang.resolve.lazy.storage.StorageManager.ReferenceKind.WEAK;
041
042public 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}