cache cls navigation elements that are not so cheap to calculate each time
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / compiled / ClsMethodImpl.java
1 /*
2  * Copyright 2000-2013 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.extensions.Extensions;
21 import com.intellij.openapi.roots.FileIndexFacade;
22 import com.intellij.openapi.util.AtomicNotNullLazyValue;
23 import com.intellij.openapi.util.NotNullLazyValue;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.psi.*;
26 import com.intellij.psi.impl.ElementPresentationUtil;
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.cache.TypeInfo;
31 import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
32 import com.intellij.psi.impl.java.stubs.PsiMethodStub;
33 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
34 import com.intellij.psi.impl.source.tree.TreeElement;
35 import com.intellij.psi.scope.PsiScopeProcessor;
36 import com.intellij.psi.scope.util.PsiScopesUtil;
37 import com.intellij.psi.search.SearchScope;
38 import com.intellij.psi.util.*;
39 import com.intellij.ui.RowIcon;
40 import com.intellij.util.PlatformIcons;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43
44 import javax.swing.*;
45 import java.util.List;
46
47 public class ClsMethodImpl extends ClsMemberImpl<PsiMethodStub> implements PsiAnnotationMethod {
48   private final NotNullLazyValue<PsiTypeElement> myReturnType;
49   private final NotNullLazyValue<PsiAnnotationMemberValue> myDefaultValue;
50
51   public ClsMethodImpl(final PsiMethodStub stub) {
52     super(stub);
53
54     myReturnType = isConstructor() ? null : new AtomicNotNullLazyValue<PsiTypeElement>() {
55       @NotNull
56       @Override
57       protected PsiTypeElement compute() {
58         PsiMethodStub stub = getStub();
59         String typeText = TypeInfo.createTypeText(stub.getReturnTypeText(false));
60         assert typeText != null : stub;
61         return new ClsTypeElementImpl(ClsMethodImpl.this, typeText, ClsTypeElementImpl.VARIANCE_NONE);
62       }
63     };
64
65     final String text = getStub().getDefaultValueText();
66     myDefaultValue = StringUtil.isEmptyOrSpaces(text) ? null : new AtomicNotNullLazyValue<PsiAnnotationMemberValue>() {
67       @NotNull
68       @Override
69       protected PsiAnnotationMemberValue compute() {
70         return ClsParsingUtil.createMemberValueFromText(text, getManager(), ClsMethodImpl.this);
71       }
72     };
73   }
74
75   @Override
76   @NotNull
77   public PsiElement[] getChildren() {
78     return getChildren(getDocComment(), getModifierList(), getReturnTypeElement(), getNameIdentifier(), getParameterList(),
79                        getThrowsList(), getDefaultValue());
80   }
81
82   @Override
83   public PsiClass getContainingClass() {
84     return (PsiClass)getParent();
85   }
86
87   @Override
88   @NotNull
89   public PsiMethod[] findSuperMethods() {
90     return PsiSuperMethodImplUtil.findSuperMethods(this);
91   }
92
93   @Override
94   @NotNull
95   public PsiMethod[] findSuperMethods(boolean checkAccess) {
96     return PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess);
97   }
98
99   @Override
100   @NotNull
101   public PsiMethod[] findSuperMethods(PsiClass parentClass) {
102     return PsiSuperMethodImplUtil.findSuperMethods(this, parentClass);
103   }
104
105   @Override
106   @NotNull
107   public List<MethodSignatureBackedByPsiMethod> findSuperMethodSignaturesIncludingStatic(boolean checkAccess) {
108     return PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess);
109   }
110
111   @Override
112   public PsiMethod findDeepestSuperMethod() {
113     return PsiSuperMethodImplUtil.findDeepestSuperMethod(this);
114   }
115
116   @Override
117   @NotNull
118   public PsiMethod[] findDeepestSuperMethods() {
119     return PsiSuperMethodImplUtil.findDeepestSuperMethods(this);
120   }
121
122   @Override
123   @NotNull
124   public HierarchicalMethodSignature getHierarchicalMethodSignature() {
125     return PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this);
126   }
127
128   @Override
129   public PsiTypeElement getReturnTypeElement() {
130     return myReturnType != null ? myReturnType.getValue() : null;
131   }
132
133   @Override
134   public PsiType getReturnType() {
135     PsiTypeElement typeElement = getReturnTypeElement();
136     return typeElement == null ? null : typeElement.getType();
137   }
138
139   @Override
140   @NotNull
141   public PsiModifierList getModifierList() {
142     return getStub().findChildStubByType(JavaStubElementTypes.MODIFIER_LIST).getPsi();
143   }
144
145   @Override
146   public boolean hasModifierProperty(@NotNull String name) {
147     return getModifierList().hasModifierProperty(name);
148   }
149
150   @Override
151   @NotNull
152   public PsiParameterList getParameterList() {
153     return getStub().findChildStubByType(JavaStubElementTypes.PARAMETER_LIST).getPsi();
154   }
155
156   @Override
157   @NotNull
158   public PsiReferenceList getThrowsList() {
159     return getStub().findChildStubByType(JavaStubElementTypes.THROWS_LIST).getPsi();
160   }
161
162   @Override
163   public PsiTypeParameterList getTypeParameterList() {
164     return getStub().findChildStubByType(JavaStubElementTypes.TYPE_PARAMETER_LIST).getPsi();
165   }
166
167   @Override
168   public PsiCodeBlock getBody() {
169     return null;
170   }
171
172   @Override
173   public boolean isDeprecated() {
174     return getStub().isDeprecated();
175   }
176
177   @Override
178   public PsiAnnotationMemberValue getDefaultValue() {
179     return myDefaultValue != null ? myDefaultValue.getValue() : null;
180   }
181
182   @Override
183   public boolean isConstructor() {
184     return getStub().isConstructor();
185   }
186
187   @Override
188   public boolean isVarArgs() {
189     return getStub().isVarArgs();
190   }
191
192   @Override
193   @NotNull
194   public MethodSignature getSignature(@NotNull PsiSubstitutor substitutor) {
195     return MethodSignatureBackedByPsiMethod.create(this, substitutor);
196   }
197
198   @Override
199   public void appendMirrorText(int indentLevel, @NotNull StringBuilder buffer) {
200     appendText(getDocComment(), indentLevel, buffer, NEXT_LINE);
201     appendText(getModifierList(), indentLevel, buffer, "");
202     appendText(getTypeParameterList(), indentLevel, buffer, " ");
203     if (!isConstructor()) {
204       appendText(getReturnTypeElement(), indentLevel, buffer, " ");
205     }
206     appendText(getNameIdentifier(), indentLevel, buffer, "");
207     appendText(getParameterList(), indentLevel, buffer);
208
209     PsiReferenceList throwsList = getThrowsList();
210     if (throwsList.getReferencedTypes().length > 0) {
211       buffer.append(' ');
212       appendText(throwsList, indentLevel, buffer);
213     }
214
215     PsiAnnotationMemberValue defaultValue = getDefaultValue();
216     if (defaultValue != null) {
217       buffer.append(" default ");
218       appendText(defaultValue, indentLevel, buffer);
219     }
220
221     if (hasModifierProperty(PsiModifier.ABSTRACT) || hasModifierProperty(PsiModifier.NATIVE)) {
222       buffer.append(";");
223     }
224     else {
225       buffer.append(" { /* compiled code */ }");
226     }
227   }
228
229   @Override
230   public void setMirror(@NotNull TreeElement element) throws InvalidMirrorException {
231     setMirrorCheckingType(element, null);
232
233     PsiMethod mirror = SourceTreeToPsiMap.treeToPsiNotNull(element);
234
235     setMirrorIfPresent(getDocComment(), mirror.getDocComment());
236     setMirror(getModifierList(), mirror.getModifierList());
237     setMirror(getTypeParameterList(), mirror.getTypeParameterList());
238     if (!isConstructor()) {
239       setMirror(getReturnTypeElement(), mirror.getReturnTypeElement());
240     }
241     setMirror(getNameIdentifier(), mirror.getNameIdentifier());
242     setMirror(getParameterList(), mirror.getParameterList());
243     setMirror(getThrowsList(), mirror.getThrowsList());
244
245     PsiAnnotationMemberValue defaultValue = getDefaultValue();
246     if (defaultValue != null) {
247       assert mirror instanceof PsiAnnotationMethod : this;
248       setMirror(defaultValue, ((PsiAnnotationMethod)mirror).getDefaultValue());
249     }
250   }
251
252   @Override
253   public void accept(@NotNull PsiElementVisitor visitor) {
254     if (visitor instanceof JavaElementVisitor) {
255       ((JavaElementVisitor)visitor).visitMethod(this);
256     }
257     else {
258       visitor.visitElement(this);
259     }
260   }
261
262   @Override
263   public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
264                                      @NotNull ResolveState state,
265                                      PsiElement lastParent,
266                                      @NotNull PsiElement place) {
267     processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this);
268     if (lastParent == null) return true;
269
270     if (!PsiScopesUtil.walkChildrenScopes(this, processor, state, lastParent, place)) return false;
271
272     final PsiParameter[] parameters = getParameterList().getParameters();
273     for (PsiParameter parameter : parameters) {
274       if (!processor.execute(parameter, state)) return false;
275     }
276
277     return true;
278   }
279
280   @Nullable
281   public PsiMethod getSourceMirrorMethod() {
282     return CachedValuesManager.getCachedValue(this, new CachedValueProvider<PsiMethod>() {
283       @Nullable
284       @Override
285       public Result<PsiMethod> compute() {
286         return Result.create(calcSourceMirrorMethod(), getContainingFile(), FileIndexFacade.getInstance(getProject()).getRootModificationTracker());
287       }
288     });
289   }
290
291   private PsiMethod calcSourceMirrorMethod() {
292     PsiClass sourceClassMirror = ((ClsClassImpl)getParent()).getSourceMirrorClass();
293     if (sourceClassMirror == null) return null;
294     for (PsiMethod sourceMethod : sourceClassMirror.findMethodsByName(getName(), false)) {
295       if (MethodSignatureUtil.areParametersErasureEqual(this, sourceMethod)) {
296         return sourceMethod;
297       }
298     }
299     return null;
300   }
301
302   @Override
303   @NotNull
304   public PsiElement getNavigationElement() {
305     for (ClsCustomNavigationPolicy customNavigationPolicy : Extensions.getExtensions(ClsCustomNavigationPolicy.EP_NAME)) {
306       PsiElement navigationElement = customNavigationPolicy.getNavigationElement(this);
307       if (navigationElement != null) {
308         return navigationElement;
309       }
310     }
311
312     final PsiMethod method = getSourceMirrorMethod();
313     return method != null ? method.getNavigationElement() : this;
314   }
315
316   @Override
317   public boolean hasTypeParameters() {
318     return PsiImplUtil.hasTypeParameters(this);
319   }
320
321   @Override
322   @NotNull public PsiTypeParameter[] getTypeParameters() {
323     return PsiImplUtil.getTypeParameters(this);
324   }
325
326   @Override
327   public ItemPresentation getPresentation() {
328     return ItemPresentationProviders.getItemPresentation(this);
329   }
330
331   @Override
332   public Icon getElementIcon(final int flags) {
333     Icon methodIcon = hasModifierProperty(PsiModifier.ABSTRACT) ? PlatformIcons.ABSTRACT_METHOD_ICON : PlatformIcons.METHOD_ICON;
334     RowIcon baseIcon = ElementPresentationUtil.createLayeredIcon(methodIcon, this, false);
335     return ElementPresentationUtil.addVisibilityIcon(this, flags, baseIcon);
336   }
337
338   @Override
339   public boolean isEquivalentTo(final PsiElement another) {
340     return PsiClassImplUtil.isMethodEquivalentTo(this, another);
341   }
342
343   @Override
344   @NotNull
345   public SearchScope getUseScope() {
346     return PsiImplUtil.getMemberUseScope(this);
347   }
348
349   @Override
350   protected boolean isVisibilitySupported() {
351     return true;
352   }
353
354   @Override
355   public String toString() {
356     return "PsiMethod:" + getName();
357   }
358 }