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 javax.inject.Inject;
037    import java.util.ArrayList;
038    import java.util.Collection;
039    import java.util.Collections;
040    import java.util.List;
041    
042    public class ScopeProvider {
043        public static class AdditionalFileScopeProvider {
044            @NotNull
045            public List<JetScope> scopes(@NotNull JetFile file) {
046                return Collections.emptyList();
047            }
048        }
049    
050        private final ResolveSession resolveSession;
051    
052        private final MemoizedFunctionToNotNull<JetFile, LazyImportScope> explicitImportScopes;
053    
054        private final NotNullLazyValue<JetScope> defaultImportsScope;
055    
056        @SuppressWarnings("ConstantConditions") @NotNull
057        private AdditionalFileScopeProvider additionalFileScopeProvider = null;
058    
059        @Inject
060        public void setAdditionalFileScopesProvider(@NotNull AdditionalFileScopeProvider additionalFileScopeProvider) {
061            this.additionalFileScopeProvider = additionalFileScopeProvider;
062        }
063    
064        public ScopeProvider(@NotNull ResolveSession resolveSession) {
065            this.resolveSession = resolveSession;
066    
067            this.explicitImportScopes = resolveSession.getStorageManager().createMemoizedFunction(new Function1<JetFile, LazyImportScope>() {
068                @Override
069                public LazyImportScope invoke(@NotNull JetFile file) {
070                    return createExplicitImportScope(file);
071                }
072            });
073    
074            this.defaultImportsScope = resolveSession.getStorageManager().createLazyValue(new Function0<JetScope>() {
075                @Override
076                public JetScope invoke() {
077                    return createScopeWithDefaultImports();
078                }
079            });
080        }
081    
082        private LazyImportScope createExplicitImportScope(@NotNull JetFile file) {
083            return LazyImportScope.OBJECT$.createImportScopeForFile(
084                    resolveSession,
085                    getFilePackageDescriptor(file),
086                    file,
087                    resolveSession.getTrace(),
088                    "Lazy Imports Scope for file " + file.getName());
089        }
090    
091        @NotNull
092        public JetScope getFileScope(@NotNull JetFile file) {
093            return new ChainedScope(resolveSession.getPackageFragment(file.getPackageFqName()),
094                                    "File scope: " + file.getName(),
095                                    collectFileScopes(file));
096        }
097    
098        public JetScope[] collectFileScopes(@NotNull JetFile file) {
099            List<JetScope> list = new ArrayList<JetScope>();
100            list.add(getFilePackageDescriptor(file).getMemberScope());
101            list.add(JetModuleUtil.getSubpackagesOfRootScope(resolveSession.getModuleDescriptor()));
102            list.add(explicitImportScopes.invoke(file));
103            list.addAll(additionalFileScopeProvider.scopes(file));
104            list.add(defaultImportsScope.invoke());
105    
106            return list.toArray(new JetScope[list.size()]);
107        }
108    
109        @NotNull
110        public LazyImportScope getExplicitImportsScopeForFile(@NotNull JetFile file) {
111            return explicitImportScopes.invoke(file);
112        }
113    
114        private JetScope createScopeWithDefaultImports() {
115            PackageViewDescriptor rootPackage = resolveSession.getModuleDescriptor().getPackage(FqName.ROOT);
116            if (rootPackage == null) {
117                throw new IllegalStateException("Root package not found");
118            }
119    
120            JetImportsFactory importsFactory = resolveSession.getJetImportsFactory();
121            List<ImportPath> defaultImports = resolveSession.getModuleDescriptor().getDefaultImports();
122    
123            Collection<JetImportDirective> defaultImportDirectives = importsFactory.createImportDirectives(defaultImports);
124    
125            return new LazyImportScope(
126                    resolveSession,
127                    rootPackage,
128                    Lists.reverse(Lists.newArrayList(defaultImportDirectives)),
129                    TemporaryBindingTrace.create(resolveSession.getTrace(), "Transient trace for default imports lazy resolve"),
130                    "Lazy default imports scope",
131                    false);
132        }
133    
134        @NotNull
135        private PackageViewDescriptor getFilePackageDescriptor(JetFile file) {
136            FqName fqName = file.getPackageFqName();
137            PackageViewDescriptor packageDescriptor = resolveSession.getModuleDescriptor().getPackage(fqName);
138    
139            if (packageDescriptor == null) {
140                throw new IllegalStateException("Package not found: " + fqName + " maybe the file is not in scope of this resolve session: " + file.getName());
141            }
142    
143            return packageDescriptor;
144        }
145    
146        @NotNull
147        public JetScope getResolutionScopeForDeclaration(@NotNull PsiElement elementOfDeclaration) {
148            JetDeclaration jetDeclaration = JetStubbedPsiUtil.getPsiOrStubParent(elementOfDeclaration, JetDeclaration.class, false);
149    
150            assert !(elementOfDeclaration instanceof JetDeclaration) || jetDeclaration == elementOfDeclaration :
151                    "For JetDeclaration element getParentOfType() should return itself.";
152            assert jetDeclaration != null : "Should be contained inside declaration.";
153    
154            JetDeclaration parentDeclaration = JetStubbedPsiUtil.getContainingDeclaration(jetDeclaration);
155    
156            if (jetDeclaration instanceof JetPropertyAccessor) {
157                parentDeclaration = JetStubbedPsiUtil.getContainingDeclaration(parentDeclaration, JetDeclaration.class);
158            }
159    
160            if (parentDeclaration == null) {
161                return getFileScope((JetFile) elementOfDeclaration.getContainingFile());
162            }
163    
164            if (parentDeclaration instanceof JetClassOrObject) {
165                JetClassOrObject classOrObject = (JetClassOrObject) parentDeclaration;
166                LazyClassDescriptor classDescriptor = (LazyClassDescriptor) resolveSession.getClassDescriptor(classOrObject);
167                if (jetDeclaration instanceof JetClassInitializer || jetDeclaration instanceof JetProperty) {
168                    return classDescriptor.getScopeForInitializerResolution();
169                }
170                return classDescriptor.getScopeForMemberDeclarationResolution();
171            }
172    
173            if (parentDeclaration instanceof JetClassObject) {
174                assert jetDeclaration instanceof JetObjectDeclaration : "Should be situation for getting scope for object in class [object {...}]";
175    
176                JetClassObject classObject = (JetClassObject) parentDeclaration;
177                LazyClassDescriptor classObjectDescriptor =
178                        (LazyClassDescriptor) resolveSession.getClassObjectDescriptor(classObject).getContainingDeclaration();
179    
180                return classObjectDescriptor.getScopeForMemberDeclarationResolution();
181            }
182    
183            throw new IllegalStateException("Don't call this method for local declarations: " + jetDeclaration + "\n" +
184                                            JetPsiUtil.getElementTextWithContext(jetDeclaration));
185        }
186    }