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.scopes;
018    
019    import com.google.common.collect.*;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.*;
023    import org.jetbrains.jet.lang.resolve.name.LabelName;
024    import org.jetbrains.jet.lang.resolve.name.Name;
025    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
026    import org.jetbrains.jet.utils.CommonSuppliers;
027    
028    import java.util.*;
029    
030    public class WritableScopeImpl extends WritableScopeWithImports {
031    
032        private final Collection<DeclarationDescriptor> allDescriptors = Lists.newArrayList();
033        private final Multimap<Name, DeclarationDescriptor> declaredDescriptorsAccessibleBySimpleName = HashMultimap.create();
034        private boolean allDescriptorsDone = false;
035    
036        private Set<ClassDescriptor> allObjectDescriptors = null;
037    
038        @NotNull
039        private final DeclarationDescriptor ownerDeclarationDescriptor;
040    
041        @Nullable
042        private SetMultimap<Name, FunctionDescriptor> functionGroups;
043    
044        @Nullable
045        private Map<Name, DeclarationDescriptor> variableClassOrNamespaceDescriptors;
046        
047        @Nullable
048        private SetMultimap<Name, VariableDescriptor> propertyGroups;
049    
050        @Nullable
051        private Map<Name, NamespaceDescriptor> namespaceAliases;
052    
053        @Nullable
054        private Map<LabelName, List<DeclarationDescriptor>> labelsToDescriptors;
055        
056        @Nullable
057        private Map<Name, ClassDescriptor> objectDescriptors;
058    
059        @Nullable
060        private ReceiverParameterDescriptor implicitReceiver;
061    
062        public WritableScopeImpl(@NotNull JetScope scope, @NotNull DeclarationDescriptor owner,
063                @NotNull RedeclarationHandler redeclarationHandler, @NotNull String debugName) {
064            super(scope, redeclarationHandler, debugName);
065            this.ownerDeclarationDescriptor = owner;
066        }
067    
068        @NotNull
069        @Override
070        public DeclarationDescriptor getContainingDeclaration() {
071            return ownerDeclarationDescriptor;
072        }
073    
074        @Override
075        public void importScope(@NotNull JetScope imported) {
076            checkMayWrite();
077            super.importScope(imported);
078        }
079    
080        @Override
081        public void importClassifierAlias(@NotNull Name importedClassifierName, @NotNull ClassifierDescriptor classifierDescriptor) {
082            checkMayWrite();
083    
084            allDescriptors.add(classifierDescriptor);
085            super.importClassifierAlias(importedClassifierName, classifierDescriptor);
086        }
087    
088        @Override
089        public void importNamespaceAlias(@NotNull Name aliasName, @NotNull NamespaceDescriptor namespaceDescriptor) {
090            checkMayWrite();
091    
092            allDescriptors.add(namespaceDescriptor);
093            super.importNamespaceAlias(aliasName, namespaceDescriptor);
094        }
095    
096        @Override
097        public void importFunctionAlias(@NotNull Name aliasName, @NotNull FunctionDescriptor functionDescriptor) {
098            checkMayWrite();
099    
100            addFunctionDescriptor(functionDescriptor);
101            super.importFunctionAlias(aliasName, functionDescriptor);
102    
103        }
104    
105        @Override
106        public void importVariableAlias(@NotNull Name aliasName, @NotNull VariableDescriptor variableDescriptor) {
107            checkMayWrite();
108    
109            addPropertyDescriptor(variableDescriptor);
110            super.importVariableAlias(aliasName, variableDescriptor);
111        }
112    
113        @Override
114        public void clearImports() {
115            checkMayWrite();
116    
117            super.clearImports();
118        }
119    
120        @NotNull
121        @Override
122        public Collection<DeclarationDescriptor> getAllDescriptors() {
123            checkMayRead();
124    
125            if (!allDescriptorsDone) {
126                allDescriptorsDone = true;
127    
128                // make sure no descriptors added to allDescriptors collection
129                changeLockLevel(LockLevel.READING);
130    
131                allDescriptors.addAll(getWorkerScope().getAllDescriptors());
132                for (JetScope imported : getImports()) {
133                    allDescriptors.addAll(imported.getAllDescriptors());
134                }
135            }
136            return allDescriptors;
137        }
138    
139        @NotNull
140        private Map<LabelName, List<DeclarationDescriptor>> getLabelsToDescriptors() {
141            if (labelsToDescriptors == null) {
142                labelsToDescriptors = new HashMap<LabelName, List<DeclarationDescriptor>>();
143            }
144            return labelsToDescriptors;
145        }
146    
147        @NotNull
148        private Map<Name, ClassDescriptor> getObjectDescriptorsMap() {
149            if (objectDescriptors == null) {
150                objectDescriptors = Maps.newHashMap();
151            }
152            return objectDescriptors;
153        }
154    
155        @NotNull
156        @Override
157        public Collection<DeclarationDescriptor> getDeclarationsByLabel(@NotNull LabelName labelName) {
158            checkMayRead();
159    
160            Collection<DeclarationDescriptor> superResult = super.getDeclarationsByLabel(labelName);
161            Map<LabelName, List<DeclarationDescriptor>> labelsToDescriptors = getLabelsToDescriptors();
162            List<DeclarationDescriptor> declarationDescriptors = labelsToDescriptors.get(labelName);
163            if (declarationDescriptors == null) {
164                return superResult;
165            }
166            if (superResult.isEmpty()) return declarationDescriptors;
167            List<DeclarationDescriptor> result = new ArrayList<DeclarationDescriptor>(declarationDescriptors);
168            result.addAll(superResult);
169            return result;
170        }
171    
172        @Override
173        public void addLabeledDeclaration(@NotNull DeclarationDescriptor descriptor) {
174            checkMayWrite();
175    
176            Map<LabelName, List<DeclarationDescriptor>> labelsToDescriptors = getLabelsToDescriptors();
177            LabelName name = new LabelName(descriptor.getName().asString());
178            List<DeclarationDescriptor> declarationDescriptors = labelsToDescriptors.get(name);
179            if (declarationDescriptors == null) {
180                declarationDescriptors = new ArrayList<DeclarationDescriptor>();
181                labelsToDescriptors.put(name, declarationDescriptors);
182            }
183            declarationDescriptors.add(descriptor);
184        }
185    
186        @NotNull
187        private Map<Name, DeclarationDescriptor> getVariableClassOrNamespaceDescriptors() {
188            if (variableClassOrNamespaceDescriptors == null) {
189                variableClassOrNamespaceDescriptors = Maps.newHashMap();
190            }
191            return variableClassOrNamespaceDescriptors;
192        }
193    
194        @NotNull
195        private Map<Name, NamespaceDescriptor> getNamespaceAliases() {
196            if (namespaceAliases == null) {
197                namespaceAliases = Maps.newHashMap();
198            }
199            return namespaceAliases;
200        }
201    
202        @Override
203        public void addVariableDescriptor(@NotNull VariableDescriptor variableDescriptor) {
204            addVariableDescriptor(variableDescriptor, false);
205        }
206        
207        @Override
208        public void addPropertyDescriptor(@NotNull VariableDescriptor propertyDescriptor) {
209            addVariableDescriptor(propertyDescriptor, true);
210        }
211        
212        private void addVariableDescriptor(@NotNull VariableDescriptor variableDescriptor, boolean isProperty) {
213            checkMayWrite();
214    
215            Name name = variableDescriptor.getName();
216            if (isProperty) {
217                checkForPropertyRedeclaration(name, variableDescriptor);
218                getPropertyGroups().put(name, variableDescriptor);
219            }
220            if (variableDescriptor.getReceiverParameter() == null) {
221                checkForRedeclaration(name, variableDescriptor);
222                // TODO : Should this always happen?
223                getVariableClassOrNamespaceDescriptors().put(name, variableDescriptor);
224            }
225            allDescriptors.add(variableDescriptor);
226            addToDeclared(variableDescriptor);
227        }
228    
229        @NotNull
230        @Override
231        public Set<VariableDescriptor> getProperties(@NotNull Name name) {
232            checkMayRead();
233    
234            Set<VariableDescriptor> result = Sets.newLinkedHashSet(getPropertyGroups().get(name));
235    
236            result.addAll(getWorkerScope().getProperties(name));
237    
238            result.addAll(super.getProperties(name));
239            
240            return result;
241        }
242    
243        @Override
244        public VariableDescriptor getLocalVariable(@NotNull Name name) {
245            checkMayRead();
246    
247            Map<Name, DeclarationDescriptor> variableClassOrNamespaceDescriptors = getVariableClassOrNamespaceDescriptors();
248            DeclarationDescriptor descriptor = variableClassOrNamespaceDescriptors.get(name);
249            if (descriptor instanceof VariableDescriptor && !getPropertyGroups().get(name).contains(descriptor)) {
250                return (VariableDescriptor) descriptor;
251            }
252    
253            VariableDescriptor variableDescriptor = getWorkerScope().getLocalVariable(name);
254            if (variableDescriptor != null) {
255                return variableDescriptor;
256            }
257            return super.getLocalVariable(name);
258        }
259    
260        @NotNull
261        private SetMultimap<Name, VariableDescriptor> getPropertyGroups() {
262            if (propertyGroups == null) {
263                propertyGroups = CommonSuppliers.newLinkedHashSetHashSetMultimap();
264            }
265            return propertyGroups;
266        }
267        
268        @NotNull
269        private SetMultimap<Name, FunctionDescriptor> getFunctionGroups() {
270            if (functionGroups == null) {
271                functionGroups = CommonSuppliers.newLinkedHashSetHashSetMultimap();
272            }
273            return functionGroups;
274        }
275    
276        @Override
277        public void addFunctionDescriptor(@NotNull FunctionDescriptor functionDescriptor) {
278            checkMayWrite();
279    
280            getFunctionGroups().put(functionDescriptor.getName(), functionDescriptor);
281            allDescriptors.add(functionDescriptor);
282        }
283    
284        @Override
285        @NotNull
286        public Collection<FunctionDescriptor> getFunctions(@NotNull Name name) {
287            checkMayRead();
288    
289            Set<FunctionDescriptor> result = Sets.newLinkedHashSet(getFunctionGroups().get(name));
290    
291            result.addAll(getWorkerScope().getFunctions(name));
292    
293            result.addAll(super.getFunctions(name));
294    
295            return result;
296        }
297    
298        @Override
299        public void addTypeParameterDescriptor(@NotNull TypeParameterDescriptor typeParameterDescriptor) {
300            checkMayWrite();
301    
302            Name name = typeParameterDescriptor.getName();
303            addClassifierAlias(name, typeParameterDescriptor);
304        }
305    
306        @Override
307        public void addClassifierDescriptor(@NotNull ClassifierDescriptor classDescriptor) {
308            checkMayWrite();
309    
310            if (isObject(classDescriptor)) {
311                throw new IllegalStateException("must not be object: " + classDescriptor);
312            }
313    
314            addClassifierAlias(classDescriptor.getName(), classDescriptor);
315        }
316    
317        @Override
318        public void addObjectDescriptor(@NotNull ClassDescriptor objectDescriptor) {
319            checkMayWrite();
320    
321            if (!objectDescriptor.getKind().isObject()) {
322                throw new IllegalStateException("must be object: " + objectDescriptor);
323            }
324            
325            getObjectDescriptorsMap().put(objectDescriptor.getName(), objectDescriptor);
326        }
327    
328        @Override
329        public void addClassifierAlias(@NotNull Name name, @NotNull ClassifierDescriptor classifierDescriptor) {
330            checkMayWrite();
331    
332            checkForRedeclaration(name, classifierDescriptor);
333            getVariableClassOrNamespaceDescriptors().put(name, classifierDescriptor);
334            allDescriptors.add(classifierDescriptor);
335            addToDeclared(classifierDescriptor);
336        }
337    
338        @Override
339        public void addNamespaceAlias(@NotNull Name name, @NotNull NamespaceDescriptor namespaceDescriptor) {
340            checkMayWrite();
341    
342            checkForRedeclaration(name, namespaceDescriptor);
343            getNamespaceAliases().put(name, namespaceDescriptor);
344            allDescriptors.add(namespaceDescriptor);
345            addToDeclared(namespaceDescriptor);
346        }
347    
348        @Override
349        public void addFunctionAlias(@NotNull Name name, @NotNull FunctionDescriptor functionDescriptor) {
350            checkMayWrite();
351            
352            checkForRedeclaration(name, functionDescriptor);
353            getFunctionGroups().put(name, functionDescriptor);
354            allDescriptors.add(functionDescriptor);
355        }
356    
357        @Override
358        public void addVariableAlias(@NotNull Name name, @NotNull VariableDescriptor variableDescriptor) {
359            checkMayWrite();
360            
361            checkForRedeclaration(name, variableDescriptor);
362            getVariableClassOrNamespaceDescriptors().put(name, variableDescriptor);
363            allDescriptors.add(variableDescriptor);
364            addToDeclared(variableDescriptor);
365        }
366        
367        private void checkForPropertyRedeclaration(@NotNull Name name, VariableDescriptor variableDescriptor) {
368            Set<VariableDescriptor> properties = getPropertyGroups().get(name);
369            ReceiverParameterDescriptor receiverParameter = variableDescriptor.getReceiverParameter();
370            for (VariableDescriptor oldProperty : properties) {
371                ReceiverParameterDescriptor receiverParameterForOldVariable = oldProperty.getReceiverParameter();
372                if (((receiverParameter != null && receiverParameterForOldVariable != null) &&
373                     (JetTypeChecker.INSTANCE.equalTypes(receiverParameter.getType(), receiverParameterForOldVariable.getType())))) {
374                    redeclarationHandler.handleRedeclaration(oldProperty, variableDescriptor);
375                }
376            }
377        }
378    
379        private void checkForRedeclaration(@NotNull Name name, DeclarationDescriptor classifierDescriptor) {
380            DeclarationDescriptor originalDescriptor = getVariableClassOrNamespaceDescriptors().get(name);
381            if (originalDescriptor != null) {
382                redeclarationHandler.handleRedeclaration(originalDescriptor, classifierDescriptor);
383            }
384        }
385    
386        @Override
387        public ClassifierDescriptor getClassifier(@NotNull Name name) {
388            checkMayRead();
389    
390            Map<Name, DeclarationDescriptor> variableClassOrNamespaceDescriptors = getVariableClassOrNamespaceDescriptors();
391            DeclarationDescriptor descriptor = variableClassOrNamespaceDescriptors.get(name);
392            if (descriptor instanceof ClassifierDescriptor) return (ClassifierDescriptor) descriptor;
393    
394            ClassifierDescriptor classifierDescriptor = getWorkerScope().getClassifier(name);
395            if (classifierDescriptor != null) return classifierDescriptor;
396    
397            return super.getClassifier(name);
398        }
399    
400        @Override
401        public ClassDescriptor getObjectDescriptor(@NotNull Name name) {
402            ClassDescriptor descriptor = getObjectDescriptorsMap().get(name);
403            if (descriptor != null) return descriptor;
404    
405            ClassDescriptor fromWorker = getWorkerScope().getObjectDescriptor(name);
406            if (fromWorker != null) return fromWorker;
407    
408            return super.getObjectDescriptor(name);
409        }
410    
411        @NotNull
412        @Override
413        public Set<ClassDescriptor> getObjectDescriptors() {
414            if (allObjectDescriptors == null) {
415                allObjectDescriptors = Sets.newHashSet(getObjectDescriptorsMap().values());
416                allObjectDescriptors.addAll(getWorkerScope().getObjectDescriptors());
417                for (JetScope imported : getImports()) {
418                    allObjectDescriptors.addAll(imported.getObjectDescriptors());
419                }
420            }
421            return allObjectDescriptors;
422        }
423    
424        @Override
425        public void addNamespace(@NotNull NamespaceDescriptor namespaceDescriptor) {
426            checkMayWrite();
427    
428            Map<Name, DeclarationDescriptor> variableClassOrNamespaceDescriptors = getVariableClassOrNamespaceDescriptors();
429            DeclarationDescriptor oldValue = variableClassOrNamespaceDescriptors.put(namespaceDescriptor.getName(), namespaceDescriptor);
430            if (oldValue != null) {
431                redeclarationHandler.handleRedeclaration(oldValue, namespaceDescriptor);
432            }
433            allDescriptors.add(namespaceDescriptor);
434            addToDeclared(namespaceDescriptor);
435        }
436    
437        @Override
438        public NamespaceDescriptor getDeclaredNamespace(@NotNull Name name) {
439            checkMayRead();
440    
441            Map<Name, DeclarationDescriptor> variableClassOrNamespaceDescriptors = getVariableClassOrNamespaceDescriptors();
442            DeclarationDescriptor namespaceDescriptor = variableClassOrNamespaceDescriptors.get(name);
443            if (namespaceDescriptor instanceof NamespaceDescriptor) return (NamespaceDescriptor) namespaceDescriptor;
444            return null;
445        }
446    
447        @Override
448        public NamespaceDescriptor getNamespace(@NotNull Name name) {
449            checkMayRead();
450    
451            NamespaceDescriptor declaredNamespace = getDeclaredNamespace(name);
452            if (declaredNamespace != null) return declaredNamespace;
453    
454            NamespaceDescriptor aliased = getNamespaceAliases().get(name);
455            if (aliased != null) return aliased;
456    
457            NamespaceDescriptor namespace = getWorkerScope().getNamespace(name);
458            if (namespace != null) return namespace;
459            return super.getNamespace(name);
460        }
461    
462        @Override
463        public void setImplicitReceiver(@NotNull ReceiverParameterDescriptor implicitReceiver) {
464            checkMayWrite();
465    
466            if (this.implicitReceiver != null) {
467                throw new UnsupportedOperationException("Receiver redeclared");
468            }
469            this.implicitReceiver = implicitReceiver;
470        }
471    
472        @Override
473        protected List<ReceiverParameterDescriptor> computeImplicitReceiversHierarchy() {
474            List<ReceiverParameterDescriptor> implicitReceiverHierarchy = Lists.newArrayList();
475            if (implicitReceiver != null) {
476                implicitReceiverHierarchy.add(implicitReceiver);
477            }
478            implicitReceiverHierarchy.addAll(super.computeImplicitReceiversHierarchy());
479            return implicitReceiverHierarchy;
480        }
481    
482        private void addToDeclared(DeclarationDescriptor descriptor) {
483            declaredDescriptorsAccessibleBySimpleName.put(descriptor.getName(), descriptor);
484        }
485    
486        @NotNull
487        @Override
488        public Multimap<Name, DeclarationDescriptor> getDeclaredDescriptorsAccessibleBySimpleName() {
489            return declaredDescriptorsAccessibleBySimpleName;
490        }
491    
492        @NotNull
493        @Override
494        public Collection<DeclarationDescriptor> getOwnDeclaredDescriptors() {
495            return declaredDescriptorsAccessibleBySimpleName.values();
496        }
497    
498        private static boolean isObject(@NotNull ClassifierDescriptor classifier) {
499            if (classifier instanceof ClassDescriptor) {
500                ClassDescriptor clazz = (ClassDescriptor) classifier;
501                return clazz.getKind().isObject();
502            }
503            else if (classifier instanceof TypeParameterDescriptor) {
504                return false;
505            }
506            else {
507                throw new IllegalStateException("unknown classifier: " + classifier);
508            }
509        }
510    }