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 }