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