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.DescriptorUtils;
026    import org.jetbrains.jet.lang.resolve.name.Name;
027    import org.jetbrains.jet.utils.Printer;
028    
029    import java.util.*;
030    
031    public abstract class WritableScopeWithImports extends JetScopeAdapter implements WritableScope {
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            super(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        protected final List<JetScope> getImports() {
084            if (imports == null) {
085                imports = new ArrayList<JetScope>();
086            }
087            return imports;
088        }
089    
090        @Override
091        public void importScope(@NotNull JetScope imported) {
092            if (imported == this) {
093                throw new IllegalStateException("cannot import scope into self");
094            }
095    
096            checkMayWrite();
097    
098            getImports().add(0, imported);
099            currentIndividualImportScope = null;
100        }
101    
102        @NotNull
103        @Override
104        public List<ReceiverParameterDescriptor> getImplicitReceiversHierarchy() {
105            checkMayRead();
106    
107            if (implicitReceiverHierarchy == null) {
108                implicitReceiverHierarchy = computeImplicitReceiversHierarchy();
109            }
110            return implicitReceiverHierarchy;
111        }
112    
113        protected List<ReceiverParameterDescriptor> computeImplicitReceiversHierarchy() {
114            List<ReceiverParameterDescriptor> implicitReceiverHierarchy = Lists.newArrayList();
115            // Imported scopes come with their receivers
116            // Example: class member resolution scope imports a scope of it's class object
117            //          members of the class object must be able to find it as an implicit receiver
118            for (JetScope scope : getImports()) {
119                implicitReceiverHierarchy.addAll(scope.getImplicitReceiversHierarchy());
120            }
121            implicitReceiverHierarchy.addAll(super.getImplicitReceiversHierarchy());
122            return implicitReceiverHierarchy;
123        }
124    
125        @NotNull
126        @Override
127        public Set<VariableDescriptor> getProperties(@NotNull Name name) {
128            checkMayRead();
129    
130            Set<VariableDescriptor> properties = Sets.newLinkedHashSet();
131            for (JetScope imported : getImports()) {
132                properties.addAll(imported.getProperties(name));
133            }
134            return properties;
135        }
136    
137        @Override
138        public VariableDescriptor getLocalVariable(@NotNull Name name) {
139            checkMayRead();
140    
141            // Meaningful lookup goes here
142            for (JetScope imported : getImports()) {
143                VariableDescriptor importedDescriptor = imported.getLocalVariable(name);
144                if (importedDescriptor != null) {
145                    return importedDescriptor;
146                }
147            }
148            return null;
149        }
150    
151        @NotNull
152        @Override
153        public Collection<FunctionDescriptor> getFunctions(@NotNull Name name) {
154            checkMayRead();
155    
156            if (getImports().isEmpty()) {
157                return Collections.emptySet();
158            }
159            Set<FunctionDescriptor> result = Sets.newLinkedHashSet();
160            for (JetScope imported : getImports()) {
161                result.addAll(imported.getFunctions(name));
162            }
163            return result;
164        }
165    
166        @Override
167        public ClassifierDescriptor getClassifier(@NotNull Name name) {
168            checkMayRead();
169    
170            for (JetScope imported : getImports()) {
171                ClassifierDescriptor importedClassifier = imported.getClassifier(name);
172                if (importedClassifier != null) {
173                    return importedClassifier;
174                }
175            }
176            return null;
177        }
178    
179        @Override
180        public ClassDescriptor getObjectDescriptor(@NotNull Name name) {
181            checkMayRead();
182    
183            for (JetScope imported : getImports()) {
184                ClassDescriptor objectDescriptor = imported.getObjectDescriptor(name);
185                if (objectDescriptor != null) {
186                    return objectDescriptor;
187                }
188            }
189            return null;
190        }
191    
192        @Override
193        public NamespaceDescriptor getNamespace(@NotNull Name name) {
194            checkMayRead();
195    
196            for (JetScope imported : getImports()) {
197                NamespaceDescriptor importedDescriptor = imported.getNamespace(name);
198                if (importedDescriptor != null) {
199                    return importedDescriptor;
200                }
201            }
202            return null;
203        }
204    
205        private WritableScope getCurrentIndividualImportScope() {
206            if (currentIndividualImportScope == null) {
207                WritableScopeImpl writableScope = new WritableScopeImpl(EMPTY, getContainingDeclaration(), RedeclarationHandler.DO_NOTHING, "Individual import scope");
208                writableScope.changeLockLevel(LockLevel.BOTH);
209                importScope(writableScope);
210                currentIndividualImportScope = writableScope;
211            }
212            return currentIndividualImportScope;
213        }
214    
215        @Override
216        public void importClassifierAlias(@NotNull Name importedClassifierName, @NotNull ClassifierDescriptor classifierDescriptor) {
217            checkMayWrite();
218    
219            if (DescriptorUtils.isSingleton(classifierDescriptor)) {
220                throw new IllegalStateException("must not be object: " + classifierDescriptor);
221            }
222    
223            getCurrentIndividualImportScope().addClassifierAlias(importedClassifierName, classifierDescriptor);
224        }
225        
226        
227        @Override
228        public void importNamespaceAlias(@NotNull Name aliasName, @NotNull NamespaceDescriptor namespaceDescriptor) {
229            checkMayWrite();
230    
231            getCurrentIndividualImportScope().addNamespaceAlias(aliasName, namespaceDescriptor);
232        }
233    
234        @Override
235        public void importFunctionAlias(@NotNull Name aliasName, @NotNull FunctionDescriptor functionDescriptor) {
236            checkMayWrite();
237    
238            getCurrentIndividualImportScope().addFunctionAlias(aliasName, functionDescriptor);
239        }
240    
241        @Override
242        public void importVariableAlias(@NotNull Name aliasName, @NotNull VariableDescriptor variableDescriptor) {
243            checkMayWrite();
244    
245            getCurrentIndividualImportScope().addVariableAlias(aliasName, variableDescriptor);
246        }
247    
248        @Override
249        public void clearImports() {
250            currentIndividualImportScope = null;
251            getImports().clear();
252        }
253    
254        @Override
255        public String toString() {
256            return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this)) + " " + debugName + " for " + getContainingDeclaration();
257        }
258    
259        @TestOnly
260        @Override
261        public void printScopeStructure(@NotNull Printer p) {
262            p.println(getClass().getSimpleName(), ": ", debugName, " for ", getContainingDeclaration(), " {");
263            p.pushIndent();
264    
265            p.println("lockLevel = ", lockLevel);
266    
267            printAdditionalScopeStructure(p);
268    
269            p.print("worker = ");
270            getWorkerScope().printScopeStructure(p.withholdIndentOnce());
271    
272            if (getImports().isEmpty()) {
273                p.println("imports = {}");
274            }
275            else {
276                p.println("imports = {");
277                p.pushIndent();
278                for (JetScope anImport : getImports()) {
279                    anImport.printScopeStructure(p);
280                }
281                p.popIndent();
282                p.println("}");
283            }
284    
285            p.popIndent();
286            p.println("}");
287        }
288    
289        @TestOnly
290        protected abstract void printAdditionalScopeStructure(@NotNull Printer p);
291    }