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