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.google.common.collect.Maps;
021import com.google.common.collect.Sets;
022import org.jetbrains.annotations.NotNull;
023import org.jetbrains.annotations.Nullable;
024import org.jetbrains.jet.lang.descriptors.*;
025import org.jetbrains.jet.lang.psi.JetFile;
026import org.jetbrains.jet.lang.psi.JetImportDirective;
027import org.jetbrains.jet.lang.resolve.BindingTrace;
028import org.jetbrains.jet.lang.resolve.Importer;
029import org.jetbrains.jet.lang.resolve.name.FqName;
030import org.jetbrains.jet.lang.resolve.name.LabelName;
031import org.jetbrains.jet.lang.resolve.name.Name;
032import org.jetbrains.jet.lang.resolve.scopes.*;
033
034import java.util.*;
035
036import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode;
037
038public class LazyImportScope implements JetScope {
039    private static class ImportResolveStatus {
040        private final LookupMode lookupMode;
041        private final JetScope scope;
042
043        ImportResolveStatus(LookupMode lookupMode, JetScope scope) {
044            this.lookupMode = lookupMode;
045            this.scope = scope;
046        }
047    }
048
049    private final ResolveSession resolveSession;
050    private final NamespaceDescriptor packageDescriptor;
051    private final ImportsProvider importsProvider;
052    private final JetScope rootScope;
053    private final BindingTrace traceForImportResolve;
054    private final String debugName;
055
056    private final Map<JetImportDirective, ImportResolveStatus> importedScopes = Maps.newHashMap();
057    private JetImportDirective directiveUnderResolve = null;
058
059    public LazyImportScope(
060            @NotNull ResolveSession resolveSession,
061            @NotNull NamespaceDescriptor packageDescriptor,
062            @NotNull List<JetImportDirective> imports,
063            @NotNull BindingTrace traceForImportResolve,
064            @NotNull String debugName
065    ) {
066        this.resolveSession = resolveSession;
067        this.packageDescriptor = packageDescriptor;
068        this.importsProvider = new ImportsProvider(imports);
069        this.traceForImportResolve = traceForImportResolve;
070        this.debugName = debugName;
071
072        NamespaceDescriptor rootPackageDescriptor = resolveSession.getPackageDescriptorByFqName(FqName.ROOT);
073        if (rootPackageDescriptor == null) {
074            throw new IllegalStateException("Root package not found");
075        }
076        rootScope = rootPackageDescriptor.getMemberScope();
077    }
078
079    public static LazyImportScope createImportScopeForFile(
080            @NotNull ResolveSession resolveSession,
081            @NotNull NamespaceDescriptor packageDescriptor,
082            @NotNull JetFile jetFile,
083            @NotNull BindingTrace traceForImportResolve,
084            @NotNull String debugName
085    ) {
086        return new LazyImportScope(
087                resolveSession,
088                packageDescriptor,
089                Lists.reverse(jetFile.getImportDirectives()),
090                traceForImportResolve,
091                debugName);
092    }
093
094    @Nullable
095    private <D extends DeclarationDescriptor> D selectFirstFromImports(
096            Name name,
097            LookupMode lookupMode,
098            JetScopeSelectorUtil.ScopeByNameSelector<D> descriptorSelector
099    ) {
100        for (JetImportDirective directive : importsProvider.getImports(name)) {
101            if (directive == directiveUnderResolve) {
102                // This is the recursion in imports analysis
103                return null;
104            }
105
106            D foundDescriptor = descriptorSelector.get(getImportScope(directive, lookupMode), name);
107            if (foundDescriptor != null) {
108                return foundDescriptor;
109            }
110        }
111
112        return null;
113    }
114
115    @NotNull
116    private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
117            Name name,
118            LookupMode lookupMode,
119            JetScopeSelectorUtil.ScopeByNameMultiSelector<D> descriptorsSelector
120    ) {
121        Set<D> descriptors = Sets.newHashSet();
122        for (JetImportDirective directive : importsProvider.getImports(name)) {
123            if (directive == directiveUnderResolve) {
124                // This is the recursion in imports analysis
125                throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
126            }
127
128            descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode), name));
129        }
130
131        return descriptors;
132    }
133
134    @NotNull
135    private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
136            LookupMode lookupMode,
137            JetScopeSelectorUtil.ScopeDescriptorSelector<D> descriptorsSelector
138    ) {
139        Set<D> descriptors = Sets.newHashSet();
140        for (JetImportDirective directive : importsProvider.getAllImports()) {
141            if (directive == directiveUnderResolve) {
142                // This is the recursion in imports analysis
143                throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
144            }
145
146            descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode)));
147        }
148
149        return descriptors;
150    }
151
152    @NotNull
153    private JetScope getImportScope(JetImportDirective directive, LookupMode lookupMode) {
154        ImportResolveStatus status = importedScopes.get(directive);
155        if (status != null && (lookupMode == status.lookupMode || status.lookupMode == LookupMode.EVERYTHING)) {
156            return status.scope;
157        }
158
159        WritableScope directiveImportScope = new WritableScopeImpl(
160                    JetScope.EMPTY, packageDescriptor, RedeclarationHandler.DO_NOTHING,
161                    "Scope for import '" + directive.getText() + "' resolve in " + toString());
162        directiveImportScope.changeLockLevel(WritableScope.LockLevel.BOTH);
163
164        Importer.StandardImporter importer = new Importer.StandardImporter(directiveImportScope);
165        directiveUnderResolve = directive;
166
167        try {
168            resolveSession.getInjector().getQualifiedExpressionResolver().processImportReference(
169                    directive,
170                    rootScope,
171                    packageDescriptor.getMemberScope(),
172                    importer,
173                    traceForImportResolve,
174                    resolveSession.getRootModuleDescriptor(),
175                    lookupMode);
176        }
177        finally {
178            directiveUnderResolve = null;
179            directiveImportScope.changeLockLevel(WritableScope.LockLevel.READING);
180            importedScopes.put(directive, new ImportResolveStatus(lookupMode, directiveImportScope));
181        }
182
183        return directiveImportScope;
184    }
185
186    @Nullable
187    @Override
188    public ClassifierDescriptor getClassifier(@NotNull Name name) {
189        return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.CLASSIFIER_DESCRIPTOR_SCOPE_SELECTOR);
190    }
191
192    @Nullable
193    @Override
194    public ClassDescriptor getObjectDescriptor(@NotNull Name name) {
195        return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.NAMED_OBJECT_SCOPE_SELECTOR);
196    }
197
198    @NotNull
199    @Override
200    public Collection<ClassDescriptor> getObjectDescriptors() {
201        return collectFromImports(LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.OBJECTS_SCOPE_SELECTOR);
202    }
203
204    @Nullable
205    @Override
206    public NamespaceDescriptor getNamespace(@NotNull Name name) {
207        return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.NAMESPACE_SCOPE_SELECTOR);
208    }
209
210    @NotNull
211    @Override
212    public Collection<VariableDescriptor> getProperties(@NotNull Name name) {
213        return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_PROPERTIES_SCOPE_SELECTOR);
214    }
215
216    @Nullable
217    @Override
218    public VariableDescriptor getLocalVariable(@NotNull Name name) {
219        return null;
220    }
221
222    @NotNull
223    @Override
224    public Collection<FunctionDescriptor> getFunctions(@NotNull Name name) {
225        return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_FUNCTION_SCOPE_SELECTOR);
226    }
227
228    @NotNull
229    @Override
230    public DeclarationDescriptor getContainingDeclaration() {
231        return packageDescriptor;
232    }
233
234    @NotNull
235    @Override
236    public Collection<DeclarationDescriptor> getDeclarationsByLabel(@NotNull LabelName labelName) {
237        return Collections.emptyList();
238    }
239
240    @Nullable
241    @Override
242    public PropertyDescriptor getPropertyByFieldReference(@NotNull Name fieldName) {
243        return null;
244    }
245
246    @NotNull
247    @Override
248    public Collection<DeclarationDescriptor> getAllDescriptors() {
249        return collectFromImports(LookupMode.EVERYTHING, JetScopeSelectorUtil.ALL_DESCRIPTORS_SCOPE_SELECTOR);
250    }
251
252    @NotNull
253    @Override
254    public List<ReceiverParameterDescriptor> getImplicitReceiversHierarchy() {
255        return Collections.emptyList();
256    }
257
258    @NotNull
259    @Override
260    public Collection<DeclarationDescriptor> getOwnDeclaredDescriptors() {
261        return Collections.emptyList();
262    }
263
264    @Override
265    public String toString() {
266        return "LazyImportScope: " + debugName;
267    }
268}