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