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;
018    
019    import com.google.common.collect.Sets;
020    import com.intellij.psi.PsiElement;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.ModuleDescriptorImpl;
024    import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
025    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
026    import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorImpl;
027    import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorParent;
028    import org.jetbrains.jet.lang.psi.JetFile;
029    import org.jetbrains.jet.lang.psi.JetNamespaceHeader;
030    import org.jetbrains.jet.lang.psi.JetReferenceExpression;
031    import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
032    import org.jetbrains.jet.lang.resolve.name.FqName;
033    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
034    import org.jetbrains.jet.lang.resolve.name.Name;
035    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
036    import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler;
037    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
038    import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
039    
040    import javax.inject.Inject;
041    import java.util.Collection;
042    import java.util.Collections;
043    
044    import static org.jetbrains.jet.lang.resolve.BindingContext.*;
045    
046    public class NamespaceFactoryImpl implements NamespaceFactory {
047    
048        private ModuleDescriptorImpl moduleDescriptor;
049        private BindingTrace trace;
050    
051        @Inject
052        public void setModuleDescriptor(ModuleDescriptorImpl moduleDescriptor) {
053            this.moduleDescriptor = moduleDescriptor;
054        }
055    
056        @Inject
057        public void setTrace(BindingTrace trace) {
058            this.trace = trace;
059        }
060    
061        @NotNull
062        public NamespaceDescriptorImpl createNamespaceDescriptorPathIfNeeded(@NotNull JetFile file,
063                                                                             @NotNull JetScope outerScope,
064                                                                             @NotNull RedeclarationHandler handler) {
065            JetNamespaceHeader namespaceHeader = file.getNamespaceHeader();
066    
067            if (moduleDescriptor.getRootNamespaceDescriptorImpl() == null) {
068                createRootNamespaceDescriptorIfNeeded(null, moduleDescriptor, null, handler);
069            }
070    
071            NamespaceDescriptorImpl currentOwner = moduleDescriptor.getRootNamespaceDescriptorImpl();
072            if (currentOwner == null) {
073                throw new IllegalStateException("must be initialized 5 lines above");
074            }
075    
076            for (JetSimpleNameExpression nameExpression : namespaceHeader.getParentNamespaceNames()) {
077                Name namespaceName = Name.identifier(nameExpression.getReferencedName());
078    
079                NamespaceDescriptorImpl namespaceDescriptor = createNamespaceDescriptorIfNeeded(
080                        null, currentOwner, namespaceName, nameExpression, handler);
081    
082                trace.record(BindingContext.NAMESPACE_IS_SRC, namespaceDescriptor, true);
083                trace.record(RESOLUTION_SCOPE, nameExpression, outerScope);
084    
085                outerScope = namespaceDescriptor.getMemberScope();
086                currentOwner = namespaceDescriptor;
087            }
088    
089            NamespaceDescriptorImpl namespaceDescriptor;
090            Name name;
091            if (namespaceHeader.isRoot()) {
092                // previous call to createRootNamespaceDescriptorIfNeeded couldn't store occurrence for current file.
093                namespaceDescriptor = moduleDescriptor.getRootNamespaceDescriptorImpl();
094                storeBindingForFileAndExpression(file, null, namespaceDescriptor);
095            }
096            else {
097                name = namespaceHeader.getNameAsName();
098                namespaceDescriptor = createNamespaceDescriptorIfNeeded(
099                        file, currentOwner, name, namespaceHeader.getLastPartExpression(), handler);
100    
101                trace.record(RESOLUTION_SCOPE, namespaceHeader, outerScope);
102            }
103            trace.record(BindingContext.NAMESPACE_IS_SRC, namespaceDescriptor, true);
104    
105            return namespaceDescriptor;
106        }
107    
108        @Override
109        @NotNull
110        public NamespaceDescriptorImpl createNamespaceDescriptorPathIfNeeded(@NotNull FqName fqName) {
111            NamespaceDescriptorImpl owner = null;
112            for (FqName pathElement : fqName.path()) {
113                if (pathElement.isRoot()) {
114                    owner = createRootNamespaceDescriptorIfNeeded(null,
115                                                                  moduleDescriptor,
116                                                                  null,
117                                                                  RedeclarationHandler.DO_NOTHING);
118                }
119                else {
120                    assert owner != null : "Should never be null as first element in the path must be root";
121                    owner = createNamespaceDescriptorIfNeeded(null,
122                                                              owner,
123                                                              pathElement.shortName(),
124                                                              null,
125                                                              RedeclarationHandler.DO_NOTHING);
126                }
127    
128            }
129    
130            assert owner != null : "Should never be null as first element in the path must be root";
131            return owner;
132        }
133    
134        private NamespaceDescriptorImpl createRootNamespaceDescriptorIfNeeded(@Nullable JetFile file,
135                                                                              @NotNull ModuleDescriptorImpl owner,
136                                                                              @Nullable JetReferenceExpression expression,
137                                                                              @NotNull RedeclarationHandler handler) {
138            FqName fqName = FqName.ROOT;
139            NamespaceDescriptorImpl namespaceDescriptor = owner.getRootNamespaceDescriptorImpl();
140    
141            if (namespaceDescriptor == null) {
142                namespaceDescriptor = createNewNamespaceDescriptor(owner, FqNameUnsafe.ROOT_NAME, expression, handler, fqName);
143            }
144    
145            storeBindingForFileAndExpression(file, expression, namespaceDescriptor);
146    
147            return namespaceDescriptor;
148        }
149    
150        @NotNull
151        private NamespaceDescriptorImpl createNamespaceDescriptorIfNeeded(@Nullable JetFile file,
152                                                                          @NotNull NamespaceDescriptorImpl owner,
153                                                                          @NotNull Name name,
154                                                                          @Nullable JetReferenceExpression expression,
155                                                                          @NotNull RedeclarationHandler handler) {
156            FqName ownerFqName = DescriptorUtils.getFQName(owner).toSafe();
157            FqName fqName = ownerFqName.child(name);
158            // !!!
159            NamespaceDescriptorImpl namespaceDescriptor = (NamespaceDescriptorImpl) owner.getMemberScope().getDeclaredNamespace(name);
160    
161            if (namespaceDescriptor == null) {
162                namespaceDescriptor = createNewNamespaceDescriptor(owner, name, expression, handler, fqName);
163            }
164    
165            storeBindingForFileAndExpression(file, expression, namespaceDescriptor);
166    
167            return namespaceDescriptor;
168        }
169    
170        private NamespaceDescriptorImpl createNewNamespaceDescriptor(NamespaceDescriptorParent owner,
171                                                                     Name name,
172                                                                     PsiElement expression,
173                                                                     RedeclarationHandler handler,
174                                                                     FqName fqName) {
175            NamespaceDescriptorImpl namespaceDescriptor;
176            namespaceDescriptor = new NamespaceDescriptorImpl(
177                    owner,
178                    Collections.<AnnotationDescriptor>emptyList(), // TODO: annotations
179                    name
180            );
181            trace.record(FQNAME_TO_NAMESPACE_DESCRIPTOR, fqName, namespaceDescriptor);
182    
183            WritableScopeImpl scope = new WritableScopeImpl(JetScope.EMPTY, namespaceDescriptor, handler, "Namespace member scope");
184            scope.changeLockLevel(WritableScope.LockLevel.BOTH);
185    
186            namespaceDescriptor.initialize(scope);
187            scope.changeLockLevel(WritableScope.LockLevel.BOTH);
188            //
189            moduleDescriptor.getModuleConfiguration().extendNamespaceScope(trace, namespaceDescriptor, scope);
190            owner.addNamespace(namespaceDescriptor);
191            if (expression != null) {
192                trace.record(BindingContext.NAMESPACE, expression, namespaceDescriptor);
193            }
194            return namespaceDescriptor;
195        }
196    
197        private void storeBindingForFileAndExpression(@Nullable JetFile file,
198                                                      @Nullable JetReferenceExpression expression,
199                                                      @NotNull NamespaceDescriptor namespaceDescriptor) {
200            if (expression != null) {
201                trace.record(REFERENCE_TARGET, expression, namespaceDescriptor);
202            }
203    
204            if (file != null) {
205                trace.record(BindingContext.FILE_TO_NAMESPACE, file, namespaceDescriptor);
206    
207                // Register files corresponding to this namespace
208                // The trace currently does not support bi-di multimaps that would handle this task nicer
209                Collection<JetFile> files = trace.get(NAMESPACE_TO_FILES, namespaceDescriptor);
210                if (files == null) {
211                    files = Sets.newIdentityHashSet();
212                }
213                files.add(file);
214                trace.record(BindingContext.NAMESPACE_TO_FILES, namespaceDescriptor, files);
215            }
216        }
217    }