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