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 org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
022    import org.jetbrains.jet.lang.descriptors.ModuleDescriptorImpl;
023    import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
024    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
025    import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorParent;
026    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
027    import org.jetbrains.jet.lang.resolve.java.*;
028    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaNamespaceDescriptor;
029    import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
030    import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
031    import org.jetbrains.jet.lang.resolve.java.scope.JavaClassStaticMembersScope;
032    import org.jetbrains.jet.lang.resolve.java.scope.JavaPackageScope;
033    import org.jetbrains.jet.lang.resolve.java.structure.JavaClass;
034    import org.jetbrains.jet.lang.resolve.java.structure.JavaField;
035    import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod;
036    import org.jetbrains.jet.lang.resolve.java.structure.JavaPackage;
037    import org.jetbrains.jet.lang.resolve.kotlin.DeserializedDescriptorResolver;
038    import org.jetbrains.jet.lang.resolve.kotlin.KotlinClassFinder;
039    import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass;
040    import org.jetbrains.jet.lang.resolve.name.FqName;
041    import org.jetbrains.jet.lang.resolve.name.Name;
042    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
043    
044    import javax.inject.Inject;
045    import java.util.*;
046    
047    import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.INCLUDE_KOTLIN_SOURCES;
048    
049    public final class JavaNamespaceResolver {
050    
051        @NotNull
052        public static final ModuleDescriptor FAKE_ROOT_MODULE = new ModuleDescriptorImpl(JavaDescriptorResolver.JAVA_ROOT,
053                                                                                         JavaBridgeConfiguration.ALL_JAVA_IMPORTS,
054                                                                                         JavaToKotlinClassMap.getInstance());
055        @NotNull
056        private final Map<FqName, JetScope> resolvedNamespaceCache = new HashMap<FqName, JetScope>();
057        @NotNull
058        private final Set<FqName> unresolvedCache = new HashSet<FqName>();
059    
060        private JavaClassFinder javaClassFinder;
061        private JavaResolverCache cache;
062        private JavaMemberResolver memberResolver;
063    
064        private DeserializedDescriptorResolver deserializedDescriptorResolver;
065        private KotlinClassFinder kotlinClassFinder;
066    
067        @Inject
068        public void setKotlinClassFinder(KotlinClassFinder kotlinClassFinder) {
069            this.kotlinClassFinder = kotlinClassFinder;
070        }
071    
072        @Inject
073        public void setJavaClassFinder(JavaClassFinder javaClassFinder) {
074            this.javaClassFinder = javaClassFinder;
075        }
076    
077        @Inject
078        public void setCache(JavaResolverCache cache) {
079            this.cache = cache;
080        }
081    
082        @Inject
083        public void setMemberResolver(@NotNull JavaMemberResolver memberResolver) {
084            this.memberResolver = memberResolver;
085        }
086    
087        @Inject
088        public void setDeserializedDescriptorResolver(DeserializedDescriptorResolver deserializedDescriptorResolver) {
089            this.deserializedDescriptorResolver = deserializedDescriptorResolver;
090        }
091    
092        @Nullable
093        public NamespaceDescriptor resolveNamespace(@NotNull FqName qualifiedName, @NotNull DescriptorSearchRule searchRule) {
094            if (searchRule == INCLUDE_KOTLIN_SOURCES) {
095                NamespaceDescriptor kotlinNamespaceDescriptor = cache.getPackageResolvedFromSource(qualifiedName);
096                if (kotlinNamespaceDescriptor != null) {
097                    return kotlinNamespaceDescriptor;
098                }
099            }
100    
101            if (unresolvedCache.contains(qualifiedName)) {
102                return null;
103            }
104            JetScope scope = resolvedNamespaceCache.get(qualifiedName);
105            if (scope != null) {
106                return (NamespaceDescriptor) scope.getContainingDeclaration();
107            }
108    
109            NamespaceDescriptorParent parentNs = resolveParentNamespace(qualifiedName);
110            if (parentNs == null) {
111                return null;
112            }
113    
114            JavaNamespaceDescriptor javaNamespaceDescriptor = new JavaNamespaceDescriptor(
115                    parentNs,
116                    Collections.<AnnotationDescriptor>emptyList(), // TODO
117                    qualifiedName
118            );
119    
120            JetScope newScope = createNamespaceScope(qualifiedName, javaNamespaceDescriptor, true);
121            if (newScope == null) {
122                return null;
123            }
124    
125            javaNamespaceDescriptor.setMemberScope(newScope);
126    
127            return javaNamespaceDescriptor;
128        }
129    
130        @Nullable
131        private NamespaceDescriptorParent resolveParentNamespace(@NotNull FqName fqName) {
132            if (fqName.isRoot()) {
133                return FAKE_ROOT_MODULE;
134            }
135            else {
136                return resolveNamespace(fqName.parent(), INCLUDE_KOTLIN_SOURCES);
137            }
138        }
139    
140        @Nullable
141        private JetScope createNamespaceScope(@NotNull FqName fqName, @NotNull NamespaceDescriptor namespaceDescriptor, boolean record) {
142            JetScope namespaceScope = doCreateNamespaceScope(fqName, namespaceDescriptor, record);
143            cache(fqName, namespaceScope);
144            return namespaceScope;
145        }
146    
147        @Nullable
148        private JetScope doCreateNamespaceScope(
149                @NotNull FqName fqName,
150                @NotNull NamespaceDescriptor namespaceDescriptor,
151                boolean record
152        ) {
153            JavaPackage javaPackage = javaClassFinder.findPackage(fqName);
154            if (javaPackage != null) {
155                FqName packageClassFqName = PackageClassUtils.getPackageClassFqName(fqName);
156                KotlinJvmBinaryClass kotlinClass = kotlinClassFinder.find(packageClassFqName);
157    
158                cache.recordProperNamespace(namespaceDescriptor);
159    
160                if (kotlinClass != null) {
161                    JetScope kotlinPackageScope = deserializedDescriptorResolver.createKotlinPackageScope(namespaceDescriptor, kotlinClass);
162                    if (kotlinPackageScope != null) {
163                        return kotlinPackageScope;
164                    }
165                }
166    
167    
168                // Otherwise (if psiClass is null or doesn't have a supported Kotlin annotation), it's a Java class and the package is empty
169                if (record) {
170                    cache.recordPackage(javaPackage, namespaceDescriptor);
171                }
172    
173                return new JavaPackageScope(namespaceDescriptor, javaPackage, fqName, memberResolver);
174            }
175    
176            JavaClass javaClass = javaClassFinder.findClass(fqName);
177            if (javaClass == null) {
178                return null;
179            }
180    
181            if (DescriptorResolverUtils.isCompiledKotlinClassOrPackageClass(javaClass)) {
182                return null;
183            }
184            if (!hasStaticMembers(javaClass)) {
185                return null;
186            }
187    
188            cache.recordClassStaticMembersNamespace(namespaceDescriptor);
189    
190            if (record) {
191                cache.recordPackage(javaClass, namespaceDescriptor);
192            }
193    
194            return new JavaClassStaticMembersScope(namespaceDescriptor, fqName, javaClass, memberResolver);
195        }
196    
197        private void cache(@NotNull FqName fqName, @Nullable JetScope packageScope) {
198            if (packageScope == null) {
199                unresolvedCache.add(fqName);
200                return;
201            }
202            JetScope oldValue = resolvedNamespaceCache.put(fqName, packageScope);
203            if (oldValue != null) {
204                throw new IllegalStateException("rewrite at " + fqName);
205            }
206        }
207    
208        @Nullable
209        public JetScope getJavaPackageScopeForExistingNamespaceDescriptor(@NotNull NamespaceDescriptor namespaceDescriptor) {
210            FqName fqName = DescriptorUtils.getFQName(namespaceDescriptor).toSafe();
211            if (unresolvedCache.contains(fqName)) {
212                throw new IllegalStateException(
213                        "This means that we are trying to create a Java package, but have a package with the same FQN defined in Kotlin: " +
214                        fqName);
215            }
216            JetScope alreadyResolvedScope = resolvedNamespaceCache.get(fqName);
217            if (alreadyResolvedScope != null) {
218                return alreadyResolvedScope;
219            }
220            return createNamespaceScope(fqName, namespaceDescriptor, false);
221        }
222    
223        private static boolean hasStaticMembers(@NotNull JavaClass javaClass) {
224            for (JavaMethod method : javaClass.getMethods()) {
225                if (method.isStatic() && !DescriptorResolverUtils.shouldBeInEnumClassObject(method)) {
226                    return true;
227                }
228            }
229    
230            for (JavaField field : javaClass.getFields()) {
231                if (field.isStatic() && !field.isEnumEntry()) {
232                    return true;
233                }
234            }
235    
236            for (JavaClass nestedClass : javaClass.getInnerClasses()) {
237                if (SingleAbstractMethodUtils.isSamInterface(nestedClass)) {
238                    return true;
239                }
240                if (nestedClass.isStatic() && hasStaticMembers(nestedClass)) {
241                    return true;
242                }
243            }
244    
245            return false;
246        }
247    
248        @NotNull
249        public Collection<Name> getClassNamesInPackage(@NotNull FqName packageName) {
250            JavaPackage javaPackage = javaClassFinder.findPackage(packageName);
251            if (javaPackage == null) return Collections.emptyList();
252    
253            Collection<JavaClass> classes = DescriptorResolverUtils.getClassesInPackage(javaPackage);
254            List<Name> result = new ArrayList<Name>(classes.size());
255            for (JavaClass javaClass : classes) {
256                if (DescriptorResolverUtils.isCompiledKotlinClass(javaClass)) {
257                    result.add(javaClass.getName());
258                }
259            }
260    
261            return result;
262        }
263    }