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.Lists; 020 import com.google.common.collect.Sets; 021 import com.intellij.openapi.util.Comparing; 022 import com.intellij.openapi.util.Key; 023 import com.intellij.openapi.util.NullableLazyValue; 024 import com.intellij.openapi.util.Pair; 025 import com.intellij.openapi.vfs.VirtualFile; 026 import com.intellij.psi.*; 027 import com.intellij.psi.PsiPackage; 028 import com.intellij.psi.impl.compiled.ClsFileImpl; 029 import com.intellij.psi.impl.java.stubs.PsiJavaFileStub; 030 import com.intellij.psi.impl.light.LightClass; 031 import com.intellij.psi.impl.light.LightMethod; 032 import com.intellij.psi.scope.PsiScopeProcessor; 033 import com.intellij.psi.search.SearchScope; 034 import com.intellij.psi.stubs.PsiClassHolderFileStub; 035 import com.intellij.psi.util.CachedValue; 036 import com.intellij.psi.util.CachedValuesManager; 037 import com.intellij.psi.util.PsiTreeUtil; 038 import com.intellij.util.ArrayUtil; 039 import com.intellij.util.IncorrectOperationException; 040 import kotlin.KotlinPackage; 041 import kotlin.jvm.functions.Function1; 042 import org.jetbrains.annotations.NonNls; 043 import org.jetbrains.annotations.NotNull; 044 import org.jetbrains.annotations.Nullable; 045 import org.jetbrains.kotlin.builtins.KotlinBuiltIns; 046 import org.jetbrains.kotlin.codegen.binding.PsiCodegenPredictor; 047 import org.jetbrains.kotlin.descriptors.ClassDescriptor; 048 import org.jetbrains.kotlin.descriptors.ClassifierDescriptor; 049 import org.jetbrains.kotlin.idea.JetLanguage; 050 import org.jetbrains.kotlin.lexer.JetModifierKeywordToken; 051 import org.jetbrains.kotlin.name.FqName; 052 import org.jetbrains.kotlin.psi.*; 053 import org.jetbrains.kotlin.resolve.DescriptorUtils; 054 import org.jetbrains.kotlin.resolve.jvm.JvmClassName; 055 import org.jetbrains.kotlin.types.JetType; 056 057 import javax.swing.*; 058 import java.util.Collection; 059 import java.util.List; 060 061 import static org.jetbrains.kotlin.lexer.JetTokens.*; 062 063 public class KotlinLightClassForExplicitDeclaration extends KotlinWrappingLightClass implements JetJavaMirrorMarker { 064 private final static Key<CachedValue<OutermostKotlinClassLightClassData>> JAVA_API_STUB = Key.create("JAVA_API_STUB"); 065 066 @Nullable 067 public static KotlinLightClassForExplicitDeclaration create(@NotNull PsiManager manager, @NotNull JetClassOrObject classOrObject) { 068 if (LightClassUtil.belongsToKotlinBuiltIns(classOrObject.getContainingJetFile())) { 069 return null; 070 } 071 072 FqName fqName = predictFqName(classOrObject); 073 if (fqName == null) return null; 074 075 if (classOrObject instanceof JetObjectDeclaration && ((JetObjectDeclaration) classOrObject).isObjectLiteral()) { 076 return new KotlinLightClassForAnonymousDeclaration(manager, fqName, classOrObject); 077 } 078 return new KotlinLightClassForExplicitDeclaration(manager, fqName, classOrObject); 079 } 080 081 @Nullable 082 private static FqName predictFqName(@NotNull JetClassOrObject classOrObject) { 083 if (classOrObject.isLocal()) { 084 LightClassDataForKotlinClass data = getLightClassDataExactly(classOrObject); 085 return data == null ? null : data.getJvmQualifiedName(); 086 } 087 String internalName = PsiCodegenPredictor.getPredefinedJvmInternalName(classOrObject); 088 return internalName == null ? null : JvmClassName.byInternalName(internalName).getFqNameForClassNameWithoutDollars(); 089 } 090 091 private final FqName classFqName; // FqName of (possibly inner) class 092 protected final JetClassOrObject classOrObject; 093 private PsiClass delegate; 094 095 private final NullableLazyValue<PsiElement> parent = new NullableLazyValue<PsiElement>() { 096 @Nullable 097 @Override 098 protected PsiElement compute() { 099 if (classOrObject.isLocal()) { 100 //noinspection unchecked 101 PsiElement declaration = JetPsiUtil.getTopmostParentOfTypes( 102 classOrObject, 103 JetNamedFunction.class, JetProperty.class, JetClassInitializer.class, JetParameter.class 104 ); 105 106 if (declaration instanceof JetParameter) { 107 declaration = PsiTreeUtil.getParentOfType(declaration, JetNamedDeclaration.class); 108 } 109 110 if (declaration instanceof JetNamedFunction) { 111 JetNamedFunction function = (JetNamedFunction) declaration; 112 return getParentByPsiMethod(LightClassUtil.getLightClassMethod(function), function.getName(), false); 113 } 114 115 // Represent the property as a fake method with the same name 116 if (declaration instanceof JetProperty) { 117 JetProperty property = (JetProperty) declaration; 118 return getParentByPsiMethod(LightClassUtil.getLightClassPropertyMethods(property).getGetter(), property.getName(), true); 119 } 120 121 if (declaration instanceof JetClassInitializer) { 122 PsiElement parent = declaration.getParent(); 123 PsiElement grandparent = parent.getParent(); 124 125 if (parent instanceof JetClassBody && grandparent instanceof JetClassOrObject) { 126 return LightClassUtil.getPsiClass((JetClassOrObject) grandparent); 127 } 128 } 129 130 if (declaration instanceof JetClass) { 131 return LightClassUtil.getPsiClass((JetClass) declaration); 132 } 133 } 134 135 return classOrObject.getParent() == classOrObject.getContainingFile() ? getContainingFile() : getContainingClass(); 136 } 137 138 private PsiElement getParentByPsiMethod(PsiMethod method, final String name, boolean forceMethodWrapping) { 139 if (method == null || name == null) return null; 140 141 PsiClass containingClass = method.getContainingClass(); 142 if (containingClass == null) return null; 143 144 final String currentFileName = classOrObject.getContainingFile().getName(); 145 146 boolean createWrapper = forceMethodWrapping; 147 // Use PsiClass wrapper instead of package light class to avoid names like "FooPackage" in Type Hierarchy and related views 148 if (containingClass instanceof KotlinLightClassForPackage) { 149 containingClass = new LightClass(containingClass, JetLanguage.INSTANCE) { 150 @Nullable 151 @Override 152 public String getName() { 153 return currentFileName; 154 } 155 }; 156 createWrapper = true; 157 } 158 159 if (createWrapper) { 160 return new LightMethod(myManager, method, containingClass, JetLanguage.INSTANCE) { 161 @Override 162 public PsiElement getParent() { 163 return getContainingClass(); 164 } 165 166 @NotNull 167 @Override 168 public String getName() { 169 return name; 170 } 171 }; 172 } 173 174 return method; 175 } 176 }; 177 178 @Nullable 179 private PsiModifierList modifierList; 180 181 private final NullableLazyValue<PsiTypeParameterList> typeParameterList = new NullableLazyValue<PsiTypeParameterList>() { 182 @Nullable 183 @Override 184 protected PsiTypeParameterList compute() { 185 return LightClassUtil.buildLightTypeParameterList(KotlinLightClassForExplicitDeclaration.this, classOrObject); 186 } 187 }; 188 189 KotlinLightClassForExplicitDeclaration( 190 @NotNull PsiManager manager, 191 @NotNull FqName name, 192 @NotNull JetClassOrObject classOrObject 193 ) { 194 super(manager); 195 this.classFqName = name; 196 this.classOrObject = classOrObject; 197 } 198 199 @Override 200 @NotNull 201 public JetClassOrObject getOrigin() { 202 return classOrObject; 203 } 204 205 @NotNull 206 @Override 207 public FqName getFqName() { 208 return classFqName; 209 } 210 211 @NotNull 212 @Override 213 public PsiElement copy() { 214 return new KotlinLightClassForExplicitDeclaration(getManager(), classFqName, (JetClassOrObject) classOrObject.copy()); 215 } 216 217 @NotNull 218 @Override 219 public PsiClass getDelegate() { 220 if (delegate == null) { 221 PsiJavaFileStub javaFileStub = getJavaFileStub(); 222 223 PsiClass psiClass = LightClassUtil.findClass(classFqName, javaFileStub); 224 if (psiClass == null) { 225 JetClassOrObject outermostClassOrObject = getOutermostClassOrObject(classOrObject); 226 throw new IllegalStateException("Class was not found " + classFqName + "\n" + 227 "in " + outermostClassOrObject.getContainingFile().getText() + "\n" + 228 "stub: \n" + javaFileStub.getPsi().getText()); 229 } 230 delegate = psiClass; 231 } 232 233 return delegate; 234 } 235 236 @NotNull 237 private PsiJavaFileStub getJavaFileStub() { 238 return getLightClassData().getJavaFileStub(); 239 } 240 241 @Nullable 242 protected final ClassDescriptor getDescriptor() { 243 LightClassDataForKotlinClass data = getLightClassDataExactly(classOrObject); 244 return data != null ? data.getDescriptor() : null; 245 } 246 247 @NotNull 248 private OutermostKotlinClassLightClassData getLightClassData() { 249 return getLightClassData(classOrObject); 250 } 251 252 @NotNull 253 public static OutermostKotlinClassLightClassData getLightClassData(@NotNull JetClassOrObject classOrObject) { 254 JetClassOrObject outermostClassOrObject = getOutermostClassOrObject(classOrObject); 255 return CachedValuesManager.getManager(classOrObject.getProject()).getCachedValue( 256 outermostClassOrObject, 257 JAVA_API_STUB, 258 KotlinJavaFileStubProvider.createForDeclaredClass(outermostClassOrObject), 259 /*trackValue = */false 260 ); 261 } 262 263 @Nullable 264 private static LightClassDataForKotlinClass getLightClassDataExactly(JetClassOrObject classOrObject) { 265 OutermostKotlinClassLightClassData data = getLightClassData(classOrObject); 266 return data.getClassOrObject().equals(classOrObject) ? data : data.getAllInnerClasses().get(classOrObject); 267 } 268 269 @NotNull 270 private static JetClassOrObject getOutermostClassOrObject(@NotNull JetClassOrObject classOrObject) { 271 JetClassOrObject outermostClass = JetPsiUtil.getOutermostClassOrObject(classOrObject); 272 if (outermostClass == null) { 273 throw new IllegalStateException("Attempt to build a light class for a local class: " + classOrObject.getText()); 274 } 275 else { 276 return outermostClass; 277 } 278 } 279 280 private final NullableLazyValue<PsiFile> _containingFile = new NullableLazyValue<PsiFile>() { 281 @Nullable 282 @Override 283 protected PsiFile compute() { 284 VirtualFile virtualFile = classOrObject.getContainingFile().getVirtualFile(); 285 assert virtualFile != null : "No virtual file for " + classOrObject.getText(); 286 return new ClsFileImpl(new ClassFileViewProvider(getManager(), virtualFile)) { 287 @NotNull 288 @Override 289 public String getPackageName() { 290 return classOrObject.getContainingJetFile().getPackageFqName().asString(); 291 } 292 293 @NotNull 294 @Override 295 public PsiClassHolderFileStub getStub() { 296 return getJavaFileStub(); 297 } 298 299 @SuppressWarnings("Contract") 300 @Override 301 public boolean processDeclarations( 302 @NotNull PsiScopeProcessor processor, 303 @NotNull ResolveState state, 304 PsiElement lastParent, 305 @NotNull PsiElement place 306 ) { 307 if (!super.processDeclarations(processor, state, lastParent, place)) return false; 308 309 // We have to explicitly process package declarations if current file belongs to default package 310 // so that Java resolve can find classes located in that package 311 String packageName = getPackageName(); 312 if (!packageName.isEmpty()) return true; 313 314 PsiPackage aPackage = JavaPsiFacade.getInstance(myManager.getProject()).findPackage(packageName); 315 if (aPackage != null && !aPackage.processDeclarations(processor, state, null, place)) return false; 316 317 return true; 318 } 319 }; 320 } 321 }; 322 323 @Override 324 public PsiFile getContainingFile() { 325 return _containingFile.getValue(); 326 } 327 328 @NotNull 329 @Override 330 public PsiElement getNavigationElement() { 331 return classOrObject; 332 } 333 334 @Override 335 public boolean isEquivalentTo(PsiElement another) { 336 return another instanceof PsiClass && Comparing.equal(((PsiClass) another).getQualifiedName(), getQualifiedName()); 337 } 338 339 @Override 340 public Icon getElementIcon(int flags) { 341 throw new UnsupportedOperationException("This should be done byt JetIconProvider"); 342 } 343 344 @Override 345 public boolean equals(Object o) { 346 if (this == o) return true; 347 if (o == null || getClass() != o.getClass()) return false; 348 349 KotlinLightClassForExplicitDeclaration aClass = (KotlinLightClassForExplicitDeclaration) o; 350 351 if (!classFqName.equals(aClass.classFqName)) return false; 352 353 return true; 354 } 355 356 @Override 357 public int hashCode() { 358 return classFqName.hashCode(); 359 } 360 361 @Nullable 362 @Override 363 public PsiClass getContainingClass() { 364 if (classOrObject.getParent() == classOrObject.getContainingFile()) return null; 365 return super.getContainingClass(); 366 } 367 368 @Nullable 369 @Override 370 public PsiElement getParent() { 371 return parent.getValue(); 372 } 373 374 @Override 375 public PsiElement getContext() { 376 return getParent(); 377 } 378 379 @Nullable 380 @Override 381 public PsiTypeParameterList getTypeParameterList() { 382 return typeParameterList.getValue(); 383 } 384 385 @NotNull 386 @Override 387 public PsiTypeParameter[] getTypeParameters() { 388 PsiTypeParameterList typeParameterList = getTypeParameterList(); 389 return typeParameterList == null ? PsiTypeParameter.EMPTY_ARRAY : typeParameterList.getTypeParameters(); 390 } 391 392 @Nullable 393 @Override 394 public String getName() { 395 return classFqName.shortName().asString(); 396 } 397 398 @Nullable 399 @Override 400 public String getQualifiedName() { 401 return classFqName.asString(); 402 } 403 404 @NotNull 405 @Override 406 public PsiModifierList getModifierList() { 407 if (modifierList == null) { 408 modifierList = new KotlinLightModifierList(this.getManager(), computeModifiers()) { 409 @Override 410 public PsiAnnotationOwner getDelegate() { 411 return KotlinLightClassForExplicitDeclaration.this.getDelegate().getModifierList(); 412 } 413 }; 414 } 415 return modifierList; 416 } 417 418 @NotNull 419 private String[] computeModifiers() { 420 Collection<String> psiModifiers = Sets.newHashSet(); 421 422 // PUBLIC, PROTECTED, PRIVATE, ABSTRACT, FINAL 423 //noinspection unchecked 424 List<Pair<JetModifierKeywordToken, String>> jetTokenToPsiModifier = Lists.newArrayList( 425 Pair.create(PUBLIC_KEYWORD, PsiModifier.PUBLIC), 426 Pair.create(INTERNAL_KEYWORD, PsiModifier.PUBLIC), 427 Pair.create(PROTECTED_KEYWORD, PsiModifier.PROTECTED), 428 Pair.create(FINAL_KEYWORD, PsiModifier.FINAL)); 429 430 for (Pair<JetModifierKeywordToken, String> tokenAndModifier : jetTokenToPsiModifier) { 431 if (classOrObject.hasModifier(tokenAndModifier.first)) { 432 psiModifiers.add(tokenAndModifier.second); 433 } 434 } 435 436 if (classOrObject.hasModifier(PRIVATE_KEYWORD)) { 437 // Top-level private class has PUBLIC visibility in Java 438 // Nested private class has PRIVATE visibility 439 psiModifiers.add(classOrObject.isTopLevel() ? PsiModifier.PUBLIC : PsiModifier.PRIVATE); 440 } 441 442 if (!psiModifiers.contains(PsiModifier.PRIVATE) && !psiModifiers.contains(PsiModifier.PROTECTED)) { 443 psiModifiers.add(PsiModifier.PUBLIC); // For internal (default) visibility 444 } 445 446 447 // FINAL 448 if (isAbstract(classOrObject)) { 449 psiModifiers.add(PsiModifier.ABSTRACT); 450 } 451 else if (!(classOrObject.hasModifier(OPEN_KEYWORD) || (classOrObject instanceof JetClass && ((JetClass) classOrObject).isEnum()))) { 452 psiModifiers.add(PsiModifier.FINAL); 453 } 454 455 if (!classOrObject.isTopLevel() && !classOrObject.hasModifier(INNER_KEYWORD)) { 456 psiModifiers.add(PsiModifier.STATIC); 457 } 458 459 return ArrayUtil.toStringArray(psiModifiers); 460 } 461 462 private boolean isAbstract(@NotNull JetClassOrObject object) { 463 return object.hasModifier(ABSTRACT_KEYWORD) || isInterface(); 464 } 465 466 @Override 467 public boolean hasModifierProperty(@NonNls @NotNull String name) { 468 return getModifierList().hasModifierProperty(name); 469 } 470 471 @Override 472 public boolean isDeprecated() { 473 JetModifierList jetModifierList = classOrObject.getModifierList(); 474 if (jetModifierList == null) { 475 return false; 476 } 477 478 FqName deprecatedFqName = KotlinBuiltIns.FQ_NAMES.deprecated; 479 String deprecatedName = deprecatedFqName.shortName().asString(); 480 481 for (JetAnnotationEntry annotationEntry : jetModifierList.getAnnotationEntries()) { 482 JetTypeReference typeReference = annotationEntry.getTypeReference(); 483 if (typeReference == null) continue; 484 485 JetTypeElement typeElement = typeReference.getTypeElement(); 486 if (!(typeElement instanceof JetUserType)) continue; // If it's not a user type, it's definitely not a ref to deprecated 487 488 FqName fqName = JetPsiUtil.toQualifiedName((JetUserType) typeElement); 489 if (fqName == null) continue; 490 491 if (deprecatedFqName.equals(fqName)) return true; 492 if (deprecatedName.equals(fqName.asString())) return true; 493 } 494 return false; 495 } 496 497 @Override 498 public boolean isInterface() { 499 if (!(classOrObject instanceof JetClass)) return false; 500 JetClass jetClass = (JetClass) classOrObject; 501 return jetClass.isInterface() || jetClass.isAnnotation(); 502 } 503 504 @Override 505 public boolean isAnnotationType() { 506 return classOrObject instanceof JetClass && ((JetClass) classOrObject).isAnnotation(); 507 } 508 509 @Override 510 public boolean isEnum() { 511 return classOrObject instanceof JetClass && ((JetClass) classOrObject).isEnum(); 512 } 513 514 @Override 515 public boolean hasTypeParameters() { 516 return classOrObject instanceof JetClass && !((JetClass) classOrObject).getTypeParameters().isEmpty(); 517 } 518 519 @Override 520 public boolean isValid() { 521 return classOrObject.isValid(); 522 } 523 524 @Override 525 public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) { 526 // Java inheritor check doesn't work when trait (interface in Java) subclasses Java class and for Kotlin local classes 527 if (baseClass instanceof KotlinLightClassForExplicitDeclaration || (isInterface() && !baseClass.isInterface())) { 528 String qualifiedName; 529 if (baseClass instanceof KotlinLightClassForExplicitDeclaration) { 530 ClassDescriptor baseDescriptor = ((KotlinLightClassForExplicitDeclaration) baseClass).getDescriptor(); 531 qualifiedName = baseDescriptor != null ? DescriptorUtils.getFqName(baseDescriptor).asString() : null; 532 } 533 else { 534 qualifiedName = baseClass.getQualifiedName(); 535 } 536 537 ClassDescriptor thisDescriptor = getDescriptor(); 538 return qualifiedName != null 539 && thisDescriptor != null 540 && checkSuperTypeByFQName(thisDescriptor, qualifiedName, checkDeep); 541 } 542 543 return super.isInheritor(baseClass, checkDeep); 544 } 545 546 @Override 547 public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException { 548 getOrigin().setName(name); 549 return this; 550 } 551 552 @Override 553 public String toString() { 554 try { 555 return KotlinLightClass.class.getSimpleName() + ":" + getQualifiedName(); 556 } 557 catch (Throwable e) { 558 return KotlinLightClass.class.getSimpleName() + ":" + e.toString(); 559 } 560 } 561 562 @NotNull 563 @Override 564 public List<PsiClass> getOwnInnerClasses() { 565 return KotlinPackage.filterNotNull( 566 KotlinPackage.map( 567 getDelegate().getInnerClasses(), 568 new Function1<PsiClass, PsiClass>() { 569 @Override 570 public PsiClass invoke(PsiClass aClass) { 571 JetClassOrObject declaration = (JetClassOrObject) ClsWrapperStubPsiFactory.getOriginalDeclaration(aClass); 572 return declaration != null ? create(myManager, declaration) : null; 573 } 574 } 575 ) 576 ); 577 } 578 579 @NotNull 580 @Override 581 public SearchScope getUseScope() { 582 return getOrigin().getUseScope(); 583 } 584 585 private static boolean checkSuperTypeByFQName(@NotNull ClassDescriptor classDescriptor, @NotNull String qualifiedName, Boolean deep) { 586 if (CommonClassNames.JAVA_LANG_OBJECT.equals(qualifiedName)) return true; 587 588 if (qualifiedName.equals(DescriptorUtils.getFqName(classDescriptor).asString())) return true; 589 590 for (JetType superType : classDescriptor.getTypeConstructor().getSupertypes()) { 591 ClassifierDescriptor superDescriptor = superType.getConstructor().getDeclarationDescriptor(); 592 593 if (superDescriptor instanceof ClassDescriptor) { 594 if (qualifiedName.equals(DescriptorUtils.getFqName(superDescriptor).asString())) return true; 595 596 if (deep) { 597 if (checkSuperTypeByFQName((ClassDescriptor)superDescriptor, qualifiedName, true)) { 598 return true; 599 } 600 } 601 } 602 } 603 604 return false; 605 } 606 }