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