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.scopes;
018
019import com.google.common.collect.*;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.lang.descriptors.*;
023import org.jetbrains.jet.lang.resolve.name.LabelName;
024import org.jetbrains.jet.lang.resolve.name.Name;
025import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
026import org.jetbrains.jet.util.CommonSuppliers;
027
028import java.util.*;
029
030public 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}