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