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 }