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 }