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