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.Lists;
020import com.google.common.collect.Sets;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.annotations.Nullable;
023import org.jetbrains.jet.lang.descriptors.*;
024import org.jetbrains.jet.lang.resolve.name.Name;
025
026import java.util.*;
027
028public abstract class WritableScopeWithImports extends JetScopeAdapter implements WritableScope {
029
030    @NotNull
031    private final String debugName;
032
033    @Nullable
034    private List<JetScope> imports;
035    private WritableScope currentIndividualImportScope;
036    protected final RedeclarationHandler redeclarationHandler;
037    private List<ReceiverParameterDescriptor> implicitReceiverHierarchy;
038
039    public WritableScopeWithImports(@NotNull JetScope scope, @NotNull RedeclarationHandler redeclarationHandler, @NotNull String debugName) {
040        super(scope);
041        this.redeclarationHandler = redeclarationHandler;
042        this.debugName = debugName;
043    }
044
045
046
047    private LockLevel lockLevel = LockLevel.WRITING;
048
049    @Override
050    public WritableScope changeLockLevel(LockLevel lockLevel) {
051        if (lockLevel.ordinal() < this.lockLevel.ordinal()) {
052            throw new IllegalStateException("cannot lower lock level from " + this.lockLevel + " to " + lockLevel + " at " + toString());
053        }
054        this.lockLevel = lockLevel;
055        return this;
056    }
057
058    protected void checkMayRead() {
059        if (lockLevel != LockLevel.READING && lockLevel != LockLevel.BOTH) {
060            throw new IllegalStateException("cannot read with lock level " + lockLevel + " at " + toString());
061        }
062    }
063
064    protected void checkMayWrite() {
065        if (lockLevel != LockLevel.WRITING && lockLevel != LockLevel.BOTH) {
066            throw new IllegalStateException("cannot write with lock level " + lockLevel + " at " + toString());
067        }
068    }
069    
070    protected void checkMayNotWrite() {
071        if (lockLevel == LockLevel.WRITING || lockLevel == LockLevel.BOTH) {
072            throw new IllegalStateException("cannot write with lock level " + lockLevel + " at " + toString());
073        }
074    }
075
076
077
078
079    @NotNull
080    protected final List<JetScope> getImports() {
081        if (imports == null) {
082            imports = new ArrayList<JetScope>();
083        }
084        return imports;
085    }
086
087    @Override
088    public void importScope(@NotNull JetScope imported) {
089        if (imported == this) {
090            throw new IllegalStateException("cannot import scope into self");
091        }
092
093        checkMayWrite();
094
095        getImports().add(0, imported);
096        currentIndividualImportScope = null;
097    }
098
099    @NotNull
100    @Override
101    public List<ReceiverParameterDescriptor> getImplicitReceiversHierarchy() {
102        checkMayRead();
103
104        if (implicitReceiverHierarchy == null) {
105            implicitReceiverHierarchy = computeImplicitReceiversHierarchy();
106        }
107        return implicitReceiverHierarchy;
108    }
109
110    protected List<ReceiverParameterDescriptor> computeImplicitReceiversHierarchy() {
111        List<ReceiverParameterDescriptor> implicitReceiverHierarchy = Lists.newArrayList();
112        // Imported scopes come with their receivers
113        // Example: class member resolution scope imports a scope of it's class object
114        //          members of the class object must be able to find it as an implicit receiver
115        for (JetScope scope : getImports()) {
116            implicitReceiverHierarchy.addAll(scope.getImplicitReceiversHierarchy());
117        }
118        implicitReceiverHierarchy.addAll(super.getImplicitReceiversHierarchy());
119        return implicitReceiverHierarchy;
120    }
121
122    @NotNull
123    @Override
124    public Set<VariableDescriptor> getProperties(@NotNull Name name) {
125        checkMayRead();
126
127        Set<VariableDescriptor> properties = Sets.newLinkedHashSet();
128        for (JetScope imported : getImports()) {
129            properties.addAll(imported.getProperties(name));
130        }
131        return properties;
132    }
133
134    @Override
135    public VariableDescriptor getLocalVariable(@NotNull Name name) {
136        checkMayRead();
137
138        // Meaningful lookup goes here
139        for (JetScope imported : getImports()) {
140            VariableDescriptor importedDescriptor = imported.getLocalVariable(name);
141            if (importedDescriptor != null) {
142                return importedDescriptor;
143            }
144        }
145        return null;
146    }
147
148    @NotNull
149    @Override
150    public Collection<FunctionDescriptor> getFunctions(@NotNull Name name) {
151        checkMayRead();
152
153        if (getImports().isEmpty()) {
154            return Collections.emptySet();
155        }
156        Set<FunctionDescriptor> result = Sets.newLinkedHashSet();
157        for (JetScope imported : getImports()) {
158            result.addAll(imported.getFunctions(name));
159        }
160        return result;
161    }
162
163    @Override
164    public ClassifierDescriptor getClassifier(@NotNull Name name) {
165        checkMayRead();
166
167        for (JetScope imported : getImports()) {
168            ClassifierDescriptor importedClassifier = imported.getClassifier(name);
169            if (importedClassifier != null) {
170                return importedClassifier;
171            }
172        }
173        return null;
174    }
175
176    @Override
177    public ClassDescriptor getObjectDescriptor(@NotNull Name name) {
178        checkMayRead();
179
180        for (JetScope imported : getImports()) {
181            ClassDescriptor objectDescriptor = imported.getObjectDescriptor(name);
182            if (objectDescriptor != null) {
183                return objectDescriptor;
184            }
185        }
186        return null;
187    }
188
189    @Override
190    public NamespaceDescriptor getNamespace(@NotNull Name name) {
191        checkMayRead();
192
193        for (JetScope imported : getImports()) {
194            NamespaceDescriptor importedDescriptor = imported.getNamespace(name);
195            if (importedDescriptor != null) {
196                return importedDescriptor;
197            }
198        }
199        return null;
200    }
201
202    private WritableScope getCurrentIndividualImportScope() {
203        if (currentIndividualImportScope == null) {
204            WritableScopeImpl writableScope = new WritableScopeImpl(EMPTY, getContainingDeclaration(), RedeclarationHandler.DO_NOTHING, "Individual import scope");
205            writableScope.changeLockLevel(LockLevel.BOTH);
206            importScope(writableScope);
207            currentIndividualImportScope = writableScope;
208        }
209        return currentIndividualImportScope;
210    }
211
212    @Override
213    public void importClassifierAlias(@NotNull Name importedClassifierName, @NotNull ClassifierDescriptor classifierDescriptor) {
214        checkMayWrite();
215
216        getCurrentIndividualImportScope().addClassifierAlias(importedClassifierName, classifierDescriptor);
217    }
218    
219    
220    @Override
221    public void importNamespaceAlias(@NotNull Name aliasName, @NotNull NamespaceDescriptor namespaceDescriptor) {
222        checkMayWrite();
223
224        getCurrentIndividualImportScope().addNamespaceAlias(aliasName, namespaceDescriptor);
225    }
226
227    @Override
228    public void importFunctionAlias(@NotNull Name aliasName, @NotNull FunctionDescriptor functionDescriptor) {
229        checkMayWrite();
230
231        getCurrentIndividualImportScope().addFunctionAlias(aliasName, functionDescriptor);
232    }
233
234    @Override
235    public void importVariableAlias(@NotNull Name aliasName, @NotNull VariableDescriptor variableDescriptor) {
236        checkMayWrite();
237
238        getCurrentIndividualImportScope().addVariableAlias(aliasName, variableDescriptor);
239    }
240
241    @Override
242    public void clearImports() {
243        currentIndividualImportScope = null;
244        getImports().clear();
245    }
246
247    @Override
248    public String toString() {
249        return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this)) + " " + debugName + " for " + getContainingDeclaration();
250    }
251
252}