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.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.LightClass;
033 import com.intellij.psi.impl.light.LightMethod;
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.psi.util.PsiTreeUtil;
038 import com.intellij.util.ArrayUtil;
039 import com.intellij.util.IncorrectOperationException;
040 import jet.runtime.typeinfo.JetValueParameter;
041 import kotlin.Function1;
042 import kotlin.KotlinPackage;
043 import org.jetbrains.annotations.NonNls;
044 import org.jetbrains.annotations.NotNull;
045 import org.jetbrains.annotations.Nullable;
046 import org.jetbrains.jet.codegen.binding.PsiCodegenPredictor;
047 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
048 import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
049 import org.jetbrains.jet.lang.psi.*;
050 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
051 import org.jetbrains.jet.lang.resolve.java.JvmClassName;
052 import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetJavaMirrorMarker;
053 import org.jetbrains.jet.lang.resolve.name.FqName;
054 import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
055 import org.jetbrains.jet.lang.types.JetType;
056 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
057 import org.jetbrains.jet.lexer.JetModifierKeywordToken;
058 import org.jetbrains.jet.plugin.JetLanguage;
059
060 import javax.swing.*;
061 import java.util.Arrays;
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, JetLanguage.INSTANCE);
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 }
304 };
305
306 @Override
307 public PsiFile getContainingFile() {
308 return _containingFile.getValue();
309 }
310
311 @NotNull
312 @Override
313 public PsiElement getNavigationElement() {
314 return classOrObject;
315 }
316
317 @Override
318 public boolean isEquivalentTo(PsiElement another) {
319 return another instanceof PsiClass && Comparing.equal(((PsiClass) another).getQualifiedName(), getQualifiedName());
320 }
321
322 @Override
323 public ItemPresentation getPresentation() {
324 return ItemPresentationProviders.getItemPresentation(this);
325 }
326
327 @Override
328 public Icon getElementIcon(int flags) {
329 throw new UnsupportedOperationException("This should be done byt JetIconProvider");
330 }
331
332 @Override
333 public boolean equals(Object o) {
334 if (this == o) return true;
335 if (o == null || getClass() != o.getClass()) return false;
336
337 KotlinLightClassForExplicitDeclaration aClass = (KotlinLightClassForExplicitDeclaration) o;
338
339 if (!classFqName.equals(aClass.classFqName)) return false;
340
341 return true;
342 }
343
344 @Override
345 public int hashCode() {
346 return classFqName.hashCode();
347 }
348
349 @Nullable
350 @Override
351 public PsiClass getContainingClass() {
352 if (classOrObject.getParent() == classOrObject.getContainingFile()) return null;
353 return super.getContainingClass();
354 }
355
356 @Nullable
357 @Override
358 public PsiElement getParent() {
359 return parent.getValue();
360 }
361
362 @Nullable
363 @Override
364 public PsiTypeParameterList getTypeParameterList() {
365 return typeParameterList.getValue();
366 }
367
368 @NotNull
369 @Override
370 public PsiTypeParameter[] getTypeParameters() {
371 PsiTypeParameterList typeParameterList = getTypeParameterList();
372 return typeParameterList == null ? PsiTypeParameter.EMPTY_ARRAY : typeParameterList.getTypeParameters();
373 }
374
375 @Nullable
376 @Override
377 public String getName() {
378 return classFqName.shortName().asString();
379 }
380
381 @Nullable
382 @Override
383 public String getQualifiedName() {
384 return classFqName.asString();
385 }
386
387 @NotNull
388 @Override
389 public PsiModifierList getModifierList() {
390 if (modifierList == null) {
391 modifierList = new KotlinLightModifierList(this.getManager(), computeModifiers()) {
392 @Override
393 public PsiAnnotationOwner getDelegate() {
394 return KotlinLightClassForExplicitDeclaration.this.getDelegate().getModifierList();
395 }
396 };
397 }
398 return modifierList;
399 }
400
401 @NotNull
402 private String[] computeModifiers() {
403 Collection<String> psiModifiers = Sets.newHashSet();
404
405 // PUBLIC, PROTECTED, PRIVATE, ABSTRACT, FINAL
406 //noinspection unchecked
407 List<Pair<JetModifierKeywordToken, String>> jetTokenToPsiModifier = Lists.newArrayList(
408 Pair.create(PUBLIC_KEYWORD, PsiModifier.PUBLIC),
409 Pair.create(INTERNAL_KEYWORD, PsiModifier.PUBLIC),
410 Pair.create(PROTECTED_KEYWORD, PsiModifier.PROTECTED),
411 Pair.create(FINAL_KEYWORD, PsiModifier.FINAL));
412
413 for (Pair<JetModifierKeywordToken, String> tokenAndModifier : jetTokenToPsiModifier) {
414 if (classOrObject.hasModifier(tokenAndModifier.first)) {
415 psiModifiers.add(tokenAndModifier.second);
416 }
417 }
418
419 if (classOrObject.hasModifier(PRIVATE_KEYWORD)) {
420 // Top-level private class has PUBLIC visibility in Java
421 // Nested private class has PRIVATE visibility
422 psiModifiers.add(classOrObject.isTopLevel() ? PsiModifier.PUBLIC : PsiModifier.PRIVATE);
423 }
424
425 if (!psiModifiers.contains(PsiModifier.PRIVATE) && !psiModifiers.contains(PsiModifier.PROTECTED)) {
426 psiModifiers.add(PsiModifier.PUBLIC); // For internal (default) visibility
427 }
428
429
430 // FINAL
431 if (isAbstract(classOrObject)) {
432 psiModifiers.add(PsiModifier.ABSTRACT);
433 }
434 else if (!(classOrObject.hasModifier(OPEN_KEYWORD) || (classOrObject instanceof JetClass && ((JetClass) classOrObject).isEnum()))) {
435 psiModifiers.add(PsiModifier.FINAL);
436 }
437
438 if (!classOrObject.isTopLevel() && !classOrObject.hasModifier(INNER_KEYWORD)) {
439 psiModifiers.add(PsiModifier.STATIC);
440 }
441
442 return ArrayUtil.toStringArray(psiModifiers);
443 }
444
445 private boolean isAbstract(@NotNull JetClassOrObject object) {
446 return object.hasModifier(ABSTRACT_KEYWORD) || isInterface();
447 }
448
449 @Override
450 public boolean hasModifierProperty(@NonNls @NotNull String name) {
451 return getModifierList().hasModifierProperty(name);
452 }
453
454 @Override
455 public boolean isDeprecated() {
456 JetModifierList jetModifierList = classOrObject.getModifierList();
457 if (jetModifierList == null) {
458 return false;
459 }
460
461 ClassDescriptor deprecatedAnnotation = KotlinBuiltIns.getInstance().getDeprecatedAnnotation();
462 String deprecatedName = deprecatedAnnotation.getName().asString();
463 FqNameUnsafe deprecatedFqName = DescriptorUtils.getFqName(deprecatedAnnotation);
464
465 for (JetAnnotationEntry annotationEntry : jetModifierList.getAnnotationEntries()) {
466 JetTypeReference typeReference = annotationEntry.getTypeReference();
467 if (typeReference == null) continue;
468
469 JetTypeElement typeElement = typeReference.getTypeElement();
470 if (!(typeElement instanceof JetUserType)) continue; // If it's not a user type, it's definitely not a ref to deprecated
471
472 FqName fqName = JetPsiUtil.toQualifiedName((JetUserType) typeElement);
473 if (fqName == null) continue;
474
475 if (deprecatedFqName.equals(fqName.toUnsafe())) return true;
476 if (deprecatedName.equals(fqName.asString())) return true;
477 }
478 return false;
479 }
480
481 @Override
482 public boolean isInterface() {
483 if (!(classOrObject instanceof JetClass)) return false;
484 JetClass jetClass = (JetClass) classOrObject;
485 return jetClass.isTrait() || jetClass.isAnnotation();
486 }
487
488 @Override
489 public boolean isAnnotationType() {
490 return classOrObject instanceof JetClass && ((JetClass) classOrObject).isAnnotation();
491 }
492
493 @Override
494 public boolean isEnum() {
495 return classOrObject instanceof JetClass && ((JetClass) classOrObject).isEnum();
496 }
497
498 @Override
499 public boolean hasTypeParameters() {
500 return classOrObject instanceof JetClass && !((JetClass) classOrObject).getTypeParameters().isEmpty();
501 }
502
503 @Override
504 public boolean isValid() {
505 return classOrObject.isValid();
506 }
507
508 @Override
509 public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) {
510 // Java inheritor check doesn't work when trait (interface in Java) subclasses Java class and for Kotlin local classes
511 if (baseClass instanceof KotlinLightClassForExplicitDeclaration || (isInterface() && !baseClass.isInterface())) {
512 String qualifiedName;
513 if (baseClass instanceof KotlinLightClassForExplicitDeclaration) {
514 ClassDescriptor baseDescriptor = ((KotlinLightClassForExplicitDeclaration) baseClass).getDescriptor();
515 qualifiedName = baseDescriptor != null ? DescriptorUtils.getFqName(baseDescriptor).asString() : null;
516 }
517 else {
518 qualifiedName = baseClass.getQualifiedName();
519 }
520
521 ClassDescriptor thisDescriptor = getDescriptor();
522 return qualifiedName != null
523 && thisDescriptor != null
524 && checkSuperTypeByFQName(thisDescriptor, qualifiedName, checkDeep);
525 }
526
527 return super.isInheritor(baseClass, checkDeep);
528 }
529
530 @Override
531 public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
532 throw new IncorrectOperationException("Cannot modify compiled kotlin element");
533 }
534
535 @Override
536 public String toString() {
537 try {
538 return KotlinLightClass.class.getSimpleName() + ":" + getQualifiedName();
539 }
540 catch (Throwable e) {
541 return KotlinLightClass.class.getSimpleName() + ":" + e.toString();
542 }
543 }
544
545 @NotNull
546 @Override
547 public List<PsiClass> getOwnInnerClasses() {
548 return KotlinPackage.filterNotNull(
549 KotlinPackage.map(
550 getDelegate().getInnerClasses(),
551 new Function1<PsiClass, PsiClass>() {
552 @Override
553 public PsiClass invoke(PsiClass aClass) {
554 JetClassOrObject declaration = (JetClassOrObject) ClsWrapperStubPsiFactory.getOriginalDeclaration(aClass);
555 return declaration != null ? KotlinLightClassForExplicitDeclaration.create(myManager, declaration) : null;
556 }
557 }
558 )
559 );
560 }
561
562 private static boolean checkSuperTypeByFQName(@NotNull ClassDescriptor classDescriptor, @NotNull String qualifiedName, Boolean deep) {
563 if (CommonClassNames.JAVA_LANG_OBJECT.equals(qualifiedName)) return true;
564
565 if (qualifiedName.equals(DescriptorUtils.getFqName(classDescriptor).asString())) return true;
566
567 for (JetType superType : classDescriptor.getTypeConstructor().getSupertypes()) {
568 ClassifierDescriptor superDescriptor = superType.getConstructor().getDeclarationDescriptor();
569
570 if (superDescriptor instanceof ClassDescriptor) {
571 if (qualifiedName.equals(DescriptorUtils.getFqName(superDescriptor).asString())) return true;
572
573 if (deep) {
574 if (checkSuperTypeByFQName((ClassDescriptor)superDescriptor, qualifiedName, true)) {
575 return true;
576 }
577 }
578 }
579 }
580
581 return false;
582 }
583 }