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