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.java.resolver;
018    
019    import com.google.common.collect.Maps;
020    import com.google.common.collect.Sets;
021    import com.intellij.psi.PsiClass;
022    import com.intellij.psi.PsiMember;
023    import com.intellij.psi.PsiModifier;
024    import com.intellij.psi.PsiPackage;
025    import com.intellij.util.containers.ContainerUtil;
026    import org.jetbrains.annotations.NotNull;
027    import org.jetbrains.annotations.Nullable;
028    import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
029    import org.jetbrains.jet.lang.descriptors.ModuleDescriptorImpl;
030    import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
031    import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorParent;
032    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
033    import org.jetbrains.jet.lang.resolve.BindingContext;
034    import org.jetbrains.jet.lang.resolve.BindingTrace;
035    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
036    import org.jetbrains.jet.lang.resolve.java.*;
037    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaNamespaceDescriptor;
038    import org.jetbrains.jet.lang.resolve.java.kt.JetPackageClassAnnotation;
039    import org.jetbrains.jet.lang.resolve.java.provider.MembersCache;
040    import org.jetbrains.jet.lang.resolve.java.scope.JavaBaseScope;
041    import org.jetbrains.jet.lang.resolve.java.scope.JavaClassStaticMembersScope;
042    import org.jetbrains.jet.lang.resolve.java.scope.JavaPackageScopeWithoutMembers;
043    import org.jetbrains.jet.lang.resolve.java.scope.JavaScopeForKotlinNamespace;
044    import org.jetbrains.jet.lang.resolve.name.FqName;
045    
046    import javax.inject.Inject;
047    import java.util.Collections;
048    import java.util.Map;
049    import java.util.Set;
050    
051    public 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    }