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
017package org.jetbrains.jet.lang.resolve;
018
019import com.google.common.collect.Sets;
020import com.intellij.psi.PsiElement;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.annotations.Nullable;
023import org.jetbrains.jet.lang.descriptors.ModuleDescriptorImpl;
024import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
025import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
026import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorImpl;
027import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorParent;
028import org.jetbrains.jet.lang.psi.JetFile;
029import org.jetbrains.jet.lang.psi.JetNamespaceHeader;
030import org.jetbrains.jet.lang.psi.JetReferenceExpression;
031import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
032import org.jetbrains.jet.lang.resolve.name.FqName;
033import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
034import org.jetbrains.jet.lang.resolve.name.Name;
035import org.jetbrains.jet.lang.resolve.scopes.JetScope;
036import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler;
037import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
038import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
039
040import javax.inject.Inject;
041import java.util.Collection;
042import java.util.Collections;
043
044import static org.jetbrains.jet.lang.resolve.BindingContext.*;
045
046public 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}