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