001 /*
002 * Copyright 2010-2014 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.asJava;
018
019 import com.google.common.collect.Sets;
020 import com.intellij.navigation.ItemPresentation;
021 import com.intellij.navigation.ItemPresentationProviders;
022 import com.intellij.openapi.components.ServiceManager;
023 import com.intellij.openapi.project.Project;
024 import com.intellij.openapi.util.Comparing;
025 import com.intellij.psi.*;
026 import com.intellij.psi.impl.light.LightEmptyImplementsList;
027 import com.intellij.psi.impl.light.LightModifierList;
028 import com.intellij.psi.javadoc.PsiDocComment;
029 import com.intellij.psi.search.GlobalSearchScope;
030 import com.intellij.psi.util.CachedValue;
031 import com.intellij.psi.util.CachedValueProvider;
032 import com.intellij.psi.util.CachedValuesManager;
033 import com.intellij.psi.util.PsiModificationTracker;
034 import com.intellij.util.containers.SLRUCache;
035 import org.jetbrains.annotations.NonNls;
036 import org.jetbrains.annotations.NotNull;
037 import org.jetbrains.annotations.Nullable;
038 import org.jetbrains.annotations.ReadOnly;
039 import org.jetbrains.jet.lang.psi.JetClassOrObject;
040 import org.jetbrains.jet.lang.psi.JetFile;
041 import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
042 import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetJavaMirrorMarker;
043 import org.jetbrains.jet.lang.resolve.name.FqName;
044 import org.jetbrains.jet.plugin.JetLanguage;
045
046 import javax.swing.*;
047 import java.util.Collection;
048 import java.util.Collections;
049 import java.util.List;
050
051 public class KotlinLightClassForPackage extends KotlinWrappingLightClass implements JetJavaMirrorMarker {
052
053 public static class FileStubCache {
054
055 @NotNull
056 public static FileStubCache getInstance(@NotNull Project project) {
057 return ServiceManager.getService(project, FileStubCache.class);
058 }
059
060 private static final class Key {
061 private final FqName fqName;
062 private final GlobalSearchScope searchScope;
063
064 private Key(
065 @NotNull FqName fqName,
066 @NotNull GlobalSearchScope searchScope
067 ) {
068 this.fqName = fqName;
069 this.searchScope = searchScope;
070 }
071
072 @Override
073 public boolean equals(Object o) {
074 if (this == o) return true;
075 if (o == null || getClass() != o.getClass()) return false;
076
077 Key key = (Key) o;
078
079 if (!fqName.equals(key.fqName)) return false;
080 if (!searchScope.equals(key.searchScope)) return false;
081
082 return true;
083 }
084
085 @Override
086 public int hashCode() {
087 int result = fqName.hashCode();
088 result = 31 * result + searchScope.hashCode();
089 return result;
090 }
091 }
092
093 private final class CacheData {
094
095 private final SLRUCache<Key, CachedValue<KotlinPackageLightClassData>> cache = new SLRUCache<Key, CachedValue<KotlinPackageLightClassData>>(20, 30) {
096 @NotNull
097 @Override
098 public CachedValue<KotlinPackageLightClassData> createValue(Key key) {
099 KotlinJavaFileStubProvider<KotlinPackageLightClassData> stubProvider =
100 KotlinJavaFileStubProvider.createForPackageClass(project, key.fqName, key.searchScope);
101 return CachedValuesManager.getManager(project).createCachedValue(stubProvider, /*trackValue = */false);
102 }
103 };
104 }
105
106 private final Project project;
107 private final CachedValue<CacheData> cachedValue;
108
109 public FileStubCache(@NotNull Project project) {
110 this.project = project;
111 this.cachedValue = CachedValuesManager.getManager(project).createCachedValue(
112 new CachedValueProvider<CacheData>() {
113 @Nullable
114 @Override
115 public Result<CacheData> compute() {
116 return Result.create(new CacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
117 }
118 },
119 /*trackValue = */ false
120 );
121 }
122
123 @NotNull
124 public CachedValue<KotlinPackageLightClassData> get(
125 @NotNull FqName qualifiedName,
126 @NotNull GlobalSearchScope searchScope
127 ) {
128 synchronized (cachedValue) {
129 return cachedValue.getValue().cache.get(new Key(qualifiedName, searchScope));
130 }
131 }
132
133 }
134
135 private final FqName packageFqName;
136 private final FqName packageClassFqName; // derived from packageFqName
137 private final GlobalSearchScope searchScope;
138 private final Collection<JetFile> files;
139 private final int hashCode;
140 private final CachedValue<KotlinPackageLightClassData> lightClassDataCache;
141 private final PsiModifierList modifierList;
142 private final LightEmptyImplementsList implementsList;
143
144 private KotlinLightClassForPackage(
145 @NotNull PsiManager manager,
146 @NotNull FqName packageFqName,
147 @NotNull GlobalSearchScope searchScope,
148 @NotNull Collection<JetFile> files
149 ) {
150 super(manager, JetLanguage.INSTANCE);
151 this.modifierList = new LightModifierList(manager, JetLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL);
152 this.implementsList = new LightEmptyImplementsList(manager);
153 this.packageFqName = packageFqName;
154 this.packageClassFqName = PackageClassUtils.getPackageClassFqName(packageFqName);
155 this.searchScope = searchScope;
156 assert !files.isEmpty() : "No files for package " + packageFqName;
157 this.files = Sets.newHashSet(files); // needed for hashCode
158 this.hashCode = computeHashCode();
159 this.lightClassDataCache = FileStubCache.getInstance(getProject()).get(packageFqName, searchScope);
160 }
161
162 @Nullable
163 public static KotlinLightClassForPackage create(
164 @NotNull PsiManager manager,
165 @NotNull FqName qualifiedName,
166 @NotNull GlobalSearchScope searchScope,
167 @NotNull Collection<JetFile> files // this is redundant, but computing it multiple times is costly
168 ) {
169 for (JetFile file : files) {
170 if (LightClassUtil.belongsToKotlinBuiltIns(file)) return null;
171 }
172 return new KotlinLightClassForPackage(manager, qualifiedName, searchScope, files);
173 }
174
175 private static boolean allValid(Collection<JetFile> files) {
176 for (JetFile file : files) {
177 if (!file.isValid()) return false;
178 }
179 return true;
180 }
181
182 @Nullable
183 @Override
184 public JetClassOrObject getOrigin() {
185 return null;
186 }
187
188 @Nullable
189 @Override
190 public PsiModifierList getModifierList() {
191 return modifierList;
192 }
193
194 @Override
195 public boolean hasModifierProperty(@NonNls @NotNull String name) {
196 return modifierList.hasModifierProperty(name);
197 }
198
199 @Override
200 public boolean isDeprecated() {
201 return false;
202 }
203
204 @Override
205 public boolean isInterface() {
206 return false;
207 }
208
209 @Override
210 public boolean isAnnotationType() {
211 return false;
212 }
213
214 @Override
215 public boolean isEnum() {
216 return false;
217 }
218
219 @Nullable
220 @Override
221 public PsiClass getContainingClass() {
222 return null;
223 }
224
225 @Override
226 public boolean hasTypeParameters() {
227 return false;
228 }
229
230 @NotNull
231 @Override
232 public PsiTypeParameter[] getTypeParameters() {
233 return PsiTypeParameter.EMPTY_ARRAY;
234 }
235
236 @Nullable
237 @Override
238 public PsiTypeParameterList getTypeParameterList() {
239 return null;
240 }
241
242 @Nullable
243 @Override
244 public PsiDocComment getDocComment() {
245 return null;
246 }
247
248 @Nullable
249 @Override
250 public PsiReferenceList getImplementsList() {
251 return implementsList;
252 }
253
254 @NotNull
255 @Override
256 public PsiClassType[] getImplementsListTypes() {
257 return PsiClassType.EMPTY_ARRAY;
258 }
259
260 @Nullable
261 @Override
262 public PsiReferenceList getExtendsList() {
263 // TODO: Find a way to return just Object
264 return super.getExtendsList();
265 }
266
267 @NotNull
268 @Override
269 public PsiClassType[] getExtendsListTypes() {
270 // TODO see getExtendsList()
271 return super.getExtendsListTypes();
272 }
273
274 @Nullable
275 @Override
276 public PsiClass getSuperClass() {
277 // TODO see getExtendsList()
278 return super.getSuperClass();
279 }
280
281 @NotNull
282 @Override
283 public PsiClass[] getSupers() {
284 // TODO see getExtendsList()
285 return super.getSupers();
286 }
287
288 @NotNull
289 @Override
290 public PsiClassType[] getSuperTypes() {
291 // TODO see getExtendsList()
292 return super.getSuperTypes();
293 }
294
295 @Override
296 public PsiClass[] getInterfaces() {
297 return PsiClass.EMPTY_ARRAY;
298 }
299
300 @NotNull
301 @Override
302 public PsiClass[] getInnerClasses() {
303 return PsiClass.EMPTY_ARRAY;
304 }
305
306 @NotNull
307 @Override
308 public List<PsiClass> getOwnInnerClasses() {
309 return Collections.emptyList();
310 }
311
312 @NotNull
313 @Override
314 public PsiClass[] getAllInnerClasses() {
315 return PsiClass.EMPTY_ARRAY;
316 }
317
318 @NotNull
319 @Override
320 public PsiClassInitializer[] getInitializers() {
321 return PsiClassInitializer.EMPTY_ARRAY;
322 }
323
324 @Nullable
325 @Override
326 public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) {
327 return null;
328 }
329
330 @NotNull
331 @Override
332 public FqName getFqName() {
333 return packageClassFqName;
334 }
335
336 @Nullable
337 @Override
338 public String getName() {
339 return packageClassFqName.shortName().asString();
340 }
341
342 @Nullable
343 @Override
344 public String getQualifiedName() {
345 return packageClassFqName.asString();
346 }
347
348 @Override
349 public boolean isValid() {
350 return allValid(files);
351 }
352
353 @NotNull
354 @Override
355 public PsiElement copy() {
356 return new KotlinLightClassForPackage(getManager(), packageFqName, searchScope, files);
357 }
358
359 @NotNull
360 @Override
361 public PsiClass getDelegate() {
362 PsiClass psiClass = LightClassUtil.findClass(packageClassFqName, lightClassDataCache.getValue().getJavaFileStub());
363 if (psiClass == null) {
364 throw new IllegalStateException("Package class was not found " + packageFqName);
365 }
366 return psiClass;
367 }
368
369 @NotNull
370 @Override
371 public PsiElement getNavigationElement() {
372 return files.iterator().next();
373 }
374
375 @Override
376 public boolean isEquivalentTo(PsiElement another) {
377 return another instanceof PsiClass && Comparing.equal(((PsiClass) another).getQualifiedName(), getQualifiedName());
378 }
379
380 @Override
381 public ItemPresentation getPresentation() {
382 return ItemPresentationProviders.getItemPresentation(this);
383 }
384
385 @Override
386 public Icon getElementIcon(int flags) {
387 throw new UnsupportedOperationException("This should be done byt JetIconProvider");
388 }
389
390 @Override
391 public int hashCode() {
392 return hashCode;
393 }
394
395 private int computeHashCode() {
396 int result = getManager().hashCode();
397 result = 31 * result + files.hashCode();
398 result = 31 * result + packageFqName.hashCode();
399 return result;
400 }
401
402 @Override
403 public boolean equals(Object obj) {
404 if (this == obj) return true;
405 if (obj == null || getClass() != obj.getClass()) {
406 return false;
407 }
408
409 KotlinLightClassForPackage lightClass = (KotlinLightClassForPackage) obj;
410
411 if (this.hashCode != lightClass.hashCode) return false;
412 if (getManager() != lightClass.getManager()) return false;
413 if (!files.equals(lightClass.files)) return false;
414 if (!packageFqName.equals(lightClass.packageFqName)) return false;
415
416 return true;
417 }
418
419 @Override
420 public String toString() {
421 try {
422 return KotlinLightClassForPackage.class.getSimpleName() + ":" + getQualifiedName();
423 }
424 catch (Throwable e) {
425 return KotlinLightClassForPackage.class.getSimpleName() + ":" + e.toString();
426 }
427 }
428
429 //NOTE: this is only needed to compute plugin module info
430 @NotNull
431 @ReadOnly
432 public final Collection<JetFile> getFiles() {
433 return files;
434 }
435 }