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