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.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.impl.PsiManagerImpl; 030 import com.intellij.psi.impl.compiled.ClsFileImpl; 031 import com.intellij.psi.impl.java.stubs.PsiJavaFileStub; 032 import com.intellij.psi.impl.light.LightModifierList; 033 import com.intellij.psi.impl.light.LightTypeParameterListBuilder; 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.util.ArrayUtil; 038 import com.intellij.util.IncorrectOperationException; 039 import org.jetbrains.annotations.NonNls; 040 import org.jetbrains.annotations.NotNull; 041 import org.jetbrains.annotations.Nullable; 042 import org.jetbrains.jet.codegen.binding.PsiCodegenPredictor; 043 import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 044 import org.jetbrains.jet.lang.psi.*; 045 import org.jetbrains.jet.lang.resolve.DescriptorUtils; 046 import org.jetbrains.jet.lang.resolve.java.JvmClassName; 047 import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetJavaMirrorMarker; 048 import org.jetbrains.jet.lang.resolve.name.FqName; 049 import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe; 050 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 051 import org.jetbrains.jet.lexer.JetKeywordToken; 052 import org.jetbrains.jet.plugin.JetLanguage; 053 054 import javax.swing.*; 055 import java.util.Arrays; 056 import java.util.Collection; 057 import java.util.List; 058 059 import static org.jetbrains.jet.lexer.JetTokens.*; 060 061 public class KotlinLightClassForExplicitDeclaration extends KotlinWrappingLightClass implements KotlinLightClass, JetJavaMirrorMarker { 062 private final static Key<CachedValue<PsiJavaFileStub>> JAVA_API_STUB = Key.create("JAVA_API_STUB"); 063 064 @Nullable 065 public static KotlinLightClassForExplicitDeclaration create(@NotNull PsiManager manager, @NotNull JetClassOrObject classOrObject) { 066 if (LightClassUtil.belongsToKotlinBuiltIns((JetFile) classOrObject.getContainingFile())) { 067 return null; 068 } 069 070 // TODO temporary not building light classes for local classes: e.g., they won't be visible in hierarchy 071 if (JetPsiUtil.getOutermostClassOrObject(classOrObject) == null) { 072 return null; 073 } 074 075 String jvmInternalName = PsiCodegenPredictor.getPredefinedJvmInternalName(classOrObject); 076 if (jvmInternalName == null) return null; 077 078 FqName fqName = JvmClassName.byInternalName(jvmInternalName).getFqNameForClassNameWithoutDollars(); 079 return new KotlinLightClassForExplicitDeclaration(manager, fqName, classOrObject); 080 } 081 082 private final FqName classFqName; // FqName of (possibly inner) class 083 private final JetClassOrObject classOrObject; 084 private PsiClass delegate; 085 086 @Nullable 087 private PsiModifierList modifierList; 088 089 private final NullableLazyValue<PsiTypeParameterList> typeParameterList = new NullableLazyValue<PsiTypeParameterList>() { 090 @Nullable 091 @Override 092 protected PsiTypeParameterList compute() { 093 LightTypeParameterListBuilder builder = new LightTypeParameterListBuilder(getManager(), getLanguage()); 094 if (classOrObject instanceof JetTypeParameterListOwner) { 095 JetTypeParameterListOwner typeParameterListOwner = (JetTypeParameterListOwner) classOrObject; 096 List<JetTypeParameter> parameters = typeParameterListOwner.getTypeParameters(); 097 for (int i = 0; i < parameters.size(); i++) { 098 JetTypeParameter jetTypeParameter = parameters.get(i); 099 String name = jetTypeParameter.getName(); 100 String safeName = name == null ? "__no_name__" : name; 101 builder.addParameter(new KotlinLightTypeParameter(KotlinLightClassForExplicitDeclaration.this, i, safeName)); 102 } 103 } 104 return builder; 105 } 106 }; 107 108 private KotlinLightClassForExplicitDeclaration( 109 @NotNull PsiManager manager, 110 @NotNull FqName name, 111 @NotNull JetClassOrObject classOrObject 112 ) { 113 super(manager, JetLanguage.INSTANCE); 114 this.classFqName = name; 115 this.classOrObject = classOrObject; 116 } 117 118 @NotNull 119 public JetClassOrObject getJetClassOrObject() { 120 return classOrObject; 121 } 122 123 @NotNull 124 @Override 125 public FqName getFqName() { 126 return classFqName; 127 } 128 129 @NotNull 130 @Override 131 public PsiElement copy() { 132 return new KotlinLightClassForExplicitDeclaration(getManager(), classFqName, classOrObject); 133 } 134 135 @NotNull 136 @Override 137 public PsiClass getDelegate() { 138 if (delegate == null) { 139 PsiJavaFileStub javaFileStub = getJavaFileStub(); 140 141 PsiClass psiClass = LightClassUtil.findClass(classFqName, javaFileStub); 142 if (psiClass == null) { 143 JetClassOrObject outermostClassOrObject = getOutermostClassOrObject(classOrObject); 144 throw new IllegalStateException("Class was not found " + classFqName + "\n" + 145 "in " + outermostClassOrObject.getContainingFile().getText() + "\n" + 146 "stub: \n" + javaFileStub.getPsi().getText()); 147 } 148 delegate = psiClass; 149 } 150 151 return delegate; 152 } 153 154 @NotNull 155 private PsiJavaFileStub getJavaFileStub() { 156 JetClassOrObject outermostClassOrObject = getOutermostClassOrObject(classOrObject); 157 return CachedValuesManager.getManager(getProject()).getCachedValue( 158 outermostClassOrObject, 159 JAVA_API_STUB, 160 KotlinJavaFileStubProvider.createForDeclaredTopLevelClass(outermostClassOrObject), 161 /*trackValue = */false); 162 } 163 164 @NotNull 165 private static JetClassOrObject getOutermostClassOrObject(@NotNull JetClassOrObject classOrObject) { 166 JetClassOrObject outermostClass = JetPsiUtil.getOutermostClassOrObject(classOrObject); 167 if (outermostClass == null) { 168 throw new IllegalStateException("Attempt to build a light class for a local class: " + classOrObject.getText()); 169 } 170 else { 171 return outermostClass; 172 } 173 } 174 175 private final NullableLazyValue<PsiFile> _containingFile = new NullableLazyValue<PsiFile>() { 176 @Nullable 177 @Override 178 protected PsiFile compute() { 179 VirtualFile virtualFile = classOrObject.getContainingFile().getVirtualFile(); 180 assert virtualFile != null : "No virtual file for " + classOrObject.getText(); 181 return new ClsFileImpl((PsiManagerImpl) getManager(), new ClassFileViewProvider(getManager(), virtualFile)) { 182 @NotNull 183 @Override 184 public String getPackageName() { 185 return JetPsiUtil.getFQName((JetFile) classOrObject.getContainingFile()).asString(); 186 } 187 188 @NotNull 189 @Override 190 public PsiClassHolderFileStub getStub() { 191 return getJavaFileStub(); 192 } 193 }; 194 } 195 }; 196 197 @Override 198 public PsiFile getContainingFile() { 199 return _containingFile.getValue(); 200 } 201 202 @NotNull 203 @Override 204 public PsiElement getNavigationElement() { 205 return classOrObject; 206 } 207 208 @Override 209 public boolean isEquivalentTo(PsiElement another) { 210 return another instanceof PsiClass && Comparing.equal(((PsiClass) another).getQualifiedName(), getQualifiedName()); 211 } 212 213 @Override 214 public ItemPresentation getPresentation() { 215 return ItemPresentationProviders.getItemPresentation(this); 216 } 217 218 @Override 219 public Icon getElementIcon(int flags) { 220 throw new UnsupportedOperationException("This should be done byt JetIconProvider"); 221 } 222 223 @Override 224 public boolean equals(Object o) { 225 if (this == o) return true; 226 if (o == null || getClass() != o.getClass()) return false; 227 228 KotlinLightClassForExplicitDeclaration aClass = (KotlinLightClassForExplicitDeclaration) o; 229 230 if (!classFqName.equals(aClass.classFqName)) return false; 231 232 return true; 233 } 234 235 @Override 236 public int hashCode() { 237 return classFqName.hashCode(); 238 } 239 240 @Nullable 241 @Override 242 public PsiClass getContainingClass() { 243 if (classOrObject.getParent() == classOrObject.getContainingFile()) return null; 244 return super.getContainingClass(); 245 } 246 247 @Nullable 248 @Override 249 public PsiElement getParent() { 250 if (classOrObject.getParent() == classOrObject.getContainingFile()) return getContainingFile(); 251 return getContainingClass(); 252 } 253 254 @Nullable 255 @Override 256 public PsiTypeParameterList getTypeParameterList() { 257 return typeParameterList.getValue(); 258 } 259 260 @NotNull 261 @Override 262 public PsiTypeParameter[] getTypeParameters() { 263 PsiTypeParameterList typeParameterList = getTypeParameterList(); 264 return typeParameterList == null ? PsiTypeParameter.EMPTY_ARRAY : typeParameterList.getTypeParameters(); 265 } 266 267 @Nullable 268 @Override 269 public String getName() { 270 return classFqName.shortName().asString(); 271 } 272 273 @Nullable 274 @Override 275 public String getQualifiedName() { 276 return classFqName.asString(); 277 } 278 279 @NotNull 280 @Override 281 public PsiModifierList getModifierList() { 282 if (modifierList == null) { 283 modifierList = new LightModifierList(getManager(), JetLanguage.INSTANCE, computeModifiers()); 284 } 285 return modifierList; 286 } 287 288 @NotNull 289 private String[] computeModifiers() { 290 boolean nestedClass = classOrObject.getParent() != classOrObject.getContainingFile(); 291 Collection<String> psiModifiers = Sets.newHashSet(); 292 293 // PUBLIC, PROTECTED, PRIVATE, ABSTRACT, FINAL 294 List<Pair<JetKeywordToken, String>> jetTokenToPsiModifier = Lists.newArrayList( 295 Pair.create(PUBLIC_KEYWORD, PsiModifier.PUBLIC), 296 Pair.create(INTERNAL_KEYWORD, PsiModifier.PUBLIC), 297 Pair.create(PROTECTED_KEYWORD, PsiModifier.PROTECTED), 298 Pair.create(FINAL_KEYWORD, PsiModifier.FINAL)); 299 300 for (Pair<JetKeywordToken, String> tokenAndModifier : jetTokenToPsiModifier) { 301 if (classOrObject.hasModifier(tokenAndModifier.first)) { 302 psiModifiers.add(tokenAndModifier.second); 303 } 304 } 305 306 if (classOrObject.hasModifier(PRIVATE_KEYWORD)) { 307 // Top-level private class has PUBLIC visibility in Java 308 // Nested private class has PRIVATE visibility 309 psiModifiers.add(nestedClass ? PsiModifier.PRIVATE : PsiModifier.PUBLIC); 310 } 311 312 if (!psiModifiers.contains(PsiModifier.PRIVATE) && !psiModifiers.contains(PsiModifier.PROTECTED)) { 313 psiModifiers.add(PsiModifier.PUBLIC); // For internal (default) visibility 314 } 315 316 317 // FINAL 318 if (isAbstract(classOrObject)) { 319 psiModifiers.add(PsiModifier.ABSTRACT); 320 } 321 else if (!classOrObject.hasModifier(OPEN_KEYWORD)) { 322 psiModifiers.add(PsiModifier.FINAL); 323 } 324 325 if (nestedClass && !classOrObject.hasModifier(INNER_KEYWORD)) { 326 psiModifiers.add(PsiModifier.STATIC); 327 } 328 329 return ArrayUtil.toStringArray(psiModifiers); 330 } 331 332 private boolean isAbstract(@NotNull JetClassOrObject object) { 333 return object.hasModifier(ABSTRACT_KEYWORD) || isInterface(); 334 } 335 336 @Override 337 public boolean hasModifierProperty(@NonNls @NotNull String name) { 338 return getModifierList().hasModifierProperty(name); 339 } 340 341 @Override 342 public boolean isDeprecated() { 343 JetModifierList jetModifierList = classOrObject.getModifierList(); 344 if (jetModifierList == null) { 345 return false; 346 } 347 348 ClassDescriptor deprecatedAnnotation = KotlinBuiltIns.getInstance().getDeprecatedAnnotation(); 349 String deprecatedName = deprecatedAnnotation.getName().asString(); 350 FqNameUnsafe deprecatedFqName = DescriptorUtils.getFQName(deprecatedAnnotation); 351 352 for (JetAnnotationEntry annotationEntry : jetModifierList.getAnnotationEntries()) { 353 JetTypeReference typeReference = annotationEntry.getTypeReference(); 354 if (typeReference == null) continue; 355 356 JetTypeElement typeElement = typeReference.getTypeElement(); 357 if (!(typeElement instanceof JetUserType)) continue; // If it's not a user type, it's definitely not a ref to deprecated 358 359 FqName fqName = JetPsiUtil.toQualifiedName((JetUserType) typeElement); 360 if (fqName == null) continue; 361 362 if (deprecatedFqName.equals(fqName.toUnsafe())) return true; 363 if (deprecatedName.equals(fqName.asString())) return true; 364 } 365 return false; 366 } 367 368 @Override 369 public boolean isInterface() { 370 if (!(classOrObject instanceof JetClass)) return false; 371 JetClass jetClass = (JetClass) classOrObject; 372 return jetClass.isTrait() || jetClass.isAnnotation(); 373 } 374 375 @Override 376 public boolean isAnnotationType() { 377 return classOrObject instanceof JetClass && ((JetClass) classOrObject).isAnnotation(); 378 } 379 380 @Override 381 public boolean isEnum() { 382 return classOrObject instanceof JetClass && ((JetClass) classOrObject).isEnum(); 383 } 384 385 @Override 386 public boolean hasTypeParameters() { 387 return classOrObject instanceof JetClass && !((JetClass) classOrObject).getTypeParameters().isEmpty(); 388 } 389 390 @Override 391 public boolean isValid() { 392 return classOrObject.isValid(); 393 } 394 395 @Override 396 public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException { 397 return super.setName(name); // TODO 398 } 399 400 @Override 401 public String toString() { 402 try { 403 return KotlinLightClass.class.getSimpleName() + ":" + getQualifiedName(); 404 } 405 catch (Throwable e) { 406 return KotlinLightClass.class.getSimpleName() + ":" + e.toString(); 407 } 408 } 409 410 @NotNull 411 @Override 412 public List<PsiClass> getOwnInnerClasses() { 413 // TODO: Should return inner class wrapper 414 return Arrays.asList(getDelegate().getInnerClasses()); 415 } 416 }