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.java.resolver;
018
019import com.google.common.collect.Maps;
020import com.google.common.collect.Sets;
021import com.intellij.psi.PsiClass;
022import com.intellij.psi.PsiMember;
023import com.intellij.psi.PsiModifier;
024import com.intellij.psi.PsiPackage;
025import com.intellij.util.containers.ContainerUtil;
026import org.jetbrains.annotations.NotNull;
027import org.jetbrains.annotations.Nullable;
028import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
029import org.jetbrains.jet.lang.descriptors.ModuleDescriptorImpl;
030import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
031import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorParent;
032import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
033import org.jetbrains.jet.lang.resolve.BindingContext;
034import org.jetbrains.jet.lang.resolve.BindingTrace;
035import org.jetbrains.jet.lang.resolve.DescriptorUtils;
036import org.jetbrains.jet.lang.resolve.java.*;
037import org.jetbrains.jet.lang.resolve.java.descriptor.JavaNamespaceDescriptor;
038import org.jetbrains.jet.lang.resolve.java.kt.JetPackageClassAnnotation;
039import org.jetbrains.jet.lang.resolve.java.provider.MembersCache;
040import org.jetbrains.jet.lang.resolve.java.scope.JavaBaseScope;
041import org.jetbrains.jet.lang.resolve.java.scope.JavaClassStaticMembersScope;
042import org.jetbrains.jet.lang.resolve.java.scope.JavaPackageScopeWithoutMembers;
043import org.jetbrains.jet.lang.resolve.java.scope.JavaScopeForKotlinNamespace;
044import org.jetbrains.jet.lang.resolve.name.FqName;
045
046import javax.inject.Inject;
047import java.util.Collections;
048import java.util.Map;
049import java.util.Set;
050
051public final class JavaNamespaceResolver {
052
053    @NotNull
054    public static final ModuleDescriptor FAKE_ROOT_MODULE = new ModuleDescriptorImpl(JavaDescriptorResolver.JAVA_ROOT,
055                                                                                     JavaBridgeConfiguration.ALL_JAVA_IMPORTS,
056                                                                                     JavaToKotlinClassMap.getInstance());
057    @NotNull
058    private final Map<FqName, JavaBaseScope> resolvedNamespaceCache = Maps.newHashMap();
059    @NotNull
060    private final Set<FqName> unresolvedCache = Sets.newHashSet();
061
062    private PsiClassFinder psiClassFinder;
063    private BindingTrace trace;
064    private JavaSemanticServices javaSemanticServices;
065
066    public JavaNamespaceResolver() {
067    }
068
069    @Inject
070    public void setPsiClassFinder(PsiClassFinder psiClassFinder) {
071        this.psiClassFinder = psiClassFinder;
072    }
073
074    @Inject
075    public void setTrace(BindingTrace trace) {
076        this.trace = trace;
077    }
078
079    @Inject
080    public void setJavaSemanticServices(JavaSemanticServices javaSemanticServices) {
081        this.javaSemanticServices = javaSemanticServices;
082    }
083
084    @Nullable
085    public NamespaceDescriptor resolveNamespace(@NotNull FqName qualifiedName, @NotNull DescriptorSearchRule searchRule) {
086        // First, let's check that there is no Kotlin package:
087        NamespaceDescriptor kotlinNamespaceDescriptor = javaSemanticServices.getKotlinNamespaceDescriptor(qualifiedName);
088        if (kotlinNamespaceDescriptor != null) {
089            return searchRule.processFoundInKotlin(kotlinNamespaceDescriptor);
090        }
091
092        if (unresolvedCache.contains(qualifiedName)) {
093            return null;
094        }
095        JavaBaseScope scope = resolvedNamespaceCache.get(qualifiedName);
096        if (scope != null) {
097            return (NamespaceDescriptor) scope.getContainingDeclaration();
098        }
099
100        NamespaceDescriptorParent parentNs = resolveParentNamespace(qualifiedName);
101        if (parentNs == null) {
102            return null;
103        }
104
105        JavaNamespaceDescriptor javaNamespaceDescriptor = new JavaNamespaceDescriptor(
106                parentNs,
107                Collections.<AnnotationDescriptor>emptyList(), // TODO
108                qualifiedName
109        );
110
111        JavaBaseScope newScope = createNamespaceScope(qualifiedName, javaNamespaceDescriptor);
112        if (newScope == null) {
113            return null;
114        }
115
116        trace.record(BindingContext.NAMESPACE, newScope.getPsiElement(), javaNamespaceDescriptor);
117
118        javaNamespaceDescriptor.setMemberScope(newScope);
119
120        return javaNamespaceDescriptor;
121    }
122
123    @Nullable
124    public NamespaceDescriptor resolveNamespace(@NotNull FqName qualifiedName) {
125        return resolveNamespace(qualifiedName, DescriptorSearchRule.ERROR_IF_FOUND_IN_KOTLIN);
126    }
127
128    @Nullable
129    private NamespaceDescriptorParent resolveParentNamespace(@NotNull FqName fqName) {
130        if (fqName.isRoot()) {
131            return FAKE_ROOT_MODULE;
132        }
133        else {
134            return resolveNamespace(fqName.parent(), DescriptorSearchRule.INCLUDE_KOTLIN);
135        }
136    }
137
138    @Nullable
139    private JavaBaseScope createNamespaceScope(
140            @NotNull FqName fqName,
141            @NotNull NamespaceDescriptor namespaceDescriptor
142    ) {
143        JavaBaseScope namespaceScope = doCreateNamespaceScope(fqName, namespaceDescriptor);
144        cache(fqName, namespaceScope);
145        return namespaceScope;
146    }
147
148    @Nullable
149    private JavaBaseScope doCreateNamespaceScope(
150            @NotNull FqName fqName,
151            @NotNull NamespaceDescriptor namespaceDescriptor
152    ) {
153        PsiPackage psiPackage = psiClassFinder.findPsiPackage(fqName);
154        if (psiPackage != null) {
155            PsiClass psiClass = getPsiClassForJavaPackageScope(fqName);
156            trace.record(JavaBindingContext.JAVA_NAMESPACE_KIND, namespaceDescriptor, JavaNamespaceKind.PROPER);
157            if (psiClass == null) {
158                return new JavaPackageScopeWithoutMembers(
159                        namespaceDescriptor,
160                        javaSemanticServices.getPsiDeclarationProviderFactory().createDeclarationProviderForNamespaceWithoutMembers(psiPackage),
161                        fqName, javaSemanticServices);
162            }
163
164            AbiVersionUtil.checkAbiVersion(psiClass, JetPackageClassAnnotation.get(psiClass), trace);
165            return new JavaScopeForKotlinNamespace(
166                    namespaceDescriptor,
167                    javaSemanticServices.getPsiDeclarationProviderFactory().createDeclarationForKotlinNamespace(psiPackage, psiClass),
168                    fqName, javaSemanticServices);
169        }
170
171        PsiClass psiClass = psiClassFinder.findPsiClass(fqName, PsiClassFinder.RuntimeClassesHandleMode.IGNORE);
172        if (psiClass == null) {
173            return null;
174        }
175        if (DescriptorResolverUtils.isKotlinClass(psiClass)) {
176            return null;
177        }
178        if (!hasStaticMembers(psiClass)) {
179            return null;
180        }
181        trace.record(JavaBindingContext.JAVA_NAMESPACE_KIND, namespaceDescriptor, JavaNamespaceKind.CLASS_STATICS);
182        return new JavaClassStaticMembersScope(
183                namespaceDescriptor,
184                javaSemanticServices.getPsiDeclarationProviderFactory().createDeclarationProviderForClassStaticMembers(psiClass),
185                fqName, javaSemanticServices);
186    }
187
188    private void cache(@NotNull FqName fqName, @Nullable JavaBaseScope packageScope) {
189        if (packageScope == null) {
190            unresolvedCache.add(fqName);
191            return;
192        }
193        JavaBaseScope oldValue = resolvedNamespaceCache.put(fqName, packageScope);
194        if (oldValue != null) {
195            throw new IllegalStateException("rewrite at " + fqName);
196        }
197    }
198
199    @Nullable
200    public JavaBaseScope getJavaPackageScopeForExistingNamespaceDescriptor(@NotNull NamespaceDescriptor namespaceDescriptor) {
201        FqName fqName = DescriptorUtils.getFQName(namespaceDescriptor).toSafe();
202        if (unresolvedCache.contains(fqName)) {
203            throw new IllegalStateException(
204                    "This means that we are trying to create a Java package, but have a package with the same FQN defined in Kotlin: " +
205                    fqName);
206        }
207        JavaBaseScope alreadyResolvedScope = resolvedNamespaceCache.get(fqName);
208        if (alreadyResolvedScope != null) {
209            return alreadyResolvedScope;
210        }
211        return createNamespaceScope(fqName, namespaceDescriptor);
212    }
213
214    @Nullable
215    private PsiClass getPsiClassForJavaPackageScope(@NotNull FqName packageFQN) {
216        return psiClassFinder.findPsiClass(PackageClassUtils.getPackageClassFqName(packageFQN), PsiClassFinder.RuntimeClassesHandleMode.IGNORE);
217    }
218
219    private static boolean hasStaticMembers(@NotNull PsiClass psiClass) {
220        for (PsiMember member : ContainerUtil.concat(psiClass.getMethods(), psiClass.getFields())) {
221            if (member.hasModifierProperty(PsiModifier.STATIC) && !DescriptorResolverUtils.shouldBeInEnumClassObject(member)) {
222                return true;
223            }
224        }
225
226        for (PsiClass nestedClass : psiClass.getInnerClasses()) {
227            if (MembersCache.isSamInterface(nestedClass)) {
228                return true;
229            }
230            if (nestedClass.hasModifierProperty(PsiModifier.STATIC) && hasStaticMembers(nestedClass)) {
231                return true;
232            }
233        }
234
235        return false;
236    }
237}