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