clear ClsParameter.name cache on roots change (IDEA-136727)
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / compiled / ClsParameterImpl.java
1 /*
2  * Copyright 2000-2014 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.openapi.project.DumbService;
19 import com.intellij.openapi.roots.FileIndexFacade;
20 import com.intellij.openapi.util.AtomicNotNullLazyValue;
21 import com.intellij.openapi.util.NotNullLazyValue;
22 import com.intellij.psi.*;
23 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
24 import com.intellij.psi.impl.ElementPresentationUtil;
25 import com.intellij.psi.impl.cache.TypeInfo;
26 import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
27 import com.intellij.psi.impl.java.stubs.PsiParameterStub;
28 import com.intellij.psi.impl.java.stubs.impl.PsiParameterStubImpl;
29 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
30 import com.intellij.psi.impl.source.tree.TreeElement;
31 import com.intellij.psi.search.LocalSearchScope;
32 import com.intellij.psi.search.SearchScope;
33 import com.intellij.psi.stubs.StubElement;
34 import com.intellij.psi.util.CachedValueProvider;
35 import com.intellij.psi.util.CachedValuesManager;
36 import com.intellij.ui.RowIcon;
37 import com.intellij.util.IncorrectOperationException;
38 import com.intellij.util.PlatformIcons;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41
42 import javax.swing.*;
43
44 public class ClsParameterImpl extends ClsRepositoryPsiElement<PsiParameterStub> implements PsiParameter {
45   private final NotNullLazyValue<PsiTypeElement> myType;
46   private volatile String myName;
47   private volatile String myMirrorName;
48
49   public ClsParameterImpl(@NotNull PsiParameterStub stub) {
50     super(stub);
51     myType = new AtomicNotNullLazyValue<PsiTypeElement>() {
52       @NotNull
53       @Override
54       protected PsiTypeElement compute() {
55         PsiParameterStub stub = getStub();
56         String typeText = TypeInfo.createTypeText(stub.getType(false));
57         assert typeText != null : stub;
58         return new ClsTypeElementImpl(ClsParameterImpl.this, typeText, ClsTypeElementImpl.VARIANCE_NONE);
59       }
60     };
61   }
62
63   @Override
64   public PsiIdentifier getNameIdentifier() {
65     return null;
66   }
67
68   @Override
69   public String getName() {
70     return CachedValuesManager.getCachedValue(this, new CachedValueProvider<String>() {
71       @Nullable
72       @Override
73       public Result<String> compute() {
74         return Result.create(calcName(), 
75                              getContainingFile(),
76                              getContainingFile().getNavigationElement(),
77                              FileIndexFacade.getInstance(getProject()).getRootModificationTracker());
78       }
79     });
80   }
81
82   @Nullable 
83   private String calcName() {
84     PsiParameterStubImpl parameterStub = (PsiParameterStubImpl)getStub();
85     if (!parameterStub.isAutoGeneratedName()) {
86       return parameterStub.getName();
87     }
88     
89     if (DumbService.getInstance(getProject()).isDumb()) {
90       return null;
91     }
92
93     ClsMethodImpl method = (ClsMethodImpl)getDeclarationScope();
94     PsiMethod sourceMethod = method.getSourceMirrorMethod();
95     if (sourceMethod != null) {
96       assert sourceMethod != method : method;
97       return sourceMethod.getParameterList().getParameters()[getIndex()].getName();
98     }
99     
100     return getMirrorName();
101   }
102
103   public boolean isAutoGeneratedName() {
104     return ((PsiParameterStubImpl)getStub()).isAutoGeneratedName() &&
105            !DumbService.getInstance(getProject()).isDumb() &&
106            ((ClsMethodImpl)getDeclarationScope()).getSourceMirrorMethod() == null;
107   }
108
109   @Override
110   public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
111     throw new IncorrectOperationException(CAN_NOT_MODIFY_MESSAGE);
112   }
113
114   @Override
115   @NotNull
116   public PsiTypeElement getTypeElement() {
117     return myType.getValue();
118   }
119
120   @Override
121   @NotNull
122   public PsiType getType() {
123     return getTypeElement().getType();
124   }
125
126   @Override
127   @NotNull
128   public PsiModifierList getModifierList() {
129     final StubElement<PsiModifierList> child = getStub().findChildStubByType(JavaStubElementTypes.MODIFIER_LIST);
130     assert child != null;
131     return child.getPsi();
132   }
133
134   @Override
135   public boolean hasModifierProperty(@NotNull String name) {
136     return getModifierList().hasModifierProperty(name);
137   }
138
139   @Override
140   public PsiExpression getInitializer() {
141     return null;
142   }
143
144   @Override
145   public boolean hasInitializer() {
146     return false;
147   }
148
149   @Override
150   public Object computeConstantValue() {
151     return null;
152   }
153
154   @Override
155   public void normalizeDeclaration() throws IncorrectOperationException {
156   }
157
158   @Override
159   public void appendMirrorText(int indentLevel, @NotNull StringBuilder buffer) {
160     PsiAnnotation[] annotations = getModifierList().getAnnotations();
161     for (PsiAnnotation annotation : annotations) {
162       appendText(annotation, indentLevel, buffer);
163       buffer.append(' ');
164     }
165     appendText(getTypeElement(), indentLevel, buffer, " ");
166     buffer.append(getMirrorName());
167   }
168
169   private String getMirrorName() {
170     String mirrorName = myMirrorName;
171     if (mirrorName == null) {
172       // parameter name may depend on a name of a previous one in a same parameter list
173       synchronized (getParent()) {
174         mirrorName = myMirrorName;
175         if (mirrorName == null) {
176           myMirrorName = mirrorName = calcNiceParameterName();
177         }
178       }
179     }
180     return mirrorName;
181   }
182
183   private String calcNiceParameterName() {
184     String name = null;
185
186     PsiParameterStubImpl stub = (PsiParameterStubImpl)getStub();
187     if (!stub.isAutoGeneratedName() || DumbService.getInstance(getProject()).isDumb()) {
188       name = stub.getName();
189     }
190
191     if (name == null) {
192       name = "p";
193
194       JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(getProject());
195       String[] nameSuggestions = codeStyleManager.suggestCompiledParameterName(getType()).names;
196       if (nameSuggestions.length > 0 && nameSuggestions[0] != null) {
197         name = nameSuggestions[0];
198       }
199
200       String base = name;
201       int n = 0;
202       AttemptsLoop:
203       while (true) {
204         for (PsiParameter parameter : ((PsiParameterList)getParent()).getParameters()) {
205           if (parameter == this) break AttemptsLoop;
206           String prevName = ((ClsParameterImpl)parameter).getMirrorName();
207           if (name.equals(prevName)) {
208             name = base + (++n);
209             continue AttemptsLoop;
210           }
211         }
212       }
213     }
214
215     return name;
216   }
217
218   @Override
219   public void setMirror(@NotNull TreeElement element) throws InvalidMirrorException {
220     setMirrorCheckingType(element, null);
221
222     PsiParameter mirror = SourceTreeToPsiMap.treeToPsiNotNull(element);
223     setMirror(getModifierList(), mirror.getModifierList());
224     setMirror(getTypeElement(), mirror.getTypeElement());
225   }
226
227   @Override
228   public void accept(@NotNull PsiElementVisitor visitor) {
229     if (visitor instanceof JavaElementVisitor) {
230       ((JavaElementVisitor)visitor).visitParameter(this);
231     }
232     else {
233       visitor.visitElement(this);
234     }
235   }
236
237   @Override
238   @NotNull
239   public PsiElement getDeclarationScope() {
240     // only method parameters exist in compiled code
241     return getParent().getParent();
242   }
243
244   private int getIndex() {
245     final PsiParameterStub stub = getStub();
246     return stub.getParentStub().getChildrenStubs().indexOf(stub);
247   }
248
249   @Override
250   public boolean isVarArgs() {
251     final PsiParameterList paramList = (PsiParameterList)getParent();
252     final PsiMethod method = (PsiMethod)paramList.getParent();
253     return method.isVarArgs() && getIndex() == paramList.getParametersCount() - 1;
254   }
255
256   @Override
257   protected boolean isVisibilitySupported() {
258     return true;
259   }
260
261   @Override
262   public Icon getElementIcon(final int flags) {
263     final RowIcon baseIcon = createLayeredIcon(this, PlatformIcons.PARAMETER_ICON, 0);
264     return ElementPresentationUtil.addVisibilityIcon(this, flags, baseIcon);
265   }
266
267   @Override
268   @NotNull
269   public SearchScope getUseScope() {
270     return new LocalSearchScope(getDeclarationScope());
271   }
272
273   @Override
274   public String toString() {
275     return "PsiParameter";
276   }
277 }