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.google.common.collect.Maps;
021    import com.google.common.collect.Sets;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.*;
025    import org.jetbrains.jet.lang.psi.JetFile;
026    import org.jetbrains.jet.lang.psi.JetImportDirective;
027    import org.jetbrains.jet.lang.resolve.BindingTrace;
028    import org.jetbrains.jet.lang.resolve.Importer;
029    import org.jetbrains.jet.lang.resolve.name.FqName;
030    import org.jetbrains.jet.lang.resolve.name.LabelName;
031    import org.jetbrains.jet.lang.resolve.name.Name;
032    import org.jetbrains.jet.lang.resolve.scopes.*;
033    
034    import java.util.*;
035    
036    import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode;
037    
038    public 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    }