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