An API (for Kotlin) for mirror source substitution.
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / compiled / ClsClassImpl.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.psi.impl.compiled;
17
18 import com.intellij.navigation.ItemPresentation;
19 import com.intellij.navigation.ItemPresentationProviders;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.ui.Queryable;
22 import com.intellij.openapi.util.Key;
23 import com.intellij.openapi.util.Pair;
24 import com.intellij.openapi.util.Ref;
25 import com.intellij.psi.*;
26 import com.intellij.psi.impl.InheritanceImplUtil;
27 import com.intellij.psi.impl.PsiClassImplUtil;
28 import com.intellij.psi.impl.PsiImplUtil;
29 import com.intellij.psi.impl.PsiSuperMethodImplUtil;
30 import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
31 import com.intellij.psi.impl.java.stubs.PsiClassStub;
32 import com.intellij.psi.impl.source.ClassInnerStuffCache;
33 import com.intellij.psi.impl.source.Constants;
34 import com.intellij.psi.impl.source.PsiClassImpl;
35 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
36 import com.intellij.psi.impl.source.tree.TreeElement;
37 import com.intellij.psi.javadoc.PsiDocComment;
38 import com.intellij.psi.scope.PsiScopeProcessor;
39 import com.intellij.psi.search.SearchScope;
40 import com.intellij.util.IncorrectOperationException;
41 import org.jetbrains.annotations.NonNls;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
44
45 import javax.swing.*;
46 import java.util.Collection;
47 import java.util.List;
48 import java.util.Map;
49
50 public class ClsClassImpl extends ClsRepositoryPsiElement<PsiClassStub<?>> implements PsiClass, PsiQualifiedNamedElement, Queryable {
51   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClsClassImpl");
52
53   private final ClassInnerStuffCache innersCache = new ClassInnerStuffCache(this);
54   private final PsiIdentifier myNameIdentifier;
55   private final PsiDocComment myDocComment;
56   public static final Key<PsiClass> DELEGATE_KEY = Key.create("DELEGATE");
57
58   public ClsClassImpl(final PsiClassStub stub) {
59     super(stub);
60     myDocComment = isDeprecated() ? new ClsDocCommentImpl(this) : null;
61     myNameIdentifier = new ClsIdentifierImpl(this, getShortName());
62   }
63
64   @Override
65   @NotNull
66   public PsiElement[] getChildren() {
67     PsiIdentifier name = getNameIdentifier();
68     PsiDocComment docComment = getDocComment();
69     PsiModifierList modifierList = getModifierList();
70     PsiReferenceList extendsList = getExtendsList();
71     PsiReferenceList implementsList = getImplementsList();
72     PsiField[] fields = getFields();
73     PsiMethod[] methods = getMethods();
74     PsiClass[] classes = getInnerClasses();
75
76     int count =
77       (docComment != null ? 1 : 0)
78       + 1 // modifierList
79       + 1 // name
80       + 1 // extends list
81       + 1 // implementsList
82       + fields.length
83       + methods.length
84       + classes.length;
85     PsiElement[] children = new PsiElement[count];
86
87     int offset = 0;
88     if (docComment != null) {
89       children[offset++] = docComment;
90     }
91
92     children[offset++] = modifierList;
93     children[offset++] = name;
94     children[offset++] = extendsList;
95     children[offset++] = implementsList;
96
97     System.arraycopy(fields, 0, children, offset, fields.length);
98     offset += fields.length;
99     System.arraycopy(methods, 0, children, offset, methods.length);
100     offset += methods.length;
101     System.arraycopy(classes, 0, children, offset, classes.length);
102     /*offset += classes.length;*/
103
104     return children;
105   }
106
107   @Override
108   @NotNull
109   public PsiIdentifier getNameIdentifier() {
110     return myNameIdentifier;
111   }
112
113   private String getShortName() {
114     String qName = getQualifiedName();
115     String name = PsiNameHelper.getShortClassName(qName);
116     if (name.length() == 0) {
117       name = "_";
118     }
119     return name;
120   }
121
122   @Override
123   @NotNull
124   public String getName() {
125     return getStub().getName();
126   }
127
128   @Override
129   @NotNull
130   public PsiTypeParameterList getTypeParameterList() {
131     return getStub().findChildStubByType(JavaStubElementTypes.TYPE_PARAMETER_LIST).getPsi();
132   }
133
134   @Override
135   public boolean hasTypeParameters() {
136     return PsiImplUtil.hasTypeParameters(this);
137   }
138
139   @Override
140   public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
141     PsiImplUtil.setName(getNameIdentifier(), name);
142     return this;
143   }
144
145   @Override
146   @NotNull
147   public String getQualifiedName() {
148     return getStub().getQualifiedName();
149   }
150
151   @Override
152   @NotNull
153   public PsiModifierList getModifierList() {
154     return getStub().findChildStubByType(JavaStubElementTypes.MODIFIER_LIST).getPsi();
155   }
156
157   @Override
158   public boolean hasModifierProperty(@NotNull String name) {
159     return getModifierList().hasModifierProperty(name);
160   }
161
162   @Override
163   @NotNull
164   public PsiReferenceList getExtendsList() {
165     return getStub().findChildStubByType(JavaStubElementTypes.EXTENDS_LIST).getPsi();
166   }
167
168
169   @Override
170   @NotNull
171   public PsiReferenceList getImplementsList() {
172     return getStub().findChildStubByType(JavaStubElementTypes.IMPLEMENTS_LIST).getPsi();
173   }
174
175   @Override
176   @NotNull
177   public PsiClassType[] getExtendsListTypes() {
178     return PsiClassImplUtil.getExtendsListTypes(this);
179   }
180
181   @Override
182   @NotNull
183   public PsiClassType[] getImplementsListTypes() {
184     return PsiClassImplUtil.getImplementsListTypes(this);
185   }
186
187   @Override
188   public PsiClass getSuperClass() {
189     return PsiClassImplUtil.getSuperClass(this);
190   }
191
192   @Override
193   public PsiClass[] getInterfaces() {
194     return PsiClassImplUtil.getInterfaces(this);
195   }
196
197   @Override
198   @NotNull
199   public PsiClass[] getSupers() {
200     return PsiClassImplUtil.getSupers(this);
201   }
202
203   @Override
204   @NotNull
205   public PsiClassType[] getSuperTypes() {
206     return PsiClassImplUtil.getSuperTypes(this);
207   }
208
209   @Override
210   public PsiClass getContainingClass() {
211     PsiElement parent = getParent();
212     return parent instanceof PsiClass ? (PsiClass)parent : null;
213   }
214
215   @Override
216   @NotNull
217   public Collection<HierarchicalMethodSignature> getVisibleSignatures() {
218     return PsiSuperMethodImplUtil.getVisibleSignatures(this);
219   }
220
221   @Override
222   @NotNull
223   public PsiField[] getFields() {
224     return getStub().getChildrenByType(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY);
225   }
226
227   @Override
228   @NotNull
229   public PsiMethod[] getMethods() {
230     return getStub().getChildrenByType(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY);
231   }
232
233   @Override
234   @NotNull
235   public PsiMethod[] getConstructors() {
236     return PsiImplUtil.getConstructors(this);
237   }
238
239   @Override
240   @NotNull
241   public PsiClass[] getInnerClasses() {
242     return getStub().getChildrenByType(JavaStubElementTypes.CLASS, ARRAY_FACTORY);
243   }
244
245   @Override
246   @NotNull
247   public PsiClassInitializer[] getInitializers() {
248     return PsiClassInitializer.EMPTY_ARRAY;
249   }
250
251   @Override
252   @NotNull
253   public PsiTypeParameter[] getTypeParameters() {
254     return PsiImplUtil.getTypeParameters(this);
255   }
256
257   @Override
258   @NotNull
259   public PsiField[] getAllFields() {
260     return PsiClassImplUtil.getAllFields(this);
261   }
262
263   @Override
264   @NotNull
265   public PsiMethod[] getAllMethods() {
266     return PsiClassImplUtil.getAllMethods(this);
267   }
268
269   @Override
270   @NotNull
271   public PsiClass[] getAllInnerClasses() {
272     return PsiClassImplUtil.getAllInnerClasses(this);
273   }
274
275   @Override
276   public PsiField findFieldByName(String name, boolean checkBases) {
277     return innersCache.findFieldByName(name, checkBases);
278   }
279
280   @Override
281   public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) {
282     return PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases);
283   }
284
285   @Override
286   @NotNull
287   public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) {
288     return PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases);
289   }
290
291   @Override
292   @NotNull
293   public PsiMethod[] findMethodsByName(String name, boolean checkBases) {
294     return innersCache.findMethodsByName(name, checkBases);
295   }
296
297   @Override
298   @NotNull
299   public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(String name, boolean checkBases) {
300     return PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases);
301   }
302
303   @Override
304   @NotNull
305   public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() {
306     return PsiClassImplUtil.getAllWithSubstitutorsByMap(this, PsiMethod.class);
307   }
308
309   @Override
310   public PsiClass findInnerClassByName(String name, boolean checkBases) {
311     return innersCache.findInnerClassByName(name, checkBases);
312   }
313
314   @Override
315   public boolean isDeprecated() {
316     return getStub().isDeprecated();
317   }
318
319   public String getSourceFileName() {
320     final String sfn = getStub().getSourceFileName();
321     return sfn != null ? sfn : obtainSourceFileNameFromClassFileName();
322   }
323
324   @NonNls
325   private String obtainSourceFileNameFromClassFileName() {
326     final String name = getContainingFile().getName();
327     int i = name.indexOf('$');
328     if (i < 0) {
329       i = name.indexOf('.');
330       if (i < 0) {
331         i = name.length();
332       }
333     }
334     return name.substring(0, i) + ".java";
335   }
336
337   @Override
338   public PsiDocComment getDocComment() {
339     return myDocComment;
340   }
341
342   @Override
343   public PsiJavaToken getLBrace() {
344     return null;
345   }
346
347   @Override
348   public PsiJavaToken getRBrace() {
349     return null;
350   }
351
352   @Override
353   public boolean isInterface() {
354     return getStub().isInterface();
355   }
356
357   @Override
358   public boolean isAnnotationType() {
359     return getStub().isAnnotationType();
360   }
361
362   @Override
363   public boolean isEnum() {
364     return getStub().isEnum();
365   }
366
367   @Override
368   public void appendMirrorText(final int indentLevel, @NonNls final StringBuilder buffer) {
369     ClsDocCommentImpl docComment = (ClsDocCommentImpl)getDocComment();
370     if (docComment != null) {
371       docComment.appendMirrorText(indentLevel, buffer);
372       goNextLine(indentLevel, buffer);
373     }
374     ((ClsElementImpl)getModifierList()).appendMirrorText(indentLevel, buffer);
375     buffer.append(isEnum() ? "enum " : isAnnotationType() ? "@interface " : isInterface() ? "interface " : "class ");
376     ((ClsElementImpl)getNameIdentifier()).appendMirrorText(indentLevel, buffer);
377     ((ClsElementImpl)getTypeParameterList()).appendMirrorText(indentLevel, buffer);
378     buffer.append(' ');
379     if (!isEnum() && !isAnnotationType()) {
380       ((ClsElementImpl)getExtendsList()).appendMirrorText(indentLevel, buffer);
381       buffer.append(' ');
382     }
383     if (!isInterface()) {
384       ((ClsElementImpl)getImplementsList()).appendMirrorText(indentLevel, buffer);
385     }
386     buffer.append('{');
387     final int newIndentLevel = indentLevel + getIndentSize();
388     PsiField[] fields = getFields();
389     if (fields.length > 0) {
390       goNextLine(newIndentLevel, buffer);
391       for (int i = 0; i < fields.length; i++) {
392         PsiField field = fields[i];
393         ((ClsElementImpl)field).appendMirrorText(newIndentLevel, buffer);
394         if (field instanceof ClsEnumConstantImpl) {
395           if (i < fields.length - 1 && fields[i + 1] instanceof ClsEnumConstantImpl) {
396             buffer.append(", ");
397           }
398           else {
399             buffer.append(";");
400             if (i < fields.length - 1) {
401               goNextLine(newIndentLevel, buffer);
402             }
403           }
404         } else if (i < fields.length - 1) {
405           goNextLine(newIndentLevel, buffer);
406         }
407       }
408     }
409
410     PsiMethod[] methods = getMethods();
411     if (methods.length > 0) {
412       goNextLine(newIndentLevel, buffer);
413       goNextLine(newIndentLevel, buffer);
414       for (int i = 0; i < methods.length; i++) {
415         PsiMethod method = methods[i];
416         ((ClsElementImpl)method).appendMirrorText(newIndentLevel, buffer);
417         if (i < methods.length - 1) {
418           goNextLine(newIndentLevel, buffer);
419           goNextLine(newIndentLevel, buffer);
420         }
421       }
422     }
423
424     PsiClass[] classes = getInnerClasses();
425     if (classes.length > 0) {
426       goNextLine(newIndentLevel, buffer);
427       goNextLine(newIndentLevel, buffer);
428       for (int i = 0; i < classes.length; i++) {
429         PsiClass aClass = classes[i];
430         ((ClsElementImpl)aClass).appendMirrorText(newIndentLevel, buffer);
431         if (i < classes.length - 1) {
432           goNextLine(newIndentLevel, buffer);
433           goNextLine(newIndentLevel, buffer);
434         }
435       }
436     }
437     goNextLine(indentLevel, buffer);
438     buffer.append('}');
439   }
440
441   @Override
442   public void setMirror(@NotNull TreeElement element) {
443     setMirrorCheckingType(element, null);
444
445     PsiClass mirror = SourceTreeToPsiMap.treeToPsiNotNull(element);
446
447     PsiDocComment docComment = getDocComment();
448     if (docComment != null) {
449       ((ClsElementImpl)docComment).setMirror((TreeElement)SourceTreeToPsiMap.psiElementToTree(mirror.getDocComment()));
450     }
451     ((ClsElementImpl)getModifierList()).setMirror((TreeElement)SourceTreeToPsiMap.psiElementToTree(mirror.getModifierList()));
452     ((ClsElementImpl)getNameIdentifier()).setMirror((TreeElement)SourceTreeToPsiMap.psiElementToTree(mirror.getNameIdentifier()));
453     if (!isAnnotationType() && !isEnum()) {
454       ((ClsElementImpl)getExtendsList()).setMirror((TreeElement)SourceTreeToPsiMap.psiElementToTree(mirror.getExtendsList()));
455     }
456     ((ClsElementImpl)getImplementsList()).setMirror((TreeElement)SourceTreeToPsiMap.psiElementToTree(mirror.getImplementsList()));
457     ((ClsElementImpl)getTypeParameterList()).setMirror((TreeElement)SourceTreeToPsiMap.psiElementToTree(mirror.getTypeParameterList()));
458
459     Ref<Boolean> extLog = Ref.create(true);
460
461     PsiField[] fields = getFields();
462     PsiField[] mirrorFields = mirror.getFields();
463     if (fields.length == mirrorFields.length) {
464       for (int i = 0; i < fields.length; i++) {
465         ((ClsElementImpl)fields[i]).setMirror(SourceTreeToPsiMap.psiToTreeNotNull(mirrorFields[i]));
466       }
467     }
468     else {
469       log(this, mirror, "fields:" + fields.length + "!=" + mirrorFields.length, extLog);
470     }
471
472     PsiMethod[] methods = getMethods();
473     PsiMethod[] mirrorMethods = mirror.getMethods();
474     if (methods.length == mirrorMethods.length) {
475       for (int i = 0; i < methods.length; i++) {
476         ((ClsElementImpl)methods[i]).setMirror(SourceTreeToPsiMap.psiToTreeNotNull(mirrorMethods[i]));
477       }
478     }
479     else {
480       log(this, mirror, "methods:" + methods.length + "!=" + mirrorMethods.length, extLog);
481     }
482
483     PsiClass[] classes = getInnerClasses();
484     PsiClass[] mirrorClasses = mirror.getInnerClasses();
485     if (classes.length == mirrorClasses.length) {
486       for (int i = 0; i < classes.length; i++) {
487         ((ClsElementImpl)classes[i]).setMirror(SourceTreeToPsiMap.psiToTreeNotNull(mirrorClasses[i]));
488       }
489     }
490     else {
491       log(this, mirror, "classes:" + classes.length + "!=" + mirrorClasses.length, extLog);
492     }
493   }
494
495   private static void log(@NotNull ClsClassImpl stub, @NotNull PsiClass mirror, @NotNull String message, @NotNull Ref<Boolean> verbose) {
496     LOG.error(message + "; file:" + stub.getContainingFile().getName());
497     if (verbose.get()) {
498       StringBuilder builder = new StringBuilder();
499       stub.appendMirrorText(0, builder);
500       LOG.error("\nStub:\n" + builder.toString() + "\nMirror:\n" + mirror.getText());
501       verbose.set(false);
502     }
503   }
504
505   @Override
506   public void accept(@NotNull PsiElementVisitor visitor) {
507     if (visitor instanceof JavaElementVisitor) {
508       ((JavaElementVisitor)visitor).visitClass(this);
509     }
510     else {
511       visitor.visitElement(this);
512     }
513   }
514
515   @NonNls
516   public String toString() {
517     return "PsiClass:" + getName();
518   }
519
520   @Override
521   public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
522                                      @NotNull ResolveState state,
523                                      PsiElement lastParent,
524                                      @NotNull PsiElement place) {
525     return PsiClassImplUtil.processDeclarationsInClass(this, processor, state, null, lastParent, place, false);
526   }
527
528   @Override
529   public PsiElement getScope() {
530     return getParent();
531   }
532
533   @Override
534   public boolean isInheritorDeep(PsiClass baseClass, PsiClass classToByPass) {
535     return InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass);
536   }
537
538   @Override
539   public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) {
540     return InheritanceImplUtil.isInheritor(this, baseClass, checkDeep);
541   }
542
543   @Nullable
544   public PsiClass getSourceMirrorClass() {
545     PsiClass delegate = getUserData(DELEGATE_KEY);
546     if (delegate instanceof ClsClassImpl) {
547       return ((ClsClassImpl)delegate).getSourceMirrorClass();
548     }
549
550     PsiElement parent = getParent();
551     final String name = getName();
552     if (parent instanceof PsiFile) {
553       if (!(parent instanceof PsiClassOwner)) return null;
554
555       PsiClassOwner fileNavigationElement = (PsiClassOwner)parent.getNavigationElement();
556       for (PsiClass aClass : fileNavigationElement.getClasses()) {
557         if (name.equals(aClass.getName())) return aClass;
558       }
559     }
560     else if (parent != null) {
561       ClsClassImpl parentClass = (ClsClassImpl)parent;
562       PsiClass parentSourceMirror = parentClass.getSourceMirrorClass();
563       if (parentSourceMirror == null) return null;
564       PsiClass[] innerClasses = parentSourceMirror.getInnerClasses();
565       for (PsiClass innerClass : innerClasses) {
566         if (name.equals(innerClass.getName())) return innerClass;
567       }
568     }
569     else {
570       throw new PsiInvalidElementAccessException(this);
571     }
572
573     return null;
574   }
575
576   @Override
577   @NotNull
578   public PsiElement getNavigationElement() {
579     PsiClass aClass = getSourceMirrorClass();
580     return aClass != null && aClass != this ? aClass.getNavigationElement() : this;
581   }
582
583   @Override
584   public ItemPresentation getPresentation() {
585     return ItemPresentationProviders.getItemPresentation(this);
586   }
587
588   @Override
589   public Icon getElementIcon(final int flags) {
590     return PsiClassImplUtil.getClassIcon(flags, this);
591   }
592
593   @Override
594   public boolean isEquivalentTo(final PsiElement another) {
595     return PsiClassImplUtil.isClassEquivalentTo(this, another);
596   }
597
598   @Override
599   @NotNull
600   public SearchScope getUseScope() {
601     return PsiClassImplUtil.getClassUseScope(this);
602   }
603
604   @Override
605   public PsiQualifiedNamedElement getContainer() {
606     final PsiFile file = getContainingFile();
607     final PsiDirectory dir;
608     return file == null ? null : (dir = file.getContainingDirectory()) == null
609                                  ? null : JavaDirectoryService.getInstance().getPackage(dir);
610   }
611
612   @Override
613   public void putInfo(@NotNull Map<String, String> info) {
614     PsiClassImpl.putInfo(this, info);
615   }
616
617   @Override
618   protected boolean isVisibilitySupported() {
619     return true;
620   }
621
622 }