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