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